ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.43
Committed: Sat Oct 13 20:15:43 2012 UTC (11 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.42: +56 -761 lines
Log Message:
Corrected errors in XML interpreter and genBSDF and removed mkillum BSDF code

File Contents

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