ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.27
Committed: Sat Jun 29 21:03:44 2013 UTC (10 years, 10 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.26: +2 -2 lines
Log Message:
Fixed problem with acos(1) returning NaN

File Contents

# User Rev Content
1 greg 3.2 #ifndef lint
2 greg 3.27 static const char RCSid[] = "$Id: bsdf_m.c,v 3.26 2013/04/23 17:25:23 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 greg 3.27 pol = 180.0/M_PI*Acos(v[2]);
174 greg 3.1 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.26 if (sd->tf != NULL)
606     extract_diffuse(&sd->tLamb, sd->tf);
607     if (sd->tb != NULL)
608     extract_diffuse(&sd->tLamb, sd->tb);
609 greg 3.15 /* return success */
610 greg 3.1 return SDEnone;
611     }
612    
613     /* Get Matrix BSDF value */
614     static int
615     SDgetMtxBSDF(float coef[SDmaxCh], const FVECT outVec,
616 greg 3.12 const FVECT inVec, SDComponent *sdc)
617 greg 3.1 {
618 greg 3.12 const SDMat *dp;
619 greg 3.1 int i_ndx, o_ndx;
620 greg 3.12 /* check arguments */
621     if ((coef == NULL) | (outVec == NULL) | (inVec == NULL) | (sdc == NULL)
622     || (dp = (SDMat *)sdc->dist) == NULL)
623     return 0;
624 greg 3.1 /* get angle indices */
625     i_ndx = mBSDF_incndx(dp, inVec);
626     o_ndx = mBSDF_outndx(dp, outVec);
627     /* try reciprocity if necessary */
628 greg 3.19 if ((i_ndx < 0) & (o_ndx < 0)) {
629 greg 3.1 i_ndx = mBSDF_incndx(dp, outVec);
630     o_ndx = mBSDF_outndx(dp, inVec);
631     }
632     if ((i_ndx < 0) | (o_ndx < 0))
633     return 0; /* nothing from this component */
634     coef[0] = mBSDF_value(dp, i_ndx, o_ndx);
635     return 1; /* XXX monochrome for now */
636     }
637    
638 greg 3.12 /* Query solid angle for vector(s) */
639 greg 3.1 static SDError
640 greg 3.10 SDqueryMtxProjSA(double *psa, const FVECT v1, const RREAL *v2,
641 greg 3.12 int qflags, SDComponent *sdc)
642 greg 3.1 {
643 greg 3.12 const SDMat *dp;
644 greg 3.5 double inc_psa, out_psa;
645     /* check arguments */
646 greg 3.12 if ((psa == NULL) | (v1 == NULL) | (sdc == NULL) ||
647     (dp = (SDMat *)sdc->dist) == NULL)
648 greg 3.1 return SDEargument;
649 greg 3.10 if (v2 == NULL)
650     v2 = v1;
651 greg 3.5 /* get projected solid angles */
652 greg 3.10 out_psa = mBSDF_outohm(dp, mBSDF_outndx(dp, v1));
653     inc_psa = mBSDF_incohm(dp, mBSDF_incndx(dp, v2));
654 greg 3.12 if ((v1 != v2) & (out_psa <= 0) & (inc_psa <= 0)) {
655 greg 3.11 inc_psa = mBSDF_outohm(dp, mBSDF_outndx(dp, v2));
656     out_psa = mBSDF_incohm(dp, mBSDF_incndx(dp, v1));
657     }
658 greg 3.5
659     switch (qflags) { /* record based on flag settings */
660     case SDqueryMax:
661     if (inc_psa > psa[0])
662     psa[0] = inc_psa;
663     if (out_psa > psa[0])
664     psa[0] = out_psa;
665     break;
666     case SDqueryMin+SDqueryMax:
667 greg 3.13 if (inc_psa > psa[1])
668 greg 3.5 psa[1] = inc_psa;
669 greg 3.13 if (out_psa > psa[1])
670 greg 3.5 psa[1] = out_psa;
671     /* fall through */
672 greg 3.12 case SDqueryVal:
673     if (qflags == SDqueryVal)
674     psa[0] = M_PI;
675 greg 3.14 /* fall through */
676     case SDqueryMin:
677 greg 3.10 if ((inc_psa > 0) & (inc_psa < psa[0]))
678 greg 3.1 psa[0] = inc_psa;
679 greg 3.10 if ((out_psa > 0) & (out_psa < psa[0]))
680 greg 3.5 psa[0] = out_psa;
681     break;
682 greg 3.1 }
683 greg 3.5 /* make sure it's legal */
684 greg 3.10 return (psa[0] <= 0) ? SDEinternal : SDEnone;
685 greg 3.1 }
686    
687     /* Compute new cumulative distribution from BSDF */
688     static int
689     make_cdist(SDMatCDst *cd, const FVECT inVec, SDMat *dp, int rev)
690     {
691     const unsigned maxval = ~0;
692     double *cmtab, scale;
693     int o;
694    
695     cmtab = (double *)malloc((cd->calen+1)*sizeof(double));
696     if (cmtab == NULL)
697     return 0;
698     cmtab[0] = .0;
699     for (o = 0; o < cd->calen; o++) {
700     if (rev)
701     cmtab[o+1] = mBSDF_value(dp, o, cd->indx) *
702     (*dp->ib_ohm)(o, dp->ib_priv);
703     else
704     cmtab[o+1] = mBSDF_value(dp, cd->indx, o) *
705     (*dp->ob_ohm)(o, dp->ob_priv);
706     cmtab[o+1] += cmtab[o];
707     }
708     cd->cTotal = cmtab[cd->calen];
709     scale = (double)maxval / cd->cTotal;
710     cd->carr[0] = 0;
711     for (o = 1; o < cd->calen; o++)
712     cd->carr[o] = scale*cmtab[o] + .5;
713     cd->carr[cd->calen] = maxval;
714     free(cmtab);
715     return 1;
716     }
717    
718     /* Get cumulative distribution for matrix BSDF */
719     static const SDCDst *
720     SDgetMtxCDist(const FVECT inVec, SDComponent *sdc)
721     {
722 greg 3.12 SDMat *dp;
723 greg 3.1 int reverse;
724     SDMatCDst myCD;
725     SDMatCDst *cd, *cdlast;
726 greg 3.5 /* check arguments */
727 greg 3.12 if ((inVec == NULL) | (sdc == NULL) ||
728     (dp = (SDMat *)sdc->dist) == NULL)
729 greg 3.1 return NULL;
730     memset(&myCD, 0, sizeof(myCD));
731     myCD.indx = mBSDF_incndx(dp, inVec);
732     if (myCD.indx >= 0) {
733     myCD.ob_priv = dp->ob_priv;
734     myCD.ob_vec = dp->ob_vec;
735     myCD.calen = dp->nout;
736     reverse = 0;
737 greg 3.19 } else { /* try reciprocity */
738 greg 3.1 myCD.indx = mBSDF_outndx(dp, inVec);
739     if (myCD.indx < 0)
740     return NULL;
741     myCD.ob_priv = dp->ib_priv;
742     myCD.ob_vec = dp->ib_vec;
743     myCD.calen = dp->ninc;
744     reverse = 1;
745     }
746     cdlast = NULL; /* check for it in cache list */
747 greg 3.14 for (cd = (SDMatCDst *)sdc->cdList; cd != NULL;
748 greg 3.20 cdlast = cd, cd = cd->next)
749 greg 3.1 if (cd->indx == myCD.indx && (cd->calen == myCD.calen) &
750     (cd->ob_priv == myCD.ob_priv) &
751     (cd->ob_vec == myCD.ob_vec))
752     break;
753     if (cd == NULL) { /* need to allocate new entry */
754     cd = (SDMatCDst *)malloc(sizeof(SDMatCDst) +
755 greg 3.14 sizeof(myCD.carr[0])*myCD.calen);
756 greg 3.1 if (cd == NULL)
757     return NULL;
758     *cd = myCD; /* compute cumulative distribution */
759     if (!make_cdist(cd, inVec, dp, reverse)) {
760     free(cd);
761     return NULL;
762     }
763     cdlast = cd;
764     }
765     if (cdlast != NULL) { /* move entry to head of cache list */
766     cdlast->next = cd->next;
767 greg 3.20 cd->next = (SDMatCDst *)sdc->cdList;
768 greg 3.1 sdc->cdList = (SDCDst *)cd;
769     }
770     return (SDCDst *)cd; /* ready to go */
771     }
772    
773     /* Sample cumulative distribution */
774     static SDError
775 greg 3.12 SDsampMtxCDist(FVECT ioVec, double randX, const SDCDst *cdp)
776 greg 3.1 {
777     const unsigned maxval = ~0;
778     const SDMatCDst *mcd = (const SDMatCDst *)cdp;
779     const unsigned target = randX*maxval;
780     int i, iupper, ilower;
781 greg 3.5 /* check arguments */
782 greg 3.12 if ((ioVec == NULL) | (mcd == NULL))
783 greg 3.5 return SDEargument;
784 greg 3.1 /* binary search to find index */
785     ilower = 0; iupper = mcd->calen;
786     while ((i = (iupper + ilower) >> 1) != ilower)
787 greg 3.18 if (target >= mcd->carr[i])
788 greg 3.1 ilower = i;
789     else
790     iupper = i;
791     /* localize random position */
792     randX = (randX*maxval - mcd->carr[ilower]) /
793     (double)(mcd->carr[iupper] - mcd->carr[ilower]);
794     /* convert index to vector */
795 greg 3.12 if ((*mcd->ob_vec)(ioVec, i+randX, mcd->ob_priv))
796 greg 3.1 return SDEnone;
797 greg 3.12 strcpy(SDerrorDetail, "Matrix BSDF sampling fault");
798 greg 3.1 return SDEinternal;
799     }
800    
801     /* Fixed resolution BSDF methods */
802     SDFunc SDhandleMtx = {
803     &SDgetMtxBSDF,
804     &SDqueryMtxProjSA,
805     &SDgetMtxCDist,
806     &SDsampMtxCDist,
807     &SDfreeMatrix,
808     };