ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_t.c
Revision: 3.15
Committed: Fri Jun 3 18:12:58 2011 UTC (12 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.14: +43 -26 lines
Log Message:
Fixed bugs in variable-resolution isotropic BSDFs

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdf_t.c,v 3.14 2011/06/01 05:21:18 greg Exp $";
3 #endif
4 /*
5 * bsdf_t.c
6 *
7 * Definitions for variable-resolution BSDF trees
8 *
9 * Created by Greg Ward on 2/2/11.
10 *
11 */
12
13 #include "rtio.h"
14 #include <stdlib.h>
15 #include <math.h>
16 #include <ctype.h>
17 #include "ezxml.h"
18 #include "bsdf.h"
19 #include "bsdf_t.h"
20 #include "hilbert.h"
21
22 /* Callback function type for SDtraverseTre() */
23 typedef int SDtreCallback(float val, const double *cmin,
24 double csiz, void *cptr);
25
26 /* reference width maximum (1.0) */
27 static const unsigned iwbits = sizeof(unsigned)*4;
28 static const unsigned iwmax = (1<<(sizeof(unsigned)*4))-1;
29 /* maximum cumulative value */
30 static const unsigned cumlmax = ~0;
31 /* constant z-vector */
32 static const FVECT zvec = {.0, .0, 1.};
33
34 /* Struct used for our distribution-building callback */
35 typedef struct {
36 int nic; /* number of input coordinates */
37 unsigned alen; /* current array length */
38 unsigned nall; /* number of allocated entries */
39 unsigned wmin; /* minimum square size so far */
40 unsigned wmax; /* maximum square size */
41 struct outdir_s {
42 unsigned hent; /* entering Hilbert index */
43 int wid; /* this square size */
44 float bsdf; /* BSDF for this square */
45 } *darr; /* output direction array */
46 } SDdistScaffold;
47
48 /* Allocate a new scattering distribution node */
49 static SDNode *
50 SDnewNode(int nd, int lg)
51 {
52 SDNode *st;
53
54 if (nd <= 0) {
55 strcpy(SDerrorDetail, "Zero dimension BSDF node request");
56 return NULL;
57 }
58 if (nd > SD_MAXDIM) {
59 sprintf(SDerrorDetail, "Illegal BSDF dimension (%d > %d)",
60 nd, SD_MAXDIM);
61 return NULL;
62 }
63 if (lg < 0) {
64 st = (SDNode *)malloc(sizeof(SDNode) +
65 sizeof(st->u.t[0])*((1<<nd) - 1));
66 if (st == NULL) {
67 sprintf(SDerrorDetail,
68 "Cannot allocate %d branch BSDF tree", 1<<nd);
69 return NULL;
70 }
71 memset(st->u.t, 0, sizeof(st->u.t[0])<<nd);
72 } else {
73 st = (SDNode *)malloc(sizeof(SDNode) +
74 sizeof(st->u.v[0])*((1 << nd*lg) - 1));
75 if (st == NULL) {
76 sprintf(SDerrorDetail,
77 "Cannot allocate %d BSDF leaves", 1 << nd*lg);
78 return NULL;
79 }
80 }
81 st->ndim = nd;
82 st->log2GR = lg;
83 return st;
84 }
85
86 /* Free an SD tree */
87 static void
88 SDfreeTre(SDNode *st)
89 {
90 int n;
91
92 if (st == NULL)
93 return;
94 for (n = (st->log2GR < 0) << st->ndim; n--; )
95 SDfreeTre(st->u.t[n]);
96 free(st);
97 }
98
99 /* Free a variable-resolution BSDF */
100 static void
101 SDFreeBTre(void *p)
102 {
103 SDTre *sdt = (SDTre *)p;
104
105 if (sdt == NULL)
106 return;
107 SDfreeTre(sdt->st);
108 free(sdt);
109 }
110
111 /* Fill branch's worth of grid values from subtree */
112 static void
113 fill_grid_branch(float *dptr, const float *sptr, int nd, int shft)
114 {
115 unsigned n = 1 << (shft-1);
116
117 if (!--nd) { /* end on the line */
118 memcpy(dptr, sptr, sizeof(*dptr)*n);
119 return;
120 }
121 while (n--) /* recurse on each slice */
122 fill_grid_branch(dptr + (n << shft*nd),
123 sptr + (n << (shft-1)*nd), nd, shft);
124 }
125
126 /* Get pointer at appropriate offset for the given branch */
127 static float *
128 grid_branch_start(SDNode *st, int n)
129 {
130 unsigned skipsiz = 1 << (st->log2GR - 1);
131 float *vptr = st->u.v;
132 int i;
133
134 for (i = st->ndim; i--; skipsiz <<= st->log2GR)
135 if (1<<i & n)
136 vptr += skipsiz;
137 return vptr;
138 }
139
140 /* Simplify (consolidate) a tree by flattening uniform depth regions */
141 static SDNode *
142 SDsimplifyTre(SDNode *st)
143 {
144 int match, n;
145
146 if (st == NULL) /* check for invalid tree */
147 return NULL;
148 if (st->log2GR >= 0) /* grid just returns unaltered */
149 return st;
150 match = 1; /* check if grids below match */
151 for (n = 0; n < 1<<st->ndim; n++) {
152 if ((st->u.t[n] = SDsimplifyTre(st->u.t[n])) == NULL)
153 return NULL; /* propogate error up call stack */
154 match &= (st->u.t[n]->log2GR == st->u.t[0]->log2GR);
155 }
156 if (match && (match = st->u.t[0]->log2GR) >= 0) {
157 SDNode *stn = SDnewNode(st->ndim, match + 1);
158 if (stn == NULL) /* out of memory? */
159 return st;
160 /* transfer values to new grid */
161 for (n = 1 << st->ndim; n--; )
162 fill_grid_branch(grid_branch_start(stn, n),
163 st->u.t[n]->u.v, stn->ndim, stn->log2GR);
164 SDfreeTre(st); /* free old tree */
165 st = stn; /* return new one */
166 }
167 return st;
168 }
169
170 /* Find smallest leaf in tree */
171 static double
172 SDsmallestLeaf(const SDNode *st)
173 {
174 if (st->log2GR < 0) { /* tree branches */
175 double lmin = 1.;
176 int n;
177 for (n = 1<<st->ndim; n--; ) {
178 double lsiz = SDsmallestLeaf(st->u.t[n]);
179 if (lsiz < lmin)
180 lmin = lsiz;
181 }
182 return .5*lmin;
183 }
184 /* leaf grid width */
185 return 1. / (double)(1 << st->log2GR);
186 }
187
188 /* Add up N-dimensional hypercube array values over the given box */
189 static double
190 SDiterSum(const float *va, int nd, int shft, const int *imin, const int *imax)
191 {
192 const unsigned skipsiz = 1 << --nd*shft;
193 double sum = .0;
194 int i;
195
196 va += *imin * skipsiz;
197
198 if (skipsiz == 1)
199 for (i = *imin; i < *imax; i++)
200 sum += *va++;
201 else
202 for (i = *imin; i < *imax; i++, va += skipsiz)
203 sum += SDiterSum(va, nd, shft, imin+1, imax+1);
204 return sum;
205 }
206
207 /* Average BSDF leaves over an orthotope defined by the unit hypercube */
208 static double
209 SDavgTreBox(const SDNode *st, const double *bmin, const double *bmax)
210 {
211 unsigned n;
212 int i;
213
214 if (!st)
215 return .0;
216 /* check box limits */
217 for (i = st->ndim; i--; ) {
218 if (bmin[i] >= 1.)
219 return .0;
220 if (bmax[i] <= 0)
221 return .0;
222 if (bmin[i] >= bmax[i])
223 return .0;
224 }
225 if (st->log2GR < 0) { /* iterate on subtree */
226 double sum = .0, wsum = 1e-20;
227 double sbmin[SD_MAXDIM], sbmax[SD_MAXDIM], w;
228 for (n = 1 << st->ndim; n--; ) {
229 w = 1.;
230 for (i = st->ndim; i--; ) {
231 sbmin[i] = 2.*bmin[i];
232 sbmax[i] = 2.*bmax[i];
233 if (n & 1<<i) {
234 sbmin[i] -= 1.;
235 sbmax[i] -= 1.;
236 }
237 if (sbmin[i] < .0) sbmin[i] = .0;
238 if (sbmax[i] > 1.) sbmax[i] = 1.;
239 if (sbmin[i] >= sbmax[i]) {
240 w = .0;
241 break;
242 }
243 w *= sbmax[i] - sbmin[i];
244 }
245 if (w > 1e-10) {
246 sum += w * SDavgTreBox(st->u.t[n], sbmin, sbmax);
247 wsum += w;
248 }
249 }
250 return sum / wsum;
251 } else { /* iterate over leaves */
252 int imin[SD_MAXDIM], imax[SD_MAXDIM];
253
254 n = 1;
255 for (i = st->ndim; i--; ) {
256 imin[i] = (bmin[i] <= 0) ? 0 :
257 (int)((1 << st->log2GR)*bmin[i]);
258 imax[i] = (bmax[i] >= 1.) ? (1 << st->log2GR) :
259 (int)((1 << st->log2GR)*bmax[i] + .999999);
260 n *= imax[i] - imin[i];
261 }
262 if (n)
263 return SDiterSum(st->u.v, st->ndim,
264 st->log2GR, imin, imax) / (double)n;
265 }
266 return .0;
267 }
268
269 /* Recursive call for SDtraverseTre() */
270 static int
271 SDdotravTre(const SDNode *st, const double *pos, int cmask,
272 SDtreCallback *cf, void *cptr,
273 const double *cmin, double csiz)
274 {
275 int rv, rval = 0;
276 double bmin[SD_MAXDIM];
277 int i, n;
278 /* in branches? */
279 if (st->log2GR < 0) {
280 unsigned skipmask = 0;
281 csiz *= .5;
282 for (i = st->ndim; i--; )
283 if (1<<i & cmask)
284 if (pos[i] < cmin[i] + csiz)
285 for (n = 1 << st->ndim; n--; ) {
286 if (n & 1<<i)
287 skipmask |= 1<<n;
288 }
289 else
290 for (n = 1 << st->ndim; n--; ) {
291 if (!(n & 1<<i))
292 skipmask |= 1<<n;
293 }
294 for (n = 1 << st->ndim; n--; ) {
295 if (1<<n & skipmask)
296 continue;
297 for (i = st->ndim; i--; )
298 if (1<<i & n)
299 bmin[i] = cmin[i] + csiz;
300 else
301 bmin[i] = cmin[i];
302 for (i = SD_MAXDIM; i-- > st->ndim; )
303 bmin[i] = .0;
304
305 rval += rv = SDdotravTre(st->u.t[n], pos, cmask,
306 cf, cptr, bmin, csiz);
307 if (rv < 0)
308 return rv;
309 }
310 } else { /* else traverse leaves */
311 int clim[SD_MAXDIM][2];
312 int cpos[SD_MAXDIM];
313
314 if (st->log2GR == 0) /* short cut */
315 return (*cf)(st->u.v[0], cmin, csiz, cptr);
316
317 csiz /= (double)(1 << st->log2GR);
318 /* assign coord. ranges */
319 for (i = st->ndim; i--; )
320 if (1<<i & cmask) {
321 clim[i][0] = (pos[i] - cmin[i])/csiz;
322 /* check overflow from f.p. error */
323 clim[i][0] -= clim[i][0] >> st->log2GR;
324 clim[i][1] = clim[i][0] + 1;
325 } else {
326 clim[i][0] = 0;
327 clim[i][1] = 1 << st->log2GR;
328 }
329 /* fill in unused dimensions */
330 for (i = SD_MAXDIM; i-- > st->ndim; ) {
331 clim[i][0] = 0; clim[i][1] = 1;
332 }
333 #if (SD_MAXDIM == 4)
334 bmin[0] = cmin[0] + csiz*clim[0][0];
335 for (cpos[0] = clim[0][0]; cpos[0] < clim[0][1]; cpos[0]++) {
336 bmin[1] = cmin[1] + csiz*clim[1][0];
337 for (cpos[1] = clim[1][0]; cpos[1] < clim[1][1]; cpos[1]++) {
338 bmin[2] = cmin[2] + csiz*clim[2][0];
339 for (cpos[2] = clim[2][0]; cpos[2] < clim[2][1]; cpos[2]++) {
340 bmin[3] = cmin[3] + csiz*(cpos[3] = clim[3][0]);
341 n = cpos[0];
342 for (i = 1; i < st->ndim; i++)
343 n = (n << st->log2GR) + cpos[i];
344 for ( ; cpos[3] < clim[3][1]; cpos[3]++) {
345 rval += rv = (*cf)(st->u.v[n++], bmin, csiz, cptr);
346 if (rv < 0)
347 return rv;
348 bmin[3] += csiz;
349 }
350 bmin[2] += csiz;
351 }
352 bmin[1] += csiz;
353 }
354 bmin[0] += csiz;
355 }
356 #else
357 _!_ "broken code segment!"
358 #endif
359 }
360 return rval;
361 }
362
363 /* Traverse a tree, visiting nodes in a slice that fits partial position */
364 static int
365 SDtraverseTre(const SDNode *st, const double *pos, int cmask,
366 SDtreCallback *cf, void *cptr)
367 {
368 static double czero[SD_MAXDIM];
369 int i;
370 /* check arguments */
371 if ((st == NULL) | (cf == NULL))
372 return -1;
373 for (i = st->ndim; i--; )
374 if (1<<i & cmask && (pos[i] < 0) | (pos[i] >= 1.))
375 return -1;
376
377 return SDdotravTre(st, pos, cmask, cf, cptr, czero, 1.);
378 }
379
380 /* Look up tree value at the given grid position */
381 static float
382 SDlookupTre(const SDNode *st, const double *pos, double *hcube)
383 {
384 double spos[SD_MAXDIM];
385 int i, n, t;
386 /* initialize voxel return */
387 if (hcube) {
388 hcube[i = st->ndim] = 1.;
389 while (i--)
390 hcube[i] = .0;
391 }
392 /* climb the tree */
393 while (st->log2GR < 0) {
394 n = 0; /* move to appropriate branch */
395 if (hcube) hcube[st->ndim] *= .5;
396 for (i = st->ndim; i--; ) {
397 spos[i] = 2.*pos[i];
398 t = (spos[i] >= 1.);
399 n |= t<<i;
400 spos[i] -= (double)t;
401 if (hcube) hcube[i] += (double)t * hcube[st->ndim];
402 }
403 st = st->u.t[n]; /* avoids tail recursion */
404 pos = spos;
405 }
406 if (st->log2GR == 0) /* short cut */
407 return st->u.v[0];
408 n = t = 0; /* find grid array index */
409 for (i = st->ndim; i--; ) {
410 n += (int)((1<<st->log2GR)*pos[i]) << t;
411 t += st->log2GR;
412 }
413 if (hcube) { /* compute final hypercube */
414 hcube[st->ndim] /= (double)(1<<st->log2GR);
415 for (i = st->ndim; i--; )
416 hcube[i] += floor((1<<st->log2GR)*pos[i])*hcube[st->ndim];
417 }
418 return st->u.v[n]; /* no interpolation */
419 }
420
421 /* Query BSDF value and sample hypercube for the given vectors */
422 static float
423 SDqueryTre(const SDTre *sdt, const FVECT outVec, const FVECT inVec, double *hc)
424 {
425 FVECT rOutVec;
426 double gridPos[4];
427
428 switch (sdt->sidef) { /* whose side are you on? */
429 case SD_UFRONT:
430 if ((outVec[2] < 0) | (inVec[2] < 0))
431 return -1.;
432 break;
433 case SD_UBACK:
434 if ((outVec[2] > 0) | (inVec[2] > 0))
435 return -1.;
436 break;
437 case SD_XMIT:
438 if ((outVec[2] > 0) == (inVec[2] > 0))
439 return -1.;
440 break;
441 default:
442 return -1.;
443 }
444 /* convert vector coordinates */
445 if (sdt->st->ndim == 3) {
446 spinvector(rOutVec, outVec, zvec, -atan2(-inVec[1],-inVec[0]));
447 gridPos[0] = .5 - .5*sqrt(inVec[0]*inVec[0] + inVec[1]*inVec[1]);
448 SDdisk2square(gridPos+1, rOutVec[0], rOutVec[1]);
449 } else if (sdt->st->ndim == 4) {
450 SDdisk2square(gridPos, -inVec[0], -inVec[1]);
451 SDdisk2square(gridPos+2, outVec[0], outVec[1]);
452 } else
453 return -1.; /* should be internal error */
454
455 return SDlookupTre(sdt->st, gridPos, hc);
456 }
457
458 /* Compute non-diffuse component for variable-resolution BSDF */
459 static int
460 SDgetTreBSDF(float coef[SDmaxCh], const FVECT outVec,
461 const FVECT inVec, SDComponent *sdc)
462 {
463 /* check arguments */
464 if ((coef == NULL) | (outVec == NULL) | (inVec == NULL) | (sdc == NULL)
465 || sdc->dist == NULL)
466 return 0;
467 /* get nearest BSDF value */
468 coef[0] = SDqueryTre((SDTre *)sdc->dist, outVec, inVec, NULL);
469 return (coef[0] >= 0); /* monochromatic for now */
470 }
471
472 /* Callback to build cumulative distribution using SDtraverseTre() */
473 static int
474 build_scaffold(float val, const double *cmin, double csiz, void *cptr)
475 {
476 SDdistScaffold *sp = (SDdistScaffold *)cptr;
477 int wid = csiz*(double)iwmax + .5;
478 bitmask_t bmin[2], bmax[2];
479
480 cmin += sp->nic; /* skip to output coords */
481 if (wid < sp->wmin) /* new minimum width? */
482 sp->wmin = wid;
483 if (wid > sp->wmax) /* new maximum? */
484 sp->wmax = wid;
485 if (sp->alen >= sp->nall) { /* need more space? */
486 struct outdir_s *ndarr;
487 sp->nall += 1024;
488 ndarr = (struct outdir_s *)realloc(sp->darr,
489 sizeof(struct outdir_s)*sp->nall);
490 if (ndarr == NULL) {
491 sprintf(SDerrorDetail,
492 "Cannot grow scaffold to %u entries", sp->nall);
493 return -1; /* abort build */
494 }
495 sp->darr = ndarr;
496 }
497 /* find Hilbert entry index */
498 bmin[0] = cmin[0]*(double)iwmax + .5;
499 bmin[1] = cmin[1]*(double)iwmax + .5;
500 bmax[0] = bmin[0] + wid-1;
501 bmax[1] = bmin[1] + wid-1;
502 hilbert_box_vtx(2, sizeof(bitmask_t), iwbits, 1, bmin, bmax);
503 sp->darr[sp->alen].hent = hilbert_c2i(2, iwbits, bmin);
504 sp->darr[sp->alen].wid = wid;
505 sp->darr[sp->alen].bsdf = val;
506 sp->alen++; /* on to the next entry */
507 return 0;
508 }
509
510 /* Scaffold comparison function for qsort -- ascending Hilbert index */
511 static int
512 sscmp(const void *p1, const void *p2)
513 {
514 unsigned h1 = (*(const struct outdir_s *)p1).hent;
515 unsigned h2 = (*(const struct outdir_s *)p2).hent;
516
517 if (h1 > h2)
518 return 1;
519 if (h1 < h2)
520 return -1;
521 return 0;
522 }
523
524 /* Create a new cumulative distribution for the given input direction */
525 static SDTreCDst *
526 make_cdist(const SDTre *sdt, const double *pos)
527 {
528 SDdistScaffold myScaffold;
529 SDTreCDst *cd;
530 struct outdir_s *sp;
531 double scale, cursum;
532 int i;
533 /* initialize scaffold */
534 myScaffold.wmin = iwmax;
535 myScaffold.wmax = 0;
536 myScaffold.nic = sdt->st->ndim - 2;
537 myScaffold.alen = 0;
538 myScaffold.nall = 512;
539 myScaffold.darr = (struct outdir_s *)malloc(sizeof(struct outdir_s) *
540 myScaffold.nall);
541 if (myScaffold.darr == NULL)
542 return NULL;
543 /* grow the distribution */
544 if (SDtraverseTre(sdt->st, pos, (1<<myScaffold.nic)-1,
545 &build_scaffold, &myScaffold) < 0) {
546 free(myScaffold.darr);
547 return NULL;
548 }
549 /* allocate result holder */
550 cd = (SDTreCDst *)malloc(sizeof(SDTreCDst) +
551 sizeof(cd->carr[0])*myScaffold.alen);
552 if (cd == NULL) {
553 sprintf(SDerrorDetail,
554 "Cannot allocate %u entry cumulative distribution",
555 myScaffold.alen);
556 free(myScaffold.darr);
557 return NULL;
558 }
559 cd->isodist = (myScaffold.nic == 1);
560 /* sort the distribution */
561 qsort(myScaffold.darr, cd->calen = myScaffold.alen,
562 sizeof(struct outdir_s), &sscmp);
563
564 /* record input range */
565 scale = myScaffold.wmin / (double)iwmax;
566 for (i = myScaffold.nic; i--; ) {
567 cd->clim[i][0] = floor(pos[i]/scale) * scale;
568 cd->clim[i][1] = cd->clim[i][0] + scale;
569 }
570 if (cd->isodist) { /* avoid issue in SDqueryTreProjSA() */
571 cd->clim[1][0] = cd->clim[0][0];
572 cd->clim[1][1] = cd->clim[0][1];
573 }
574 cd->max_psa = myScaffold.wmax / (double)iwmax;
575 cd->max_psa *= cd->max_psa * M_PI;
576 cd->sidef = sdt->sidef;
577 cd->cTotal = 1e-20; /* compute directional total */
578 sp = myScaffold.darr;
579 for (i = myScaffold.alen; i--; sp++)
580 cd->cTotal += sp->bsdf * (double)sp->wid * sp->wid;
581 cursum = .0; /* go back and get cumulative values */
582 scale = (double)cumlmax / cd->cTotal;
583 sp = myScaffold.darr;
584 for (i = 0; i < cd->calen; i++, sp++) {
585 cd->carr[i].hndx = sp->hent;
586 cd->carr[i].cuml = scale*cursum + .5;
587 cursum += sp->bsdf * (double)sp->wid * sp->wid;
588 }
589 cd->carr[i].hndx = ~0; /* make final entry */
590 cd->carr[i].cuml = cumlmax;
591 cd->cTotal *= M_PI/(double)iwmax/iwmax;
592 /* all done, clean up and return */
593 free(myScaffold.darr);
594 return cd;
595 }
596
597 /* Find or allocate a cumulative distribution for the given incoming vector */
598 const SDCDst *
599 SDgetTreCDist(const FVECT inVec, SDComponent *sdc)
600 {
601 const SDTre *sdt;
602 double inCoord[2];
603 int vflags;
604 int i;
605 SDTreCDst *cd, *cdlast;
606 /* check arguments */
607 if ((inVec == NULL) | (sdc == NULL) ||
608 (sdt = (SDTre *)sdc->dist) == NULL)
609 return NULL;
610 if (sdt->st->ndim == 3) /* isotropic BSDF? */
611 inCoord[0] = .5 - .5*sqrt(inVec[0]*inVec[0] + inVec[1]*inVec[1]);
612 else if (sdt->st->ndim == 4)
613 SDdisk2square(inCoord, -inVec[0], -inVec[1]);
614 else
615 return NULL; /* should be internal error */
616 cdlast = NULL; /* check for direction in cache list */
617 for (cd = (SDTreCDst *)sdc->cdList; cd != NULL;
618 cdlast = cd, cd = (SDTreCDst *)cd->next) {
619 for (i = sdt->st->ndim - 2; i--; )
620 if ((cd->clim[i][0] > inCoord[i]) |
621 (inCoord[i] >= cd->clim[i][1]))
622 break;
623 if (i < 0)
624 break; /* means we have a match */
625 }
626 if (cd == NULL) /* need to create new entry? */
627 cdlast = cd = make_cdist(sdt, inCoord);
628 if (cdlast != NULL) { /* move entry to head of cache list */
629 cdlast->next = cd->next;
630 cd->next = sdc->cdList;
631 sdc->cdList = (SDCDst *)cd;
632 }
633 return (SDCDst *)cd; /* ready to go */
634 }
635
636 /* Query solid angle for vector(s) */
637 static SDError
638 SDqueryTreProjSA(double *psa, const FVECT v1, const RREAL *v2,
639 int qflags, SDComponent *sdc)
640 {
641 double myPSA[2];
642 /* check arguments */
643 if ((psa == NULL) | (v1 == NULL) | (sdc == NULL) ||
644 sdc->dist == NULL)
645 return SDEargument;
646 /* get projected solid angle(s) */
647 if (v2 != NULL) {
648 const SDTre *sdt = (SDTre *)sdc->dist;
649 double hcube[SD_MAXDIM];
650 if (SDqueryTre(sdt, v1, v2, hcube) < 0) {
651 strcpy(SDerrorDetail, "Bad call to SDqueryTreProjSA");
652 return SDEinternal;
653 }
654 myPSA[0] = hcube[sdt->st->ndim];
655 myPSA[1] = myPSA[0] *= myPSA[0] * M_PI;
656 } else {
657 const SDTreCDst *cd = (const SDTreCDst *)SDgetTreCDist(v1, sdc);
658 if (cd == NULL)
659 return SDEmemory;
660 myPSA[0] = M_PI * (cd->clim[0][1] - cd->clim[0][0]) *
661 (cd->clim[1][1] - cd->clim[1][0]);
662 myPSA[1] = cd->max_psa;
663 }
664 switch (qflags) { /* record based on flag settings */
665 case SDqueryVal:
666 *psa = myPSA[0];
667 break;
668 case SDqueryMax:
669 if (myPSA[1] > *psa)
670 *psa = myPSA[1];
671 break;
672 case SDqueryMin+SDqueryMax:
673 if (myPSA[1] > psa[1])
674 psa[1] = myPSA[1];
675 /* fall through */
676 case SDqueryMin:
677 if (myPSA[0] < psa[0])
678 psa[0] = myPSA[0];
679 break;
680 }
681 return SDEnone;
682 }
683
684 /* Sample cumulative distribution */
685 static SDError
686 SDsampTreCDist(FVECT ioVec, double randX, const SDCDst *cdp)
687 {
688 const unsigned nBitsC = 4*sizeof(bitmask_t);
689 const unsigned nExtraBits = 8*(sizeof(bitmask_t)-sizeof(unsigned));
690 const SDTreCDst *cd = (const SDTreCDst *)cdp;
691 const unsigned target = randX*cumlmax;
692 bitmask_t hndx, hcoord[2];
693 double gpos[3], rotangle;
694 int i, iupper, ilower;
695 /* check arguments */
696 if ((ioVec == NULL) | (cd == NULL))
697 return SDEargument;
698 if (ioVec[2] > 0) {
699 if (!(cd->sidef & SD_UFRONT))
700 return SDEargument;
701 } else if (!(cd->sidef & SD_UBACK))
702 return SDEargument;
703 /* binary search to find position */
704 ilower = 0; iupper = cd->calen;
705 while ((i = (iupper + ilower) >> 1) != ilower)
706 if ((long)target >= (long)cd->carr[i].cuml)
707 ilower = i;
708 else
709 iupper = i;
710 /* localize random position */
711 randX = (randX*cumlmax - cd->carr[ilower].cuml) /
712 (double)(cd->carr[iupper].cuml - cd->carr[ilower].cuml);
713 /* index in longer Hilbert curve */
714 hndx = (randX*cd->carr[iupper].hndx + (1.-randX)*cd->carr[ilower].hndx)
715 * (double)((bitmask_t)1 << nExtraBits);
716 /* convert Hilbert index to vector */
717 hilbert_i2c(2, nBitsC, hndx, hcoord);
718 for (i = 2; i--; )
719 gpos[i] = ((double)hcoord[i] + rand()*(1./(RAND_MAX+.5))) /
720 (double)((bitmask_t)1 << nBitsC);
721 SDsquare2disk(gpos, gpos[0], gpos[1]);
722 /* compute Z-coordinate */
723 gpos[2] = 1. - gpos[0]*gpos[0] - gpos[1]*gpos[1];
724 if (gpos[2] > 0) /* paranoia, I hope */
725 gpos[2] = sqrt(gpos[2]);
726 /* emit from back? */
727 if (ioVec[2] > 0 ^ cd->sidef != SD_XMIT)
728 gpos[2] = -gpos[2];
729 if (cd->isodist) { /* rotate isotropic result */
730 rotangle = atan2(-ioVec[1],-ioVec[0]);
731 VCOPY(ioVec, gpos);
732 spinvector(ioVec, ioVec, zvec, rotangle);
733 } else
734 VCOPY(ioVec, gpos);
735 return SDEnone;
736 }
737
738 /* Advance pointer to the next non-white character in the string (or nul) */
739 static int
740 next_token(char **spp)
741 {
742 while (isspace(**spp))
743 ++*spp;
744 return **spp;
745 }
746
747 /* Advance pointer past matching token (or any token if c==0) */
748 #define eat_token(spp,c) (next_token(spp)==(c) ^ !(c) ? *(*(spp))++ : 0)
749
750 /* Count words from this point in string to '}' */
751 static int
752 count_values(char *cp)
753 {
754 int n = 0;
755
756 while (next_token(&cp) != '}' && *cp) {
757 while (!isspace(*cp) & (*cp != ',') & (*cp != '}'))
758 if (!*++cp)
759 break;
760 ++n;
761 eat_token(&cp, ',');
762 }
763 return n;
764 }
765
766 /* Load an array of real numbers, returning total */
767 static int
768 load_values(char **spp, float *va, int n)
769 {
770 float *v = va;
771 char *svnext;
772
773 while (n-- > 0 && (svnext = fskip(*spp)) != NULL) {
774 *v++ = atof(*spp);
775 *spp = svnext;
776 eat_token(spp, ',');
777 }
778 return v - va;
779 }
780
781 /* Load BSDF tree data */
782 static SDNode *
783 load_tree_data(char **spp, int nd)
784 {
785 SDNode *st;
786 int n;
787
788 if (!eat_token(spp, '{')) {
789 strcpy(SDerrorDetail, "Missing '{' in tensor tree");
790 return NULL;
791 }
792 if (next_token(spp) == '{') { /* tree branches */
793 st = SDnewNode(nd, -1);
794 if (st == NULL)
795 return NULL;
796 for (n = 0; n < 1<<nd; n++)
797 if ((st->u.t[n] = load_tree_data(spp, nd)) == NULL) {
798 SDfreeTre(st);
799 return NULL;
800 }
801 } else { /* else load value grid */
802 int bsiz;
803 n = count_values(*spp); /* see how big the grid is */
804 for (bsiz = 0; bsiz < 8*sizeof(size_t); bsiz += nd)
805 if (1<<bsiz == n)
806 break;
807 if (bsiz >= 8*sizeof(size_t)) {
808 strcpy(SDerrorDetail, "Illegal value count in tensor tree");
809 return NULL;
810 }
811 st = SDnewNode(nd, bsiz/nd);
812 if (st == NULL)
813 return NULL;
814 if (load_values(spp, st->u.v, n) != n) {
815 strcpy(SDerrorDetail, "Real format error in tensor tree");
816 SDfreeTre(st);
817 return NULL;
818 }
819 }
820 if (!eat_token(spp, '}')) {
821 strcpy(SDerrorDetail, "Missing '}' in tensor tree");
822 SDfreeTre(st);
823 return NULL;
824 }
825 eat_token(spp, ',');
826 return st;
827 }
828
829 /* Compute min. proj. solid angle and max. direct hemispherical scattering */
830 static SDError
831 get_extrema(SDSpectralDF *df)
832 {
833 SDNode *st = (*(SDTre *)df->comp[0].dist).st;
834 double stepWidth, dhemi, bmin[4], bmax[4];
835
836 stepWidth = SDsmallestLeaf(st);
837 df->minProjSA = M_PI*stepWidth*stepWidth;
838 if (stepWidth < .03125)
839 stepWidth = .03125; /* 1/32 resolution good enough */
840 df->maxHemi = .0;
841 if (st->ndim == 3) { /* isotropic BSDF */
842 bmin[1] = bmin[2] = .0;
843 bmax[1] = bmax[2] = 1.;
844 for (bmin[0] = .0; bmin[0] < .5-FTINY; bmin[0] += stepWidth) {
845 bmax[0] = bmin[0] + stepWidth;
846 dhemi = SDavgTreBox(st, bmin, bmax);
847 if (dhemi > df->maxHemi)
848 df->maxHemi = dhemi;
849 }
850 } else if (st->ndim == 4) { /* anisotropic BSDF */
851 bmin[2] = bmin[3] = .0;
852 bmax[2] = bmax[3] = 1.;
853 for (bmin[0] = .0; bmin[0] < 1.-FTINY; bmin[0] += stepWidth) {
854 bmax[0] = bmin[0] + stepWidth;
855 for (bmin[1] = .0; bmin[1] < 1.-FTINY; bmin[1] += stepWidth) {
856 bmax[1] = bmin[1] + stepWidth;
857 dhemi = SDavgTreBox(st, bmin, bmax);
858 if (dhemi > df->maxHemi)
859 df->maxHemi = dhemi;
860 }
861 }
862 } else
863 return SDEinternal;
864 /* correct hemispherical value */
865 df->maxHemi *= M_PI;
866 return SDEnone;
867 }
868
869 /* Load BSDF distribution for this wavelength */
870 static SDError
871 load_bsdf_data(SDData *sd, ezxml_t wdb, int ndim)
872 {
873 SDSpectralDF *df;
874 SDTre *sdt;
875 char *sdata;
876 int i;
877 /* allocate BSDF component */
878 sdata = ezxml_txt(ezxml_child(wdb, "WavelengthDataDirection"));
879 if (!sdata)
880 return SDEnone;
881 /*
882 * Remember that front and back are reversed from WINDOW 6 orientations
883 */
884 if (!strcasecmp(sdata, "Transmission")) {
885 if (sd->tf != NULL)
886 SDfreeSpectralDF(sd->tf);
887 if ((sd->tf = SDnewSpectralDF(1)) == NULL)
888 return SDEmemory;
889 df = sd->tf;
890 } else if (!strcasecmp(sdata, "Reflection Front")) {
891 if (sd->rb != NULL) /* note back-front reversal */
892 SDfreeSpectralDF(sd->rb);
893 if ((sd->rb = SDnewSpectralDF(1)) == NULL)
894 return SDEmemory;
895 df = sd->rb;
896 } else if (!strcasecmp(sdata, "Reflection Back")) {
897 if (sd->rf != NULL) /* note front-back reversal */
898 SDfreeSpectralDF(sd->rf);
899 if ((sd->rf = SDnewSpectralDF(1)) == NULL)
900 return SDEmemory;
901 df = sd->rf;
902 } else
903 return SDEnone;
904 /* XXX should also check "ScatteringDataType" for consistency? */
905 /* get angle bases */
906 sdata = ezxml_txt(ezxml_child(wdb,"AngleBasis"));
907 if (!sdata || strcasecmp(sdata, "LBNL/Shirley-Chiu")) {
908 sprintf(SDerrorDetail, "%s angle basis for BSDF '%s'",
909 !sdata ? "Missing" : "Unsupported", sd->name);
910 return !sdata ? SDEformat : SDEsupport;
911 }
912 /* allocate BSDF tree */
913 sdt = (SDTre *)malloc(sizeof(SDTre));
914 if (sdt == NULL)
915 return SDEmemory;
916 if (df == sd->rf)
917 sdt->sidef = SD_UFRONT;
918 else if (df == sd->rb)
919 sdt->sidef = SD_UBACK;
920 else
921 sdt->sidef = SD_XMIT;
922 sdt->st = NULL;
923 df->comp[0].cspec[0] = c_dfcolor; /* XXX monochrome for now */
924 df->comp[0].dist = sdt;
925 df->comp[0].func = &SDhandleTre;
926 /* read BSDF data */
927 sdata = ezxml_txt(ezxml_child(wdb, "ScatteringData"));
928 if (!sdata || !next_token(&sdata)) {
929 sprintf(SDerrorDetail, "Missing BSDF ScatteringData in '%s'",
930 sd->name);
931 return SDEformat;
932 }
933 sdt->st = load_tree_data(&sdata, ndim);
934 if (sdt->st == NULL)
935 return SDEformat;
936 if (next_token(&sdata)) { /* check for unconsumed characters */
937 sprintf(SDerrorDetail,
938 "Extra characters at end of ScatteringData in '%s'",
939 sd->name);
940 return SDEformat;
941 }
942 /* flatten branches where possible */
943 sdt->st = SDsimplifyTre(sdt->st);
944 if (sdt->st == NULL)
945 return SDEinternal;
946 return get_extrema(df); /* compute global quantities */
947 }
948
949 /* Find minimum value in tree */
950 static float
951 SDgetTreMin(const SDNode *st)
952 {
953 float vmin = FHUGE;
954 int n;
955
956 if (st->log2GR < 0) {
957 for (n = 1<<st->ndim; n--; ) {
958 float v = SDgetTreMin(st->u.t[n]);
959 if (v < vmin)
960 vmin = v;
961 }
962 } else {
963 for (n = 1<<(st->ndim*st->log2GR); n--; )
964 if (st->u.v[n] < vmin)
965 vmin = st->u.v[n];
966 }
967 return vmin;
968 }
969
970 /* Subtract the given value from all tree nodes */
971 static void
972 SDsubtractTreVal(SDNode *st, float val)
973 {
974 int n;
975
976 if (st->log2GR < 0) {
977 for (n = 1<<st->ndim; n--; )
978 SDsubtractTreVal(st->u.t[n], val);
979 } else {
980 for (n = 1<<(st->ndim*st->log2GR); n--; )
981 if ((st->u.v[n] -= val) < 0)
982 st->u.v[n] = .0f;
983 }
984 }
985
986 /* Subtract minimum value from BSDF */
987 static double
988 subtract_min(SDNode *st)
989 {
990 float vmin;
991 /* be sure to skip unused portion */
992 if (st->ndim == 3) {
993 int n;
994 vmin = 1./M_PI;
995 if (st->log2GR < 0) {
996 for (n = 0; n < 8; n += 2) {
997 float v = SDgetTreMin(st->u.t[n]);
998 if (v < vmin)
999 vmin = v;
1000 }
1001 } else if (st->log2GR) {
1002 for (n = 1 << (3*st->log2GR - 1); n--; )
1003 if (st->u.v[n] < vmin)
1004 vmin = st->u.v[n];
1005 } else
1006 vmin = st->u.v[0];
1007 } else /* anisotropic covers entire tree */
1008 vmin = SDgetTreMin(st);
1009
1010 if (vmin <= FTINY)
1011 return .0;
1012
1013 SDsubtractTreVal(st, vmin);
1014
1015 return M_PI * vmin; /* return hemispherical value */
1016 }
1017
1018 /* Extract and separate diffuse portion of BSDF */
1019 static void
1020 extract_diffuse(SDValue *dv, SDSpectralDF *df)
1021 {
1022 int n;
1023
1024 if (df == NULL || df->ncomp <= 0) {
1025 dv->spec = c_dfcolor;
1026 dv->cieY = .0;
1027 return;
1028 }
1029 dv->spec = df->comp[0].cspec[0];
1030 dv->cieY = subtract_min((*(SDTre *)df->comp[0].dist).st);
1031 /* in case of multiple components */
1032 for (n = df->ncomp; --n; ) {
1033 double ymin = subtract_min((*(SDTre *)df->comp[n].dist).st);
1034 c_cmix(&dv->spec, dv->cieY, &dv->spec, ymin, &df->comp[n].cspec[0]);
1035 dv->cieY += ymin;
1036 }
1037 df->maxHemi -= dv->cieY; /* adjust maximum hemispherical */
1038 /* make sure everything is set */
1039 c_ccvt(&dv->spec, C_CSXY+C_CSSPEC);
1040 }
1041
1042 /* Load a variable-resolution BSDF tree from an open XML file */
1043 SDError
1044 SDloadTre(SDData *sd, ezxml_t wtl)
1045 {
1046 SDError ec;
1047 ezxml_t wld, wdb;
1048 int rank;
1049 char *txt;
1050 /* basic checks and tensor rank */
1051 txt = ezxml_txt(ezxml_child(ezxml_child(wtl,
1052 "DataDefinition"), "IncidentDataStructure"));
1053 if (txt == NULL || !*txt) {
1054 sprintf(SDerrorDetail,
1055 "BSDF \"%s\": missing IncidentDataStructure",
1056 sd->name);
1057 return SDEformat;
1058 }
1059 if (!strcasecmp(txt, "TensorTree3"))
1060 rank = 3;
1061 else if (!strcasecmp(txt, "TensorTree4"))
1062 rank = 4;
1063 else {
1064 sprintf(SDerrorDetail,
1065 "BSDF \"%s\": unsupported IncidentDataStructure",
1066 sd->name);
1067 return SDEsupport;
1068 }
1069 /* load BSDF components */
1070 for (wld = ezxml_child(wtl, "WavelengthData");
1071 wld != NULL; wld = wld->next) {
1072 if (strcasecmp(ezxml_txt(ezxml_child(wld,"Wavelength")),
1073 "Visible"))
1074 continue; /* just visible for now */
1075 for (wdb = ezxml_child(wld, "WavelengthDataBlock");
1076 wdb != NULL; wdb = wdb->next)
1077 if ((ec = load_bsdf_data(sd, wdb, rank)) != SDEnone)
1078 return ec;
1079 }
1080 /* separate diffuse components */
1081 extract_diffuse(&sd->rLambFront, sd->rf);
1082 extract_diffuse(&sd->rLambBack, sd->rb);
1083 extract_diffuse(&sd->tLamb, sd->tf);
1084 /* return success */
1085 return SDEnone;
1086 }
1087
1088 /* Variable resolution BSDF methods */
1089 SDFunc SDhandleTre = {
1090 &SDgetTreBSDF,
1091 &SDqueryTreProjSA,
1092 &SDgetTreCDist,
1093 &SDsampTreCDist,
1094 &SDFreeBTre,
1095 };