ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.7
Committed: Wed Feb 23 21:58:31 2011 UTC (13 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.6: +16 -7 lines
Log Message:
Fixed issue with coordinate orientation for front transmission

File Contents

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