ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.19
Committed: Sat Apr 9 15:39:16 2011 UTC (13 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.18: +3 -3 lines
Log Message:
Fixed range of rand() -> double conversion

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.19 static const char RCSid[] = "$Id: bsdf.c,v 2.18 2011/04/08 23:23:28 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     #include <stdio.h>
14     #include <stdlib.h>
15     #include <math.h>
16     #include "ezxml.h"
17     #include "hilbert.h"
18     #include "bsdf.h"
19     #include "bsdf_m.h"
20     #include "bsdf_t.h"
21    
22     /* English ASCII strings corresponding to ennumerated errors */
23     const char *SDerrorEnglish[] = {
24     "No error",
25     "Memory error",
26     "File input/output error",
27     "File format error",
28     "Illegal argument",
29     "Invalid data",
30     "Unsupported feature",
31     "Internal program error",
32     "Unknown error"
33     };
34    
35     /* Additional information on last error (ASCII English) */
36     char SDerrorDetail[256];
37    
38     /* Cache of loaded BSDFs */
39     struct SDCache_s *SDcacheList = NULL;
40    
41     /* Retain BSDFs in cache list */
42     int SDretainSet = SDretainNone;
43    
44     /* Report any error to the indicated stream (in English) */
45     SDError
46     SDreportEnglish(SDError ec, FILE *fp)
47     {
48     if (fp == NULL)
49     return ec;
50     if (!ec)
51     return SDEnone;
52     fputs(SDerrorEnglish[ec], fp);
53     if (SDerrorDetail[0]) {
54     fputs(": ", fp);
55     fputs(SDerrorDetail, fp);
56     }
57     fputc('\n', fp);
58     if (fp != stderr)
59     fflush(fp);
60     return ec;
61     }
62    
63     static double
64     to_meters( /* return factor to convert given unit to meters */
65     const char *unit
66     )
67     {
68     if (unit == NULL) return(1.); /* safe assumption? */
69     if (!strcasecmp(unit, "Meter")) return(1.);
70     if (!strcasecmp(unit, "Foot")) return(.3048);
71     if (!strcasecmp(unit, "Inch")) return(.0254);
72     if (!strcasecmp(unit, "Centimeter")) return(.01);
73     if (!strcasecmp(unit, "Millimeter")) return(.001);
74     sprintf(SDerrorDetail, "Unknown dimensional unit '%s'", unit);
75     return(-1.);
76     }
77    
78     /* Load geometric dimensions and description (if any) */
79     static SDError
80 greg 2.16 SDloadGeometry(SDData *sd, ezxml_t wdb)
81 greg 2.15 {
82     ezxml_t geom;
83     double cfact;
84     const char *fmt, *mgfstr;
85    
86 greg 2.16 if (wdb == NULL) /* no geometry section? */
87     return SDEnone;
88     sd->dim[0] = sd->dim[1] = sd->dim[2] = .0;
89 greg 2.15 if ((geom = ezxml_child(wdb, "Width")) != NULL)
90 greg 2.16 sd->dim[0] = atof(ezxml_txt(geom)) *
91 greg 2.15 to_meters(ezxml_attr(geom, "unit"));
92     if ((geom = ezxml_child(wdb, "Height")) != NULL)
93 greg 2.16 sd->dim[1] = atof(ezxml_txt(geom)) *
94 greg 2.15 to_meters(ezxml_attr(geom, "unit"));
95     if ((geom = ezxml_child(wdb, "Thickness")) != NULL)
96 greg 2.16 sd->dim[2] = atof(ezxml_txt(geom)) *
97 greg 2.15 to_meters(ezxml_attr(geom, "unit"));
98 greg 2.17 if ((sd->dim[0] < .0) | (sd->dim[1] < .0) | (sd->dim[2] < .0)) {
99     sprintf(SDerrorDetail, "Negative size in \"%s\"", sd->name);
100 greg 2.15 return SDEdata;
101 greg 2.17 }
102 greg 2.15 if ((geom = ezxml_child(wdb, "Geometry")) == NULL ||
103     (mgfstr = ezxml_txt(geom)) == NULL)
104     return SDEnone;
105     if ((fmt = ezxml_attr(geom, "format")) != NULL &&
106     strcasecmp(fmt, "MGF")) {
107     sprintf(SDerrorDetail,
108     "Unrecognized geometry format '%s' in \"%s\"",
109 greg 2.16 fmt, sd->name);
110 greg 2.15 return SDEsupport;
111     }
112     cfact = to_meters(ezxml_attr(geom, "unit"));
113 greg 2.16 sd->mgf = (char *)malloc(strlen(mgfstr)+32);
114     if (sd->mgf == NULL) {
115 greg 2.15 strcpy(SDerrorDetail, "Out of memory in SDloadGeometry");
116     return SDEmemory;
117     }
118     if (cfact < 0.99 || cfact > 1.01)
119 greg 2.16 sprintf(sd->mgf, "xf -s %.5f\n%s\nxf\n", cfact, mgfstr);
120 greg 2.15 else
121 greg 2.16 strcpy(sd->mgf, mgfstr);
122 greg 2.15 return SDEnone;
123     }
124    
125     /* Load a BSDF struct from the given file (free first and keep name) */
126     SDError
127     SDloadFile(SDData *sd, const char *fname)
128     {
129     SDError lastErr;
130 greg 2.16 ezxml_t fl, wtl;
131 greg 2.15
132     if ((sd == NULL) | (fname == NULL || !*fname))
133     return SDEargument;
134     /* free old data, keeping name */
135     SDfreeBSDF(sd);
136     /* parse XML file */
137     fl = ezxml_parse_file(fname);
138     if (fl == NULL) {
139     sprintf(SDerrorDetail, "Cannot open BSDF \"%s\"", fname);
140     return SDEfile;
141     }
142     if (ezxml_error(fl)[0]) {
143     sprintf(SDerrorDetail, "BSDF \"%s\" %s", fname, ezxml_error(fl));
144     ezxml_free(fl);
145     return SDEformat;
146     }
147 greg 2.16 if (strcmp(ezxml_name(fl), "WindowElement")) {
148     sprintf(SDerrorDetail,
149     "BSDF \"%s\": top level node not 'WindowElement'",
150     sd->name);
151     ezxml_free(fl);
152     return SDEformat;
153     }
154     wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
155     if (wtl == NULL) {
156     sprintf(SDerrorDetail, "BSDF \"%s\": no optical layer'",
157     sd->name);
158     ezxml_free(fl);
159     return SDEformat;
160     }
161 greg 2.15 /* load geometry if present */
162 greg 2.16 lastErr = SDloadGeometry(sd, ezxml_child(wtl, "Material"));
163     if (lastErr)
164 greg 2.15 return lastErr;
165     /* try loading variable resolution data */
166 greg 2.16 lastErr = SDloadTre(sd, wtl);
167 greg 2.15 /* check our result */
168     switch (lastErr) {
169     case SDEformat:
170     case SDEdata:
171     case SDEsupport: /* possibly we just tried the wrong format */
172 greg 2.16 lastErr = SDloadMtx(sd, wtl);
173 greg 2.15 break;
174     default: /* variable res. OK else serious error */
175     break;
176     }
177     /* done with XML file */
178     ezxml_free(fl);
179 greg 2.16
180     if (lastErr) { /* was there a load error? */
181     SDfreeBSDF(sd);
182     return lastErr;
183     }
184     /* remove any insignificant components */
185     if (sd->rf != NULL && sd->rf->maxHemi <= .001) {
186     SDfreeSpectralDF(sd->rf); sd->rf = NULL;
187     }
188     if (sd->rb != NULL && sd->rb->maxHemi <= .001) {
189     SDfreeSpectralDF(sd->rb); sd->rb = NULL;
190     }
191     if (sd->tf != NULL && sd->tf->maxHemi <= .001) {
192     SDfreeSpectralDF(sd->tf); sd->tf = NULL;
193     }
194     /* return success */
195     return SDEnone;
196 greg 2.15 }
197    
198     /* Allocate new spectral distribution function */
199     SDSpectralDF *
200     SDnewSpectralDF(int nc)
201     {
202     SDSpectralDF *df;
203    
204     if (nc <= 0) {
205     strcpy(SDerrorDetail, "Zero component spectral DF request");
206     return NULL;
207     }
208     df = (SDSpectralDF *)malloc(sizeof(SDSpectralDF) +
209     (nc-1)*sizeof(SDComponent));
210     if (df == NULL) {
211     sprintf(SDerrorDetail,
212     "Cannot allocate %d component spectral DF", nc);
213     return NULL;
214     }
215     df->minProjSA = .0;
216     df->maxHemi = .0;
217     df->ncomp = nc;
218     memset(df->comp, 0, nc*sizeof(SDComponent));
219     return df;
220     }
221    
222     /* Free cached cumulative distributions for BSDF component */
223     void
224     SDfreeCumulativeCache(SDSpectralDF *df)
225     {
226     int n;
227     SDCDst *cdp;
228    
229     if (df == NULL)
230     return;
231     for (n = df->ncomp; n-- > 0; )
232     while ((cdp = df->comp[n].cdList) != NULL) {
233     df->comp[n].cdList = cdp->next;
234     free(cdp);
235     }
236     }
237    
238     /* Free a spectral distribution function */
239     void
240     SDfreeSpectralDF(SDSpectralDF *df)
241     {
242     int n;
243    
244     if (df == NULL)
245     return;
246     SDfreeCumulativeCache(df);
247     for (n = df->ncomp; n-- > 0; )
248     (*df->comp[n].func->freeSC)(df->comp[n].dist);
249     free(df);
250     }
251    
252     /* Shorten file path to useable BSDF name, removing suffix */
253     void
254 greg 2.16 SDclipName(char *res, const char *fname)
255 greg 2.15 {
256     const char *cp, *dot = NULL;
257    
258     for (cp = fname; *cp; cp++)
259     if (*cp == '.')
260     dot = cp;
261     if ((dot == NULL) | (dot < fname+2))
262     dot = cp;
263     if (dot - fname >= SDnameLn)
264     fname = dot - SDnameLn + 1;
265     while (fname < dot)
266     *res++ = *fname++;
267     *res = '\0';
268     }
269    
270     /* Initialize an unused BSDF struct (simply clears to zeroes) */
271     void
272     SDclearBSDF(SDData *sd)
273     {
274     if (sd != NULL)
275     memset(sd, 0, sizeof(SDData));
276     }
277    
278     /* Free data associated with BSDF struct */
279     void
280     SDfreeBSDF(SDData *sd)
281     {
282     if (sd == NULL)
283     return;
284     if (sd->mgf != NULL) {
285     free(sd->mgf);
286     sd->mgf = NULL;
287     }
288     if (sd->rf != NULL) {
289     SDfreeSpectralDF(sd->rf);
290     sd->rf = NULL;
291     }
292     if (sd->rb != NULL) {
293     SDfreeSpectralDF(sd->rb);
294     sd->rb = NULL;
295     }
296     if (sd->tf != NULL) {
297     SDfreeSpectralDF(sd->tf);
298     sd->tf = NULL;
299     }
300     sd->rLambFront.cieY = .0;
301 greg 2.16 sd->rLambFront.spec.flags = 0;
302 greg 2.15 sd->rLambBack.cieY = .0;
303 greg 2.16 sd->rLambBack.spec.flags = 0;
304 greg 2.15 sd->tLamb.cieY = .0;
305 greg 2.16 sd->tLamb.spec.flags = 0;
306 greg 2.15 }
307    
308     /* Find writeable BSDF by name, or allocate new cache entry if absent */
309     SDData *
310     SDgetCache(const char *bname)
311     {
312     struct SDCache_s *sdl;
313     char sdnam[SDnameLn];
314    
315     if (bname == NULL)
316     return NULL;
317    
318     SDclipName(sdnam, bname);
319     for (sdl = SDcacheList; sdl != NULL; sdl = sdl->next)
320     if (!strcmp(sdl->bsdf.name, sdnam)) {
321     sdl->refcnt++;
322     return &sdl->bsdf;
323     }
324    
325     sdl = (struct SDCache_s *)calloc(1, sizeof(struct SDCache_s));
326     if (sdl == NULL)
327     return NULL;
328    
329     strcpy(sdl->bsdf.name, sdnam);
330     sdl->next = SDcacheList;
331     SDcacheList = sdl;
332    
333     sdl->refcnt++;
334     return &sdl->bsdf;
335     }
336    
337     /* Get loaded BSDF from cache (or load and cache it on first call) */
338     /* Report any problem to stderr and return NULL on failure */
339     const SDData *
340     SDcacheFile(const char *fname)
341     {
342     SDData *sd;
343     SDError ec;
344    
345     if (fname == NULL || !*fname)
346     return NULL;
347     SDerrorDetail[0] = '\0';
348     if ((sd = SDgetCache(fname)) == NULL) {
349     SDreportEnglish(SDEmemory, stderr);
350     return NULL;
351     }
352     if (!SDisLoaded(sd) && (ec = SDloadFile(sd, fname))) {
353     SDreportEnglish(ec, stderr);
354     SDfreeCache(sd);
355     return NULL;
356     }
357     return sd;
358     }
359    
360     /* Free a BSDF from our cache (clear all if NULL) */
361     void
362     SDfreeCache(const SDData *sd)
363     {
364     struct SDCache_s *sdl, *sdLast = NULL;
365    
366     if (sd == NULL) { /* free entire list */
367     while ((sdl = SDcacheList) != NULL) {
368     SDcacheList = sdl->next;
369     SDfreeBSDF(&sdl->bsdf);
370     free(sdl);
371     }
372     return;
373     }
374     for (sdl = SDcacheList; sdl != NULL; sdl = (sdLast=sdl)->next)
375     if (&sdl->bsdf == sd)
376     break;
377     if (sdl == NULL || --sdl->refcnt)
378     return; /* missing or still in use */
379     /* keep unreferenced data? */
380     if (SDisLoaded(sd) && SDretainSet) {
381     if (SDretainSet == SDretainAll)
382     return; /* keep everything */
383     /* else free cumulative data */
384     SDfreeCumulativeCache(sd->rf);
385     SDfreeCumulativeCache(sd->rb);
386     SDfreeCumulativeCache(sd->tf);
387     return;
388     }
389     /* remove from list and free */
390     if (sdLast == NULL)
391     SDcacheList = sdl->next;
392     else
393     sdLast->next = sdl->next;
394     SDfreeBSDF(&sdl->bsdf);
395     free(sdl);
396     }
397    
398     /* Sample an individual BSDF component */
399     SDError
400     SDsampComponent(SDValue *sv, FVECT outVec, const FVECT inVec,
401     double randX, SDComponent *sdc)
402     {
403     float coef[SDmaxCh];
404     SDError ec;
405     const SDCDst *cd;
406     double d;
407     int n;
408     /* check arguments */
409     if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sdc == NULL))
410     return SDEargument;
411     /* get cumulative distribution */
412     cd = (*sdc->func->getCDist)(inVec, sdc);
413     if (cd == NULL)
414     return SDEmemory;
415     if (cd->cTotal <= 1e-7) { /* anything to sample? */
416     sv->spec = c_dfcolor;
417     sv->cieY = .0;
418     memset(outVec, 0, 3*sizeof(double));
419     return SDEnone;
420     }
421     sv->cieY = cd->cTotal;
422     /* compute sample direction */
423     ec = (*sdc->func->sampCDist)(outVec, randX, cd);
424     if (ec)
425     return ec;
426     /* get BSDF color */
427     n = (*sdc->func->getBSDFs)(coef, outVec, inVec, sdc->dist);
428     if (n <= 0) {
429     strcpy(SDerrorDetail, "BSDF sample value error");
430     return SDEinternal;
431     }
432     sv->spec = sdc->cspec[0];
433     d = coef[0];
434     while (--n) {
435     c_cmix(&sv->spec, d, &sv->spec, coef[n], &sdc->cspec[n]);
436     d += coef[n];
437     }
438     /* make sure everything is set */
439     c_ccvt(&sv->spec, C_CSXY+C_CSSPEC);
440     return SDEnone;
441     }
442    
443     #define MS_MAXDIM 15
444    
445     /* Convert 1-dimensional random variable to N-dimensional */
446     void
447     SDmultiSamp(double t[], int n, double randX)
448     {
449     unsigned nBits;
450     double scale;
451     bitmask_t ndx, coord[MS_MAXDIM];
452    
453     while (n > MS_MAXDIM) /* punt for higher dimensions */
454 greg 2.19 t[--n] = rand()*(1./(RAND_MAX+.5));
455 greg 2.15 nBits = (8*sizeof(bitmask_t) - 1) / n;
456     ndx = randX * (double)((bitmask_t)1 << (nBits*n));
457     /* get coordinate on Hilbert curve */
458     hilbert_i2c(n, nBits, ndx, coord);
459     /* convert back to [0,1) range */
460     scale = 1. / (double)((bitmask_t)1 << nBits);
461     while (n--)
462 greg 2.19 t[n] = scale * ((double)coord[n] + rand()*(1./(RAND_MAX+.5)));
463 greg 2.15 }
464    
465     #undef MS_MAXDIM
466    
467     /* Generate diffuse hemispherical sample */
468     static void
469     SDdiffuseSamp(FVECT outVec, int outFront, double randX)
470     {
471     /* convert to position on hemisphere */
472     SDmultiSamp(outVec, 2, randX);
473     SDsquare2disk(outVec, outVec[0], outVec[1]);
474     outVec[2] = 1. - outVec[0]*outVec[0] - outVec[1]*outVec[1];
475     if (outVec[2] > .0) /* a bit of paranoia */
476     outVec[2] = sqrt(outVec[2]);
477     if (!outFront) /* going out back? */
478     outVec[2] = -outVec[2];
479     }
480    
481     /* Query projected solid angle coverage for non-diffuse BSDF direction */
482     SDError
483     SDsizeBSDF(double *projSA, const FVECT vec, int qflags, const SDData *sd)
484     {
485     SDSpectralDF *rdf;
486     SDError ec;
487     int i;
488     /* check arguments */
489     if ((projSA == NULL) | (vec == NULL) | (sd == NULL))
490     return SDEargument;
491     /* initialize extrema */
492 greg 2.17 switch (qflags) {
493 greg 2.15 case SDqueryMax:
494     projSA[0] = .0;
495     break;
496     case SDqueryMin+SDqueryMax:
497     projSA[1] = .0;
498     /* fall through */
499     case SDqueryMin:
500     projSA[0] = 10.;
501     break;
502     case 0:
503     return SDEargument;
504     }
505     if (vec[2] > .0) /* front surface query? */
506     rdf = sd->rf;
507     else
508     rdf = sd->rb;
509     ec = SDEdata; /* run through components */
510     for (i = (rdf==NULL) ? 0 : rdf->ncomp; i--; ) {
511     ec = (*rdf->comp[i].func->queryProjSA)(projSA, vec, qflags,
512     rdf->comp[i].dist);
513     if (ec)
514     return ec;
515     }
516     for (i = (sd->tf==NULL) ? 0 : sd->tf->ncomp; i--; ) {
517     ec = (*sd->tf->comp[i].func->queryProjSA)(projSA, vec, qflags,
518     sd->tf->comp[i].dist);
519     if (ec)
520     return ec;
521     }
522 greg 2.17 if (ec) { /* all diffuse? */
523     projSA[0] = M_PI;
524     if (qflags == SDqueryMin+SDqueryMax)
525     projSA[1] = M_PI;
526     }
527     return SDEnone;
528 greg 2.15 }
529    
530     /* Return BSDF for the given incident and scattered ray vectors */
531     SDError
532     SDevalBSDF(SDValue *sv, const FVECT outVec, const FVECT inVec, const SDData *sd)
533     {
534     int inFront, outFront;
535     SDSpectralDF *sdf;
536     float coef[SDmaxCh];
537     int nch, i;
538     /* check arguments */
539     if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL))
540     return SDEargument;
541     /* whose side are we on? */
542     inFront = (inVec[2] > .0);
543     outFront = (outVec[2] > .0);
544     /* start with diffuse portion */
545     if (inFront & outFront) {
546     *sv = sd->rLambFront;
547     sdf = sd->rf;
548     } else if (!(inFront | outFront)) {
549     *sv = sd->rLambBack;
550     sdf = sd->rb;
551     } else /* inFront ^ outFront */ {
552     *sv = sd->tLamb;
553     sdf = sd->tf;
554     }
555     sv->cieY *= 1./M_PI;
556     /* add non-diffuse components */
557     i = (sdf != NULL) ? sdf->ncomp : 0;
558     while (i-- > 0) {
559     nch = (*sdf->comp[i].func->getBSDFs)(coef, outVec, inVec,
560     sdf->comp[i].dist);
561     while (nch-- > 0) {
562     c_cmix(&sv->spec, sv->cieY, &sv->spec,
563     coef[nch], &sdf->comp[i].cspec[nch]);
564     sv->cieY += coef[nch];
565     }
566     }
567     /* make sure everything is set */
568     c_ccvt(&sv->spec, C_CSXY+C_CSSPEC);
569     return SDEnone;
570     }
571    
572     /* Compute directional hemispherical scattering at this incident angle */
573     double
574     SDdirectHemi(const FVECT inVec, int sflags, const SDData *sd)
575     {
576     double hsum;
577     SDSpectralDF *rdf;
578     const SDCDst *cd;
579     int i;
580     /* check arguments */
581     if ((inVec == NULL) | (sd == NULL))
582     return .0;
583     /* gather diffuse components */
584     if (inVec[2] > .0) {
585     hsum = sd->rLambFront.cieY;
586     rdf = sd->rf;
587     } else /* !inFront */ {
588     hsum = sd->rLambBack.cieY;
589     rdf = sd->rb;
590     }
591     if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
592     hsum = .0;
593     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
594     hsum += sd->tLamb.cieY;
595     /* gather non-diffuse components */
596     i = ((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR &&
597     rdf != NULL) ? rdf->ncomp : 0;
598     while (i-- > 0) { /* non-diffuse reflection */
599     cd = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
600     if (cd != NULL)
601     hsum += cd->cTotal;
602     }
603     i = ((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT &&
604     sd->tf != NULL) ? sd->tf->ncomp : 0;
605     while (i-- > 0) { /* non-diffuse transmission */
606     cd = (*sd->tf->comp[i].func->getCDist)(inVec, &sd->tf->comp[i]);
607     if (cd != NULL)
608     hsum += cd->cTotal;
609     }
610     return hsum;
611     }
612    
613     /* Sample BSDF direction based on the given random variable */
614     SDError
615     SDsampBSDF(SDValue *sv, FVECT outVec, const FVECT inVec,
616     double randX, int sflags, const SDData *sd)
617     {
618     SDError ec;
619     int inFront;
620     SDSpectralDF *rdf;
621     double rdiff;
622     float coef[SDmaxCh];
623     int i, j, n, nr;
624     SDComponent *sdc;
625     const SDCDst **cdarr = NULL;
626     /* check arguments */
627     if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL) |
628     (randX < .0) | (randX >= 1.))
629     return SDEargument;
630     /* whose side are we on? */
631     inFront = (inVec[2] > .0);
632     /* remember diffuse portions */
633     if (inFront) {
634     *sv = sd->rLambFront;
635     rdf = sd->rf;
636     } else /* !inFront */ {
637     *sv = sd->rLambBack;
638     rdf = sd->rb;
639     }
640     if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
641     sv->cieY = .0;
642     rdiff = sv->cieY;
643     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
644     sv->cieY += sd->tLamb.cieY;
645     /* gather non-diffuse components */
646     i = nr = ((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR &&
647     rdf != NULL) ? rdf->ncomp : 0;
648     j = ((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT &&
649     sd->tf != NULL) ? sd->tf->ncomp : 0;
650     n = i + j;
651     if (n > 0 && (cdarr = (const SDCDst **)malloc(n*sizeof(SDCDst *))) == NULL)
652     return SDEmemory;
653     while (j-- > 0) { /* non-diffuse transmission */
654     cdarr[i+j] = (*sd->tf->comp[j].func->getCDist)(inVec, &sd->tf->comp[j]);
655     if (cdarr[i+j] == NULL) {
656     free(cdarr);
657     return SDEmemory;
658     }
659     sv->cieY += cdarr[i+j]->cTotal;
660     }
661     while (i-- > 0) { /* non-diffuse reflection */
662     cdarr[i] = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
663     if (cdarr[i] == NULL) {
664     free(cdarr);
665     return SDEmemory;
666     }
667     sv->cieY += cdarr[i]->cTotal;
668     }
669     if (sv->cieY <= 1e-7) { /* anything to sample? */
670     sv->cieY = .0;
671     memset(outVec, 0, 3*sizeof(double));
672     return SDEnone;
673     }
674     /* scale random variable */
675     randX *= sv->cieY;
676     /* diffuse reflection? */
677     if (randX < rdiff) {
678     SDdiffuseSamp(outVec, inFront, randX/rdiff);
679     goto done;
680     }
681     randX -= rdiff;
682     /* diffuse transmission? */
683     if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) {
684     if (randX < sd->tLamb.cieY) {
685     sv->spec = sd->tLamb.spec;
686     SDdiffuseSamp(outVec, !inFront, randX/sd->tLamb.cieY);
687     goto done;
688     }
689     randX -= sd->tLamb.cieY;
690     }
691     /* else one of cumulative dist. */
692     for (i = 0; i < n && randX < cdarr[i]->cTotal; i++)
693     randX -= cdarr[i]->cTotal;
694     if (i >= n)
695     return SDEinternal;
696     /* compute sample direction */
697     sdc = (i < nr) ? &rdf->comp[i] : &sd->tf->comp[i-nr];
698     ec = (*sdc->func->sampCDist)(outVec, randX/cdarr[i]->cTotal, cdarr[i]);
699     if (ec)
700     return ec;
701     /* compute color */
702     j = (*sdc->func->getBSDFs)(coef, outVec, inVec, sdc->dist);
703     if (j <= 0) {
704     sprintf(SDerrorDetail, "BSDF \"%s\" sampling value error",
705     sd->name);
706     return SDEinternal;
707     }
708     sv->spec = sdc->cspec[0];
709     rdiff = coef[0];
710     while (--j) {
711     c_cmix(&sv->spec, rdiff, &sv->spec, coef[j], &sdc->cspec[j]);
712     rdiff += coef[j];
713     }
714     done:
715     if (cdarr != NULL)
716     free(cdarr);
717     /* make sure everything is set */
718     c_ccvt(&sv->spec, C_CSXY+C_CSSPEC);
719     return SDEnone;
720     }
721    
722     /* Compute World->BSDF transform from surface normal and up (Y) vector */
723     SDError
724     SDcompXform(RREAL vMtx[3][3], const FVECT sNrm, const FVECT uVec)
725     {
726     if ((vMtx == NULL) | (sNrm == NULL) | (uVec == NULL))
727     return SDEargument;
728     VCOPY(vMtx[2], sNrm);
729     if (normalize(vMtx[2]) == .0)
730     return SDEargument;
731     fcross(vMtx[0], uVec, vMtx[2]);
732     if (normalize(vMtx[0]) == .0)
733     return SDEargument;
734     fcross(vMtx[1], vMtx[2], vMtx[0]);
735     return SDEnone;
736     }
737    
738     /* Compute inverse transform */
739     SDError
740     SDinvXform(RREAL iMtx[3][3], RREAL vMtx[3][3])
741     {
742     RREAL mTmp[3][3];
743     double d;
744    
745     if ((iMtx == NULL) | (vMtx == NULL))
746     return SDEargument;
747     /* compute determinant */
748     mTmp[0][0] = vMtx[2][2]*vMtx[1][1] - vMtx[2][1]*vMtx[1][2];
749     mTmp[0][1] = vMtx[2][1]*vMtx[0][2] - vMtx[2][2]*vMtx[0][1];
750     mTmp[0][2] = vMtx[1][2]*vMtx[0][1] - vMtx[1][1]*vMtx[0][2];
751     d = vMtx[0][0]*mTmp[0][0] + vMtx[1][0]*mTmp[0][1] + vMtx[2][0]*mTmp[0][2];
752     if (d == .0) {
753     strcpy(SDerrorDetail, "Zero determinant in matrix inversion");
754     return SDEargument;
755     }
756     d = 1./d; /* invert matrix */
757     mTmp[0][0] *= d; mTmp[0][1] *= d; mTmp[0][2] *= d;
758     mTmp[1][0] = d*(vMtx[2][0]*vMtx[1][2] - vMtx[2][2]*vMtx[1][0]);
759     mTmp[1][1] = d*(vMtx[2][2]*vMtx[0][0] - vMtx[2][0]*vMtx[0][2]);
760     mTmp[1][2] = d*(vMtx[1][0]*vMtx[0][2] - vMtx[1][2]*vMtx[0][0]);
761     mTmp[2][0] = d*(vMtx[2][1]*vMtx[1][0] - vMtx[2][0]*vMtx[1][1]);
762     mTmp[2][1] = d*(vMtx[2][0]*vMtx[0][1] - vMtx[2][1]*vMtx[0][0]);
763     mTmp[2][2] = d*(vMtx[1][1]*vMtx[0][0] - vMtx[1][0]*vMtx[0][1]);
764     memcpy(iMtx, mTmp, sizeof(mTmp));
765     return SDEnone;
766     }
767    
768     /* Transform and normalize direction (column) vector */
769     SDError
770     SDmapDir(FVECT resVec, RREAL vMtx[3][3], const FVECT inpVec)
771     {
772     FVECT vTmp;
773    
774     if ((resVec == NULL) | (inpVec == NULL))
775     return SDEargument;
776     if (vMtx == NULL) { /* assume they just want to normalize */
777     if (resVec != inpVec)
778     VCOPY(resVec, inpVec);
779     return (normalize(resVec) > .0) ? SDEnone : SDEargument;
780     }
781     vTmp[0] = DOT(vMtx[0], inpVec);
782     vTmp[1] = DOT(vMtx[1], inpVec);
783     vTmp[2] = DOT(vMtx[2], inpVec);
784     if (normalize(vTmp) == .0)
785     return SDEargument;
786     VCOPY(resVec, vTmp);
787     return SDEnone;
788     }
789    
790     /*################################################################*/
791     /*######### DEPRECATED ROUTINES AWAITING PERMANENT REMOVAL #######*/
792    
793     /*
794 greg 2.1 * Routines for handling BSDF data
795     */
796    
797     #include "standard.h"
798     #include "paths.h"
799     #include <ctype.h>
800    
801     #define MAXLATS 46 /* maximum number of latitudes */
802    
803     /* BSDF angle specification */
804     typedef struct {
805     char name[64]; /* basis name */
806     int nangles; /* total number of directions */
807     struct {
808     float tmin; /* starting theta */
809     short nphis; /* number of phis (0 term) */
810     } lat[MAXLATS+1]; /* latitudes */
811     } ANGLE_BASIS;
812    
813 greg 2.4 #define MAXABASES 7 /* limit on defined bases */
814 greg 2.1
815     static ANGLE_BASIS abase_list[MAXABASES] = {
816     {
817     "LBNL/Klems Full", 145,
818     { {-5., 1},
819     {5., 8},
820     {15., 16},
821     {25., 20},
822     {35., 24},
823     {45., 24},
824     {55., 24},
825     {65., 16},
826     {75., 12},
827     {90., 0} }
828     }, {
829     "LBNL/Klems Half", 73,
830     { {-6.5, 1},
831     {6.5, 8},
832     {19.5, 12},
833     {32.5, 16},
834     {46.5, 20},
835     {61.5, 12},
836     {76.5, 4},
837     {90., 0} }
838     }, {
839     "LBNL/Klems Quarter", 41,
840     { {-9., 1},
841     {9., 8},
842     {27., 12},
843     {46., 12},
844     {66., 8},
845     {90., 0} }
846     }
847     };
848    
849     static int nabases = 3; /* current number of defined bases */
850    
851 greg 2.9 #define FEQ(a,b) ((a)-(b) <= 1e-6 && (b)-(a) <= 1e-6)
852    
853     static int
854     fequal(double a, double b)
855     {
856     if (b != .0)
857     a = a/b - 1.;
858     return((a <= 1e-6) & (a >= -1e-6));
859     }
860 greg 2.3
861 greg 2.14 /* Returns the name of the given tag */
862 greg 2.3 #ifdef ezxml_name
863     #undef ezxml_name
864     static char *
865     ezxml_name(ezxml_t xml)
866     {
867     if (xml == NULL)
868     return(NULL);
869     return(xml->name);
870     }
871     #endif
872    
873 greg 2.14 /* Returns the given tag's character content or empty string if none */
874 greg 2.3 #ifdef ezxml_txt
875     #undef ezxml_txt
876     static char *
877     ezxml_txt(ezxml_t xml)
878     {
879     if (xml == NULL)
880     return("");
881     return(xml->txt);
882     }
883     #endif
884    
885 greg 2.1
886     static int
887     ab_getvec( /* get vector for this angle basis index */
888     FVECT v,
889     int ndx,
890     void *p
891     )
892     {
893     ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
894     int li;
895 greg 2.2 double pol, azi, d;
896 greg 2.1
897     if ((ndx < 0) | (ndx >= ab->nangles))
898     return(0);
899     for (li = 0; ndx >= ab->lat[li].nphis; li++)
900     ndx -= ab->lat[li].nphis;
901 greg 2.2 pol = PI/180.*0.5*(ab->lat[li].tmin + ab->lat[li+1].tmin);
902 greg 2.1 azi = 2.*PI*ndx/ab->lat[li].nphis;
903 greg 2.2 v[2] = d = cos(pol);
904     d = sqrt(1. - d*d); /* sin(pol) */
905 greg 2.1 v[0] = cos(azi)*d;
906     v[1] = sin(azi)*d;
907     return(1);
908     }
909    
910    
911     static int
912     ab_getndx( /* get index corresponding to the given vector */
913     FVECT v,
914     void *p
915     )
916     {
917     ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
918     int li, ndx;
919 greg 2.2 double pol, azi, d;
920 greg 2.1
921     if ((v[2] < -1.0) | (v[2] > 1.0))
922     return(-1);
923 greg 2.2 pol = 180.0/PI*acos(v[2]);
924 greg 2.1 azi = 180.0/PI*atan2(v[1], v[0]);
925     if (azi < 0.0) azi += 360.0;
926 greg 2.2 for (li = 1; ab->lat[li].tmin <= pol; li++)
927 greg 2.1 if (!ab->lat[li].nphis)
928     return(-1);
929     --li;
930     ndx = (int)((1./360.)*azi*ab->lat[li].nphis + 0.5);
931     if (ndx >= ab->lat[li].nphis) ndx = 0;
932     while (li--)
933     ndx += ab->lat[li].nphis;
934     return(ndx);
935     }
936    
937    
938     static double
939     ab_getohm( /* get solid angle for this angle basis index */
940     int ndx,
941     void *p
942     )
943     {
944     ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
945     int li;
946     double theta, theta1;
947    
948     if ((ndx < 0) | (ndx >= ab->nangles))
949     return(0);
950     for (li = 0; ndx >= ab->lat[li].nphis; li++)
951     ndx -= ab->lat[li].nphis;
952     theta1 = PI/180. * ab->lat[li+1].tmin;
953     if (ab->lat[li].nphis == 1) { /* special case */
954     if (ab->lat[li].tmin > FTINY)
955     error(USER, "unsupported BSDF coordinate system");
956     return(2.*PI*(1. - cos(theta1)));
957     }
958     theta = PI/180. * ab->lat[li].tmin;
959     return(2.*PI*(cos(theta) - cos(theta1))/(double)ab->lat[li].nphis);
960     }
961    
962    
963     static int
964     ab_getvecR( /* get reverse vector for this angle basis index */
965     FVECT v,
966     int ndx,
967     void *p
968     )
969     {
970     if (!ab_getvec(v, ndx, p))
971     return(0);
972    
973     v[0] = -v[0];
974     v[1] = -v[1];
975     v[2] = -v[2];
976    
977     return(1);
978     }
979    
980    
981     static int
982     ab_getndxR( /* get index corresponding to the reverse vector */
983     FVECT v,
984     void *p
985     )
986     {
987     FVECT v2;
988    
989     v2[0] = -v[0];
990     v2[1] = -v[1];
991     v2[2] = -v[2];
992    
993     return ab_getndx(v2, p);
994     }
995    
996    
997     static void
998 greg 2.4 load_angle_basis( /* load custom BSDF angle basis */
999 greg 2.3 ezxml_t wab
1000     )
1001     {
1002     char *abname = ezxml_txt(ezxml_child(wab, "AngleBasisName"));
1003     ezxml_t wbb;
1004     int i;
1005    
1006 greg 2.4 if (!abname || !*abname)
1007 greg 2.3 return;
1008     for (i = nabases; i--; )
1009 greg 2.12 if (!strcasecmp(abname, abase_list[i].name))
1010 greg 2.4 return; /* assume it's the same */
1011 greg 2.3 if (nabases >= MAXABASES)
1012     error(INTERNAL, "too many angle bases");
1013     strcpy(abase_list[nabases].name, abname);
1014     abase_list[nabases].nangles = 0;
1015     for (i = 0, wbb = ezxml_child(wab, "AngleBasisBlock");
1016     wbb != NULL; i++, wbb = wbb->next) {
1017     if (i >= MAXLATS)
1018     error(INTERNAL, "too many latitudes in custom basis");
1019     abase_list[nabases].lat[i+1].tmin = atof(ezxml_txt(
1020     ezxml_child(ezxml_child(wbb,
1021     "ThetaBounds"), "UpperTheta")));
1022     if (!i)
1023     abase_list[nabases].lat[i].tmin =
1024     -abase_list[nabases].lat[i+1].tmin;
1025 greg 2.9 else if (!fequal(atof(ezxml_txt(ezxml_child(ezxml_child(wbb,
1026 greg 2.3 "ThetaBounds"), "LowerTheta"))),
1027     abase_list[nabases].lat[i].tmin))
1028     error(WARNING, "theta values disagree in custom basis");
1029     abase_list[nabases].nangles +=
1030     abase_list[nabases].lat[i].nphis =
1031     atoi(ezxml_txt(ezxml_child(wbb, "nPhis")));
1032     }
1033     abase_list[nabases++].lat[i].nphis = 0;
1034     }
1035    
1036    
1037 greg 2.6 static void
1038     load_geometry( /* load geometric dimensions and description (if any) */
1039     struct BSDF_data *dp,
1040     ezxml_t wdb
1041     )
1042     {
1043     ezxml_t geom;
1044     double cfact;
1045     const char *fmt, *mgfstr;
1046    
1047     dp->dim[0] = dp->dim[1] = dp->dim[2] = 0;
1048     dp->mgf = NULL;
1049     if ((geom = ezxml_child(wdb, "Width")) != NULL)
1050     dp->dim[0] = atof(ezxml_txt(geom)) *
1051     to_meters(ezxml_attr(geom, "unit"));
1052     if ((geom = ezxml_child(wdb, "Height")) != NULL)
1053     dp->dim[1] = atof(ezxml_txt(geom)) *
1054     to_meters(ezxml_attr(geom, "unit"));
1055     if ((geom = ezxml_child(wdb, "Thickness")) != NULL)
1056     dp->dim[2] = atof(ezxml_txt(geom)) *
1057     to_meters(ezxml_attr(geom, "unit"));
1058     if ((geom = ezxml_child(wdb, "Geometry")) == NULL ||
1059     (mgfstr = ezxml_txt(geom)) == NULL)
1060     return;
1061     if ((fmt = ezxml_attr(geom, "format")) != NULL &&
1062     strcasecmp(fmt, "MGF")) {
1063     sprintf(errmsg, "unrecognized geometry format '%s'", fmt);
1064     error(WARNING, errmsg);
1065     return;
1066     }
1067     cfact = to_meters(ezxml_attr(geom, "unit"));
1068     dp->mgf = (char *)malloc(strlen(mgfstr)+32);
1069     if (dp->mgf == NULL)
1070     error(SYSTEM, "out of memory in load_geometry");
1071     if (cfact < 0.99 || cfact > 1.01)
1072     sprintf(dp->mgf, "xf -s %.5f\n%s\nxf\n", cfact, mgfstr);
1073     else
1074     strcpy(dp->mgf, mgfstr);
1075     }
1076    
1077    
1078 greg 2.3 static void
1079 greg 2.1 load_bsdf_data( /* load BSDF distribution for this wavelength */
1080     struct BSDF_data *dp,
1081     ezxml_t wdb
1082     )
1083     {
1084     char *cbasis = ezxml_txt(ezxml_child(wdb,"ColumnAngleBasis"));
1085     char *rbasis = ezxml_txt(ezxml_child(wdb,"RowAngleBasis"));
1086     char *sdata;
1087     int i;
1088    
1089 greg 2.4 if ((!cbasis || !*cbasis) | (!rbasis || !*rbasis)) {
1090 greg 2.1 error(WARNING, "missing column/row basis for BSDF");
1091     return;
1092     }
1093     for (i = nabases; i--; )
1094 greg 2.12 if (!strcasecmp(cbasis, abase_list[i].name)) {
1095 greg 2.1 dp->ninc = abase_list[i].nangles;
1096     dp->ib_priv = (void *)&abase_list[i];
1097     dp->ib_vec = ab_getvecR;
1098     dp->ib_ndx = ab_getndxR;
1099     dp->ib_ohm = ab_getohm;
1100     break;
1101     }
1102     if (i < 0) {
1103 greg 2.4 sprintf(errmsg, "undefined ColumnAngleBasis '%s'", cbasis);
1104 greg 2.1 error(WARNING, errmsg);
1105     return;
1106     }
1107     for (i = nabases; i--; )
1108 greg 2.12 if (!strcasecmp(rbasis, abase_list[i].name)) {
1109 greg 2.1 dp->nout = abase_list[i].nangles;
1110     dp->ob_priv = (void *)&abase_list[i];
1111     dp->ob_vec = ab_getvec;
1112     dp->ob_ndx = ab_getndx;
1113     dp->ob_ohm = ab_getohm;
1114     break;
1115     }
1116     if (i < 0) {
1117 greg 2.16 sprintf(errmsg, "undefined RowAngleBasis '%s'", rbasis);
1118 greg 2.1 error(WARNING, errmsg);
1119     return;
1120     }
1121     /* read BSDF data */
1122     sdata = ezxml_txt(ezxml_child(wdb,"ScatteringData"));
1123 greg 2.4 if (!sdata || !*sdata) {
1124 greg 2.1 error(WARNING, "missing BSDF ScatteringData");
1125     return;
1126     }
1127     dp->bsdf = (float *)malloc(sizeof(float)*dp->ninc*dp->nout);
1128     if (dp->bsdf == NULL)
1129     error(SYSTEM, "out of memory in load_bsdf_data");
1130     for (i = 0; i < dp->ninc*dp->nout; i++) {
1131     char *sdnext = fskip(sdata);
1132     if (sdnext == NULL) {
1133     error(WARNING, "bad/missing BSDF ScatteringData");
1134     free(dp->bsdf); dp->bsdf = NULL;
1135     return;
1136     }
1137     while (*sdnext && isspace(*sdnext))
1138     sdnext++;
1139     if (*sdnext == ',') sdnext++;
1140     dp->bsdf[i] = atof(sdata);
1141     sdata = sdnext;
1142     }
1143     while (isspace(*sdata))
1144     sdata++;
1145     if (*sdata) {
1146     sprintf(errmsg, "%d extra characters after BSDF ScatteringData",
1147 greg 2.5 (int)strlen(sdata));
1148 greg 2.1 error(WARNING, errmsg);
1149     }
1150     }
1151    
1152    
1153     static int
1154     check_bsdf_data( /* check that BSDF data is sane */
1155     struct BSDF_data *dp
1156     )
1157     {
1158 greg 2.2 double *omega_iarr, *omega_oarr;
1159 greg 2.7 double dom, contrib, hemi_total, full_total;
1160 greg 2.1 int nneg;
1161 greg 2.2 FVECT v;
1162 greg 2.1 int i, o;
1163    
1164     if (dp == NULL || dp->bsdf == NULL)
1165     return(0);
1166 greg 2.2 omega_iarr = (double *)calloc(dp->ninc, sizeof(double));
1167     omega_oarr = (double *)calloc(dp->nout, sizeof(double));
1168     if ((omega_iarr == NULL) | (omega_oarr == NULL))
1169 greg 2.1 error(SYSTEM, "out of memory in check_bsdf_data");
1170 greg 2.2 /* incoming projected solid angles */
1171     hemi_total = .0;
1172     for (i = dp->ninc; i--; ) {
1173     dom = getBSDF_incohm(dp,i);
1174     if (dom <= .0) {
1175     error(WARNING, "zero/negative incoming solid angle");
1176     continue;
1177     }
1178     if (!getBSDF_incvec(v,dp,i) || v[2] > FTINY) {
1179     error(WARNING, "illegal incoming BSDF direction");
1180     free(omega_iarr); free(omega_oarr);
1181     return(0);
1182     }
1183     hemi_total += omega_iarr[i] = dom * -v[2];
1184     }
1185     if ((hemi_total > 1.02*PI) | (hemi_total < 0.98*PI)) {
1186     sprintf(errmsg, "incoming BSDF hemisphere off by %.1f%%",
1187     100.*(hemi_total/PI - 1.));
1188     error(WARNING, errmsg);
1189     }
1190     dom = PI / hemi_total; /* fix normalization */
1191     for (i = dp->ninc; i--; )
1192     omega_iarr[i] *= dom;
1193     /* outgoing projected solid angles */
1194 greg 2.1 hemi_total = .0;
1195     for (o = dp->nout; o--; ) {
1196     dom = getBSDF_outohm(dp,o);
1197     if (dom <= .0) {
1198 greg 2.2 error(WARNING, "zero/negative outgoing solid angle");
1199 greg 2.1 continue;
1200     }
1201     if (!getBSDF_outvec(v,dp,o) || v[2] < -FTINY) {
1202     error(WARNING, "illegal outgoing BSDF direction");
1203 greg 2.2 free(omega_iarr); free(omega_oarr);
1204 greg 2.1 return(0);
1205     }
1206 greg 2.2 hemi_total += omega_oarr[o] = dom * v[2];
1207 greg 2.1 }
1208     if ((hemi_total > 1.02*PI) | (hemi_total < 0.98*PI)) {
1209     sprintf(errmsg, "outgoing BSDF hemisphere off by %.1f%%",
1210     100.*(hemi_total/PI - 1.));
1211     error(WARNING, errmsg);
1212     }
1213 greg 2.2 dom = PI / hemi_total; /* fix normalization */
1214 greg 2.1 for (o = dp->nout; o--; )
1215 greg 2.2 omega_oarr[o] *= dom;
1216     nneg = 0; /* check outgoing totals */
1217     for (i = 0; i < dp->ninc; i++) {
1218 greg 2.1 hemi_total = .0;
1219     for (o = dp->nout; o--; ) {
1220     double f = BSDF_value(dp,i,o);
1221 greg 2.2 if (f >= .0)
1222     hemi_total += f*omega_oarr[o];
1223     else {
1224     nneg += (f < -FTINY);
1225     BSDF_value(dp,i,o) = .0f;
1226     }
1227 greg 2.1 }
1228 greg 2.8 if (hemi_total > 1.01) {
1229 greg 2.2 sprintf(errmsg,
1230     "incoming BSDF direction %d passes %.1f%% of light",
1231     i, 100.*hemi_total);
1232 greg 2.1 error(WARNING, errmsg);
1233     }
1234     }
1235 greg 2.2 if (nneg) {
1236     sprintf(errmsg, "%d negative BSDF values (ignored)", nneg);
1237 greg 2.1 error(WARNING, errmsg);
1238     }
1239 greg 2.7 full_total = .0; /* reverse roles and check again */
1240 greg 2.2 for (o = 0; o < dp->nout; o++) {
1241     hemi_total = .0;
1242     for (i = dp->ninc; i--; )
1243     hemi_total += BSDF_value(dp,i,o) * omega_iarr[i];
1244    
1245 greg 2.8 if (hemi_total > 1.01) {
1246 greg 2.2 sprintf(errmsg,
1247     "outgoing BSDF direction %d collects %.1f%% of light",
1248     o, 100.*hemi_total);
1249     error(WARNING, errmsg);
1250     }
1251 greg 2.7 full_total += hemi_total*omega_oarr[o];
1252     }
1253     full_total /= PI;
1254 greg 2.8 if (full_total > 1.00001) {
1255     sprintf(errmsg, "BSDF transfers %.4f%% of light",
1256 greg 2.7 100.*full_total);
1257     error(WARNING, errmsg);
1258 greg 2.2 }
1259     free(omega_iarr); free(omega_oarr);
1260 greg 2.1 return(1);
1261     }
1262    
1263 greg 2.6
1264 greg 2.1 struct BSDF_data *
1265     load_BSDF( /* load BSDF data from file */
1266     char *fname
1267     )
1268     {
1269     char *path;
1270     ezxml_t fl, wtl, wld, wdb;
1271     struct BSDF_data *dp;
1272    
1273     path = getpath(fname, getrlibpath(), R_OK);
1274     if (path == NULL) {
1275     sprintf(errmsg, "cannot find BSDF file \"%s\"", fname);
1276     error(WARNING, errmsg);
1277     return(NULL);
1278     }
1279     fl = ezxml_parse_file(path);
1280     if (fl == NULL) {
1281     sprintf(errmsg, "cannot open BSDF \"%s\"", path);
1282     error(WARNING, errmsg);
1283     return(NULL);
1284     }
1285     if (ezxml_error(fl)[0]) {
1286     sprintf(errmsg, "BSDF \"%s\" %s", path, ezxml_error(fl));
1287     error(WARNING, errmsg);
1288     ezxml_free(fl);
1289     return(NULL);
1290     }
1291     if (strcmp(ezxml_name(fl), "WindowElement")) {
1292     sprintf(errmsg,
1293     "BSDF \"%s\": top level node not 'WindowElement'",
1294     path);
1295     error(WARNING, errmsg);
1296     ezxml_free(fl);
1297     return(NULL);
1298     }
1299     wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
1300 greg 2.10 if (strcasecmp(ezxml_txt(ezxml_child(ezxml_child(wtl,
1301     "DataDefinition"), "IncidentDataStructure")),
1302     "Columns")) {
1303     sprintf(errmsg,
1304     "BSDF \"%s\": unsupported IncidentDataStructure",
1305     path);
1306     error(WARNING, errmsg);
1307     ezxml_free(fl);
1308     return(NULL);
1309     }
1310 greg 2.3 load_angle_basis(ezxml_child(ezxml_child(wtl,
1311     "DataDefinition"), "AngleBasis"));
1312 greg 2.1 dp = (struct BSDF_data *)calloc(1, sizeof(struct BSDF_data));
1313 greg 2.6 load_geometry(dp, ezxml_child(wtl, "Material"));
1314 greg 2.1 for (wld = ezxml_child(wtl, "WavelengthData");
1315     wld != NULL; wld = wld->next) {
1316 greg 2.13 if (strcasecmp(ezxml_txt(ezxml_child(wld,"Wavelength")),
1317     "Visible"))
1318 greg 2.1 continue;
1319 greg 2.13 for (wdb = ezxml_child(wld, "WavelengthDataBlock");
1320     wdb != NULL; wdb = wdb->next)
1321     if (!strcasecmp(ezxml_txt(ezxml_child(wdb,
1322     "WavelengthDataDirection")),
1323 greg 2.1 "Transmission Front"))
1324 greg 2.13 break;
1325     if (wdb != NULL) { /* load front BTDF */
1326     load_bsdf_data(dp, wdb);
1327     break; /* ignore the rest */
1328     }
1329 greg 2.1 }
1330     ezxml_free(fl); /* done with XML file */
1331     if (!check_bsdf_data(dp)) {
1332     sprintf(errmsg, "bad/missing BTDF data in \"%s\"", path);
1333     error(WARNING, errmsg);
1334     free_BSDF(dp);
1335     dp = NULL;
1336     }
1337     return(dp);
1338     }
1339    
1340    
1341     void
1342     free_BSDF( /* free BSDF data structure */
1343     struct BSDF_data *b
1344     )
1345     {
1346     if (b == NULL)
1347     return;
1348 greg 2.6 if (b->mgf != NULL)
1349     free(b->mgf);
1350 greg 2.1 if (b->bsdf != NULL)
1351     free(b->bsdf);
1352     free(b);
1353     }
1354    
1355    
1356     int
1357     r_BSDF_incvec( /* compute random input vector at given location */
1358     FVECT v,
1359     struct BSDF_data *b,
1360     int i,
1361     double rv,
1362     MAT4 xm
1363     )
1364     {
1365     FVECT pert;
1366     double rad;
1367     int j;
1368    
1369     if (!getBSDF_incvec(v, b, i))
1370     return(0);
1371     rad = sqrt(getBSDF_incohm(b, i) / PI);
1372     multisamp(pert, 3, rv);
1373     for (j = 0; j < 3; j++)
1374     v[j] += rad*(2.*pert[j] - 1.);
1375     if (xm != NULL)
1376     multv3(v, v, xm);
1377     return(normalize(v) != 0.0);
1378     }
1379    
1380    
1381     int
1382     r_BSDF_outvec( /* compute random output vector at given location */
1383     FVECT v,
1384     struct BSDF_data *b,
1385     int o,
1386     double rv,
1387     MAT4 xm
1388     )
1389     {
1390     FVECT pert;
1391     double rad;
1392     int j;
1393    
1394     if (!getBSDF_outvec(v, b, o))
1395     return(0);
1396     rad = sqrt(getBSDF_outohm(b, o) / PI);
1397     multisamp(pert, 3, rv);
1398     for (j = 0; j < 3; j++)
1399     v[j] += rad*(2.*pert[j] - 1.);
1400     if (xm != NULL)
1401     multv3(v, v, xm);
1402     return(normalize(v) != 0.0);
1403     }
1404    
1405    
1406     static int
1407     addrot( /* compute rotation (x,y,z) => (xp,yp,zp) */
1408     char *xfarg[],
1409     FVECT xp,
1410     FVECT yp,
1411     FVECT zp
1412     )
1413     {
1414     static char bufs[3][16];
1415     int bn = 0;
1416     char **xfp = xfarg;
1417     double theta;
1418    
1419     if (yp[2]*yp[2] + zp[2]*zp[2] < 2.*FTINY*FTINY) {
1420     /* Special case for X' along Z-axis */
1421     theta = -atan2(yp[0], yp[1]);
1422     *xfp++ = "-ry";
1423     *xfp++ = xp[2] < 0.0 ? "90" : "-90";
1424     *xfp++ = "-rz";
1425     sprintf(bufs[bn], "%f", theta*(180./PI));
1426     *xfp++ = bufs[bn++];
1427     return(xfp - xfarg);
1428     }
1429     theta = atan2(yp[2], zp[2]);
1430     if (!FEQ(theta,0.0)) {
1431     *xfp++ = "-rx";
1432     sprintf(bufs[bn], "%f", theta*(180./PI));
1433     *xfp++ = bufs[bn++];
1434     }
1435     theta = asin(-xp[2]);
1436     if (!FEQ(theta,0.0)) {
1437     *xfp++ = "-ry";
1438     sprintf(bufs[bn], " %f", theta*(180./PI));
1439     *xfp++ = bufs[bn++];
1440     }
1441     theta = atan2(xp[1], xp[0]);
1442     if (!FEQ(theta,0.0)) {
1443     *xfp++ = "-rz";
1444     sprintf(bufs[bn], "%f", theta*(180./PI));
1445     *xfp++ = bufs[bn++];
1446     }
1447     *xfp = NULL;
1448     return(xfp - xfarg);
1449     }
1450    
1451    
1452     int
1453     getBSDF_xfm( /* compute BSDF orient. -> world orient. transform */
1454     MAT4 xm,
1455     FVECT nrm,
1456 greg 2.6 UpDir ud,
1457     char *xfbuf
1458 greg 2.1 )
1459     {
1460     char *xfargs[7];
1461     XF myxf;
1462     FVECT updir, xdest, ydest;
1463 greg 2.6 int i;
1464 greg 2.1
1465     updir[0] = updir[1] = updir[2] = 0.;
1466     switch (ud) {
1467     case UDzneg:
1468     updir[2] = -1.;
1469     break;
1470     case UDyneg:
1471     updir[1] = -1.;
1472     break;
1473     case UDxneg:
1474     updir[0] = -1.;
1475     break;
1476     case UDxpos:
1477     updir[0] = 1.;
1478     break;
1479     case UDypos:
1480     updir[1] = 1.;
1481     break;
1482     case UDzpos:
1483     updir[2] = 1.;
1484     break;
1485     case UDunknown:
1486     return(0);
1487     }
1488     fcross(xdest, updir, nrm);
1489     if (normalize(xdest) == 0.0)
1490     return(0);
1491     fcross(ydest, nrm, xdest);
1492     xf(&myxf, addrot(xfargs, xdest, ydest, nrm), xfargs);
1493     copymat4(xm, myxf.xfm);
1494 greg 2.6 if (xfbuf == NULL)
1495     return(1);
1496     /* return xf arguments as well */
1497     for (i = 0; xfargs[i] != NULL; i++) {
1498     *xfbuf++ = ' ';
1499     strcpy(xfbuf, xfargs[i]);
1500     while (*xfbuf) ++xfbuf;
1501     }
1502 greg 2.1 return(1);
1503     }
1504 greg 2.15
1505     /*######### END DEPRECATED ROUTINES #######*/
1506     /*################################################################*/