ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.61
Committed: Tue Dec 7 23:49:50 2021 UTC (2 years, 5 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.60: +11 -11 lines
Log Message:
fix: Repaired issue with reciprocity and BSDF sampling, thanks to Jon Sargent

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.61 static const char RCSid[] = "$Id: bsdf.c,v 2.60 2021/04/28 00:59:10 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 greg 2.60 else if (*cp == '/')
334     dot = NULL;
335 greg 2.15 if ((dot == NULL) | (dot < fname+2))
336     dot = cp;
337     if (dot - fname >= SDnameLn)
338     fname = dot - SDnameLn + 1;
339     while (fname < dot)
340     *res++ = *fname++;
341     *res = '\0';
342     }
343    
344     /* Initialize an unused BSDF struct (simply clears to zeroes) */
345     void
346 greg 2.20 SDclearBSDF(SDData *sd, const char *fname)
347 greg 2.15 {
348 greg 2.20 if (sd == NULL)
349     return;
350     memset(sd, 0, sizeof(SDData));
351     if (fname == NULL)
352     return;
353     SDclipName(sd->name, fname);
354 greg 2.15 }
355    
356     /* Free data associated with BSDF struct */
357     void
358     SDfreeBSDF(SDData *sd)
359     {
360     if (sd == NULL)
361     return;
362     if (sd->mgf != NULL) {
363     free(sd->mgf);
364     sd->mgf = NULL;
365     }
366     if (sd->rf != NULL) {
367     SDfreeSpectralDF(sd->rf);
368     sd->rf = NULL;
369     }
370     if (sd->rb != NULL) {
371     SDfreeSpectralDF(sd->rb);
372     sd->rb = NULL;
373     }
374     if (sd->tf != NULL) {
375     SDfreeSpectralDF(sd->tf);
376     sd->tf = NULL;
377     }
378 greg 2.42 if (sd->tb != NULL) {
379     SDfreeSpectralDF(sd->tb);
380     sd->tb = NULL;
381     }
382 greg 2.15 sd->rLambFront.cieY = .0;
383 greg 2.16 sd->rLambFront.spec.flags = 0;
384 greg 2.15 sd->rLambBack.cieY = .0;
385 greg 2.16 sd->rLambBack.spec.flags = 0;
386 greg 2.59 sd->tLambFront.cieY = .0;
387     sd->tLambFront.spec.flags = 0;
388     sd->tLambBack.cieY = .0;
389     sd->tLambBack.spec.flags = 0;
390 greg 2.15 }
391    
392     /* Find writeable BSDF by name, or allocate new cache entry if absent */
393     SDData *
394     SDgetCache(const char *bname)
395     {
396     struct SDCache_s *sdl;
397     char sdnam[SDnameLn];
398    
399     if (bname == NULL)
400     return NULL;
401    
402     SDclipName(sdnam, bname);
403     for (sdl = SDcacheList; sdl != NULL; sdl = sdl->next)
404     if (!strcmp(sdl->bsdf.name, sdnam)) {
405     sdl->refcnt++;
406     return &sdl->bsdf;
407     }
408    
409     sdl = (struct SDCache_s *)calloc(1, sizeof(struct SDCache_s));
410     if (sdl == NULL)
411     return NULL;
412    
413     strcpy(sdl->bsdf.name, sdnam);
414     sdl->next = SDcacheList;
415     SDcacheList = sdl;
416    
417 greg 2.21 sdl->refcnt = 1;
418 greg 2.15 return &sdl->bsdf;
419     }
420    
421     /* Get loaded BSDF from cache (or load and cache it on first call) */
422     /* Report any problem to stderr and return NULL on failure */
423     const SDData *
424     SDcacheFile(const char *fname)
425     {
426     SDData *sd;
427     SDError ec;
428    
429     if (fname == NULL || !*fname)
430     return NULL;
431     SDerrorDetail[0] = '\0';
432 greg 2.52 /* PLACE MUTEX LOCK HERE FOR THREAD-SAFE */
433 greg 2.15 if ((sd = SDgetCache(fname)) == NULL) {
434 greg 2.44 SDreportError(SDEmemory, stderr);
435 greg 2.15 return NULL;
436     }
437     if (!SDisLoaded(sd) && (ec = SDloadFile(sd, fname))) {
438 greg 2.44 SDreportError(ec, stderr);
439 greg 2.15 SDfreeCache(sd);
440 greg 2.52 sd = NULL;
441 greg 2.15 }
442 greg 2.52 /* END MUTEX LOCK */
443 greg 2.15 return sd;
444     }
445    
446     /* Free a BSDF from our cache (clear all if NULL) */
447     void
448     SDfreeCache(const SDData *sd)
449     {
450     struct SDCache_s *sdl, *sdLast = NULL;
451    
452     if (sd == NULL) { /* free entire list */
453     while ((sdl = SDcacheList) != NULL) {
454     SDcacheList = sdl->next;
455     SDfreeBSDF(&sdl->bsdf);
456     free(sdl);
457     }
458     return;
459     }
460     for (sdl = SDcacheList; sdl != NULL; sdl = (sdLast=sdl)->next)
461     if (&sdl->bsdf == sd)
462     break;
463 greg 2.21 if (sdl == NULL || (sdl->refcnt -= (sdl->refcnt > 0)))
464 greg 2.15 return; /* missing or still in use */
465     /* keep unreferenced data? */
466     if (SDisLoaded(sd) && SDretainSet) {
467     if (SDretainSet == SDretainAll)
468     return; /* keep everything */
469     /* else free cumulative data */
470     SDfreeCumulativeCache(sd->rf);
471     SDfreeCumulativeCache(sd->rb);
472     SDfreeCumulativeCache(sd->tf);
473 greg 2.42 SDfreeCumulativeCache(sd->tb);
474 greg 2.15 return;
475     }
476     /* remove from list and free */
477     if (sdLast == NULL)
478     SDcacheList = sdl->next;
479     else
480     sdLast->next = sdl->next;
481     SDfreeBSDF(&sdl->bsdf);
482     free(sdl);
483     }
484    
485     /* Sample an individual BSDF component */
486     SDError
487 greg 2.24 SDsampComponent(SDValue *sv, FVECT ioVec, double randX, SDComponent *sdc)
488 greg 2.15 {
489     float coef[SDmaxCh];
490     SDError ec;
491 greg 2.24 FVECT inVec;
492 greg 2.15 const SDCDst *cd;
493     double d;
494     int n;
495     /* check arguments */
496 greg 2.24 if ((sv == NULL) | (ioVec == NULL) | (sdc == NULL))
497 greg 2.15 return SDEargument;
498     /* get cumulative distribution */
499 greg 2.24 VCOPY(inVec, ioVec);
500 greg 2.45 sv->cieY = 0;
501 greg 2.15 cd = (*sdc->func->getCDist)(inVec, sdc);
502 greg 2.45 if (cd != NULL)
503     sv->cieY = cd->cTotal;
504     if (sv->cieY <= 1e-6) { /* nothing to sample? */
505 greg 2.15 sv->spec = c_dfcolor;
506 greg 2.53 memset(ioVec, 0, sizeof(FVECT));
507 greg 2.15 return SDEnone;
508     }
509     /* compute sample direction */
510 greg 2.24 ec = (*sdc->func->sampCDist)(ioVec, randX, cd);
511 greg 2.15 if (ec)
512     return ec;
513     /* get BSDF color */
514 greg 2.61 n = (*sdc->func->getBSDFs)(coef, inVec, ioVec, sdc);
515 greg 2.15 if (n <= 0) {
516     strcpy(SDerrorDetail, "BSDF sample value error");
517     return SDEinternal;
518     }
519     sv->spec = sdc->cspec[0];
520     d = coef[0];
521     while (--n) {
522     c_cmix(&sv->spec, d, &sv->spec, coef[n], &sdc->cspec[n]);
523     d += coef[n];
524     }
525 greg 2.55 c_ccvt(&sv->spec, C_CSXY); /* make sure (x,y) is set */
526 greg 2.15 return SDEnone;
527     }
528    
529     #define MS_MAXDIM 15
530    
531     /* Convert 1-dimensional random variable to N-dimensional */
532     void
533     SDmultiSamp(double t[], int n, double randX)
534     {
535     unsigned nBits;
536     double scale;
537     bitmask_t ndx, coord[MS_MAXDIM];
538 greg 2.41
539     if (n <= 0) /* check corner cases */
540     return;
541     if (randX < 0) randX = 0;
542     else if (randX >= 1.) randX = 0.999999999999999;
543     if (n == 1) {
544     t[0] = randX;
545     return;
546     }
547 greg 2.15 while (n > MS_MAXDIM) /* punt for higher dimensions */
548 greg 2.19 t[--n] = rand()*(1./(RAND_MAX+.5));
549 greg 2.15 nBits = (8*sizeof(bitmask_t) - 1) / n;
550     ndx = randX * (double)((bitmask_t)1 << (nBits*n));
551     /* get coordinate on Hilbert curve */
552     hilbert_i2c(n, nBits, ndx, coord);
553     /* convert back to [0,1) range */
554     scale = 1. / (double)((bitmask_t)1 << nBits);
555     while (n--)
556 greg 2.19 t[n] = scale * ((double)coord[n] + rand()*(1./(RAND_MAX+.5)));
557 greg 2.15 }
558    
559     #undef MS_MAXDIM
560    
561     /* Generate diffuse hemispherical sample */
562     static void
563 greg 2.61 SDdiffuseSamp(FVECT ioVec, int outFront, double randX)
564 greg 2.15 {
565     /* convert to position on hemisphere */
566 greg 2.61 SDmultiSamp(ioVec, 2, randX);
567     SDsquare2disk(ioVec, ioVec[0], ioVec[1]);
568     ioVec[2] = 1. - ioVec[0]*ioVec[0] - ioVec[1]*ioVec[1];
569     ioVec[2] = sqrt(ioVec[2]*(ioVec[2]>0));
570 greg 2.15 if (!outFront) /* going out back? */
571 greg 2.61 ioVec[2] = -ioVec[2];
572 greg 2.15 }
573    
574     /* Query projected solid angle coverage for non-diffuse BSDF direction */
575     SDError
576 greg 2.22 SDsizeBSDF(double *projSA, const FVECT v1, const RREAL *v2,
577     int qflags, const SDData *sd)
578 greg 2.15 {
579 greg 2.23 SDSpectralDF *rdf, *tdf;
580 greg 2.15 SDError ec;
581     int i;
582     /* check arguments */
583 greg 2.26 if ((projSA == NULL) | (v1 == NULL) | (sd == NULL))
584 greg 2.15 return SDEargument;
585     /* initialize extrema */
586 greg 2.17 switch (qflags) {
587 greg 2.15 case SDqueryMax:
588     projSA[0] = .0;
589     break;
590     case SDqueryMin+SDqueryMax:
591     projSA[1] = .0;
592     /* fall through */
593     case SDqueryMin:
594     projSA[0] = 10.;
595     break;
596     case 0:
597     return SDEargument;
598     }
599 greg 2.42 if (v1[2] > 0) { /* front surface query? */
600 greg 2.15 rdf = sd->rf;
601 greg 2.42 tdf = (sd->tf != NULL) ? sd->tf : sd->tb;
602     } else {
603 greg 2.15 rdf = sd->rb;
604 greg 2.42 tdf = (sd->tb != NULL) ? sd->tb : sd->tf;
605     }
606 greg 2.51 if (v2 != NULL) { /* bidirectional? */
607 greg 2.26 if (v1[2] > 0 ^ v2[2] > 0)
608     rdf = NULL;
609     else
610     tdf = NULL;
611 greg 2.51 }
612 greg 2.15 ec = SDEdata; /* run through components */
613     for (i = (rdf==NULL) ? 0 : rdf->ncomp; i--; ) {
614 greg 2.22 ec = (*rdf->comp[i].func->queryProjSA)(projSA, v1, v2,
615 greg 2.25 qflags, &rdf->comp[i]);
616 greg 2.15 if (ec)
617     return ec;
618     }
619 greg 2.23 for (i = (tdf==NULL) ? 0 : tdf->ncomp; i--; ) {
620     ec = (*tdf->comp[i].func->queryProjSA)(projSA, v1, v2,
621 greg 2.25 qflags, &tdf->comp[i]);
622 greg 2.15 if (ec)
623     return ec;
624     }
625 greg 2.17 if (ec) { /* all diffuse? */
626     projSA[0] = M_PI;
627     if (qflags == SDqueryMin+SDqueryMax)
628     projSA[1] = M_PI;
629 greg 2.46 } else if (qflags == SDqueryMin+SDqueryMax && projSA[0] > projSA[1])
630     projSA[0] = projSA[1];
631 greg 2.17 return SDEnone;
632 greg 2.15 }
633    
634     /* Return BSDF for the given incident and scattered ray vectors */
635     SDError
636 greg 2.61 SDevalBSDF(SDValue *sv, const FVECT inVec, const FVECT outVec, const SDData *sd)
637 greg 2.15 {
638     int inFront, outFront;
639     SDSpectralDF *sdf;
640     float coef[SDmaxCh];
641     int nch, i;
642     /* check arguments */
643     if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL))
644     return SDEargument;
645     /* whose side are we on? */
646 greg 2.23 inFront = (inVec[2] > 0);
647     outFront = (outVec[2] > 0);
648 greg 2.15 /* start with diffuse portion */
649     if (inFront & outFront) {
650     *sv = sd->rLambFront;
651     sdf = sd->rf;
652     } else if (!(inFront | outFront)) {
653     *sv = sd->rLambBack;
654     sdf = sd->rb;
655 greg 2.57 } else if (inFront) {
656 greg 2.59 *sv = sd->tLambFront;
657 greg 2.42 sdf = (sd->tf != NULL) ? sd->tf : sd->tb;
658 greg 2.57 } else /* outFront & !inFront */ {
659 greg 2.59 *sv = sd->tLambBack;
660 greg 2.42 sdf = (sd->tb != NULL) ? sd->tb : sd->tf;
661 greg 2.15 }
662     sv->cieY *= 1./M_PI;
663     /* add non-diffuse components */
664     i = (sdf != NULL) ? sdf->ncomp : 0;
665     while (i-- > 0) {
666 greg 2.61 nch = (*sdf->comp[i].func->getBSDFs)(coef, inVec, outVec,
667 greg 2.25 &sdf->comp[i]);
668 greg 2.15 while (nch-- > 0) {
669     c_cmix(&sv->spec, sv->cieY, &sv->spec,
670     coef[nch], &sdf->comp[i].cspec[nch]);
671     sv->cieY += coef[nch];
672     }
673     }
674 greg 2.55 c_ccvt(&sv->spec, C_CSXY); /* make sure (x,y) is set */
675 greg 2.15 return SDEnone;
676     }
677    
678     /* Compute directional hemispherical scattering at this incident angle */
679     double
680     SDdirectHemi(const FVECT inVec, int sflags, const SDData *sd)
681     {
682     double hsum;
683 greg 2.42 SDSpectralDF *rdf, *tdf;
684 greg 2.15 const SDCDst *cd;
685     int i;
686     /* check arguments */
687     if ((inVec == NULL) | (sd == NULL))
688     return .0;
689     /* gather diffuse components */
690 greg 2.23 if (inVec[2] > 0) {
691 greg 2.15 hsum = sd->rLambFront.cieY;
692     rdf = sd->rf;
693 greg 2.42 tdf = (sd->tf != NULL) ? sd->tf : sd->tb;
694 greg 2.15 } else /* !inFront */ {
695     hsum = sd->rLambBack.cieY;
696     rdf = sd->rb;
697 greg 2.42 tdf = (sd->tb != NULL) ? sd->tb : sd->tf;
698 greg 2.15 }
699     if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
700     hsum = .0;
701     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
702 greg 2.59 hsum += (inVec[2] > 0) ?
703     sd->tLambFront.cieY : sd->tLambBack.cieY;
704 greg 2.15 /* gather non-diffuse components */
705 greg 2.42 i = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) &
706     (rdf != NULL)) ? rdf->ncomp : 0;
707 greg 2.15 while (i-- > 0) { /* non-diffuse reflection */
708     cd = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
709     if (cd != NULL)
710     hsum += cd->cTotal;
711     }
712 greg 2.42 i = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) &
713     (tdf != NULL)) ? tdf->ncomp : 0;
714 greg 2.15 while (i-- > 0) { /* non-diffuse transmission */
715 greg 2.42 cd = (*tdf->comp[i].func->getCDist)(inVec, &tdf->comp[i]);
716 greg 2.15 if (cd != NULL)
717     hsum += cd->cTotal;
718     }
719     return hsum;
720     }
721    
722     /* Sample BSDF direction based on the given random variable */
723     SDError
724 greg 2.24 SDsampBSDF(SDValue *sv, FVECT ioVec, double randX, int sflags, const SDData *sd)
725 greg 2.15 {
726     SDError ec;
727 greg 2.24 FVECT inVec;
728 greg 2.15 int inFront;
729 greg 2.42 SDSpectralDF *rdf, *tdf;
730 greg 2.15 double rdiff;
731     float coef[SDmaxCh];
732     int i, j, n, nr;
733     SDComponent *sdc;
734     const SDCDst **cdarr = NULL;
735     /* check arguments */
736 greg 2.24 if ((sv == NULL) | (ioVec == NULL) | (sd == NULL) |
737 greg 2.23 (randX < 0) | (randX >= 1.))
738 greg 2.15 return SDEargument;
739     /* whose side are we on? */
740 greg 2.24 VCOPY(inVec, ioVec);
741 greg 2.23 inFront = (inVec[2] > 0);
742 greg 2.15 /* remember diffuse portions */
743     if (inFront) {
744     *sv = sd->rLambFront;
745     rdf = sd->rf;
746 greg 2.42 tdf = (sd->tf != NULL) ? sd->tf : sd->tb;
747 greg 2.15 } else /* !inFront */ {
748     *sv = sd->rLambBack;
749     rdf = sd->rb;
750 greg 2.42 tdf = (sd->tb != NULL) ? sd->tb : sd->tf;
751 greg 2.15 }
752     if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
753     sv->cieY = .0;
754     rdiff = sv->cieY;
755     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
756 greg 2.59 sv->cieY += inFront ? sd->tLambFront.cieY : sd->tLambBack.cieY;
757 greg 2.15 /* gather non-diffuse components */
758 greg 2.42 i = nr = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) &
759     (rdf != NULL)) ? rdf->ncomp : 0;
760     j = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) &
761     (tdf != NULL)) ? tdf->ncomp : 0;
762 greg 2.15 n = i + j;
763     if (n > 0 && (cdarr = (const SDCDst **)malloc(n*sizeof(SDCDst *))) == NULL)
764     return SDEmemory;
765     while (j-- > 0) { /* non-diffuse transmission */
766 greg 2.42 cdarr[i+j] = (*tdf->comp[j].func->getCDist)(inVec, &tdf->comp[j]);
767 greg 2.45 if (cdarr[i+j] == NULL)
768     cdarr[i+j] = &SDemptyCD;
769 greg 2.15 sv->cieY += cdarr[i+j]->cTotal;
770     }
771     while (i-- > 0) { /* non-diffuse reflection */
772     cdarr[i] = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
773 greg 2.45 if (cdarr[i] == NULL)
774     cdarr[i] = &SDemptyCD;
775 greg 2.49 sv->cieY += cdarr[i]->cTotal;
776 greg 2.15 }
777 greg 2.35 if (sv->cieY <= 1e-6) { /* anything to sample? */
778 greg 2.15 sv->cieY = .0;
779 greg 2.53 memset(ioVec, 0, sizeof(FVECT));
780 greg 2.15 return SDEnone;
781     }
782     /* scale random variable */
783     randX *= sv->cieY;
784     /* diffuse reflection? */
785     if (randX < rdiff) {
786 greg 2.24 SDdiffuseSamp(ioVec, inFront, randX/rdiff);
787 greg 2.15 goto done;
788     }
789     randX -= rdiff;
790     /* diffuse transmission? */
791     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) {
792 greg 2.59 const SDValue *sdt = inFront ? &sd->tLambFront : &sd->tLambBack;
793     if (randX < sdt->cieY) {
794     sv->spec = sdt->spec;
795     SDdiffuseSamp(ioVec, !inFront, randX/sdt->cieY);
796 greg 2.15 goto done;
797     }
798 greg 2.59 randX -= sdt->cieY;
799 greg 2.15 }
800     /* else one of cumulative dist. */
801 greg 2.56 for (i = 0; i < n && randX >= cdarr[i]->cTotal; i++)
802 greg 2.15 randX -= cdarr[i]->cTotal;
803     if (i >= n)
804     return SDEinternal;
805     /* compute sample direction */
806 greg 2.42 sdc = (i < nr) ? &rdf->comp[i] : &tdf->comp[i-nr];
807 greg 2.24 ec = (*sdc->func->sampCDist)(ioVec, randX/cdarr[i]->cTotal, cdarr[i]);
808 greg 2.15 if (ec)
809     return ec;
810     /* compute color */
811 greg 2.61 j = (*sdc->func->getBSDFs)(coef, inVec, ioVec, sdc);
812 greg 2.15 if (j <= 0) {
813     sprintf(SDerrorDetail, "BSDF \"%s\" sampling value error",
814     sd->name);
815     return SDEinternal;
816     }
817     sv->spec = sdc->cspec[0];
818     rdiff = coef[0];
819     while (--j) {
820     c_cmix(&sv->spec, rdiff, &sv->spec, coef[j], &sdc->cspec[j]);
821     rdiff += coef[j];
822     }
823     done:
824     if (cdarr != NULL)
825     free(cdarr);
826 greg 2.55 c_ccvt(&sv->spec, C_CSXY); /* make sure (x,y) is set */
827 greg 2.15 return SDEnone;
828     }
829    
830     /* Compute World->BSDF transform from surface normal and up (Y) vector */
831     SDError
832     SDcompXform(RREAL vMtx[3][3], const FVECT sNrm, const FVECT uVec)
833     {
834     if ((vMtx == NULL) | (sNrm == NULL) | (uVec == NULL))
835     return SDEargument;
836     VCOPY(vMtx[2], sNrm);
837 greg 2.23 if (normalize(vMtx[2]) == 0)
838 greg 2.15 return SDEargument;
839     fcross(vMtx[0], uVec, vMtx[2]);
840 greg 2.23 if (normalize(vMtx[0]) == 0)
841 greg 2.15 return SDEargument;
842     fcross(vMtx[1], vMtx[2], vMtx[0]);
843     return SDEnone;
844     }
845    
846     /* Compute inverse transform */
847     SDError
848     SDinvXform(RREAL iMtx[3][3], RREAL vMtx[3][3])
849     {
850     RREAL mTmp[3][3];
851     double d;
852    
853     if ((iMtx == NULL) | (vMtx == NULL))
854     return SDEargument;
855     /* compute determinant */
856     mTmp[0][0] = vMtx[2][2]*vMtx[1][1] - vMtx[2][1]*vMtx[1][2];
857     mTmp[0][1] = vMtx[2][1]*vMtx[0][2] - vMtx[2][2]*vMtx[0][1];
858     mTmp[0][2] = vMtx[1][2]*vMtx[0][1] - vMtx[1][1]*vMtx[0][2];
859     d = vMtx[0][0]*mTmp[0][0] + vMtx[1][0]*mTmp[0][1] + vMtx[2][0]*mTmp[0][2];
860 greg 2.23 if (d == 0) {
861 greg 2.15 strcpy(SDerrorDetail, "Zero determinant in matrix inversion");
862     return SDEargument;
863     }
864     d = 1./d; /* invert matrix */
865     mTmp[0][0] *= d; mTmp[0][1] *= d; mTmp[0][2] *= d;
866     mTmp[1][0] = d*(vMtx[2][0]*vMtx[1][2] - vMtx[2][2]*vMtx[1][0]);
867     mTmp[1][1] = d*(vMtx[2][2]*vMtx[0][0] - vMtx[2][0]*vMtx[0][2]);
868     mTmp[1][2] = d*(vMtx[1][0]*vMtx[0][2] - vMtx[1][2]*vMtx[0][0]);
869     mTmp[2][0] = d*(vMtx[2][1]*vMtx[1][0] - vMtx[2][0]*vMtx[1][1]);
870     mTmp[2][1] = d*(vMtx[2][0]*vMtx[0][1] - vMtx[2][1]*vMtx[0][0]);
871     mTmp[2][2] = d*(vMtx[1][1]*vMtx[0][0] - vMtx[1][0]*vMtx[0][1]);
872     memcpy(iMtx, mTmp, sizeof(mTmp));
873     return SDEnone;
874     }
875    
876     /* Transform and normalize direction (column) vector */
877     SDError
878     SDmapDir(FVECT resVec, RREAL vMtx[3][3], const FVECT inpVec)
879     {
880     FVECT vTmp;
881    
882     if ((resVec == NULL) | (inpVec == NULL))
883     return SDEargument;
884     if (vMtx == NULL) { /* assume they just want to normalize */
885     if (resVec != inpVec)
886     VCOPY(resVec, inpVec);
887 greg 2.23 return (normalize(resVec) > 0) ? SDEnone : SDEargument;
888 greg 2.15 }
889     vTmp[0] = DOT(vMtx[0], inpVec);
890     vTmp[1] = DOT(vMtx[1], inpVec);
891     vTmp[2] = DOT(vMtx[2], inpVec);
892 greg 2.23 if (normalize(vTmp) == 0)
893 greg 2.15 return SDEargument;
894     VCOPY(resVec, vTmp);
895     return SDEnone;
896     }