ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf_m.c
Revision: 3.15
Committed: Wed Apr 27 20:03:25 2011 UTC (13 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.14: +19 -14 lines
Log Message:
Initial untested variable-resolution BSDF

File Contents

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