ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.8
Committed: Thu Feb 24 20:14:26 2011 UTC (13 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.7: +88 -36 lines
Log Message:
Fixed interpretation of Klems directions

File Contents

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