ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_t.c
Revision: 3.6
Committed: Sun Apr 24 19:39:21 2011 UTC (13 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.5: +437 -31 lines
Log Message:
Partial implementation of variable-resolution BSDFs

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdf_t.c,v 3.5 2011/04/19 21:31:22 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 iwmax = (1<<(sizeof(unsigned)*4))-1;
28
29 /* Struct used for our distribution-building callback */
30 typedef struct {
31 int wmin; /* minimum square size so far */
32 int wmax; /* maximum square size */
33 int nic; /* number of input coordinates */
34 int alen; /* current array length */
35 int nall; /* number of allocated entries */
36 struct outdir_s {
37 unsigned hent; /* entering Hilbert index */
38 int wid; /* this square size */
39 float bsdf; /* BSDF for this square */
40 } *darr; /* output direction array */
41 } SDdistScaffold;
42
43 /* Allocate a new scattering distribution node */
44 static SDNode *
45 SDnewNode(int nd, int lg)
46 {
47 SDNode *st;
48
49 if (nd <= 0) {
50 strcpy(SDerrorDetail, "Zero dimension BSDF node request");
51 return NULL;
52 }
53 if (nd > SD_MAXDIM) {
54 sprintf(SDerrorDetail, "Illegal BSDF dimension (%d > %d)",
55 nd, SD_MAXDIM);
56 return NULL;
57 }
58 if (lg < 0) {
59 st = (SDNode *)malloc(sizeof(SDNode) +
60 ((1<<nd) - 1)*sizeof(st->u.t[0]));
61 if (st != NULL)
62 memset(st->u.t, 0, sizeof(st->u.t[0])<<nd);
63 } else
64 st = (SDNode *)malloc(sizeof(SDNode) +
65 ((1 << nd*lg) - 1)*sizeof(st->u.v[0]));
66
67 if (st == NULL) {
68 if (lg < 0)
69 sprintf(SDerrorDetail,
70 "Cannot allocate %d branch BSDF tree", 1<<nd);
71 else
72 sprintf(SDerrorDetail,
73 "Cannot allocate %d BSDF leaves", 1 << nd*lg);
74 return NULL;
75 }
76 st->ndim = nd;
77 st->log2GR = lg;
78 return st;
79 }
80
81 /* Free an SD tree */
82 static void
83 SDfreeTre(SDNode *st)
84 {
85 int i;
86
87 if (st == NULL)
88 return;
89 for (i = (st->log2GR < 0) << st->ndim; i--; )
90 SDfreeTre(st->u.t[i]);
91 free((void *)st);
92 }
93
94 /* Free a variable-resolution BSDF */
95 static void
96 SDFreeBTre(void *p)
97 {
98 SDTre *sdt = (SDTre *)p;
99
100 if (sdt == NULL)
101 return;
102 SDfreeTre(sdt->st);
103 free(sdt);
104 }
105
106 /* Add up N-dimensional hypercube array values over the given box */
107 static double
108 SDiterSum(const float *va, int nd, int siz, const int *imin, const int *imax)
109 {
110 double sum = .0;
111 unsigned skipsiz = 1;
112 int i;
113
114 for (i = nd; --i > 0; )
115 skipsiz *= siz;
116 if (skipsiz == 1)
117 for (i = *imin; i < *imax; i++)
118 sum += va[i];
119 else
120 for (i = *imin; i < *imax; i++)
121 sum += SDiterSum(va + i*skipsiz,
122 nd-1, siz, imin+1, imax+1);
123 return sum;
124 }
125
126 /* Average BSDF leaves over an orthotope defined by the unit hypercube */
127 static double
128 SDavgTreBox(const SDNode *st, const double *bmin, const double *bmax)
129 {
130 int imin[SD_MAXDIM], imax[SD_MAXDIM];
131 unsigned n;
132 int i;
133
134 if (!st)
135 return .0;
136 /* check box limits */
137 for (i = st->ndim; i--; ) {
138 if (bmin[i] >= 1.)
139 return .0;
140 if (bmax[i] <= .0)
141 return .0;
142 if (bmin[i] >= bmax[i])
143 return .0;
144 }
145 if (st->log2GR < 0) { /* iterate on subtree */
146 double sum = .0, wsum = 1e-20;
147 double sbmin[SD_MAXDIM], sbmax[SD_MAXDIM], w;
148
149 for (n = 1 << st->ndim; n--; ) {
150 w = 1.;
151 for (i = st->ndim; i--; ) {
152 sbmin[i] = 2.*bmin[i];
153 sbmax[i] = 2.*bmax[i];
154 if (n & 1<<i) {
155 sbmin[i] -= 1.;
156 sbmax[i] -= 1.;
157 }
158 if (sbmin[i] < .0) sbmin[i] = .0;
159 if (sbmax[i] > 1.) sbmax[i] = 1.;
160 w *= sbmax[i] - sbmin[i];
161 }
162 if (w > 1e-10) {
163 sum += w * SDavgTreBox(st->u.t[n], sbmin, sbmax);
164 wsum += w;
165 }
166 }
167 return sum / wsum;
168 }
169 n = 1; /* iterate over leaves */
170 for (i = st->ndim; i--; ) {
171 imin[i] = (bmin[i] <= .0) ? 0
172 : (int)((1 << st->log2GR)*bmin[i]);
173 imax[i] = (bmax[i] >= 1.) ? (1 << st->log2GR)
174 : (int)((1 << st->log2GR)*bmax[i] + .999999);
175 n *= imax[i] - imin[i];
176 }
177 if (!n)
178 return .0;
179
180 return SDiterSum(st->u.v, st->ndim, 1 << st->log2GR, imin, imax) /
181 (double)n;
182 }
183
184 /* Recursive call for SDtraverseTre() */
185 static int
186 SDdotravTre(const SDNode *st, const double *pos, int cmask,
187 SDtreCallback *cf, void *cptr,
188 const double *cmin, double csiz)
189 {
190 int rv, rval = 0;
191 double bmin[SD_MAXDIM];
192 int i, n;
193 /* in branches? */
194 if (st->log2GR < 0) {
195 unsigned skipmask = 0;
196
197 csiz *= .5;
198 for (i = st->ndim; i--; )
199 if (1<<i & cmask)
200 if (pos[i] < cmin[i] + csiz)
201 for (n = 1 << st->ndim; n--; )
202 if (n & 1<<i)
203 skipmask |= 1<<n;
204 else
205 for (n = 1 << st->ndim; n--; )
206 if (!(n & 1<<i))
207 skipmask |= 1<<n;
208 for (n = 1 << st->ndim; n--; ) {
209 if (1<<n & skipmask)
210 continue;
211 for (i = st->ndim; i--; )
212 if (1<<i & n)
213 bmin[i] = cmin[i] + csiz;
214 else
215 bmin[i] = cmin[i];
216
217 rval += rv = SDdotravTre(st->u.t[n], pos, cmask,
218 cf, cptr, bmin, csiz);
219 if (rv < 0)
220 return rv;
221 }
222 } else { /* else traverse leaves */
223 int clim[SD_MAXDIM][2];
224 int cpos[SD_MAXDIM];
225
226 if (st->log2GR == 0) /* short cut */
227 return (*cf)(st->u.v[0], cmin, csiz, cptr);
228
229 csiz /= (double)(1 << st->log2GR);
230 /* assign coord. ranges */
231 for (i = st->ndim; i--; )
232 if (1<<i & cmask) {
233 clim[i][0] = (pos[i] - cmin[i])/csiz;
234 /* check overflow from f.p. error */
235 clim[i][0] -= clim[i][0] >> st->log2GR;
236 clim[i][1] = clim[i][0] + 1;
237 } else {
238 clim[i][0] = 0;
239 clim[i][1] = 1 << st->log2GR;
240 }
241 /* fill in unused dimensions */
242 for (i = SD_MAXDIM; i-- > st->ndim; ) {
243 clim[i][0] = 0; clim[i][1] = 1;
244 }
245 #if (SD_MAXDIM == 4)
246 bmin[0] = cmin[0] + csiz*clim[0][0];
247 for (cpos[0] = clim[0][0]; cpos[0] < clim[0][1]; cpos[0]++) {
248 bmin[1] = cmin[1] + csiz*clim[1][0];
249 for (cpos[1] = clim[1][0]; cpos[1] < clim[1][1]; cpos[1]++) {
250 bmin[2] = cmin[2] + csiz*clim[2][0];
251 for (cpos[2] = clim[2][0]; cpos[2] < clim[2][1]; cpos[2]++) {
252 bmin[3] = cmin[3] + csiz*(cpos[3] = clim[3][0]);
253 n = cpos[0];
254 for (i = 1; i < st->ndim; i++)
255 n = (n << st->log2GR) + cpos[i];
256 for ( ; cpos[3] < clim[3][1]; cpos[3]++) {
257 rval += rv = (*cf)(st->u.v[n++], bmin, csiz, cptr);
258 if (rv < 0)
259 return rv;
260 bmin[3] += csiz;
261 }
262 bmin[2] += csiz;
263 }
264 bmin[1] += csiz;
265 }
266 bmin[0] += csiz;
267 }
268 #else
269 _!_ "broken code segment!"
270 #endif
271 }
272 return rval;
273 }
274
275 /* Traverse a tree, visiting nodes in a slice that fits partial position */
276 static int
277 SDtraverseTre(const SDNode *st, const double *pos, int cmask,
278 SDtreCallback *cf, void *cptr)
279 {
280 static double czero[SD_MAXDIM];
281 int i;
282 /* check arguments */
283 if ((st == NULL) | (cf == NULL))
284 return -1;
285 for (i = st->ndim; i--; )
286 if (1<<i & cmask && (pos[i] < 0) | (pos[i] >= 1.))
287 return -1;
288
289 return SDdotravTre(st, pos, cmask, cf, cptr, czero, 1.);
290 }
291
292 /* Look up tree value at the given grid position */
293 static float
294 SDlookupTre(const SDNode *st, const double *pos, double *hcube)
295 {
296 double spos[SD_MAXDIM];
297 int i, n, t;
298 /* initialize voxel return */
299 if (hcube) {
300 hcube[i = st->ndim] = 1.;
301 while (i--)
302 hcube[i] = .0;
303 }
304 /* climb the tree */
305 while (st->log2GR < 0) {
306 n = 0; /* move to appropriate branch */
307 if (hcube) hcube[st->ndim] *= .5;
308 for (i = st->ndim; i--; ) {
309 spos[i] = 2.*pos[i];
310 t = (spos[i] >= 1.);
311 n |= t<<i;
312 spos[i] -= (double)t;
313 if (hcube) hcube[i] += (double)t * hcube[st->ndim];
314 }
315 st = st->u.t[n]; /* avoids tail recursion */
316 pos = spos;
317 }
318 if (st->log2GR == 0) /* short cut */
319 return st->u.v[0];
320 n = t = 0; /* find grid array index */
321 for (i = st->ndim; i--; ) {
322 n += (int)((1<<st->log2GR)*pos[i]) << t;
323 t += st->log2GR;
324 }
325 if (hcube) { /* compute final hypercube */
326 hcube[st->ndim] /= (double)(1<<st->log2GR);
327 for (i = st->ndim; i--; )
328 hcube[i] += floor((1<<st->log2GR)*pos[i])*hcube[st->ndim];
329 }
330 return st->u.v[n]; /* no interpolation */
331 }
332
333 /* Query BSDF value and sample hypercube for the given vectors */
334 static float
335 SDqueryTre(const SDTre *sdt, const FVECT outVec, const FVECT inVec, double *hc)
336 {
337 static const FVECT zvec = {.0, .0, 1.};
338 FVECT rOutVec;
339 double gridPos[4];
340 /* check transmission */
341 if (!sdt->isxmit ^ outVec[2] > 0 ^ inVec[2] > 0)
342 return -1.;
343 /* convert vector coordinates */
344 if (sdt->st->ndim == 3) {
345 spinvector(rOutVec, outVec, zvec, -atan2(inVec[1],inVec[0]));
346 gridPos[0] = .5 - .5*sqrt(inVec[0]*inVec[0] + inVec[1]*inVec[1]);
347 SDdisk2square(gridPos+1, rOutVec[0], rOutVec[1]);
348 } else if (sdt->st->ndim == 4) {
349 SDdisk2square(gridPos, -inVec[0], -inVec[1]);
350 SDdisk2square(gridPos+2, outVec[0], outVec[1]);
351 } else
352 return -1.; /* should be internal error */
353
354 return SDlookupTre(sdt->st, gridPos, hc);
355 }
356
357 /* Compute non-diffuse component for variable-resolution BSDF */
358 static int
359 SDgetTreBSDF(float coef[SDmaxCh], const FVECT outVec,
360 const FVECT inVec, SDComponent *sdc)
361 {
362 /* check arguments */
363 if ((coef == NULL) | (outVec == NULL) | (inVec == NULL) | (sdc == NULL)
364 || sdc->dist == NULL)
365 return 0;
366 /* get nearest BSDF value */
367 coef[0] = SDqueryTre((SDTre *)sdc->dist, outVec, inVec, NULL);
368 return (coef[0] >= 0); /* monochromatic for now */
369 }
370
371 /* Callback to build cumulative distribution using SDtraverseTre() */
372 static int
373 build_scaffold(float val, const double *cmin, double csiz, void *cptr)
374 {
375 SDdistScaffold *sp = (SDdistScaffold *)cptr;
376 int wid = csiz*(double)iwmax + .5;
377 bitmask_t bmin[2], bmax[2];
378
379 cmin += sp->nic; /* skip to output coords */
380 if (wid < sp->wmin) /* new minimum width? */
381 sp->wmin = wid;
382 if (wid > sp->wmax) /* new maximum? */
383 sp->wmax = wid;
384 if (sp->alen >= sp->nall) { /* need more space? */
385 struct outdir_s *ndarr;
386 sp->nall += 8192;
387 ndarr = (struct outdir_s *)realloc(sp->darr,
388 sizeof(struct outdir_s)*sp->nall);
389 if (ndarr == NULL)
390 return -1; /* abort build */
391 sp->darr = ndarr;
392 }
393 /* find Hilbert entry index */
394 bmin[0] = cmin[0]*(double)iwmax + .5;
395 bmin[1] = cmin[1]*(double)iwmax + .5;
396 bmax[0] = bmin[0] + wid;
397 bmax[1] = bmin[1] + wid;
398 hilbert_box_vtx(2, sizeof(bitmask_t), sizeof(unsigned)*4,
399 1, bmin, bmax);
400 sp->darr[sp->alen].hent = hilbert_c2i(2, sizeof(unsigned)*4, bmin);
401 sp->darr[sp->alen].wid = wid;
402 sp->darr[sp->alen].bsdf = val;
403 sp->alen++; /* on to the next entry */
404 return 0;
405 }
406
407 /* Scaffold comparison function for qsort -- ascending Hilbert index */
408 static int
409 sscmp(const void *p1, const void *p2)
410 {
411 return (int)((*(const struct outdir_s *)p1).hent -
412 (*(const struct outdir_s *)p2).hent);
413 }
414
415 /* Create a new cumulative distribution for the given input direction */
416 static SDTreCDst *
417 make_cdist(const SDTre *sdt, const double *pos)
418 {
419 const unsigned cumlmax = ~0;
420 SDdistScaffold myScaffold;
421 SDTreCDst *cd;
422 struct outdir_s *sp;
423 double scale, cursum;
424 int i;
425 /* initialize scaffold */
426 myScaffold.wmin = iwmax;
427 myScaffold.wmax = 0;
428 myScaffold.nic = sdt->st->ndim - 2;
429 myScaffold.alen = 0;
430 myScaffold.nall = 8192;
431 myScaffold.darr = (struct outdir_s *)malloc(sizeof(struct outdir_s) *
432 myScaffold.nall);
433 if (myScaffold.darr == NULL)
434 return NULL;
435 /* grow the distribution */
436 if (SDtraverseTre(sdt->st, pos, (1<<myScaffold.nic)-1,
437 &build_scaffold, &myScaffold) < 0) {
438 free(myScaffold.darr);
439 return NULL;
440 }
441 /* allocate result holder */
442 cd = (SDTreCDst *)malloc(sizeof(SDTreCDst) +
443 sizeof(cd->carr[0])*myScaffold.alen);
444 if (cd == NULL) {
445 free(myScaffold.darr);
446 return NULL;
447 }
448 /* sort the distribution */
449 qsort(myScaffold.darr, cd->calen = myScaffold.alen,
450 sizeof(struct outdir_s), &sscmp);
451
452 /* record input range */
453 scale = (double)myScaffold.wmin / iwmax;
454 for (i = myScaffold.nic; i--; ) {
455 cd->clim[i][0] = floor(pos[i]/scale + .5) * scale;
456 cd->clim[i][1] = cd->clim[i][0] + scale;
457 }
458 cd->max_psa = myScaffold.wmax / (double)iwmax;
459 cd->max_psa *= cd->max_psa * M_PI;
460 cd->isxmit = sdt->isxmit;
461 cd->cTotal = 1e-20; /* compute directional total */
462 sp = myScaffold.darr;
463 for (i = myScaffold.alen; i--; sp++)
464 cd->cTotal += sp->bsdf * (double)sp->wid * sp->wid;
465 cursum = .0; /* go back and get cumulative values */
466 scale = (double)cumlmax / cd->cTotal;
467 sp = myScaffold.darr;
468 for (i = 0; i < cd->calen; i++, sp++) {
469 cd->carr[i].cuml = scale*cursum + .5;
470 cursum += sp->bsdf * (double)sp->wid * sp->wid;
471 }
472 cd->carr[i].hndx = ~0; /* make final entry */
473 cd->carr[i].cuml = cumlmax;
474 cd->cTotal *= M_PI/(double)iwmax/iwmax;
475 /* all done, clean up and return */
476 free(myScaffold.darr);
477 return cd;
478 }
479
480 /* Find or allocate a cumulative distribution for the given incoming vector */
481 const SDCDst *
482 SDgetTreCDist(const FVECT inVec, SDComponent *sdc)
483 {
484 const SDTre *sdt;
485 double inCoord[2];
486 int vflags;
487 int i;
488 SDTreCDst *cd, *cdlast;
489 /* check arguments */
490 if ((inVec == NULL) | (sdc == NULL) ||
491 (sdt = (SDTre *)sdc->dist) == NULL)
492 return NULL;
493 if (sdt->st->ndim == 3) /* isotropic BSDF? */
494 inCoord[0] = .5 - .5*sqrt(inVec[0]*inVec[0] + inVec[1]*inVec[1]);
495 else if (sdt->st->ndim == 4)
496 SDdisk2square(inCoord, -inVec[0], -inVec[1]);
497 else
498 return NULL; /* should be internal error */
499 cdlast = NULL; /* check for direction in cache list */
500 for (cd = (SDTreCDst *)sdc->cdList; cd != NULL;
501 cdlast = cd, cd = (SDTreCDst *)cd->next) {
502 for (i = sdt->st->ndim - 2; i--; )
503 if ((cd->clim[i][0] > inCoord[i]) |
504 (inCoord[i] >= cd->clim[i][1]))
505 break;
506 if (i < 0)
507 break; /* means we have a match */
508 }
509 if (cd == NULL) /* need to create new entry? */
510 cdlast = cd = make_cdist(sdt, inCoord);
511 if (cdlast != NULL) { /* move entry to head of cache list */
512 cdlast->next = cd->next;
513 cd->next = sdc->cdList;
514 sdc->cdList = (SDCDst *)cd;
515 }
516 return (SDCDst *)cd; /* ready to go */
517 }
518
519 /* Query solid angle for vector(s) */
520 static SDError
521 SDqueryTreProjSA(double *psa, const FVECT v1, const RREAL *v2,
522 int qflags, SDComponent *sdc)
523 {
524 double myPSA[2];
525 /* check arguments */
526 if ((psa == NULL) | (v1 == NULL) | (sdc == NULL) ||
527 sdc->dist == NULL)
528 return SDEargument;
529 /* get projected solid angle(s) */
530 if (v2 != NULL) {
531 const SDTre *sdt = (SDTre *)sdc->dist;
532 double hcube[SD_MAXDIM];
533 if (SDqueryTre(sdt, v1, v2, hcube) < 0) {
534 if (qflags == SDqueryVal)
535 *psa = M_PI;
536 return SDEnone;
537 }
538 myPSA[0] = hcube[sdt->st->ndim];
539 myPSA[1] = myPSA[0] *= myPSA[0] * M_PI;
540 } else {
541 const SDTreCDst *cd = (const SDTreCDst *)SDgetTreCDist(v1, sdc);
542 if (cd == NULL)
543 return SDEmemory;
544 myPSA[0] = M_PI * (cd->clim[0][1] - cd->clim[0][0]) *
545 (cd->clim[1][1] - cd->clim[1][0]);
546 myPSA[1] = cd->max_psa;
547 }
548 switch (qflags) { /* record based on flag settings */
549 case SDqueryVal:
550 *psa = myPSA[0];
551 break;
552 case SDqueryMax:
553 if (myPSA[1] > *psa)
554 *psa = myPSA[1];
555 break;
556 case SDqueryMin+SDqueryMax:
557 if (myPSA[1] > psa[1])
558 psa[1] = myPSA[1];
559 /* fall through */
560 case SDqueryMin:
561 if (myPSA[0] < psa[0])
562 psa[0] = myPSA[0];
563 break;
564 }
565 return SDEnone;
566 }
567
568 /* Sample cumulative distribution */
569 static SDError
570 SDsampTreCDist(FVECT ioVec, double randX, const SDCDst *cdp)
571 {
572 const unsigned nBitsC = 4*sizeof(bitmask_t);
573 const unsigned nExtraBits = 8*(sizeof(bitmask_t)-sizeof(unsigned));
574 const unsigned maxval = ~0;
575 const SDTreCDst *cd = (const SDTreCDst *)cdp;
576 const unsigned target = randX*maxval;
577 bitmask_t hndx, hcoord[2];
578 double gpos[3];
579 int i, iupper, ilower;
580 /* check arguments */
581 if ((ioVec == NULL) | (cd == NULL))
582 return SDEargument;
583 /* binary search to find position */
584 ilower = 0; iupper = cd->calen;
585 while ((i = (iupper + ilower) >> 1) != ilower)
586 if ((long)target >= (long)cd->carr[i].cuml)
587 ilower = i;
588 else
589 iupper = i;
590 /* localize random position */
591 randX = (randX*maxval - cd->carr[ilower].cuml) /
592 (double)(cd->carr[iupper].cuml - cd->carr[ilower].cuml);
593 /* index in longer Hilbert curve */
594 hndx = (randX*cd->carr[iupper].hndx + (1.-randX)*cd->carr[ilower].hndx)
595 * (double)((bitmask_t)1 << nExtraBits);
596 /* convert Hilbert index to vector */
597 hilbert_i2c(2, nBitsC, hndx, hcoord);
598 for (i = 2; i--; )
599 gpos[i] = ((double)hcoord[i] + rand()*(1./(RAND_MAX+.5))) /
600 (double)((bitmask_t)1 << nBitsC);
601 SDsquare2disk(gpos, gpos[0], gpos[1]);
602 gpos[2] = 1. - gpos[0]*gpos[0] - gpos[1]*gpos[1];
603 if (gpos[2] > 0) /* paranoia, I hope */
604 gpos[2] = sqrt(gpos[2]);
605 if (ioVec[2] > 0 ^ !cd->isxmit)
606 gpos[2] = -gpos[2];
607 VCOPY(ioVec, gpos);
608 return SDEnone;
609 }
610
611 /* Load a variable-resolution BSDF tree from an open XML file */
612 SDError
613 SDloadTre(SDData *sd, ezxml_t wtl)
614 {
615 return SDEsupport;
616 }
617
618 /* Variable resolution BSDF methods */
619 SDFunc SDhandleTre = {
620 &SDgetTreBSDF,
621 &SDqueryTreProjSA,
622 &SDgetTreCDist,
623 &SDsampTreCDist,
624 &SDFreeBTre,
625 };