ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.38
Committed: Sun Mar 4 23:28:34 2012 UTC (12 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.37: +8 -3 lines
Log Message:
Improved error diagnostics

File Contents

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