ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.28
Committed: Fri Mar 21 17:49:53 2014 UTC (10 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad4R2P2, rad4R2, rad4R2P1
Changes since 3.27: +7 -4 lines
Log Message:
Added protection against negative BSDF values

File Contents

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