ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.1
Committed: Fri Feb 18 00:40:25 2011 UTC (13 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Major code reorg, moving mgflib to common and introducing BSDF material

File Contents

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