ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.48
Committed: Mon Mar 24 04:00:45 2014 UTC (10 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad4R2P2, rad4R2, rad4R2P1
Changes since 2.47: +2 -3 lines
Log Message:
Minor optimizations should not affect results

File Contents

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