ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.59
Committed: Sat Mar 27 17:50:18 2021 UTC (4 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.58: +15 -11 lines
Log Message:
fix: Allow different front/back diffuse reflectance in BSDF library

File Contents

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