ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.3
Committed: Fri Feb 18 02:41:55 2011 UTC (13 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.2: +3 -45 lines
Log Message:
Minor fixes and adjustments

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdf_m.c,v 3.2 2011/02/18 00:41:44 greg Exp $";
3 #endif
4 /*
5 * bsdf_m.c
6 *
7 * Definitions supporting BSDF matrices
8 *
9 * Created by Greg Ward on 2/2/11.
10 * Copyright 2011 Anyhere Software. All rights reserved.
11 *
12 */
13
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_m.h"
21
22 #ifndef FTINY
23 #define FTINY 1e-6
24 #endif
25
26 /* Function return codes */
27 #define RC_GOOD 1
28 #define RC_FAIL 0
29 #define RC_FORMERR (-1)
30 #define RC_DATERR (-2)
31 #define RC_UNSUPP (-3)
32 #define RC_INTERR (-4)
33 #define RC_MEMERR (-5)
34
35 #define MAXLATS 46 /* maximum number of latitudes */
36
37 /* BSDF angle specification */
38 typedef struct {
39 char name[64]; /* basis name */
40 int nangles; /* total number of directions */
41 struct {
42 float tmin; /* starting theta */
43 int nphis; /* number of phis (0 term) */
44 } lat[MAXLATS+1]; /* latitudes */
45 } ANGLE_BASIS;
46
47 #define MAXABASES 7 /* limit on defined bases */
48
49 static ANGLE_BASIS abase_list[MAXABASES] = {
50 {
51 "LBNL/Klems Full", 145,
52 { {-5., 1},
53 {5., 8},
54 {15., 16},
55 {25., 20},
56 {35., 24},
57 {45., 24},
58 {55., 24},
59 {65., 16},
60 {75., 12},
61 {90., 0} }
62 }, {
63 "LBNL/Klems Half", 73,
64 { {-6.5, 1},
65 {6.5, 8},
66 {19.5, 12},
67 {32.5, 16},
68 {46.5, 20},
69 {61.5, 12},
70 {76.5, 4},
71 {90., 0} }
72 }, {
73 "LBNL/Klems Quarter", 41,
74 { {-9., 1},
75 {9., 8},
76 {27., 12},
77 {46., 12},
78 {66., 8},
79 {90., 0} }
80 }
81 };
82
83 static int nabases = 3; /* current number of defined bases */
84
85 static int
86 fequal(double a, double b)
87 {
88 if (b != .0)
89 a = a/b - 1.;
90 return (a <= 1e-6) & (a >= -1e-6);
91 }
92
93 /* returns the name of the given tag */
94 #ifdef ezxml_name
95 #undef ezxml_name
96 static char *
97 ezxml_name(ezxml_t xml)
98 {
99 if (xml == NULL)
100 return NULL;
101 return xml->name;
102 }
103 #endif
104
105 /* returns the given tag's character content or empty string if none */
106 #ifdef ezxml_txt
107 #undef ezxml_txt
108 static char *
109 ezxml_txt(ezxml_t xml)
110 {
111 if (xml == NULL)
112 return "";
113 return xml->txt;
114 }
115 #endif
116
117 /* Convert error to standard BSDF code */
118 static SDError
119 convert_errcode(int ec)
120 {
121 switch (ec) {
122 case RC_GOOD:
123 return SDEnone;
124 case RC_FORMERR:
125 return SDEformat;
126 case RC_DATERR:
127 return SDEdata;
128 case RC_UNSUPP:
129 return SDEsupport;
130 case RC_INTERR:
131 return SDEinternal;
132 case RC_MEMERR:
133 return SDEmemory;
134 }
135 return SDEunknown;
136 }
137
138 /* Allocate a BSDF matrix of the given size */
139 static SDMat *
140 SDnewMatrix(int ni, int no)
141 {
142 SDMat *sm;
143
144 if ((ni <= 0) | (no <= 0)) {
145 strcpy(SDerrorDetail, "Empty BSDF matrix request");
146 return NULL;
147 }
148 sm = (SDMat *)malloc(sizeof(SDMat) + (ni*no - 1)*sizeof(float));
149 if (sm == NULL) {
150 sprintf(SDerrorDetail, "Cannot allocate %dx%d BSDF matrix",
151 ni, no);
152 return NULL;
153 }
154 memset(sm, 0, sizeof(SDMat)-sizeof(float));
155 sm->ninc = ni;
156 sm->nout = no;
157
158 return sm;
159 }
160
161 /* Free a BSDF matrix */
162 #define SDfreeMatrix free
163
164 /* get vector for this angle basis index */
165 static int
166 ab_getvec(FVECT v, int ndx, double randX, void *p)
167 {
168 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
169 double rx[2];
170 int li;
171 double pol, azi, d;
172
173 if ((ndx < 0) | (ndx >= ab->nangles))
174 return RC_FAIL;
175 for (li = 0; ndx >= ab->lat[li].nphis; li++)
176 ndx -= ab->lat[li].nphis;
177 SDmultiSamp(rx, 2, randX);
178 pol = M_PI/180.*( (1.-rx[0])*ab->lat[li].tmin +
179 rx[0]*ab->lat[li+1].tmin );
180 azi = 2.*M_PI*(ndx + rx[1] - .5)/ab->lat[li].nphis;
181 v[2] = d = cos(pol);
182 d = sqrt(1. - d*d); /* sin(pol) */
183 v[0] = cos(azi)*d;
184 v[1] = sin(azi)*d;
185 return RC_GOOD;
186 }
187
188 /* get index corresponding to the given vector */
189 static int
190 ab_getndx(const FVECT v, void *p)
191 {
192 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
193 int li, ndx;
194 double pol, azi, d;
195
196 if (v == NULL)
197 return -1;
198 if ((v[2] < .0) | (v[2] > 1.0))
199 return -1;
200 pol = 180.0/M_PI*acos(v[2]);
201 azi = 180.0/M_PI*atan2(v[1], v[0]);
202 if (azi < 0.0) azi += 360.0;
203 for (li = 1; ab->lat[li].tmin <= pol; li++)
204 if (!ab->lat[li].nphis)
205 return -1;
206 --li;
207 ndx = (int)((1./360.)*azi*ab->lat[li].nphis + 0.5);
208 if (ndx >= ab->lat[li].nphis) ndx = 0;
209 while (li--)
210 ndx += ab->lat[li].nphis;
211 return ndx;
212 }
213
214 /* compute square of real value */
215 static double sq(double x) { return x*x; }
216
217 /* get projected solid angle for this angle basis index */
218 static double
219 ab_getohm(int ndx, void *p)
220 {
221 static int last_li = -1;
222 static double last_ohm;
223 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
224 int li;
225 double theta, theta1;
226
227 if ((ndx < 0) | (ndx >= ab->nangles))
228 return -1.;
229 for (li = 0; ndx >= ab->lat[li].nphis; li++)
230 ndx -= ab->lat[li].nphis;
231 if (li == last_li) /* cached latitude? */
232 return last_ohm;
233 last_li = li;
234 theta1 = M_PI/180. * ab->lat[li+1].tmin;
235 if (ab->lat[li].nphis == 1) /* special case */
236 return last_ohm = M_PI*(1. - sq(cos(theta1)));
237 theta = M_PI/180. * ab->lat[li].tmin;
238 return last_ohm = M_PI*(sq(cos(theta)) - sq(cos(theta1))) /
239 (double)ab->lat[li].nphis;
240 }
241
242 /* get reverse vector for this angle basis index */
243 static int
244 ab_getvecR(FVECT v, int ndx, double randX, void *p)
245 {
246 int na = (*(ANGLE_BASIS *)p).nangles;
247
248 if (!ab_getvec(v, ndx, randX, p))
249 return RC_FAIL;
250
251 v[0] = -v[0];
252 v[1] = -v[1];
253 v[2] = -v[2];
254
255 return RC_GOOD;
256 }
257
258 /* get index corresponding to the reverse vector */
259 static int
260 ab_getndxR(const FVECT v, void *p)
261 {
262 FVECT v2;
263
264 v2[0] = -v[0];
265 v2[1] = -v[1];
266 v2[2] = -v[2];
267
268 return ab_getndx(v2, p);
269 }
270
271 /* load custom BSDF angle basis */
272 static int
273 load_angle_basis(ezxml_t wab)
274 {
275 char *abname = ezxml_txt(ezxml_child(wab, "AngleBasisName"));
276 ezxml_t wbb;
277 int i;
278
279 if (!abname || !*abname)
280 return RC_FAIL;
281 for (i = nabases; i--; )
282 if (!strcasecmp(abname, abase_list[i].name))
283 return RC_GOOD; /* assume it's the same */
284 if (nabases >= MAXABASES) {
285 sprintf(SDerrorDetail, "Out of angle bases reading '%s'",
286 abname);
287 return RC_INTERR;
288 }
289 strcpy(abase_list[nabases].name, abname);
290 abase_list[nabases].nangles = 0;
291 for (i = 0, wbb = ezxml_child(wab, "AngleBasisBlock");
292 wbb != NULL; i++, wbb = wbb->next) {
293 if (i >= MAXLATS) {
294 sprintf(SDerrorDetail, "Too many latitudes for '%s'",
295 abname);
296 return RC_INTERR;
297 }
298 abase_list[nabases].lat[i+1].tmin = atof(ezxml_txt(
299 ezxml_child(ezxml_child(wbb,
300 "ThetaBounds"), "UpperTheta")));
301 if (!i)
302 abase_list[nabases].lat[i].tmin =
303 -abase_list[nabases].lat[i+1].tmin;
304 else if (!fequal(atof(ezxml_txt(ezxml_child(ezxml_child(wbb,
305 "ThetaBounds"), "LowerTheta"))),
306 abase_list[nabases].lat[i].tmin)) {
307 sprintf(SDerrorDetail, "Theta values disagree in '%s'",
308 abname);
309 return RC_DATERR;
310 }
311 abase_list[nabases].nangles +=
312 abase_list[nabases].lat[i].nphis =
313 atoi(ezxml_txt(ezxml_child(wbb, "nPhis")));
314 if (abase_list[nabases].lat[i].nphis <= 0 ||
315 (abase_list[nabases].lat[i].nphis == 1 &&
316 abase_list[nabases].lat[i].tmin > FTINY)) {
317 sprintf(SDerrorDetail, "Illegal phi count in '%s'",
318 abname);
319 return RC_DATERR;
320 }
321 }
322 abase_list[nabases++].lat[i].nphis = 0;
323 return RC_GOOD;
324 }
325
326 /* compute min. proj. solid angle and max. direct hemispherical scattering */
327 static int
328 get_extrema(SDSpectralDF *df)
329 {
330 SDMat *dp = (SDMat *)df->comp[0].dist;
331 double *ohma;
332 int i, o;
333 /* initialize extrema */
334 df->minProjSA = M_PI;
335 df->maxHemi = .0;
336 ohma = (double *)malloc(dp->nout*sizeof(double));
337 if (ohma == NULL)
338 return RC_MEMERR;
339 /* get outgoing solid angles */
340 for (o = dp->nout; o--; )
341 if ((ohma[o] = mBSDF_outohm(dp,o)) < df->minProjSA)
342 df->minProjSA = ohma[o];
343 /* compute hemispherical sums */
344 for (i = dp->ninc; i--; ) {
345 double hemi = .0;
346 for (o = dp->nout; o--; )
347 hemi += ohma[o] * mBSDF_value(dp, i, o);
348 if (hemi > df->maxHemi)
349 df->maxHemi = hemi;
350 }
351 free(ohma);
352 /* need incoming solid angles, too? */
353 if (dp->ninc < dp->nout || dp->ib_ohm != dp->ob_ohm ||
354 dp->ib_priv != dp->ob_priv) {
355 double ohm;
356 for (i = dp->ninc; i--; )
357 if ((ohm = mBSDF_incohm(dp,i)) < df->minProjSA)
358 df->minProjSA = ohm;
359 }
360 return (df->maxHemi <= 1.01);
361 }
362
363 /* load BSDF distribution for this wavelength */
364 static int
365 load_bsdf_data(SDData *sd, ezxml_t wdb, int rowinc)
366 {
367 char *cbasis = ezxml_txt(ezxml_child(wdb,"ColumnAngleBasis"));
368 char *rbasis = ezxml_txt(ezxml_child(wdb,"RowAngleBasis"));
369 SDSpectralDF *df;
370 SDMat *dp;
371 char *sdata;
372 int inbi, outbi;
373 int i;
374
375 if ((!cbasis || !*cbasis) | (!rbasis || !*rbasis)) {
376 sprintf(SDerrorDetail, "Missing column/row basis for BSDF '%s'",
377 sd->name);
378 return RC_FORMERR;
379 }
380 /* allocate BSDF component */
381 sdata = ezxml_txt(ezxml_child(wdb, "WavelengthDataDirection"));
382 if (!strcasecmp(sdata, "Transmission Front")) {
383 if (sd->tf != NULL)
384 SDfreeSpectralDF(sd->tf);
385 if ((sd->tf = SDnewSpectralDF(1)) == NULL)
386 return RC_MEMERR;
387 df = sd->tf;
388 } else if (!strcasecmp(sdata, "Reflection Front")) {
389 if (sd->rf != NULL)
390 SDfreeSpectralDF(sd->rf);
391 if ((sd->rf = SDnewSpectralDF(1)) == NULL)
392 return RC_MEMERR;
393 df = sd->rf;
394 } else if (!strcasecmp(sdata, "Reflection Back")) {
395 if (sd->rb != NULL)
396 SDfreeSpectralDF(sd->rb);
397 if ((sd->rb = SDnewSpectralDF(1)) == NULL)
398 return RC_MEMERR;
399 df = sd->rb;
400 } else
401 return RC_FAIL;
402 /* get angle bases */
403 sdata = ezxml_txt(ezxml_child(wdb,"ColumnAngleBasis"));
404 if (!sdata || !*sdata) {
405 sprintf(SDerrorDetail, "Missing column basis for BSDF '%s'",
406 sd->name);
407 return RC_FORMERR;
408 }
409 for (inbi = nabases; inbi--; )
410 if (!strcasecmp(cbasis, abase_list[inbi].name))
411 break;
412 if (inbi < 0) {
413 sprintf(SDerrorDetail, "Undefined ColumnAngleBasis '%s'",
414 cbasis);
415 return RC_FORMERR;
416 }
417 sdata = ezxml_txt(ezxml_child(wdb,"RowAngleBasis"));
418 if (!sdata || !*sdata) {
419 sprintf(SDerrorDetail, "Missing row basis for BSDF '%s'",
420 sd->name);
421 return RC_FORMERR;
422 }
423 for (outbi = nabases; outbi--; )
424 if (!strcasecmp(rbasis, abase_list[outbi].name))
425 break;
426 if (outbi < 0) {
427 sprintf(SDerrorDetail, "Undefined RowAngleBasis '%s'", cbasis);
428 return RC_FORMERR;
429 }
430 /* allocate BSDF matrix */
431 dp = SDnewMatrix(abase_list[inbi].nangles, abase_list[outbi].nangles);
432 if (dp == NULL)
433 return RC_MEMERR;
434 dp->ib_priv = (void *)&abase_list[inbi];
435 dp->ob_priv = (void *)&abase_list[outbi];
436 if (df == sd->tf) {
437 dp->ib_vec = ab_getvecR;
438 dp->ib_ndx = ab_getndxR;
439 dp->ob_vec = ab_getvec;
440 dp->ob_ndx = ab_getndx;
441 } else if (df == sd->rf) {
442 dp->ib_vec = ab_getvec;
443 dp->ib_ndx = ab_getndx;
444 dp->ob_vec = ab_getvec;
445 dp->ob_ndx = ab_getndx;
446 } else /* df == sd->rb */ {
447 dp->ib_vec = ab_getvecR;
448 dp->ib_ndx = ab_getndxR;
449 dp->ob_vec = ab_getvecR;
450 dp->ob_ndx = ab_getndxR;
451 }
452 dp->ib_ohm = ab_getohm;
453 dp->ob_ohm = ab_getohm;
454 df->comp[0].cspec[0] = c_dfcolor; /* XXX monochrome for now */
455 df->comp[0].dist = dp;
456 df->comp[0].func = &SDhandleMtx;
457 /* read BSDF data */
458 sdata = ezxml_txt(ezxml_child(wdb,"ScatteringData"));
459 if (!sdata || !*sdata) {
460 sprintf(SDerrorDetail, "Missing BSDF ScatteringData in '%s'",
461 sd->name);
462 return RC_FORMERR;
463 }
464 for (i = 0; i < dp->ninc*dp->nout; i++) {
465 char *sdnext = fskip(sdata);
466 if (sdnext == NULL) {
467 sprintf(SDerrorDetail,
468 "Bad/missing BSDF ScatteringData in '%s'",
469 sd->name);
470 return RC_FORMERR;
471 }
472 while (*sdnext && isspace(*sdnext))
473 sdnext++;
474 if (*sdnext == ',') sdnext++;
475 if (rowinc) {
476 int r = i/dp->nout;
477 int c = i - c*dp->nout;
478 mBSDF_value(dp,r,c) = atof(sdata);
479 } else
480 dp->bsdf[i] = atof(sdata);
481 sdata = sdnext;
482 }
483 return get_extrema(df);
484 }
485
486 /* Subtract minimum (diffuse) scattering amount from BSDF */
487 static double
488 subtract_min(SDMat *sm)
489 {
490 float minv = sm->bsdf[0];
491 int n = sm->ninc*sm->nout;
492 int i;
493
494 for (i = n; --i; )
495 if (sm->bsdf[i] < minv)
496 minv = sm->bsdf[i];
497 for (i = n; i--; )
498 sm->bsdf[i] -= minv;
499
500 return minv*M_PI; /* be sure to include multiplier */
501 }
502
503 /* Extract and separate diffuse portion of BSDF */
504 static void
505 extract_diffuse(SDValue *dv, SDSpectralDF *df)
506 {
507 int n;
508
509 if (df == NULL || df->ncomp <= 0) {
510 dv->spec = c_dfcolor;
511 dv->cieY = .0;
512 return;
513 }
514 dv->spec = df->comp[0].cspec[0];
515 dv->cieY = subtract_min((SDMat *)df->comp[0].dist);
516 /* in case of multiple components */
517 for (n = df->ncomp; --n; ) {
518 double ymin = subtract_min((SDMat *)df->comp[n].dist);
519 c_cmix(&dv->spec, dv->cieY, &dv->spec, ymin, &df->comp[n].cspec[0]);
520 dv->cieY += ymin;
521 }
522 df->maxHemi -= dv->cieY; /* correct minimum hemispherical */
523 dv->spec.clock++; /* make sure everything is set */
524 c_ccvt(&dv->spec, C_CSXY+C_CSSPEC);
525 }
526
527 /* Load a BSDF matrix from an open XML file */
528 SDError
529 SDloadMtx(SDData *sd, ezxml_t fl)
530 {
531 ezxml_t wtl, wld, wdb;
532 int rowIn;
533 struct BSDF_data *dp;
534 char *txt;
535 int rval;
536
537 if (strcmp(ezxml_name(fl), "WindowElement")) {
538 sprintf(SDerrorDetail,
539 "BSDF \"%s\": top level node not 'WindowElement'",
540 sd->name);
541 return SDEformat;
542 }
543 wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
544 txt = ezxml_txt(ezxml_child(ezxml_child(wtl,
545 "DataDefinition"), "IncidentDataStructure"));
546 if (!strcasecmp(txt, "Rows"))
547 rowIn = 1;
548 else if (!strcasecmp(txt, "Columns"))
549 rowIn = 0;
550 else {
551 sprintf(SDerrorDetail,
552 "BSDF \"%s\": unsupported IncidentDataStructure",
553 sd->name);
554 return SDEsupport;
555 }
556 /* get angle basis */
557 rval = load_angle_basis(ezxml_child(ezxml_child(wtl,
558 "DataDefinition"), "AngleBasis"));
559 if (rval < 0)
560 goto err_return;
561 /* load BSDF components */
562 for (wld = ezxml_child(wtl, "WavelengthData");
563 wld != NULL; wld = wld->next) {
564 if (strcasecmp(ezxml_txt(ezxml_child(wld,"Wavelength")),
565 "Visible"))
566 continue; /* just visible for now */
567 for (wdb = ezxml_child(wld, "WavelengthDataBlock");
568 wdb != NULL; wdb = wdb->next)
569 if ((rval = load_bsdf_data(sd, wdb, rowIn)) < 0)
570 goto err_return;
571 }
572 /* separate diffuse components */
573 extract_diffuse(&sd->rLambFront, sd->rf);
574 extract_diffuse(&sd->rLambBack, sd->rb);
575 extract_diffuse(&sd->tLamb, sd->tf);
576 /* return success */
577 return SDEnone;
578 err_return: /* jump here on failure */
579 if (sd->rf != NULL) {
580 SDfreeSpectralDF(sd->rf);
581 sd->rf = NULL;
582 }
583 if (sd->rb != NULL) {
584 SDfreeSpectralDF(sd->rb);
585 sd->rb = NULL;
586 }
587 if (sd->tf != NULL) {
588 SDfreeSpectralDF(sd->tf);
589 sd->tf = NULL;
590 }
591 return convert_errcode(rval);
592 }
593
594 /* Get Matrix BSDF value */
595 static int
596 SDgetMtxBSDF(float coef[SDmaxCh], const FVECT outVec,
597 const FVECT inVec, const void *dist)
598 {
599 const SDMat *dp = (const SDMat *)dist;
600 int i_ndx, o_ndx;
601 /* get angle indices */
602 i_ndx = mBSDF_incndx(dp, inVec);
603 o_ndx = mBSDF_outndx(dp, outVec);
604 /* try reciprocity if necessary */
605 if ((i_ndx < 0) & (o_ndx < 0)) {
606 i_ndx = mBSDF_incndx(dp, outVec);
607 o_ndx = mBSDF_outndx(dp, inVec);
608 }
609 if ((i_ndx < 0) | (o_ndx < 0))
610 return 0; /* nothing from this component */
611 coef[0] = mBSDF_value(dp, i_ndx, o_ndx);
612 return 1; /* XXX monochrome for now */
613 }
614
615 /* Query solid angle for vector */
616 static SDError
617 SDqueryMtxProjSA(double *psa, const FVECT vec, int qflags, const void *dist)
618 {
619 const SDMat *dp = (const SDMat *)dist;
620
621 if (!(qflags & SDqueryInc+SDqueryOut))
622 return SDEargument;
623 if (qflags & SDqueryInc) {
624 double inc_psa = mBSDF_incohm(dp, mBSDF_incndx(dp, vec));
625 if (inc_psa < .0)
626 return SDEinternal;
627 switch (qflags & SDqueryMin+SDqueryMax) {
628 case SDqueryMax:
629 if (inc_psa > psa[0])
630 psa[0] = inc_psa;
631 break;
632 case SDqueryMin+SDqueryMax:
633 if (inc_psa > psa[1])
634 psa[1] = inc_psa;
635 /* fall through */
636 case SDqueryMin:
637 if (inc_psa < psa[0])
638 psa[0] = inc_psa;
639 break;
640 case 0:
641 psa[0] = inc_psa;
642 break;
643 }
644 }
645 if (qflags & SDqueryOut) {
646 double out_psa = mBSDF_outohm(dp, mBSDF_outndx(dp, vec));
647 if (out_psa < .0)
648 return SDEinternal;
649 switch (qflags & SDqueryMin+SDqueryMax) {
650 case SDqueryMax:
651 if (out_psa > psa[0])
652 psa[0] = out_psa;
653 break;
654 case SDqueryMin+SDqueryMax:
655 if (out_psa > psa[1])
656 psa[1] = out_psa;
657 /* fall through */
658 case SDqueryMin:
659 if (out_psa < psa[0])
660 psa[0] = out_psa;
661 break;
662 case 0:
663 psa[(qflags&SDqueryInc)!=0] = out_psa;
664 break;
665 }
666 }
667 return SDEnone;
668 }
669
670 /* Compute new cumulative distribution from BSDF */
671 static int
672 make_cdist(SDMatCDst *cd, const FVECT inVec, SDMat *dp, int rev)
673 {
674 const unsigned maxval = ~0;
675 double *cmtab, scale;
676 int o;
677
678 cmtab = (double *)malloc((cd->calen+1)*sizeof(double));
679 if (cmtab == NULL)
680 return 0;
681 cmtab[0] = .0;
682 for (o = 0; o < cd->calen; o++) {
683 if (rev)
684 cmtab[o+1] = mBSDF_value(dp, o, cd->indx) *
685 (*dp->ib_ohm)(o, dp->ib_priv);
686 else
687 cmtab[o+1] = mBSDF_value(dp, cd->indx, o) *
688 (*dp->ob_ohm)(o, dp->ob_priv);
689 cmtab[o+1] += cmtab[o];
690 }
691 cd->cTotal = cmtab[cd->calen];
692 scale = (double)maxval / cd->cTotal;
693 cd->carr[0] = 0;
694 for (o = 1; o < cd->calen; o++)
695 cd->carr[o] = scale*cmtab[o] + .5;
696 cd->carr[cd->calen] = maxval;
697 free(cmtab);
698 return 1;
699 }
700
701 /* Get cumulative distribution for matrix BSDF */
702 static const SDCDst *
703 SDgetMtxCDist(const FVECT inVec, SDComponent *sdc)
704 {
705 SDMat *dp = (SDMat *)sdc->dist;
706 int reverse;
707 SDMatCDst myCD;
708 SDMatCDst *cd, *cdlast;
709
710 if (dp == NULL)
711 return NULL;
712 memset(&myCD, 0, sizeof(myCD));
713 myCD.indx = mBSDF_incndx(dp, inVec);
714 if (myCD.indx >= 0) {
715 myCD.ob_priv = dp->ob_priv;
716 myCD.ob_vec = dp->ob_vec;
717 myCD.calen = dp->nout;
718 reverse = 0;
719 } else { /* try reciprocity */
720 myCD.indx = mBSDF_outndx(dp, inVec);
721 if (myCD.indx < 0)
722 return NULL;
723 myCD.ob_priv = dp->ib_priv;
724 myCD.ob_vec = dp->ib_vec;
725 myCD.calen = dp->ninc;
726 reverse = 1;
727 }
728 cdlast = NULL; /* check for it in cache list */
729 for (cd = (SDMatCDst *)sdc->cdList;
730 cd != NULL; cd = (SDMatCDst *)cd->next) {
731 if (cd->indx == myCD.indx && (cd->calen == myCD.calen) &
732 (cd->ob_priv == myCD.ob_priv) &
733 (cd->ob_vec == myCD.ob_vec))
734 break;
735 cdlast = cd;
736 }
737 if (cd == NULL) { /* need to allocate new entry */
738 cd = (SDMatCDst *)malloc(sizeof(SDMatCDst) +
739 myCD.calen*sizeof(myCD.carr[0]));
740 if (cd == NULL)
741 return NULL;
742 *cd = myCD; /* compute cumulative distribution */
743 if (!make_cdist(cd, inVec, dp, reverse)) {
744 free(cd);
745 return NULL;
746 }
747 cdlast = cd;
748 }
749 if (cdlast != NULL) { /* move entry to head of cache list */
750 cdlast->next = cd->next;
751 cd->next = sdc->cdList;
752 sdc->cdList = (SDCDst *)cd;
753 }
754 return (SDCDst *)cd; /* ready to go */
755 }
756
757 /* Sample cumulative distribution */
758 static SDError
759 SDsampMtxCDist(FVECT outVec, double randX, const SDCDst *cdp)
760 {
761 const unsigned maxval = ~0;
762 const SDMatCDst *mcd = (const SDMatCDst *)cdp;
763 const unsigned target = randX*maxval;
764 int i, iupper, ilower;
765 /* binary search to find index */
766 ilower = 0; iupper = mcd->calen;
767 while ((i = (iupper + ilower) >> 1) != ilower)
768 if ((long)target >= (long)mcd->carr[i])
769 ilower = i;
770 else
771 iupper = i;
772 /* localize random position */
773 randX = (randX*maxval - mcd->carr[ilower]) /
774 (double)(mcd->carr[iupper] - mcd->carr[ilower]);
775 /* convert index to vector */
776 if ((*mcd->ob_vec)(outVec, i, randX, mcd->ob_priv))
777 return SDEnone;
778 strcpy(SDerrorDetail, "BSDF sampling fault");
779 return SDEinternal;
780 }
781
782 /* Fixed resolution BSDF methods */
783 SDFunc SDhandleMtx = {
784 &SDgetMtxBSDF,
785 &SDqueryMtxProjSA,
786 &SDgetMtxCDist,
787 &SDsampMtxCDist,
788 &SDfreeMatrix,
789 };