ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.30
Committed: Thu Jun 9 17:09:39 2011 UTC (12 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.29: +2 -1 lines
Log Message:
Fixes for Windows and bug fix in bsdf_m.c

File Contents

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