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

File Contents

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