ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
Revision: 2.5
Committed: Sun Aug 1 22:26:37 2010 UTC (13 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.4: +2 -2 lines
Log Message:
Minor warning for 64-bit architectures

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdf.c,v 2.4 2010/07/03 05:28:05 greg Exp $";
3 #endif
4 /*
5 * Routines for handling BSDF data
6 */
7
8 #include "standard.h"
9 #include "bsdf.h"
10 #include "paths.h"
11 #include "ezxml.h"
12 #include <ctype.h>
13
14 #define MAXLATS 46 /* maximum number of latitudes */
15
16 /* BSDF angle specification */
17 typedef struct {
18 char name[64]; /* basis name */
19 int nangles; /* total number of directions */
20 struct {
21 float tmin; /* starting theta */
22 short nphis; /* number of phis (0 term) */
23 } lat[MAXLATS+1]; /* latitudes */
24 } ANGLE_BASIS;
25
26 #define MAXABASES 7 /* limit on defined bases */
27
28 static ANGLE_BASIS abase_list[MAXABASES] = {
29 {
30 "LBNL/Klems Full", 145,
31 { {-5., 1},
32 {5., 8},
33 {15., 16},
34 {25., 20},
35 {35., 24},
36 {45., 24},
37 {55., 24},
38 {65., 16},
39 {75., 12},
40 {90., 0} }
41 }, {
42 "LBNL/Klems Half", 73,
43 { {-6.5, 1},
44 {6.5, 8},
45 {19.5, 12},
46 {32.5, 16},
47 {46.5, 20},
48 {61.5, 12},
49 {76.5, 4},
50 {90., 0} }
51 }, {
52 "LBNL/Klems Quarter", 41,
53 { {-9., 1},
54 {9., 8},
55 {27., 12},
56 {46., 12},
57 {66., 8},
58 {90., 0} }
59 }
60 };
61
62 static int nabases = 3; /* current number of defined bases */
63
64 #define FEQ(a,b) ((a)-(b) <= 1e-7 && (b)-(a) <= 1e-7)
65
66 // returns the name of the given tag
67 #ifdef ezxml_name
68 #undef ezxml_name
69 static char *
70 ezxml_name(ezxml_t xml)
71 {
72 if (xml == NULL)
73 return(NULL);
74 return(xml->name);
75 }
76 #endif
77
78 // returns the given tag's character content or empty string if none
79 #ifdef ezxml_txt
80 #undef ezxml_txt
81 static char *
82 ezxml_txt(ezxml_t xml)
83 {
84 if (xml == NULL)
85 return("");
86 return(xml->txt);
87 }
88 #endif
89
90
91 static int
92 ab_getvec( /* get vector for this angle basis index */
93 FVECT v,
94 int ndx,
95 void *p
96 )
97 {
98 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
99 int li;
100 double pol, azi, d;
101
102 if ((ndx < 0) | (ndx >= ab->nangles))
103 return(0);
104 for (li = 0; ndx >= ab->lat[li].nphis; li++)
105 ndx -= ab->lat[li].nphis;
106 pol = PI/180.*0.5*(ab->lat[li].tmin + ab->lat[li+1].tmin);
107 azi = 2.*PI*ndx/ab->lat[li].nphis;
108 v[2] = d = cos(pol);
109 d = sqrt(1. - d*d); /* sin(pol) */
110 v[0] = cos(azi)*d;
111 v[1] = sin(azi)*d;
112 return(1);
113 }
114
115
116 static int
117 ab_getndx( /* get index corresponding to the given vector */
118 FVECT v,
119 void *p
120 )
121 {
122 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
123 int li, ndx;
124 double pol, azi, d;
125
126 if ((v[2] < -1.0) | (v[2] > 1.0))
127 return(-1);
128 pol = 180.0/PI*acos(v[2]);
129 azi = 180.0/PI*atan2(v[1], v[0]);
130 if (azi < 0.0) azi += 360.0;
131 for (li = 1; ab->lat[li].tmin <= pol; li++)
132 if (!ab->lat[li].nphis)
133 return(-1);
134 --li;
135 ndx = (int)((1./360.)*azi*ab->lat[li].nphis + 0.5);
136 if (ndx >= ab->lat[li].nphis) ndx = 0;
137 while (li--)
138 ndx += ab->lat[li].nphis;
139 return(ndx);
140 }
141
142
143 static double
144 ab_getohm( /* get solid angle for this angle basis index */
145 int ndx,
146 void *p
147 )
148 {
149 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
150 int li;
151 double theta, theta1;
152
153 if ((ndx < 0) | (ndx >= ab->nangles))
154 return(0);
155 for (li = 0; ndx >= ab->lat[li].nphis; li++)
156 ndx -= ab->lat[li].nphis;
157 theta1 = PI/180. * ab->lat[li+1].tmin;
158 if (ab->lat[li].nphis == 1) { /* special case */
159 if (ab->lat[li].tmin > FTINY)
160 error(USER, "unsupported BSDF coordinate system");
161 return(2.*PI*(1. - cos(theta1)));
162 }
163 theta = PI/180. * ab->lat[li].tmin;
164 return(2.*PI*(cos(theta) - cos(theta1))/(double)ab->lat[li].nphis);
165 }
166
167
168 static int
169 ab_getvecR( /* get reverse vector for this angle basis index */
170 FVECT v,
171 int ndx,
172 void *p
173 )
174 {
175 if (!ab_getvec(v, ndx, p))
176 return(0);
177
178 v[0] = -v[0];
179 v[1] = -v[1];
180 v[2] = -v[2];
181
182 return(1);
183 }
184
185
186 static int
187 ab_getndxR( /* get index corresponding to the reverse vector */
188 FVECT v,
189 void *p
190 )
191 {
192 FVECT v2;
193
194 v2[0] = -v[0];
195 v2[1] = -v[1];
196 v2[2] = -v[2];
197
198 return ab_getndx(v2, p);
199 }
200
201
202 static void
203 load_angle_basis( /* load custom BSDF angle basis */
204 ezxml_t wab
205 )
206 {
207 char *abname = ezxml_txt(ezxml_child(wab, "AngleBasisName"));
208 ezxml_t wbb;
209 int i;
210
211 if (!abname || !*abname)
212 return;
213 for (i = nabases; i--; )
214 if (!strcmp(abname, abase_list[i].name))
215 return; /* assume it's the same */
216 if (nabases >= MAXABASES)
217 error(INTERNAL, "too many angle bases");
218 strcpy(abase_list[nabases].name, abname);
219 abase_list[nabases].nangles = 0;
220 for (i = 0, wbb = ezxml_child(wab, "AngleBasisBlock");
221 wbb != NULL; i++, wbb = wbb->next) {
222 if (i >= MAXLATS)
223 error(INTERNAL, "too many latitudes in custom basis");
224 abase_list[nabases].lat[i+1].tmin = atof(ezxml_txt(
225 ezxml_child(ezxml_child(wbb,
226 "ThetaBounds"), "UpperTheta")));
227 if (!i)
228 abase_list[nabases].lat[i].tmin =
229 -abase_list[nabases].lat[i+1].tmin;
230 else if (!FEQ(atof(ezxml_txt(ezxml_child(ezxml_child(wbb,
231 "ThetaBounds"), "LowerTheta"))),
232 abase_list[nabases].lat[i].tmin))
233 error(WARNING, "theta values disagree in custom basis");
234 abase_list[nabases].nangles +=
235 abase_list[nabases].lat[i].nphis =
236 atoi(ezxml_txt(ezxml_child(wbb, "nPhis")));
237 }
238 abase_list[nabases++].lat[i].nphis = 0;
239 }
240
241
242 static void
243 load_bsdf_data( /* load BSDF distribution for this wavelength */
244 struct BSDF_data *dp,
245 ezxml_t wdb
246 )
247 {
248 char *cbasis = ezxml_txt(ezxml_child(wdb,"ColumnAngleBasis"));
249 char *rbasis = ezxml_txt(ezxml_child(wdb,"RowAngleBasis"));
250 char *sdata;
251 int i;
252
253 if ((!cbasis || !*cbasis) | (!rbasis || !*rbasis)) {
254 error(WARNING, "missing column/row basis for BSDF");
255 return;
256 }
257 for (i = nabases; i--; )
258 if (!strcmp(cbasis, abase_list[i].name)) {
259 dp->ninc = abase_list[i].nangles;
260 dp->ib_priv = (void *)&abase_list[i];
261 dp->ib_vec = ab_getvecR;
262 dp->ib_ndx = ab_getndxR;
263 dp->ib_ohm = ab_getohm;
264 break;
265 }
266 if (i < 0) {
267 sprintf(errmsg, "undefined ColumnAngleBasis '%s'", cbasis);
268 error(WARNING, errmsg);
269 return;
270 }
271 for (i = nabases; i--; )
272 if (!strcmp(rbasis, abase_list[i].name)) {
273 dp->nout = abase_list[i].nangles;
274 dp->ob_priv = (void *)&abase_list[i];
275 dp->ob_vec = ab_getvec;
276 dp->ob_ndx = ab_getndx;
277 dp->ob_ohm = ab_getohm;
278 break;
279 }
280 if (i < 0) {
281 sprintf(errmsg, "undefined RowAngleBasis '%s'", cbasis);
282 error(WARNING, errmsg);
283 return;
284 }
285 /* read BSDF data */
286 sdata = ezxml_txt(ezxml_child(wdb,"ScatteringData"));
287 if (!sdata || !*sdata) {
288 error(WARNING, "missing BSDF ScatteringData");
289 return;
290 }
291 dp->bsdf = (float *)malloc(sizeof(float)*dp->ninc*dp->nout);
292 if (dp->bsdf == NULL)
293 error(SYSTEM, "out of memory in load_bsdf_data");
294 for (i = 0; i < dp->ninc*dp->nout; i++) {
295 char *sdnext = fskip(sdata);
296 if (sdnext == NULL) {
297 error(WARNING, "bad/missing BSDF ScatteringData");
298 free(dp->bsdf); dp->bsdf = NULL;
299 return;
300 }
301 while (*sdnext && isspace(*sdnext))
302 sdnext++;
303 if (*sdnext == ',') sdnext++;
304 dp->bsdf[i] = atof(sdata);
305 sdata = sdnext;
306 }
307 while (isspace(*sdata))
308 sdata++;
309 if (*sdata) {
310 sprintf(errmsg, "%d extra characters after BSDF ScatteringData",
311 (int)strlen(sdata));
312 error(WARNING, errmsg);
313 }
314 }
315
316
317 static int
318 check_bsdf_data( /* check that BSDF data is sane */
319 struct BSDF_data *dp
320 )
321 {
322 double *omega_iarr, *omega_oarr;
323 double dom, contrib, hemi_total;
324 int nneg;
325 FVECT v;
326 int i, o;
327
328 if (dp == NULL || dp->bsdf == NULL)
329 return(0);
330 omega_iarr = (double *)calloc(dp->ninc, sizeof(double));
331 omega_oarr = (double *)calloc(dp->nout, sizeof(double));
332 if ((omega_iarr == NULL) | (omega_oarr == NULL))
333 error(SYSTEM, "out of memory in check_bsdf_data");
334 /* incoming projected solid angles */
335 hemi_total = .0;
336 for (i = dp->ninc; i--; ) {
337 dom = getBSDF_incohm(dp,i);
338 if (dom <= .0) {
339 error(WARNING, "zero/negative incoming solid angle");
340 continue;
341 }
342 if (!getBSDF_incvec(v,dp,i) || v[2] > FTINY) {
343 error(WARNING, "illegal incoming BSDF direction");
344 free(omega_iarr); free(omega_oarr);
345 return(0);
346 }
347 hemi_total += omega_iarr[i] = dom * -v[2];
348 }
349 if ((hemi_total > 1.02*PI) | (hemi_total < 0.98*PI)) {
350 sprintf(errmsg, "incoming BSDF hemisphere off by %.1f%%",
351 100.*(hemi_total/PI - 1.));
352 error(WARNING, errmsg);
353 }
354 dom = PI / hemi_total; /* fix normalization */
355 for (i = dp->ninc; i--; )
356 omega_iarr[i] *= dom;
357 /* outgoing projected solid angles */
358 hemi_total = .0;
359 for (o = dp->nout; o--; ) {
360 dom = getBSDF_outohm(dp,o);
361 if (dom <= .0) {
362 error(WARNING, "zero/negative outgoing solid angle");
363 continue;
364 }
365 if (!getBSDF_outvec(v,dp,o) || v[2] < -FTINY) {
366 error(WARNING, "illegal outgoing BSDF direction");
367 free(omega_iarr); free(omega_oarr);
368 return(0);
369 }
370 hemi_total += omega_oarr[o] = dom * v[2];
371 }
372 if ((hemi_total > 1.02*PI) | (hemi_total < 0.98*PI)) {
373 sprintf(errmsg, "outgoing BSDF hemisphere off by %.1f%%",
374 100.*(hemi_total/PI - 1.));
375 error(WARNING, errmsg);
376 }
377 dom = PI / hemi_total; /* fix normalization */
378 for (o = dp->nout; o--; )
379 omega_oarr[o] *= dom;
380 nneg = 0; /* check outgoing totals */
381 for (i = 0; i < dp->ninc; i++) {
382 hemi_total = .0;
383 for (o = dp->nout; o--; ) {
384 double f = BSDF_value(dp,i,o);
385 if (f >= .0)
386 hemi_total += f*omega_oarr[o];
387 else {
388 nneg += (f < -FTINY);
389 BSDF_value(dp,i,o) = .0f;
390 }
391 }
392 if (hemi_total > 1.02) {
393 sprintf(errmsg,
394 "incoming BSDF direction %d passes %.1f%% of light",
395 i, 100.*hemi_total);
396 error(WARNING, errmsg);
397 }
398 }
399 if (nneg) {
400 sprintf(errmsg, "%d negative BSDF values (ignored)", nneg);
401 error(WARNING, errmsg);
402 }
403 /* reverse roles and check again */
404 for (o = 0; o < dp->nout; o++) {
405 hemi_total = .0;
406 for (i = dp->ninc; i--; )
407 hemi_total += BSDF_value(dp,i,o) * omega_iarr[i];
408
409 if (hemi_total > 1.02) {
410 sprintf(errmsg,
411 "outgoing BSDF direction %d collects %.1f%% of light",
412 o, 100.*hemi_total);
413 error(WARNING, errmsg);
414 }
415 }
416 free(omega_iarr); free(omega_oarr);
417 return(1);
418 }
419
420 struct BSDF_data *
421 load_BSDF( /* load BSDF data from file */
422 char *fname
423 )
424 {
425 char *path;
426 ezxml_t fl, wtl, wld, wdb;
427 struct BSDF_data *dp;
428
429 path = getpath(fname, getrlibpath(), R_OK);
430 if (path == NULL) {
431 sprintf(errmsg, "cannot find BSDF file \"%s\"", fname);
432 error(WARNING, errmsg);
433 return(NULL);
434 }
435 fl = ezxml_parse_file(path);
436 if (fl == NULL) {
437 sprintf(errmsg, "cannot open BSDF \"%s\"", path);
438 error(WARNING, errmsg);
439 return(NULL);
440 }
441 if (ezxml_error(fl)[0]) {
442 sprintf(errmsg, "BSDF \"%s\" %s", path, ezxml_error(fl));
443 error(WARNING, errmsg);
444 ezxml_free(fl);
445 return(NULL);
446 }
447 if (strcmp(ezxml_name(fl), "WindowElement")) {
448 sprintf(errmsg,
449 "BSDF \"%s\": top level node not 'WindowElement'",
450 path);
451 error(WARNING, errmsg);
452 ezxml_free(fl);
453 return(NULL);
454 }
455 wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
456 load_angle_basis(ezxml_child(ezxml_child(wtl,
457 "DataDefinition"), "AngleBasis"));
458 dp = (struct BSDF_data *)calloc(1, sizeof(struct BSDF_data));
459 for (wld = ezxml_child(wtl, "WavelengthData");
460 wld != NULL; wld = wld->next) {
461 if (strcmp(ezxml_txt(ezxml_child(wld,"Wavelength")), "Visible"))
462 continue;
463 wdb = ezxml_child(wld, "WavelengthDataBlock");
464 if (wdb == NULL) continue;
465 if (strcmp(ezxml_txt(ezxml_child(wdb,"WavelengthDataDirection")),
466 "Transmission Front"))
467 continue;
468 load_bsdf_data(dp, wdb); /* load front BTDF */
469 break; /* ignore the rest */
470 }
471 ezxml_free(fl); /* done with XML file */
472 if (!check_bsdf_data(dp)) {
473 sprintf(errmsg, "bad/missing BTDF data in \"%s\"", path);
474 error(WARNING, errmsg);
475 free_BSDF(dp);
476 dp = NULL;
477 }
478 return(dp);
479 }
480
481
482 void
483 free_BSDF( /* free BSDF data structure */
484 struct BSDF_data *b
485 )
486 {
487 if (b == NULL)
488 return;
489 if (b->bsdf != NULL)
490 free(b->bsdf);
491 free(b);
492 }
493
494
495 int
496 r_BSDF_incvec( /* compute random input vector at given location */
497 FVECT v,
498 struct BSDF_data *b,
499 int i,
500 double rv,
501 MAT4 xm
502 )
503 {
504 FVECT pert;
505 double rad;
506 int j;
507
508 if (!getBSDF_incvec(v, b, i))
509 return(0);
510 rad = sqrt(getBSDF_incohm(b, i) / PI);
511 multisamp(pert, 3, rv);
512 for (j = 0; j < 3; j++)
513 v[j] += rad*(2.*pert[j] - 1.);
514 if (xm != NULL)
515 multv3(v, v, xm);
516 return(normalize(v) != 0.0);
517 }
518
519
520 int
521 r_BSDF_outvec( /* compute random output vector at given location */
522 FVECT v,
523 struct BSDF_data *b,
524 int o,
525 double rv,
526 MAT4 xm
527 )
528 {
529 FVECT pert;
530 double rad;
531 int j;
532
533 if (!getBSDF_outvec(v, b, o))
534 return(0);
535 rad = sqrt(getBSDF_outohm(b, o) / PI);
536 multisamp(pert, 3, rv);
537 for (j = 0; j < 3; j++)
538 v[j] += rad*(2.*pert[j] - 1.);
539 if (xm != NULL)
540 multv3(v, v, xm);
541 return(normalize(v) != 0.0);
542 }
543
544
545 static int
546 addrot( /* compute rotation (x,y,z) => (xp,yp,zp) */
547 char *xfarg[],
548 FVECT xp,
549 FVECT yp,
550 FVECT zp
551 )
552 {
553 static char bufs[3][16];
554 int bn = 0;
555 char **xfp = xfarg;
556 double theta;
557
558 if (yp[2]*yp[2] + zp[2]*zp[2] < 2.*FTINY*FTINY) {
559 /* Special case for X' along Z-axis */
560 theta = -atan2(yp[0], yp[1]);
561 *xfp++ = "-ry";
562 *xfp++ = xp[2] < 0.0 ? "90" : "-90";
563 *xfp++ = "-rz";
564 sprintf(bufs[bn], "%f", theta*(180./PI));
565 *xfp++ = bufs[bn++];
566 return(xfp - xfarg);
567 }
568 theta = atan2(yp[2], zp[2]);
569 if (!FEQ(theta,0.0)) {
570 *xfp++ = "-rx";
571 sprintf(bufs[bn], "%f", theta*(180./PI));
572 *xfp++ = bufs[bn++];
573 }
574 theta = asin(-xp[2]);
575 if (!FEQ(theta,0.0)) {
576 *xfp++ = "-ry";
577 sprintf(bufs[bn], " %f", theta*(180./PI));
578 *xfp++ = bufs[bn++];
579 }
580 theta = atan2(xp[1], xp[0]);
581 if (!FEQ(theta,0.0)) {
582 *xfp++ = "-rz";
583 sprintf(bufs[bn], "%f", theta*(180./PI));
584 *xfp++ = bufs[bn++];
585 }
586 *xfp = NULL;
587 return(xfp - xfarg);
588 }
589
590
591 int
592 getBSDF_xfm( /* compute BSDF orient. -> world orient. transform */
593 MAT4 xm,
594 FVECT nrm,
595 UpDir ud
596 )
597 {
598 char *xfargs[7];
599 XF myxf;
600 FVECT updir, xdest, ydest;
601
602 updir[0] = updir[1] = updir[2] = 0.;
603 switch (ud) {
604 case UDzneg:
605 updir[2] = -1.;
606 break;
607 case UDyneg:
608 updir[1] = -1.;
609 break;
610 case UDxneg:
611 updir[0] = -1.;
612 break;
613 case UDxpos:
614 updir[0] = 1.;
615 break;
616 case UDypos:
617 updir[1] = 1.;
618 break;
619 case UDzpos:
620 updir[2] = 1.;
621 break;
622 case UDunknown:
623 return(0);
624 }
625 fcross(xdest, updir, nrm);
626 if (normalize(xdest) == 0.0)
627 return(0);
628 fcross(ydest, nrm, xdest);
629 xf(&myxf, addrot(xfargs, xdest, ydest, nrm), xfargs);
630 copymat4(xm, myxf.xfm);
631 return(1);
632 }