ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.3
Committed: Fri Feb 18 02:41:55 2011 UTC (13 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.2: +3 -45 lines
Log Message:
Minor fixes and adjustments

File Contents

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