ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.16
Committed: Thu Jun 9 17:09:39 2011 UTC (12 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.15: +3 -2 lines
Log Message:
Fixes for Windows and bug fix in bsdf_m.c

File Contents

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