ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.53
Committed: Thu Feb 2 04:46:38 2017 UTC (7 years, 3 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.52: +3 -3 lines
Log Message:
Bug fixes and improvements -- sampling was not working, now reports color

File Contents

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