ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.16
Committed: Thu Jun 9 17:09:39 2011 UTC (12 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.15: +3 -2 lines
Log Message:
Fixes for Windows and bug fix in bsdf_m.c

File Contents

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