ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_t.c
Revision: 3.23
Committed: Mon Aug 22 18:21:05 2011 UTC (12 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad4R1
Changes since 3.22: +2 -2 lines
Log Message:
Fixed bug in Hilbert conversion

File Contents

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