ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/mkillum4.c
Revision: 2.19
Committed: Fri Jun 12 17:37:37 2009 UTC (14 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.18: +1 -1 lines
State: FILE REMOVED
Log Message:
Broke BSDF-handling routines into their own module

File Contents

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