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

# User Rev Content
1 greg 3.2 #ifndef lint
2 greg 3.28 static const char RCSid[] = "$Id: bsdf_m.c,v 3.27 2013/06/29 21:03:44 greg Exp $";
3 greg 3.2 #endif
4 greg 3.1 /*
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 greg 3.16 #define _USE_MATH_DEFINES
15 greg 3.3 #include "rtio.h"
16 greg 3.1 #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 greg 3.25 ANGLE_BASIS abase_list[MAXABASES] = {
33 greg 3.1 {
34     "LBNL/Klems Full", 145,
35 greg 3.22 { {0., 1},
36 greg 3.1 {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 greg 3.22 { {0., 1},
48 greg 3.1 {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 greg 3.22 { {0., 1},
58 greg 3.1 {9., 8},
59     {27., 12},
60     {46., 12},
61     {66., 8},
62     {90., 0} }
63     }
64     };
65    
66 greg 3.25 int nabases = 3; /* current number of defined bases */
67 greg 3.1
68     static int
69     fequal(double a, double b)
70     {
71 greg 3.10 if (b != 0)
72 greg 3.1 a = a/b - 1.;
73     return (a <= 1e-6) & (a >= -1e-6);
74     }
75    
76 greg 3.9 /* Returns the given tag's character content or empty string if none */
77 greg 3.1 #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 greg 3.25 /* Get vector for this angle basis index (front exiting) */
136     int
137 greg 3.9 fo_getvec(FVECT v, double ndxr, void *p)
138 greg 3.1 {
139 greg 3.9 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
140     int ndx = (int)ndxr;
141     double randX = ndxr - ndx;
142 greg 3.1 double rx[2];
143     int li;
144     double pol, azi, d;
145    
146 greg 3.9 if ((ndxr < 0) | (ndx >= ab->nangles))
147 greg 3.1 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 greg 3.25 /* Get index corresponding to the given vector (front exiting) */
162     int
163 greg 3.8 fo_getndx(const FVECT v, void *p)
164 greg 3.1 {
165 greg 3.9 ANGLE_BASIS *ab = (ANGLE_BASIS *)p;
166 greg 3.1 int li, ndx;
167 greg 3.17 double pol, azi;
168 greg 3.1
169     if (v == NULL)
170     return -1;
171 greg 3.10 if ((v[2] < 0) | (v[2] > 1.))
172 greg 3.1 return -1;
173 greg 3.27 pol = 180.0/M_PI*Acos(v[2]);
174 greg 3.1 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 greg 3.25 /* Get projected solid angle for this angle basis index (universal) */
191     double
192 greg 3.8 io_getohm(int ndx, void *p)
193 greg 3.1 {
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 greg 3.21 theta = M_PI/180. * ab->lat[li].tmin;
208 greg 3.1 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 greg 3.25 /* Get vector for this angle basis index (back incident) */
214     int
215 greg 3.9 bi_getvec(FVECT v, double ndxr, void *p)
216 greg 3.1 {
217 greg 3.9 if (!fo_getvec(v, ndxr, p))
218 greg 3.1 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 greg 3.25 /* Get index corresponding to the vector (back incident) */
228     int
229 greg 3.8 bi_getndx(const FVECT v, void *p)
230 greg 3.1 {
231     FVECT v2;
232    
233     v2[0] = -v[0];
234     v2[1] = -v[1];
235     v2[2] = -v[2];
236    
237 greg 3.8 return fo_getndx(v2, p);
238     }
239    
240 greg 3.25 /* Get vector for this angle basis index (back exiting) */
241     int
242 greg 3.9 bo_getvec(FVECT v, double ndxr, void *p)
243 greg 3.8 {
244 greg 3.9 if (!fo_getvec(v, ndxr, p))
245 greg 3.8 return RC_FAIL;
246    
247     v[2] = -v[2];
248    
249     return RC_GOOD;
250     }
251    
252 greg 3.25 /* Get index corresponding to the vector (back exiting) */
253     int
254 greg 3.8 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 greg 3.25 /* Get vector for this angle basis index (front incident) */
266     int
267 greg 3.9 fi_getvec(FVECT v, double ndxr, void *p)
268 greg 3.8 {
269 greg 3.9 if (!fo_getvec(v, ndxr, p))
270 greg 3.8 return RC_FAIL;
271    
272     v[0] = -v[0];
273     v[1] = -v[1];
274    
275     return RC_GOOD;
276     }
277    
278 greg 3.25 /* Get index corresponding to the vector (front incident) */
279     int
280 greg 3.8 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 greg 3.1 }
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 greg 3.21 abase_list[nabases].lat[0].tmin = 0;
323 greg 3.1 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 greg 3.12 abname);
328 greg 3.1 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 greg 3.12 abname);
338 greg 3.1 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 greg 3.5 if ((dp->ib_ohm != dp->ob_ohm) | (dp->ib_priv != dp->ob_priv)) {
373 greg 3.1 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 greg 3.15 if (!sdata)
393     return RC_FAIL;
394 greg 3.8 /*
395     * Remember that front and back are reversed from WINDOW 6 orientations
396     */
397 greg 3.23 if (!strcasecmp(sdata, "Transmission Front")) {
398 greg 3.24 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 greg 3.1 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 greg 3.24 if (sd->rb != NULL)
411 greg 3.6 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 greg 3.24 if (sd->rf != NULL)
417 greg 3.1 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 greg 3.4 /* XXX should also check "ScatteringDataType" for consistency? */
424 greg 3.1 /* 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 greg 3.4 if (!strcasecmp(sdata, abase_list[inbi].name))
433 greg 3.1 break;
434     if (inbi < 0) {
435 greg 3.4 sprintf(SDerrorDetail, "Undefined ColumnAngleBasis '%s'", sdata);
436 greg 3.1 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 greg 3.4 if (!strcasecmp(sdata, abase_list[outbi].name))
446 greg 3.1 break;
447     if (outbi < 0) {
448 greg 3.4 sprintf(SDerrorDetail, "Undefined RowAngleBasis '%s'", sdata);
449 greg 3.1 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 greg 3.5 dp->ib_priv = &abase_list[inbi];
456     dp->ob_priv = &abase_list[outbi];
457 greg 3.1 if (df == sd->tf) {
458 greg 3.23 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 greg 3.1 } else if (df == sd->rf) {
468 greg 3.8 dp->ib_vec = &fi_getvec;
469     dp->ib_ndx = &fi_getndx;
470     dp->ob_vec = &fo_getvec;
471     dp->ob_ndx = &fo_getndx;
472 greg 3.1 } else /* df == sd->rb */ {
473 greg 3.8 dp->ib_vec = &bi_getvec;
474     dp->ib_ndx = &bi_getndx;
475     dp->ob_vec = &bo_getvec;
476     dp->ob_ndx = &bo_getndx;
477 greg 3.1 }
478 greg 3.8 dp->ib_ohm = &io_getohm;
479     dp->ob_ohm = &io_getohm;
480 greg 3.1 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 greg 3.15 sdata = ezxml_txt(ezxml_child(wdb, "ScatteringData"));
485 greg 3.1 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 greg 3.28 char *sdnext = fskip(sdata);
492     double val;
493 greg 3.1 if (sdnext == NULL) {
494     sprintf(SDerrorDetail,
495     "Bad/missing BSDF ScatteringData in '%s'",
496     sd->name);
497     return RC_FORMERR;
498     }
499 greg 3.15 while (isspace(*sdnext))
500 greg 3.1 sdnext++;
501     if (*sdnext == ',') sdnext++;
502 greg 3.28 if ((val = atof(sdata)) < 0)
503     val = 0; /* don't allow negative values */
504 greg 3.1 if (rowinc) {
505     int r = i/dp->nout;
506 greg 3.16 int c = i - r*dp->nout;
507 greg 3.28 mBSDF_value(dp,r,c) = val;
508 greg 3.1 } else
509 greg 3.28 dp->bsdf[i] = val;
510 greg 3.1 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 greg 3.15
527     if (minv <= FTINY)
528     return .0;
529    
530 greg 3.1 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 greg 3.15 df->maxHemi -= dv->cieY; /* adjust maximum hemispherical */
556 greg 3.4 /* make sure everything is set */
557 greg 3.1 c_ccvt(&dv->spec, C_CSXY+C_CSSPEC);
558     }
559    
560     /* Load a BSDF matrix from an open XML file */
561     SDError
562 greg 3.4 SDloadMtx(SDData *sd, ezxml_t wtl)
563 greg 3.1 {
564 greg 3.15 ezxml_t wld, wdb;
565     int rowIn;
566     char *txt;
567     int rval;
568     /* basic checks and data ordering */
569 greg 3.4 txt = ezxml_txt(ezxml_child(ezxml_child(wtl,
570     "DataDefinition"), "IncidentDataStructure"));
571     if (txt == NULL || !*txt) {
572 greg 3.1 sprintf(SDerrorDetail,
573 greg 3.4 "BSDF \"%s\": missing IncidentDataStructure",
574 greg 3.1 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 greg 3.18 /* 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 greg 3.15 /* load BSDF components */
595 greg 3.1 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 greg 3.4 return convert_errcode(rval);
604 greg 3.1 }
605 greg 3.15 /* separate diffuse components */
606 greg 3.1 extract_diffuse(&sd->rLambFront, sd->rf);
607     extract_diffuse(&sd->rLambBack, sd->rb);
608 greg 3.26 if (sd->tf != NULL)
609     extract_diffuse(&sd->tLamb, sd->tf);
610     if (sd->tb != NULL)
611     extract_diffuse(&sd->tLamb, sd->tb);
612 greg 3.15 /* return success */
613 greg 3.1 return SDEnone;
614     }
615    
616     /* Get Matrix BSDF value */
617     static int
618     SDgetMtxBSDF(float coef[SDmaxCh], const FVECT outVec,
619 greg 3.12 const FVECT inVec, SDComponent *sdc)
620 greg 3.1 {
621 greg 3.12 const SDMat *dp;
622 greg 3.1 int i_ndx, o_ndx;
623 greg 3.12 /* check arguments */
624     if ((coef == NULL) | (outVec == NULL) | (inVec == NULL) | (sdc == NULL)
625     || (dp = (SDMat *)sdc->dist) == NULL)
626     return 0;
627 greg 3.1 /* get angle indices */
628     i_ndx = mBSDF_incndx(dp, inVec);
629     o_ndx = mBSDF_outndx(dp, outVec);
630     /* try reciprocity if necessary */
631 greg 3.19 if ((i_ndx < 0) & (o_ndx < 0)) {
632 greg 3.1 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 greg 3.12 /* Query solid angle for vector(s) */
642 greg 3.1 static SDError
643 greg 3.10 SDqueryMtxProjSA(double *psa, const FVECT v1, const RREAL *v2,
644 greg 3.12 int qflags, SDComponent *sdc)
645 greg 3.1 {
646 greg 3.12 const SDMat *dp;
647 greg 3.5 double inc_psa, out_psa;
648     /* check arguments */
649 greg 3.12 if ((psa == NULL) | (v1 == NULL) | (sdc == NULL) ||
650     (dp = (SDMat *)sdc->dist) == NULL)
651 greg 3.1 return SDEargument;
652 greg 3.10 if (v2 == NULL)
653     v2 = v1;
654 greg 3.5 /* get projected solid angles */
655 greg 3.10 out_psa = mBSDF_outohm(dp, mBSDF_outndx(dp, v1));
656     inc_psa = mBSDF_incohm(dp, mBSDF_incndx(dp, v2));
657 greg 3.12 if ((v1 != v2) & (out_psa <= 0) & (inc_psa <= 0)) {
658 greg 3.11 inc_psa = mBSDF_outohm(dp, mBSDF_outndx(dp, v2));
659     out_psa = mBSDF_incohm(dp, mBSDF_incndx(dp, v1));
660     }
661 greg 3.5
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 greg 3.13 if (inc_psa > psa[1])
671 greg 3.5 psa[1] = inc_psa;
672 greg 3.13 if (out_psa > psa[1])
673 greg 3.5 psa[1] = out_psa;
674     /* fall through */
675 greg 3.12 case SDqueryVal:
676     if (qflags == SDqueryVal)
677     psa[0] = M_PI;
678 greg 3.14 /* fall through */
679     case SDqueryMin:
680 greg 3.10 if ((inc_psa > 0) & (inc_psa < psa[0]))
681 greg 3.1 psa[0] = inc_psa;
682 greg 3.10 if ((out_psa > 0) & (out_psa < psa[0]))
683 greg 3.5 psa[0] = out_psa;
684     break;
685 greg 3.1 }
686 greg 3.5 /* make sure it's legal */
687 greg 3.10 return (psa[0] <= 0) ? SDEinternal : SDEnone;
688 greg 3.1 }
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 greg 3.12 SDMat *dp;
726 greg 3.1 int reverse;
727     SDMatCDst myCD;
728     SDMatCDst *cd, *cdlast;
729 greg 3.5 /* check arguments */
730 greg 3.12 if ((inVec == NULL) | (sdc == NULL) ||
731     (dp = (SDMat *)sdc->dist) == NULL)
732 greg 3.1 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 greg 3.19 } else { /* try reciprocity */
741 greg 3.1 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 greg 3.14 for (cd = (SDMatCDst *)sdc->cdList; cd != NULL;
751 greg 3.20 cdlast = cd, cd = cd->next)
752 greg 3.1 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 greg 3.14 sizeof(myCD.carr[0])*myCD.calen);
759 greg 3.1 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 greg 3.20 cd->next = (SDMatCDst *)sdc->cdList;
771 greg 3.1 sdc->cdList = (SDCDst *)cd;
772     }
773     return (SDCDst *)cd; /* ready to go */
774     }
775    
776     /* Sample cumulative distribution */
777     static SDError
778 greg 3.12 SDsampMtxCDist(FVECT ioVec, double randX, const SDCDst *cdp)
779 greg 3.1 {
780     const unsigned maxval = ~0;
781     const SDMatCDst *mcd = (const SDMatCDst *)cdp;
782     const unsigned target = randX*maxval;
783     int i, iupper, ilower;
784 greg 3.5 /* check arguments */
785 greg 3.12 if ((ioVec == NULL) | (mcd == NULL))
786 greg 3.5 return SDEargument;
787 greg 3.1 /* binary search to find index */
788     ilower = 0; iupper = mcd->calen;
789     while ((i = (iupper + ilower) >> 1) != ilower)
790 greg 3.18 if (target >= mcd->carr[i])
791 greg 3.1 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 greg 3.12 if ((*mcd->ob_vec)(ioVec, i+randX, mcd->ob_priv))
799 greg 3.1 return SDEnone;
800 greg 3.12 strcpy(SDerrorDetail, "Matrix BSDF sampling fault");
801 greg 3.1 return SDEinternal;
802     }
803    
804     /* Fixed resolution BSDF methods */
805     SDFunc SDhandleMtx = {
806     &SDgetMtxBSDF,
807     &SDqueryMtxProjSA,
808     &SDgetMtxCDist,
809     &SDsampMtxCDist,
810     &SDfreeMatrix,
811     };