ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.2
Committed: Fri Feb 18 00:41:44 2011 UTC (13 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.1: +3 -0 lines
Log Message:
Added missing RCS tags

File Contents

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