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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdf.c,v 2.18 2011/04/08 23:23:28 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 #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 SDloadGeometry(SDData *sd, ezxml_t wdb)
81 {
82 ezxml_t geom;
83 double cfact;
84 const char *fmt, *mgfstr;
85
86 if (wdb == NULL) /* no geometry section? */
87 return SDEnone;
88 sd->dim[0] = sd->dim[1] = sd->dim[2] = .0;
89 if ((geom = ezxml_child(wdb, "Width")) != NULL)
90 sd->dim[0] = atof(ezxml_txt(geom)) *
91 to_meters(ezxml_attr(geom, "unit"));
92 if ((geom = ezxml_child(wdb, "Height")) != NULL)
93 sd->dim[1] = atof(ezxml_txt(geom)) *
94 to_meters(ezxml_attr(geom, "unit"));
95 if ((geom = ezxml_child(wdb, "Thickness")) != NULL)
96 sd->dim[2] = atof(ezxml_txt(geom)) *
97 to_meters(ezxml_attr(geom, "unit"));
98 if ((sd->dim[0] < .0) | (sd->dim[1] < .0) | (sd->dim[2] < .0)) {
99 sprintf(SDerrorDetail, "Negative size in \"%s\"", sd->name);
100 return SDEdata;
101 }
102 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 fmt, sd->name);
110 return SDEsupport;
111 }
112 cfact = to_meters(ezxml_attr(geom, "unit"));
113 sd->mgf = (char *)malloc(strlen(mgfstr)+32);
114 if (sd->mgf == NULL) {
115 strcpy(SDerrorDetail, "Out of memory in SDloadGeometry");
116 return SDEmemory;
117 }
118 if (cfact < 0.99 || cfact > 1.01)
119 sprintf(sd->mgf, "xf -s %.5f\n%s\nxf\n", cfact, mgfstr);
120 else
121 strcpy(sd->mgf, mgfstr);
122 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 ezxml_t fl, wtl;
131
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 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 /* load geometry if present */
162 lastErr = SDloadGeometry(sd, ezxml_child(wtl, "Material"));
163 if (lastErr)
164 return lastErr;
165 /* try loading variable resolution data */
166 lastErr = SDloadTre(sd, wtl);
167 /* check our result */
168 switch (lastErr) {
169 case SDEformat:
170 case SDEdata:
171 case SDEsupport: /* possibly we just tried the wrong format */
172 lastErr = SDloadMtx(sd, wtl);
173 break;
174 default: /* variable res. OK else serious error */
175 break;
176 }
177 /* done with XML file */
178 ezxml_free(fl);
179
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 }
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 SDclipName(char *res, const char *fname)
255 {
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 sd->rLambFront.spec.flags = 0;
302 sd->rLambBack.cieY = .0;
303 sd->rLambBack.spec.flags = 0;
304 sd->tLamb.cieY = .0;
305 sd->tLamb.spec.flags = 0;
306 }
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 t[--n] = rand()*(1./(RAND_MAX+.5));
455 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 t[n] = scale * ((double)coord[n] + rand()*(1./(RAND_MAX+.5)));
463 }
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 switch (qflags) {
493 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 if (ec) { /* all diffuse? */
523 projSA[0] = M_PI;
524 if (qflags == SDqueryMin+SDqueryMax)
525 projSA[1] = M_PI;
526 }
527 return SDEnone;
528 }
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 * 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 #define MAXABASES 7 /* limit on defined bases */
814
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 #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
861 /* Returns the name of the given tag */
862 #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 /* Returns the given tag's character content or empty string if none */
874 #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
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 double pol, azi, d;
896
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 pol = PI/180.*0.5*(ab->lat[li].tmin + ab->lat[li+1].tmin);
902 azi = 2.*PI*ndx/ab->lat[li].nphis;
903 v[2] = d = cos(pol);
904 d = sqrt(1. - d*d); /* sin(pol) */
905 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 double pol, azi, d;
920
921 if ((v[2] < -1.0) | (v[2] > 1.0))
922 return(-1);
923 pol = 180.0/PI*acos(v[2]);
924 azi = 180.0/PI*atan2(v[1], v[0]);
925 if (azi < 0.0) azi += 360.0;
926 for (li = 1; ab->lat[li].tmin <= pol; li++)
927 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 load_angle_basis( /* load custom BSDF angle basis */
999 ezxml_t wab
1000 )
1001 {
1002 char *abname = ezxml_txt(ezxml_child(wab, "AngleBasisName"));
1003 ezxml_t wbb;
1004 int i;
1005
1006 if (!abname || !*abname)
1007 return;
1008 for (i = nabases; i--; )
1009 if (!strcasecmp(abname, abase_list[i].name))
1010 return; /* assume it's the same */
1011 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 else if (!fequal(atof(ezxml_txt(ezxml_child(ezxml_child(wbb,
1026 "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 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 static void
1079 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 if ((!cbasis || !*cbasis) | (!rbasis || !*rbasis)) {
1090 error(WARNING, "missing column/row basis for BSDF");
1091 return;
1092 }
1093 for (i = nabases; i--; )
1094 if (!strcasecmp(cbasis, abase_list[i].name)) {
1095 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 sprintf(errmsg, "undefined ColumnAngleBasis '%s'", cbasis);
1104 error(WARNING, errmsg);
1105 return;
1106 }
1107 for (i = nabases; i--; )
1108 if (!strcasecmp(rbasis, abase_list[i].name)) {
1109 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 sprintf(errmsg, "undefined RowAngleBasis '%s'", rbasis);
1118 error(WARNING, errmsg);
1119 return;
1120 }
1121 /* read BSDF data */
1122 sdata = ezxml_txt(ezxml_child(wdb,"ScatteringData"));
1123 if (!sdata || !*sdata) {
1124 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 (int)strlen(sdata));
1148 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 double *omega_iarr, *omega_oarr;
1159 double dom, contrib, hemi_total, full_total;
1160 int nneg;
1161 FVECT v;
1162 int i, o;
1163
1164 if (dp == NULL || dp->bsdf == NULL)
1165 return(0);
1166 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 error(SYSTEM, "out of memory in check_bsdf_data");
1170 /* 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 hemi_total = .0;
1195 for (o = dp->nout; o--; ) {
1196 dom = getBSDF_outohm(dp,o);
1197 if (dom <= .0) {
1198 error(WARNING, "zero/negative outgoing solid angle");
1199 continue;
1200 }
1201 if (!getBSDF_outvec(v,dp,o) || v[2] < -FTINY) {
1202 error(WARNING, "illegal outgoing BSDF direction");
1203 free(omega_iarr); free(omega_oarr);
1204 return(0);
1205 }
1206 hemi_total += omega_oarr[o] = dom * v[2];
1207 }
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 dom = PI / hemi_total; /* fix normalization */
1214 for (o = dp->nout; o--; )
1215 omega_oarr[o] *= dom;
1216 nneg = 0; /* check outgoing totals */
1217 for (i = 0; i < dp->ninc; i++) {
1218 hemi_total = .0;
1219 for (o = dp->nout; o--; ) {
1220 double f = BSDF_value(dp,i,o);
1221 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 }
1228 if (hemi_total > 1.01) {
1229 sprintf(errmsg,
1230 "incoming BSDF direction %d passes %.1f%% of light",
1231 i, 100.*hemi_total);
1232 error(WARNING, errmsg);
1233 }
1234 }
1235 if (nneg) {
1236 sprintf(errmsg, "%d negative BSDF values (ignored)", nneg);
1237 error(WARNING, errmsg);
1238 }
1239 full_total = .0; /* reverse roles and check again */
1240 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 if (hemi_total > 1.01) {
1246 sprintf(errmsg,
1247 "outgoing BSDF direction %d collects %.1f%% of light",
1248 o, 100.*hemi_total);
1249 error(WARNING, errmsg);
1250 }
1251 full_total += hemi_total*omega_oarr[o];
1252 }
1253 full_total /= PI;
1254 if (full_total > 1.00001) {
1255 sprintf(errmsg, "BSDF transfers %.4f%% of light",
1256 100.*full_total);
1257 error(WARNING, errmsg);
1258 }
1259 free(omega_iarr); free(omega_oarr);
1260 return(1);
1261 }
1262
1263
1264 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 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 load_angle_basis(ezxml_child(ezxml_child(wtl,
1311 "DataDefinition"), "AngleBasis"));
1312 dp = (struct BSDF_data *)calloc(1, sizeof(struct BSDF_data));
1313 load_geometry(dp, ezxml_child(wtl, "Material"));
1314 for (wld = ezxml_child(wtl, "WavelengthData");
1315 wld != NULL; wld = wld->next) {
1316 if (strcasecmp(ezxml_txt(ezxml_child(wld,"Wavelength")),
1317 "Visible"))
1318 continue;
1319 for (wdb = ezxml_child(wld, "WavelengthDataBlock");
1320 wdb != NULL; wdb = wdb->next)
1321 if (!strcasecmp(ezxml_txt(ezxml_child(wdb,
1322 "WavelengthDataDirection")),
1323 "Transmission Front"))
1324 break;
1325 if (wdb != NULL) { /* load front BTDF */
1326 load_bsdf_data(dp, wdb);
1327 break; /* ignore the rest */
1328 }
1329 }
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 if (b->mgf != NULL)
1349 free(b->mgf);
1350 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 UpDir ud,
1457 char *xfbuf
1458 )
1459 {
1460 char *xfargs[7];
1461 XF myxf;
1462 FVECT updir, xdest, ydest;
1463 int i;
1464
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 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 return(1);
1503 }
1504
1505 /*######### END DEPRECATED ROUTINES #######*/
1506 /*################################################################*/