ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.15
Committed: Wed Apr 27 20:03:25 2011 UTC (13 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.14: +19 -14 lines
Log Message:
Initial untested variable-resolution BSDF

File Contents

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