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

File Contents

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