ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.25
Committed: Sun Apr 21 22:58:40 2013 UTC (11 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.24: +21 -35 lines
Log Message:
Exposed functions and data structures needed by bsdf2klems

File Contents

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