ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.58
Committed: Thu May 14 19:20:13 2020 UTC (3 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R3
Changes since 2.57: +5 -2 lines
Log Message:
Added cumulative cache size limit (per loaded BSDF)

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.58 static const char RCSid[] = "$Id: bsdf.c,v 2.57 2019/03/08 03:42:12 greg Exp $";
3 greg 2.1 #endif
4     /*
5 greg 2.15 * bsdf.c
6     *
7     * Definitions for bidirectional scattering distribution functions.
8     *
9     * Created by Greg Ward on 1/10/11.
10     *
11     */
12    
13 greg 2.30 #define _USE_MATH_DEFINES
14 greg 2.15 #include <stdio.h>
15     #include <stdlib.h>
16 greg 2.31 #include <string.h>
17 greg 2.15 #include <math.h>
18 greg 2.32 #include <ctype.h>
19 greg 2.15 #include "ezxml.h"
20     #include "hilbert.h"
21     #include "bsdf.h"
22     #include "bsdf_m.h"
23     #include "bsdf_t.h"
24    
25     /* English ASCII strings corresponding to ennumerated errors */
26     const char *SDerrorEnglish[] = {
27     "No error",
28     "Memory error",
29     "File input/output error",
30     "File format error",
31     "Illegal argument",
32     "Invalid data",
33     "Unsupported feature",
34     "Internal program error",
35     "Unknown error"
36     };
37    
38 greg 2.44 /* Pointer to error list in preferred language */
39     const char **SDerrorList = SDerrorEnglish;
40    
41 greg 2.15 /* Additional information on last error (ASCII English) */
42     char SDerrorDetail[256];
43    
44 greg 2.42 /* Empty distribution for getCDist() calls that fail for some reason */
45     const SDCDst SDemptyCD;
46    
47 greg 2.15 /* Cache of loaded BSDFs */
48     struct SDCache_s *SDcacheList = NULL;
49    
50 greg 2.58 /* Retain BSDFs in cache list? */
51 greg 2.15 int SDretainSet = SDretainNone;
52    
53 greg 2.58 /* Maximum cache size for any given BSDF? */
54     unsigned long SDmaxCache = 0; /* 0 == unlimited */
55    
56 greg 2.44 /* Report any error to the indicated stream */
57 greg 2.15 SDError
58 greg 2.44 SDreportError(SDError ec, FILE *fp)
59 greg 2.15 {
60 greg 2.21 if (!ec)
61     return SDEnone;
62     if ((ec < SDEnone) | (ec > SDEunknown)) {
63     SDerrorDetail[0] = '\0';
64     ec = SDEunknown;
65     }
66 greg 2.15 if (fp == NULL)
67     return ec;
68 greg 2.44 fputs(SDerrorList[ec], fp);
69 greg 2.15 if (SDerrorDetail[0]) {
70     fputs(": ", fp);
71     fputs(SDerrorDetail, fp);
72     }
73     fputc('\n', fp);
74     if (fp != stderr)
75     fflush(fp);
76     return ec;
77     }
78    
79     static double
80     to_meters( /* return factor to convert given unit to meters */
81     const char *unit
82     )
83     {
84     if (unit == NULL) return(1.); /* safe assumption? */
85     if (!strcasecmp(unit, "Meter")) return(1.);
86     if (!strcasecmp(unit, "Foot")) return(.3048);
87     if (!strcasecmp(unit, "Inch")) return(.0254);
88     if (!strcasecmp(unit, "Centimeter")) return(.01);
89     if (!strcasecmp(unit, "Millimeter")) return(.001);
90     sprintf(SDerrorDetail, "Unknown dimensional unit '%s'", unit);
91     return(-1.);
92     }
93    
94     /* Load geometric dimensions and description (if any) */
95     static SDError
96 greg 2.43 SDloadGeometry(SDData *sd, ezxml_t wtl)
97 greg 2.15 {
98 greg 2.43 ezxml_t node, matl, geom;
99 greg 2.15 double cfact;
100 greg 2.43 const char *fmt = NULL, *mgfstr;
101 greg 2.15
102 greg 2.43 SDerrorDetail[0] = '\0';
103     sd->matn[0] = '\0'; sd->makr[0] = '\0';
104     sd->dim[0] = sd->dim[1] = sd->dim[2] = 0;
105     matl = ezxml_child(wtl, "Material");
106     if (matl != NULL) { /* get material info. */
107     if ((node = ezxml_child(matl, "Name")) != NULL) {
108     strncpy(sd->matn, ezxml_txt(node), SDnameLn);
109     if (sd->matn[SDnameLn-1])
110     strcpy(sd->matn+(SDnameLn-4), "...");
111     }
112     if ((node = ezxml_child(matl, "Manufacturer")) != NULL) {
113     strncpy(sd->makr, ezxml_txt(node), SDnameLn);
114     if (sd->makr[SDnameLn-1])
115     strcpy(sd->makr+(SDnameLn-4), "...");
116     }
117     if ((node = ezxml_child(matl, "Width")) != NULL)
118     sd->dim[0] = atof(ezxml_txt(node)) *
119     to_meters(ezxml_attr(node, "unit"));
120     if ((node = ezxml_child(matl, "Height")) != NULL)
121     sd->dim[1] = atof(ezxml_txt(node)) *
122     to_meters(ezxml_attr(node, "unit"));
123     if ((node = ezxml_child(matl, "Thickness")) != NULL)
124     sd->dim[2] = atof(ezxml_txt(node)) *
125     to_meters(ezxml_attr(node, "unit"));
126     if ((sd->dim[0] < 0) | (sd->dim[1] < 0) | (sd->dim[2] < 0)) {
127     if (!SDerrorDetail[0])
128     sprintf(SDerrorDetail, "Negative dimension in \"%s\"",
129     sd->name);
130     return SDEdata;
131     }
132     }
133     sd->mgf = NULL;
134     geom = ezxml_child(wtl, "Geometry");
135     if (geom == NULL) /* no actual geometry? */
136 greg 2.16 return SDEnone;
137 greg 2.43 fmt = ezxml_attr(geom, "format");
138     if (fmt != NULL && strcasecmp(fmt, "MGF")) {
139     sprintf(SDerrorDetail,
140     "Unrecognized geometry format '%s' in \"%s\"",
141     fmt, sd->name);
142     return SDEsupport;
143 greg 2.17 }
144 greg 2.43 if ((node = ezxml_child(geom, "MGFblock")) == NULL ||
145     (mgfstr = ezxml_txt(node)) == NULL)
146 greg 2.15 return SDEnone;
147 greg 2.32 while (isspace(*mgfstr))
148     ++mgfstr;
149     if (!*mgfstr)
150     return SDEnone;
151 greg 2.43 cfact = to_meters(ezxml_attr(node, "unit"));
152 greg 2.38 if (cfact <= 0)
153     return SDEformat;
154 greg 2.16 sd->mgf = (char *)malloc(strlen(mgfstr)+32);
155     if (sd->mgf == NULL) {
156 greg 2.15 strcpy(SDerrorDetail, "Out of memory in SDloadGeometry");
157     return SDEmemory;
158     }
159     if (cfact < 0.99 || cfact > 1.01)
160 greg 2.16 sprintf(sd->mgf, "xf -s %.5f\n%s\nxf\n", cfact, mgfstr);
161 greg 2.15 else
162 greg 2.16 strcpy(sd->mgf, mgfstr);
163 greg 2.15 return SDEnone;
164     }
165    
166     /* Load a BSDF struct from the given file (free first and keep name) */
167     SDError
168     SDloadFile(SDData *sd, const char *fname)
169     {
170     SDError lastErr;
171 greg 2.16 ezxml_t fl, wtl;
172 greg 2.15
173     if ((sd == NULL) | (fname == NULL || !*fname))
174     return SDEargument;
175     /* free old data, keeping name */
176     SDfreeBSDF(sd);
177     /* parse XML file */
178     fl = ezxml_parse_file(fname);
179     if (fl == NULL) {
180     sprintf(SDerrorDetail, "Cannot open BSDF \"%s\"", fname);
181     return SDEfile;
182     }
183     if (ezxml_error(fl)[0]) {
184     sprintf(SDerrorDetail, "BSDF \"%s\" %s", fname, ezxml_error(fl));
185     ezxml_free(fl);
186     return SDEformat;
187     }
188 greg 2.16 if (strcmp(ezxml_name(fl), "WindowElement")) {
189     sprintf(SDerrorDetail,
190     "BSDF \"%s\": top level node not 'WindowElement'",
191     sd->name);
192     ezxml_free(fl);
193     return SDEformat;
194     }
195 greg 2.43 wtl = ezxml_child(fl, "FileType");
196     if (wtl != NULL && strcmp(ezxml_txt(wtl), "BSDF")) {
197     sprintf(SDerrorDetail,
198     "XML \"%s\": wrong FileType (must be 'BSDF')",
199     sd->name);
200     ezxml_free(fl);
201     return SDEformat;
202     }
203 greg 2.16 wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
204     if (wtl == NULL) {
205 greg 2.50 sprintf(SDerrorDetail, "BSDF \"%s\": no optical layers",
206 greg 2.16 sd->name);
207     ezxml_free(fl);
208     return SDEformat;
209     }
210 greg 2.15 /* load geometry if present */
211 greg 2.43 lastErr = SDloadGeometry(sd, wtl);
212 greg 2.36 if (lastErr) {
213     ezxml_free(fl);
214 greg 2.15 return lastErr;
215 greg 2.36 }
216 greg 2.15 /* try loading variable resolution data */
217 greg 2.16 lastErr = SDloadTre(sd, wtl);
218 greg 2.15 /* check our result */
219 greg 2.29 if (lastErr == SDEsupport) /* try matrix BSDF if not tree data */
220 greg 2.16 lastErr = SDloadMtx(sd, wtl);
221 greg 2.29
222 greg 2.15 /* done with XML file */
223     ezxml_free(fl);
224 greg 2.16
225     if (lastErr) { /* was there a load error? */
226     SDfreeBSDF(sd);
227     return lastErr;
228     }
229     /* remove any insignificant components */
230     if (sd->rf != NULL && sd->rf->maxHemi <= .001) {
231     SDfreeSpectralDF(sd->rf); sd->rf = NULL;
232     }
233     if (sd->rb != NULL && sd->rb->maxHemi <= .001) {
234     SDfreeSpectralDF(sd->rb); sd->rb = NULL;
235     }
236     if (sd->tf != NULL && sd->tf->maxHemi <= .001) {
237     SDfreeSpectralDF(sd->tf); sd->tf = NULL;
238     }
239 greg 2.42 if (sd->tb != NULL && sd->tb->maxHemi <= .001) {
240     SDfreeSpectralDF(sd->tb); sd->tb = NULL;
241     }
242 greg 2.16 /* return success */
243     return SDEnone;
244 greg 2.15 }
245    
246     /* Allocate new spectral distribution function */
247     SDSpectralDF *
248     SDnewSpectralDF(int nc)
249     {
250     SDSpectralDF *df;
251    
252     if (nc <= 0) {
253     strcpy(SDerrorDetail, "Zero component spectral DF request");
254     return NULL;
255     }
256     df = (SDSpectralDF *)malloc(sizeof(SDSpectralDF) +
257     (nc-1)*sizeof(SDComponent));
258     if (df == NULL) {
259     sprintf(SDerrorDetail,
260     "Cannot allocate %d component spectral DF", nc);
261     return NULL;
262     }
263     df->minProjSA = .0;
264     df->maxHemi = .0;
265     df->ncomp = nc;
266     memset(df->comp, 0, nc*sizeof(SDComponent));
267     return df;
268     }
269    
270 greg 2.37 /* Add component(s) to spectral distribution function */
271     SDSpectralDF *
272     SDaddComponent(SDSpectralDF *odf, int nadd)
273     {
274     SDSpectralDF *df;
275    
276     if (odf == NULL)
277     return SDnewSpectralDF(nadd);
278     if (nadd <= 0)
279     return odf;
280     df = (SDSpectralDF *)realloc(odf, sizeof(SDSpectralDF) +
281     (odf->ncomp+nadd-1)*sizeof(SDComponent));
282     if (df == NULL) {
283     sprintf(SDerrorDetail,
284     "Cannot add %d component(s) to spectral DF", nadd);
285     SDfreeSpectralDF(odf);
286     return NULL;
287     }
288     memset(df->comp+df->ncomp, 0, nadd*sizeof(SDComponent));
289     df->ncomp += nadd;
290     return df;
291     }
292    
293 greg 2.15 /* Free cached cumulative distributions for BSDF component */
294     void
295     SDfreeCumulativeCache(SDSpectralDF *df)
296     {
297     int n;
298     SDCDst *cdp;
299    
300     if (df == NULL)
301     return;
302     for (n = df->ncomp; n-- > 0; )
303     while ((cdp = df->comp[n].cdList) != NULL) {
304     df->comp[n].cdList = cdp->next;
305     free(cdp);
306     }
307     }
308    
309     /* Free a spectral distribution function */
310     void
311     SDfreeSpectralDF(SDSpectralDF *df)
312     {
313     int n;
314    
315     if (df == NULL)
316     return;
317     SDfreeCumulativeCache(df);
318     for (n = df->ncomp; n-- > 0; )
319 greg 2.34 if (df->comp[n].dist != NULL)
320     (*df->comp[n].func->freeSC)(df->comp[n].dist);
321 greg 2.15 free(df);
322     }
323    
324     /* Shorten file path to useable BSDF name, removing suffix */
325     void
326 greg 2.16 SDclipName(char *res, const char *fname)
327 greg 2.15 {
328     const char *cp, *dot = NULL;
329    
330     for (cp = fname; *cp; cp++)
331     if (*cp == '.')
332     dot = cp;
333     if ((dot == NULL) | (dot < fname+2))
334     dot = cp;
335     if (dot - fname >= SDnameLn)
336     fname = dot - SDnameLn + 1;
337     while (fname < dot)
338     *res++ = *fname++;
339     *res = '\0';
340     }
341    
342     /* Initialize an unused BSDF struct (simply clears to zeroes) */
343     void
344 greg 2.20 SDclearBSDF(SDData *sd, const char *fname)
345 greg 2.15 {
346 greg 2.20 if (sd == NULL)
347     return;
348     memset(sd, 0, sizeof(SDData));
349     if (fname == NULL)
350     return;
351     SDclipName(sd->name, fname);
352 greg 2.15 }
353    
354     /* Free data associated with BSDF struct */
355     void
356     SDfreeBSDF(SDData *sd)
357     {
358     if (sd == NULL)
359     return;
360     if (sd->mgf != NULL) {
361     free(sd->mgf);
362     sd->mgf = NULL;
363     }
364     if (sd->rf != NULL) {
365     SDfreeSpectralDF(sd->rf);
366     sd->rf = NULL;
367     }
368     if (sd->rb != NULL) {
369     SDfreeSpectralDF(sd->rb);
370     sd->rb = NULL;
371     }
372     if (sd->tf != NULL) {
373     SDfreeSpectralDF(sd->tf);
374     sd->tf = NULL;
375     }
376 greg 2.42 if (sd->tb != NULL) {
377     SDfreeSpectralDF(sd->tb);
378     sd->tb = NULL;
379     }
380 greg 2.15 sd->rLambFront.cieY = .0;
381 greg 2.16 sd->rLambFront.spec.flags = 0;
382 greg 2.15 sd->rLambBack.cieY = .0;
383 greg 2.16 sd->rLambBack.spec.flags = 0;
384 greg 2.15 sd->tLamb.cieY = .0;
385 greg 2.16 sd->tLamb.spec.flags = 0;
386 greg 2.15 }
387    
388     /* Find writeable BSDF by name, or allocate new cache entry if absent */
389     SDData *
390     SDgetCache(const char *bname)
391     {
392     struct SDCache_s *sdl;
393     char sdnam[SDnameLn];
394    
395     if (bname == NULL)
396     return NULL;
397    
398     SDclipName(sdnam, bname);
399     for (sdl = SDcacheList; sdl != NULL; sdl = sdl->next)
400     if (!strcmp(sdl->bsdf.name, sdnam)) {
401     sdl->refcnt++;
402     return &sdl->bsdf;
403     }
404    
405     sdl = (struct SDCache_s *)calloc(1, sizeof(struct SDCache_s));
406     if (sdl == NULL)
407     return NULL;
408    
409     strcpy(sdl->bsdf.name, sdnam);
410     sdl->next = SDcacheList;
411     SDcacheList = sdl;
412    
413 greg 2.21 sdl->refcnt = 1;
414 greg 2.15 return &sdl->bsdf;
415     }
416    
417     /* Get loaded BSDF from cache (or load and cache it on first call) */
418     /* Report any problem to stderr and return NULL on failure */
419     const SDData *
420     SDcacheFile(const char *fname)
421     {
422     SDData *sd;
423     SDError ec;
424    
425     if (fname == NULL || !*fname)
426     return NULL;
427     SDerrorDetail[0] = '\0';
428 greg 2.52 /* PLACE MUTEX LOCK HERE FOR THREAD-SAFE */
429 greg 2.15 if ((sd = SDgetCache(fname)) == NULL) {
430 greg 2.44 SDreportError(SDEmemory, stderr);
431 greg 2.15 return NULL;
432     }
433     if (!SDisLoaded(sd) && (ec = SDloadFile(sd, fname))) {
434 greg 2.44 SDreportError(ec, stderr);
435 greg 2.15 SDfreeCache(sd);
436 greg 2.52 sd = NULL;
437 greg 2.15 }
438 greg 2.52 /* END MUTEX LOCK */
439 greg 2.15 return sd;
440     }
441    
442     /* Free a BSDF from our cache (clear all if NULL) */
443     void
444     SDfreeCache(const SDData *sd)
445     {
446     struct SDCache_s *sdl, *sdLast = NULL;
447    
448     if (sd == NULL) { /* free entire list */
449     while ((sdl = SDcacheList) != NULL) {
450     SDcacheList = sdl->next;
451     SDfreeBSDF(&sdl->bsdf);
452     free(sdl);
453     }
454     return;
455     }
456     for (sdl = SDcacheList; sdl != NULL; sdl = (sdLast=sdl)->next)
457     if (&sdl->bsdf == sd)
458     break;
459 greg 2.21 if (sdl == NULL || (sdl->refcnt -= (sdl->refcnt > 0)))
460 greg 2.15 return; /* missing or still in use */
461     /* keep unreferenced data? */
462     if (SDisLoaded(sd) && SDretainSet) {
463     if (SDretainSet == SDretainAll)
464     return; /* keep everything */
465     /* else free cumulative data */
466     SDfreeCumulativeCache(sd->rf);
467     SDfreeCumulativeCache(sd->rb);
468     SDfreeCumulativeCache(sd->tf);
469 greg 2.42 SDfreeCumulativeCache(sd->tb);
470 greg 2.15 return;
471     }
472     /* remove from list and free */
473     if (sdLast == NULL)
474     SDcacheList = sdl->next;
475     else
476     sdLast->next = sdl->next;
477     SDfreeBSDF(&sdl->bsdf);
478     free(sdl);
479     }
480    
481     /* Sample an individual BSDF component */
482     SDError
483 greg 2.24 SDsampComponent(SDValue *sv, FVECT ioVec, double randX, SDComponent *sdc)
484 greg 2.15 {
485     float coef[SDmaxCh];
486     SDError ec;
487 greg 2.24 FVECT inVec;
488 greg 2.15 const SDCDst *cd;
489     double d;
490     int n;
491     /* check arguments */
492 greg 2.24 if ((sv == NULL) | (ioVec == NULL) | (sdc == NULL))
493 greg 2.15 return SDEargument;
494     /* get cumulative distribution */
495 greg 2.24 VCOPY(inVec, ioVec);
496 greg 2.45 sv->cieY = 0;
497 greg 2.15 cd = (*sdc->func->getCDist)(inVec, sdc);
498 greg 2.45 if (cd != NULL)
499     sv->cieY = cd->cTotal;
500     if (sv->cieY <= 1e-6) { /* nothing to sample? */
501 greg 2.15 sv->spec = c_dfcolor;
502 greg 2.53 memset(ioVec, 0, sizeof(FVECT));
503 greg 2.15 return SDEnone;
504     }
505     /* compute sample direction */
506 greg 2.24 ec = (*sdc->func->sampCDist)(ioVec, randX, cd);
507 greg 2.15 if (ec)
508     return ec;
509     /* get BSDF color */
510 greg 2.25 n = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc);
511 greg 2.15 if (n <= 0) {
512     strcpy(SDerrorDetail, "BSDF sample value error");
513     return SDEinternal;
514     }
515     sv->spec = sdc->cspec[0];
516     d = coef[0];
517     while (--n) {
518     c_cmix(&sv->spec, d, &sv->spec, coef[n], &sdc->cspec[n]);
519     d += coef[n];
520     }
521 greg 2.55 c_ccvt(&sv->spec, C_CSXY); /* make sure (x,y) is set */
522 greg 2.15 return SDEnone;
523     }
524    
525     #define MS_MAXDIM 15
526    
527     /* Convert 1-dimensional random variable to N-dimensional */
528     void
529     SDmultiSamp(double t[], int n, double randX)
530     {
531     unsigned nBits;
532     double scale;
533     bitmask_t ndx, coord[MS_MAXDIM];
534 greg 2.41
535     if (n <= 0) /* check corner cases */
536     return;
537     if (randX < 0) randX = 0;
538     else if (randX >= 1.) randX = 0.999999999999999;
539     if (n == 1) {
540     t[0] = randX;
541     return;
542     }
543 greg 2.15 while (n > MS_MAXDIM) /* punt for higher dimensions */
544 greg 2.19 t[--n] = rand()*(1./(RAND_MAX+.5));
545 greg 2.15 nBits = (8*sizeof(bitmask_t) - 1) / n;
546     ndx = randX * (double)((bitmask_t)1 << (nBits*n));
547     /* get coordinate on Hilbert curve */
548     hilbert_i2c(n, nBits, ndx, coord);
549     /* convert back to [0,1) range */
550     scale = 1. / (double)((bitmask_t)1 << nBits);
551     while (n--)
552 greg 2.19 t[n] = scale * ((double)coord[n] + rand()*(1./(RAND_MAX+.5)));
553 greg 2.15 }
554    
555     #undef MS_MAXDIM
556    
557     /* Generate diffuse hemispherical sample */
558     static void
559     SDdiffuseSamp(FVECT outVec, int outFront, double randX)
560     {
561     /* convert to position on hemisphere */
562     SDmultiSamp(outVec, 2, randX);
563     SDsquare2disk(outVec, outVec[0], outVec[1]);
564     outVec[2] = 1. - outVec[0]*outVec[0] - outVec[1]*outVec[1];
565 greg 2.48 outVec[2] = sqrt(outVec[2]*(outVec[2]>0));
566 greg 2.15 if (!outFront) /* going out back? */
567     outVec[2] = -outVec[2];
568     }
569    
570     /* Query projected solid angle coverage for non-diffuse BSDF direction */
571     SDError
572 greg 2.22 SDsizeBSDF(double *projSA, const FVECT v1, const RREAL *v2,
573     int qflags, const SDData *sd)
574 greg 2.15 {
575 greg 2.23 SDSpectralDF *rdf, *tdf;
576 greg 2.15 SDError ec;
577     int i;
578     /* check arguments */
579 greg 2.26 if ((projSA == NULL) | (v1 == NULL) | (sd == NULL))
580 greg 2.15 return SDEargument;
581     /* initialize extrema */
582 greg 2.17 switch (qflags) {
583 greg 2.15 case SDqueryMax:
584     projSA[0] = .0;
585     break;
586     case SDqueryMin+SDqueryMax:
587     projSA[1] = .0;
588     /* fall through */
589     case SDqueryMin:
590     projSA[0] = 10.;
591     break;
592     case 0:
593     return SDEargument;
594     }
595 greg 2.42 if (v1[2] > 0) { /* front surface query? */
596 greg 2.15 rdf = sd->rf;
597 greg 2.42 tdf = (sd->tf != NULL) ? sd->tf : sd->tb;
598     } else {
599 greg 2.15 rdf = sd->rb;
600 greg 2.42 tdf = (sd->tb != NULL) ? sd->tb : sd->tf;
601     }
602 greg 2.51 if (v2 != NULL) { /* bidirectional? */
603 greg 2.26 if (v1[2] > 0 ^ v2[2] > 0)
604     rdf = NULL;
605     else
606     tdf = NULL;
607 greg 2.51 }
608 greg 2.15 ec = SDEdata; /* run through components */
609     for (i = (rdf==NULL) ? 0 : rdf->ncomp; i--; ) {
610 greg 2.22 ec = (*rdf->comp[i].func->queryProjSA)(projSA, v1, v2,
611 greg 2.25 qflags, &rdf->comp[i]);
612 greg 2.15 if (ec)
613     return ec;
614     }
615 greg 2.23 for (i = (tdf==NULL) ? 0 : tdf->ncomp; i--; ) {
616     ec = (*tdf->comp[i].func->queryProjSA)(projSA, v1, v2,
617 greg 2.25 qflags, &tdf->comp[i]);
618 greg 2.15 if (ec)
619     return ec;
620     }
621 greg 2.17 if (ec) { /* all diffuse? */
622     projSA[0] = M_PI;
623     if (qflags == SDqueryMin+SDqueryMax)
624     projSA[1] = M_PI;
625 greg 2.46 } else if (qflags == SDqueryMin+SDqueryMax && projSA[0] > projSA[1])
626     projSA[0] = projSA[1];
627 greg 2.17 return SDEnone;
628 greg 2.15 }
629    
630     /* Return BSDF for the given incident and scattered ray vectors */
631     SDError
632     SDevalBSDF(SDValue *sv, const FVECT outVec, const FVECT inVec, const SDData *sd)
633     {
634     int inFront, outFront;
635     SDSpectralDF *sdf;
636     float coef[SDmaxCh];
637     int nch, i;
638     /* check arguments */
639     if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL))
640     return SDEargument;
641     /* whose side are we on? */
642 greg 2.23 inFront = (inVec[2] > 0);
643     outFront = (outVec[2] > 0);
644 greg 2.15 /* start with diffuse portion */
645     if (inFront & outFront) {
646     *sv = sd->rLambFront;
647     sdf = sd->rf;
648     } else if (!(inFront | outFront)) {
649     *sv = sd->rLambBack;
650     sdf = sd->rb;
651 greg 2.57 } else if (inFront) {
652 greg 2.42 *sv = sd->tLamb;
653     sdf = (sd->tf != NULL) ? sd->tf : sd->tb;
654 greg 2.57 } else /* outFront & !inFront */ {
655 greg 2.15 *sv = sd->tLamb;
656 greg 2.42 sdf = (sd->tb != NULL) ? sd->tb : sd->tf;
657 greg 2.15 }
658     sv->cieY *= 1./M_PI;
659     /* add non-diffuse components */
660     i = (sdf != NULL) ? sdf->ncomp : 0;
661     while (i-- > 0) {
662     nch = (*sdf->comp[i].func->getBSDFs)(coef, outVec, inVec,
663 greg 2.25 &sdf->comp[i]);
664 greg 2.15 while (nch-- > 0) {
665     c_cmix(&sv->spec, sv->cieY, &sv->spec,
666     coef[nch], &sdf->comp[i].cspec[nch]);
667     sv->cieY += coef[nch];
668     }
669     }
670 greg 2.55 c_ccvt(&sv->spec, C_CSXY); /* make sure (x,y) is set */
671 greg 2.15 return SDEnone;
672     }
673    
674     /* Compute directional hemispherical scattering at this incident angle */
675     double
676     SDdirectHemi(const FVECT inVec, int sflags, const SDData *sd)
677     {
678     double hsum;
679 greg 2.42 SDSpectralDF *rdf, *tdf;
680 greg 2.15 const SDCDst *cd;
681     int i;
682     /* check arguments */
683     if ((inVec == NULL) | (sd == NULL))
684     return .0;
685     /* gather diffuse components */
686 greg 2.23 if (inVec[2] > 0) {
687 greg 2.15 hsum = sd->rLambFront.cieY;
688     rdf = sd->rf;
689 greg 2.42 tdf = (sd->tf != NULL) ? sd->tf : sd->tb;
690 greg 2.15 } else /* !inFront */ {
691     hsum = sd->rLambBack.cieY;
692     rdf = sd->rb;
693 greg 2.42 tdf = (sd->tb != NULL) ? sd->tb : sd->tf;
694 greg 2.15 }
695     if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
696     hsum = .0;
697     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
698     hsum += sd->tLamb.cieY;
699     /* gather non-diffuse components */
700 greg 2.42 i = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) &
701     (rdf != NULL)) ? rdf->ncomp : 0;
702 greg 2.15 while (i-- > 0) { /* non-diffuse reflection */
703     cd = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
704     if (cd != NULL)
705     hsum += cd->cTotal;
706     }
707 greg 2.42 i = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) &
708     (tdf != NULL)) ? tdf->ncomp : 0;
709 greg 2.15 while (i-- > 0) { /* non-diffuse transmission */
710 greg 2.42 cd = (*tdf->comp[i].func->getCDist)(inVec, &tdf->comp[i]);
711 greg 2.15 if (cd != NULL)
712     hsum += cd->cTotal;
713     }
714     return hsum;
715     }
716    
717     /* Sample BSDF direction based on the given random variable */
718     SDError
719 greg 2.24 SDsampBSDF(SDValue *sv, FVECT ioVec, double randX, int sflags, const SDData *sd)
720 greg 2.15 {
721     SDError ec;
722 greg 2.24 FVECT inVec;
723 greg 2.15 int inFront;
724 greg 2.42 SDSpectralDF *rdf, *tdf;
725 greg 2.15 double rdiff;
726     float coef[SDmaxCh];
727     int i, j, n, nr;
728     SDComponent *sdc;
729     const SDCDst **cdarr = NULL;
730     /* check arguments */
731 greg 2.24 if ((sv == NULL) | (ioVec == NULL) | (sd == NULL) |
732 greg 2.23 (randX < 0) | (randX >= 1.))
733 greg 2.15 return SDEargument;
734     /* whose side are we on? */
735 greg 2.24 VCOPY(inVec, ioVec);
736 greg 2.23 inFront = (inVec[2] > 0);
737 greg 2.15 /* remember diffuse portions */
738     if (inFront) {
739     *sv = sd->rLambFront;
740     rdf = sd->rf;
741 greg 2.42 tdf = (sd->tf != NULL) ? sd->tf : sd->tb;
742 greg 2.15 } else /* !inFront */ {
743     *sv = sd->rLambBack;
744     rdf = sd->rb;
745 greg 2.42 tdf = (sd->tb != NULL) ? sd->tb : sd->tf;
746 greg 2.15 }
747     if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
748     sv->cieY = .0;
749     rdiff = sv->cieY;
750     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
751     sv->cieY += sd->tLamb.cieY;
752     /* gather non-diffuse components */
753 greg 2.42 i = nr = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) &
754     (rdf != NULL)) ? rdf->ncomp : 0;
755     j = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) &
756     (tdf != NULL)) ? tdf->ncomp : 0;
757 greg 2.15 n = i + j;
758     if (n > 0 && (cdarr = (const SDCDst **)malloc(n*sizeof(SDCDst *))) == NULL)
759     return SDEmemory;
760     while (j-- > 0) { /* non-diffuse transmission */
761 greg 2.42 cdarr[i+j] = (*tdf->comp[j].func->getCDist)(inVec, &tdf->comp[j]);
762 greg 2.45 if (cdarr[i+j] == NULL)
763     cdarr[i+j] = &SDemptyCD;
764 greg 2.15 sv->cieY += cdarr[i+j]->cTotal;
765     }
766     while (i-- > 0) { /* non-diffuse reflection */
767     cdarr[i] = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
768 greg 2.45 if (cdarr[i] == NULL)
769     cdarr[i] = &SDemptyCD;
770 greg 2.49 sv->cieY += cdarr[i]->cTotal;
771 greg 2.15 }
772 greg 2.35 if (sv->cieY <= 1e-6) { /* anything to sample? */
773 greg 2.15 sv->cieY = .0;
774 greg 2.53 memset(ioVec, 0, sizeof(FVECT));
775 greg 2.15 return SDEnone;
776     }
777     /* scale random variable */
778     randX *= sv->cieY;
779     /* diffuse reflection? */
780     if (randX < rdiff) {
781 greg 2.24 SDdiffuseSamp(ioVec, inFront, randX/rdiff);
782 greg 2.15 goto done;
783     }
784     randX -= rdiff;
785     /* diffuse transmission? */
786     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) {
787     if (randX < sd->tLamb.cieY) {
788     sv->spec = sd->tLamb.spec;
789 greg 2.24 SDdiffuseSamp(ioVec, !inFront, randX/sd->tLamb.cieY);
790 greg 2.15 goto done;
791     }
792     randX -= sd->tLamb.cieY;
793     }
794     /* else one of cumulative dist. */
795 greg 2.56 for (i = 0; i < n && randX >= cdarr[i]->cTotal; i++)
796 greg 2.15 randX -= cdarr[i]->cTotal;
797     if (i >= n)
798     return SDEinternal;
799     /* compute sample direction */
800 greg 2.42 sdc = (i < nr) ? &rdf->comp[i] : &tdf->comp[i-nr];
801 greg 2.24 ec = (*sdc->func->sampCDist)(ioVec, randX/cdarr[i]->cTotal, cdarr[i]);
802 greg 2.15 if (ec)
803     return ec;
804     /* compute color */
805 greg 2.25 j = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc);
806 greg 2.15 if (j <= 0) {
807     sprintf(SDerrorDetail, "BSDF \"%s\" sampling value error",
808     sd->name);
809     return SDEinternal;
810     }
811     sv->spec = sdc->cspec[0];
812     rdiff = coef[0];
813     while (--j) {
814     c_cmix(&sv->spec, rdiff, &sv->spec, coef[j], &sdc->cspec[j]);
815     rdiff += coef[j];
816     }
817     done:
818     if (cdarr != NULL)
819     free(cdarr);
820 greg 2.55 c_ccvt(&sv->spec, C_CSXY); /* make sure (x,y) is set */
821 greg 2.15 return SDEnone;
822     }
823    
824     /* Compute World->BSDF transform from surface normal and up (Y) vector */
825     SDError
826     SDcompXform(RREAL vMtx[3][3], const FVECT sNrm, const FVECT uVec)
827     {
828     if ((vMtx == NULL) | (sNrm == NULL) | (uVec == NULL))
829     return SDEargument;
830     VCOPY(vMtx[2], sNrm);
831 greg 2.23 if (normalize(vMtx[2]) == 0)
832 greg 2.15 return SDEargument;
833     fcross(vMtx[0], uVec, vMtx[2]);
834 greg 2.23 if (normalize(vMtx[0]) == 0)
835 greg 2.15 return SDEargument;
836     fcross(vMtx[1], vMtx[2], vMtx[0]);
837     return SDEnone;
838     }
839    
840     /* Compute inverse transform */
841     SDError
842     SDinvXform(RREAL iMtx[3][3], RREAL vMtx[3][3])
843     {
844     RREAL mTmp[3][3];
845     double d;
846    
847     if ((iMtx == NULL) | (vMtx == NULL))
848     return SDEargument;
849     /* compute determinant */
850     mTmp[0][0] = vMtx[2][2]*vMtx[1][1] - vMtx[2][1]*vMtx[1][2];
851     mTmp[0][1] = vMtx[2][1]*vMtx[0][2] - vMtx[2][2]*vMtx[0][1];
852     mTmp[0][2] = vMtx[1][2]*vMtx[0][1] - vMtx[1][1]*vMtx[0][2];
853     d = vMtx[0][0]*mTmp[0][0] + vMtx[1][0]*mTmp[0][1] + vMtx[2][0]*mTmp[0][2];
854 greg 2.23 if (d == 0) {
855 greg 2.15 strcpy(SDerrorDetail, "Zero determinant in matrix inversion");
856     return SDEargument;
857     }
858     d = 1./d; /* invert matrix */
859     mTmp[0][0] *= d; mTmp[0][1] *= d; mTmp[0][2] *= d;
860     mTmp[1][0] = d*(vMtx[2][0]*vMtx[1][2] - vMtx[2][2]*vMtx[1][0]);
861     mTmp[1][1] = d*(vMtx[2][2]*vMtx[0][0] - vMtx[2][0]*vMtx[0][2]);
862     mTmp[1][2] = d*(vMtx[1][0]*vMtx[0][2] - vMtx[1][2]*vMtx[0][0]);
863     mTmp[2][0] = d*(vMtx[2][1]*vMtx[1][0] - vMtx[2][0]*vMtx[1][1]);
864     mTmp[2][1] = d*(vMtx[2][0]*vMtx[0][1] - vMtx[2][1]*vMtx[0][0]);
865     mTmp[2][2] = d*(vMtx[1][1]*vMtx[0][0] - vMtx[1][0]*vMtx[0][1]);
866     memcpy(iMtx, mTmp, sizeof(mTmp));
867     return SDEnone;
868     }
869    
870     /* Transform and normalize direction (column) vector */
871     SDError
872     SDmapDir(FVECT resVec, RREAL vMtx[3][3], const FVECT inpVec)
873     {
874     FVECT vTmp;
875    
876     if ((resVec == NULL) | (inpVec == NULL))
877     return SDEargument;
878     if (vMtx == NULL) { /* assume they just want to normalize */
879     if (resVec != inpVec)
880     VCOPY(resVec, inpVec);
881 greg 2.23 return (normalize(resVec) > 0) ? SDEnone : SDEargument;
882 greg 2.15 }
883     vTmp[0] = DOT(vMtx[0], inpVec);
884     vTmp[1] = DOT(vMtx[1], inpVec);
885     vTmp[2] = DOT(vMtx[2], inpVec);
886 greg 2.23 if (normalize(vTmp) == 0)
887 greg 2.15 return SDEargument;
888     VCOPY(resVec, vTmp);
889     return SDEnone;
890     }