ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.19
Committed: Thu Jul 7 15:48:00 2011 UTC (12 years, 10 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.18: +3 -3 lines
Log Message:
Removed unnecessary restriction on reciprocity

File Contents

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