ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bsdf.c
(Generate patch)

Comparing ray/src/common/bsdf.c (file contents):
Revision 2.12 by greg, Thu Jan 6 04:40:22 2011 UTC vs.
Revision 2.29 by greg, Fri Jun 3 18:12:58 2011 UTC

# Line 2 | Line 2
2   static const char RCSid[] = "$Id$";
3   #endif
4   /*
5 + *  bsdf.c
6 + *  
7 + *  Definitions for bidirectional scattering distribution functions.
8 + *
9 + *  Created by Greg Ward on 1/10/11.
10 + *
11 + */
12 +
13 + #include <stdio.h>
14 + #include <stdlib.h>
15 + #include <math.h>
16 + #include "ezxml.h"
17 + #include "hilbert.h"
18 + #include "bsdf.h"
19 + #include "bsdf_m.h"
20 + #include "bsdf_t.h"
21 +
22 + /* English ASCII strings corresponding to ennumerated errors */
23 + const char              *SDerrorEnglish[] = {
24 +                                "No error",
25 +                                "Memory error",
26 +                                "File input/output error",
27 +                                "File format error",
28 +                                "Illegal argument",
29 +                                "Invalid data",
30 +                                "Unsupported feature",
31 +                                "Internal program error",
32 +                                "Unknown error"
33 +                        };
34 +
35 + /* Additional information on last error (ASCII English) */
36 + char                    SDerrorDetail[256];
37 +
38 + /* Cache of loaded BSDFs */
39 + struct SDCache_s        *SDcacheList = NULL;
40 +
41 + /* Retain BSDFs in cache list */
42 + int                     SDretainSet = SDretainNone;
43 +
44 + /* Report any error to the indicated stream (in English) */
45 + SDError
46 + SDreportEnglish(SDError ec, FILE *fp)
47 + {
48 +        if (!ec)
49 +                return SDEnone;
50 +        if ((ec < SDEnone) | (ec > SDEunknown)) {
51 +                SDerrorDetail[0] = '\0';
52 +                ec = SDEunknown;
53 +        }
54 +        if (fp == NULL)
55 +                return ec;
56 +        fputs(SDerrorEnglish[ec], fp);
57 +        if (SDerrorDetail[0]) {
58 +                fputs(": ", fp);
59 +                fputs(SDerrorDetail, fp);
60 +        }
61 +        fputc('\n', fp);
62 +        if (fp != stderr)
63 +                fflush(fp);
64 +        return ec;
65 + }
66 +
67 + static double
68 + to_meters(              /* return factor to convert given unit to meters */
69 +        const char *unit
70 + )
71 + {
72 +        if (unit == NULL) return(1.);           /* safe assumption? */
73 +        if (!strcasecmp(unit, "Meter")) return(1.);
74 +        if (!strcasecmp(unit, "Foot")) return(.3048);
75 +        if (!strcasecmp(unit, "Inch")) return(.0254);
76 +        if (!strcasecmp(unit, "Centimeter")) return(.01);
77 +        if (!strcasecmp(unit, "Millimeter")) return(.001);
78 +        sprintf(SDerrorDetail, "Unknown dimensional unit '%s'", unit);
79 +        return(-1.);
80 + }
81 +
82 + /* Load geometric dimensions and description (if any) */
83 + static SDError
84 + SDloadGeometry(SDData *sd, ezxml_t wdb)
85 + {
86 +        ezxml_t         geom;
87 +        double          cfact;
88 +        const char      *fmt, *mgfstr;
89 +
90 +        if (wdb == NULL)                /* no geometry section? */
91 +                return SDEnone;
92 +        sd->dim[0] = sd->dim[1] = sd->dim[2] = .0;
93 +        if ((geom = ezxml_child(wdb, "Width")) != NULL)
94 +                sd->dim[0] = atof(ezxml_txt(geom)) *
95 +                                to_meters(ezxml_attr(geom, "unit"));
96 +        if ((geom = ezxml_child(wdb, "Height")) != NULL)
97 +                sd->dim[1] = atof(ezxml_txt(geom)) *
98 +                                to_meters(ezxml_attr(geom, "unit"));
99 +        if ((geom = ezxml_child(wdb, "Thickness")) != NULL)
100 +                sd->dim[2] = atof(ezxml_txt(geom)) *
101 +                                to_meters(ezxml_attr(geom, "unit"));
102 +        if ((sd->dim[0] < 0) | (sd->dim[1] < 0) | (sd->dim[2] < 0)) {
103 +                sprintf(SDerrorDetail, "Negative size in \"%s\"", sd->name);
104 +                return SDEdata;
105 +        }
106 +        if ((geom = ezxml_child(wdb, "Geometry")) == NULL ||
107 +                        (mgfstr = ezxml_txt(geom)) == NULL)
108 +                return SDEnone;
109 +        if ((fmt = ezxml_attr(geom, "format")) != NULL &&
110 +                        strcasecmp(fmt, "MGF")) {
111 +                sprintf(SDerrorDetail,
112 +                        "Unrecognized geometry format '%s' in \"%s\"",
113 +                                        fmt, sd->name);
114 +                return SDEsupport;
115 +        }
116 +        cfact = to_meters(ezxml_attr(geom, "unit"));
117 +        sd->mgf = (char *)malloc(strlen(mgfstr)+32);
118 +        if (sd->mgf == NULL) {
119 +                strcpy(SDerrorDetail, "Out of memory in SDloadGeometry");
120 +                return SDEmemory;
121 +        }
122 +        if (cfact < 0.99 || cfact > 1.01)
123 +                sprintf(sd->mgf, "xf -s %.5f\n%s\nxf\n", cfact, mgfstr);
124 +        else
125 +                strcpy(sd->mgf, mgfstr);
126 +        return SDEnone;
127 + }
128 +
129 + /* Load a BSDF struct from the given file (free first and keep name) */
130 + SDError
131 + SDloadFile(SDData *sd, const char *fname)
132 + {
133 +        SDError         lastErr;
134 +        ezxml_t         fl, wtl;
135 +
136 +        if ((sd == NULL) | (fname == NULL || !*fname))
137 +                return SDEargument;
138 +                                /* free old data, keeping name */
139 +        SDfreeBSDF(sd);
140 +                                /* parse XML file */
141 +        fl = ezxml_parse_file(fname);
142 +        if (fl == NULL) {
143 +                sprintf(SDerrorDetail, "Cannot open BSDF \"%s\"", fname);
144 +                return SDEfile;
145 +        }
146 +        if (ezxml_error(fl)[0]) {
147 +                sprintf(SDerrorDetail, "BSDF \"%s\" %s", fname, ezxml_error(fl));
148 +                ezxml_free(fl);
149 +                return SDEformat;
150 +        }
151 +        if (strcmp(ezxml_name(fl), "WindowElement")) {
152 +                sprintf(SDerrorDetail,
153 +                        "BSDF \"%s\": top level node not 'WindowElement'",
154 +                                sd->name);
155 +                ezxml_free(fl);
156 +                return SDEformat;
157 +        }
158 +        wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
159 +        if (wtl == NULL) {
160 +                sprintf(SDerrorDetail, "BSDF \"%s\": no optical layer'",
161 +                                sd->name);
162 +                ezxml_free(fl);
163 +                return SDEformat;
164 +        }
165 +                                /* load geometry if present */
166 +        lastErr = SDloadGeometry(sd, ezxml_child(wtl, "Material"));
167 +        if (lastErr)
168 +                return lastErr;
169 +                                /* try loading variable resolution data */
170 +        lastErr = SDloadTre(sd, wtl);
171 +                                /* check our result */
172 +        if (lastErr == SDEsupport)      /* try matrix BSDF if not tree data */
173 +                lastErr = SDloadMtx(sd, wtl);
174 +                
175 +                                /* done with XML file */
176 +        ezxml_free(fl);
177 +        
178 +        if (lastErr) {          /* was there a load error? */
179 +                SDfreeBSDF(sd);
180 +                return lastErr;
181 +        }
182 +                                /* remove any insignificant components */
183 +        if (sd->rf != NULL && sd->rf->maxHemi <= .001) {
184 +                SDfreeSpectralDF(sd->rf); sd->rf = NULL;
185 +        }
186 +        if (sd->rb != NULL && sd->rb->maxHemi <= .001) {
187 +                SDfreeSpectralDF(sd->rb); sd->rb = NULL;
188 +        }
189 +        if (sd->tf != NULL && sd->tf->maxHemi <= .001) {
190 +                SDfreeSpectralDF(sd->tf); sd->tf = NULL;
191 +        }
192 +                                /* return success */
193 +        return SDEnone;
194 + }
195 +
196 + /* Allocate new spectral distribution function */
197 + SDSpectralDF *
198 + SDnewSpectralDF(int nc)
199 + {
200 +        SDSpectralDF    *df;
201 +        
202 +        if (nc <= 0) {
203 +                strcpy(SDerrorDetail, "Zero component spectral DF request");
204 +                return NULL;
205 +        }
206 +        df = (SDSpectralDF *)malloc(sizeof(SDSpectralDF) +
207 +                                        (nc-1)*sizeof(SDComponent));
208 +        if (df == NULL) {
209 +                sprintf(SDerrorDetail,
210 +                                "Cannot allocate %d component spectral DF", nc);
211 +                return NULL;
212 +        }
213 +        df->minProjSA = .0;
214 +        df->maxHemi = .0;
215 +        df->ncomp = nc;
216 +        memset(df->comp, 0, nc*sizeof(SDComponent));
217 +        return df;
218 + }
219 +
220 + /* Free cached cumulative distributions for BSDF component */
221 + void
222 + SDfreeCumulativeCache(SDSpectralDF *df)
223 + {
224 +        int     n;
225 +        SDCDst  *cdp;
226 +
227 +        if (df == NULL)
228 +                return;
229 +        for (n = df->ncomp; n-- > 0; )
230 +                while ((cdp = df->comp[n].cdList) != NULL) {
231 +                        df->comp[n].cdList = cdp->next;
232 +                        free(cdp);
233 +                }
234 + }
235 +
236 + /* Free a spectral distribution function */
237 + void
238 + SDfreeSpectralDF(SDSpectralDF *df)
239 + {
240 +        int     n;
241 +
242 +        if (df == NULL)
243 +                return;
244 +        SDfreeCumulativeCache(df);
245 +        for (n = df->ncomp; n-- > 0; )
246 +                (*df->comp[n].func->freeSC)(df->comp[n].dist);
247 +        free(df);
248 + }
249 +
250 + /* Shorten file path to useable BSDF name, removing suffix */
251 + void
252 + SDclipName(char *res, const char *fname)
253 + {
254 +        const char      *cp, *dot = NULL;
255 +        
256 +        for (cp = fname; *cp; cp++)
257 +                if (*cp == '.')
258 +                        dot = cp;
259 +        if ((dot == NULL) | (dot < fname+2))
260 +                dot = cp;
261 +        if (dot - fname >= SDnameLn)
262 +                fname = dot - SDnameLn + 1;
263 +        while (fname < dot)
264 +                *res++ = *fname++;
265 +        *res = '\0';
266 + }
267 +
268 + /* Initialize an unused BSDF struct (simply clears to zeroes) */
269 + void
270 + SDclearBSDF(SDData *sd, const char *fname)
271 + {
272 +        if (sd == NULL)
273 +                return;
274 +        memset(sd, 0, sizeof(SDData));
275 +        if (fname == NULL)
276 +                return;
277 +        SDclipName(sd->name, fname);
278 + }
279 +
280 + /* Free data associated with BSDF struct */
281 + void
282 + SDfreeBSDF(SDData *sd)
283 + {
284 +        if (sd == NULL)
285 +                return;
286 +        if (sd->mgf != NULL) {
287 +                free(sd->mgf);
288 +                sd->mgf = NULL;
289 +        }
290 +        if (sd->rf != NULL) {
291 +                SDfreeSpectralDF(sd->rf);
292 +                sd->rf = NULL;
293 +        }
294 +        if (sd->rb != NULL) {
295 +                SDfreeSpectralDF(sd->rb);
296 +                sd->rb = NULL;
297 +        }
298 +        if (sd->tf != NULL) {
299 +                SDfreeSpectralDF(sd->tf);
300 +                sd->tf = NULL;
301 +        }
302 +        sd->rLambFront.cieY = .0;
303 +        sd->rLambFront.spec.flags = 0;
304 +        sd->rLambBack.cieY = .0;
305 +        sd->rLambBack.spec.flags = 0;
306 +        sd->tLamb.cieY = .0;
307 +        sd->tLamb.spec.flags = 0;
308 + }
309 +
310 + /* Find writeable BSDF by name, or allocate new cache entry if absent */
311 + SDData *
312 + SDgetCache(const char *bname)
313 + {
314 +        struct SDCache_s        *sdl;
315 +        char                    sdnam[SDnameLn];
316 +
317 +        if (bname == NULL)
318 +                return NULL;
319 +
320 +        SDclipName(sdnam, bname);
321 +        for (sdl = SDcacheList; sdl != NULL; sdl = sdl->next)
322 +                if (!strcmp(sdl->bsdf.name, sdnam)) {
323 +                        sdl->refcnt++;
324 +                        return &sdl->bsdf;
325 +                }
326 +
327 +        sdl = (struct SDCache_s *)calloc(1, sizeof(struct SDCache_s));
328 +        if (sdl == NULL)
329 +                return NULL;
330 +
331 +        strcpy(sdl->bsdf.name, sdnam);
332 +        sdl->next = SDcacheList;
333 +        SDcacheList = sdl;
334 +
335 +        sdl->refcnt = 1;
336 +        return &sdl->bsdf;
337 + }
338 +
339 + /* Get loaded BSDF from cache (or load and cache it on first call) */
340 + /* Report any problem to stderr and return NULL on failure */
341 + const SDData *
342 + SDcacheFile(const char *fname)
343 + {
344 +        SDData          *sd;
345 +        SDError         ec;
346 +        
347 +        if (fname == NULL || !*fname)
348 +                return NULL;
349 +        SDerrorDetail[0] = '\0';
350 +        if ((sd = SDgetCache(fname)) == NULL) {
351 +                SDreportEnglish(SDEmemory, stderr);
352 +                return NULL;
353 +        }
354 +        if (!SDisLoaded(sd) && (ec = SDloadFile(sd, fname))) {
355 +                SDreportEnglish(ec, stderr);
356 +                SDfreeCache(sd);
357 +                return NULL;
358 +        }
359 +        return sd;
360 + }
361 +
362 + /* Free a BSDF from our cache (clear all if NULL) */
363 + void
364 + SDfreeCache(const SDData *sd)
365 + {
366 +        struct SDCache_s        *sdl, *sdLast = NULL;
367 +
368 +        if (sd == NULL) {               /* free entire list */
369 +                while ((sdl = SDcacheList) != NULL) {
370 +                        SDcacheList = sdl->next;
371 +                        SDfreeBSDF(&sdl->bsdf);
372 +                        free(sdl);
373 +                }
374 +                return;
375 +        }
376 +        for (sdl = SDcacheList; sdl != NULL; sdl = (sdLast=sdl)->next)
377 +                if (&sdl->bsdf == sd)
378 +                        break;
379 +        if (sdl == NULL || (sdl->refcnt -= (sdl->refcnt > 0)))
380 +                return;                 /* missing or still in use */
381 +                                        /* keep unreferenced data? */
382 +        if (SDisLoaded(sd) && SDretainSet) {
383 +                if (SDretainSet == SDretainAll)
384 +                        return;         /* keep everything */
385 +                                        /* else free cumulative data */
386 +                SDfreeCumulativeCache(sd->rf);
387 +                SDfreeCumulativeCache(sd->rb);
388 +                SDfreeCumulativeCache(sd->tf);
389 +                return;
390 +        }
391 +                                        /* remove from list and free */
392 +        if (sdLast == NULL)
393 +                SDcacheList = sdl->next;
394 +        else
395 +                sdLast->next = sdl->next;
396 +        SDfreeBSDF(&sdl->bsdf);
397 +        free(sdl);
398 + }
399 +
400 + /* Sample an individual BSDF component */
401 + SDError
402 + SDsampComponent(SDValue *sv, FVECT ioVec, double randX, SDComponent *sdc)
403 + {
404 +        float           coef[SDmaxCh];
405 +        SDError         ec;
406 +        FVECT           inVec;
407 +        const SDCDst    *cd;
408 +        double          d;
409 +        int             n;
410 +                                        /* check arguments */
411 +        if ((sv == NULL) | (ioVec == NULL) | (sdc == NULL))
412 +                return SDEargument;
413 +                                        /* get cumulative distribution */
414 +        VCOPY(inVec, ioVec);
415 +        cd = (*sdc->func->getCDist)(inVec, sdc);
416 +        if (cd == NULL)
417 +                return SDEmemory;
418 +        if (cd->cTotal <= 1e-7) {       /* anything to sample? */
419 +                sv->spec = c_dfcolor;
420 +                sv->cieY = .0;
421 +                memset(ioVec, 0, 3*sizeof(double));
422 +                return SDEnone;
423 +        }
424 +        sv->cieY = cd->cTotal;
425 +                                        /* compute sample direction */
426 +        ec = (*sdc->func->sampCDist)(ioVec, randX, cd);
427 +        if (ec)
428 +                return ec;
429 +                                        /* get BSDF color */
430 +        n = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc);
431 +        if (n <= 0) {
432 +                strcpy(SDerrorDetail, "BSDF sample value error");
433 +                return SDEinternal;
434 +        }
435 +        sv->spec = sdc->cspec[0];
436 +        d = coef[0];
437 +        while (--n) {
438 +                c_cmix(&sv->spec, d, &sv->spec, coef[n], &sdc->cspec[n]);
439 +                d += coef[n];
440 +        }
441 +                                        /* make sure everything is set */
442 +        c_ccvt(&sv->spec, C_CSXY+C_CSSPEC);
443 +        return SDEnone;
444 + }
445 +
446 + #define MS_MAXDIM       15
447 +
448 + /* Convert 1-dimensional random variable to N-dimensional */
449 + void
450 + SDmultiSamp(double t[], int n, double randX)
451 + {
452 +        unsigned        nBits;
453 +        double          scale;
454 +        bitmask_t       ndx, coord[MS_MAXDIM];
455 +        
456 +        while (n > MS_MAXDIM)           /* punt for higher dimensions */
457 +                t[--n] = rand()*(1./(RAND_MAX+.5));
458 +        nBits = (8*sizeof(bitmask_t) - 1) / n;
459 +        ndx = randX * (double)((bitmask_t)1 << (nBits*n));
460 +                                        /* get coordinate on Hilbert curve */
461 +        hilbert_i2c(n, nBits, ndx, coord);
462 +                                        /* convert back to [0,1) range */
463 +        scale = 1. / (double)((bitmask_t)1 << nBits);
464 +        while (n--)
465 +                t[n] = scale * ((double)coord[n] + rand()*(1./(RAND_MAX+.5)));
466 + }
467 +
468 + #undef MS_MAXDIM
469 +
470 + /* Generate diffuse hemispherical sample */
471 + static void
472 + SDdiffuseSamp(FVECT outVec, int outFront, double randX)
473 + {
474 +                                        /* convert to position on hemisphere */
475 +        SDmultiSamp(outVec, 2, randX);
476 +        SDsquare2disk(outVec, outVec[0], outVec[1]);
477 +        outVec[2] = 1. - outVec[0]*outVec[0] - outVec[1]*outVec[1];
478 +        if (outVec[2] > 0)              /* a bit of paranoia */
479 +                outVec[2] = sqrt(outVec[2]);
480 +        if (!outFront)                  /* going out back? */
481 +                outVec[2] = -outVec[2];
482 + }
483 +
484 + /* Query projected solid angle coverage for non-diffuse BSDF direction */
485 + SDError
486 + SDsizeBSDF(double *projSA, const FVECT v1, const RREAL *v2,
487 +                                int qflags, const SDData *sd)
488 + {
489 +        SDSpectralDF    *rdf, *tdf;
490 +        SDError         ec;
491 +        int             i;
492 +                                        /* check arguments */
493 +        if ((projSA == NULL) | (v1 == NULL) | (sd == NULL))
494 +                return SDEargument;
495 +                                        /* initialize extrema */
496 +        switch (qflags) {
497 +        case SDqueryMax:
498 +                projSA[0] = .0;
499 +                break;
500 +        case SDqueryMin+SDqueryMax:
501 +                projSA[1] = .0;
502 +                /* fall through */
503 +        case SDqueryMin:
504 +                projSA[0] = 10.;
505 +                break;
506 +        case 0:
507 +                return SDEargument;
508 +        }
509 +        if (v1[2] > 0)                  /* front surface query? */
510 +                rdf = sd->rf;
511 +        else
512 +                rdf = sd->rb;
513 +        tdf = sd->tf;
514 +        if (v2 != NULL)                 /* bidirectional? */
515 +                if (v1[2] > 0 ^ v2[2] > 0)
516 +                        rdf = NULL;
517 +                else
518 +                        tdf = NULL;
519 +        ec = SDEdata;                   /* run through components */
520 +        for (i = (rdf==NULL) ? 0 : rdf->ncomp; i--; ) {
521 +                ec = (*rdf->comp[i].func->queryProjSA)(projSA, v1, v2,
522 +                                                qflags, &rdf->comp[i]);
523 +                if (ec)
524 +                        return ec;
525 +        }
526 +        for (i = (tdf==NULL) ? 0 : tdf->ncomp; i--; ) {
527 +                ec = (*tdf->comp[i].func->queryProjSA)(projSA, v1, v2,
528 +                                                qflags, &tdf->comp[i]);
529 +                if (ec)
530 +                        return ec;
531 +        }
532 +        if (ec) {                       /* all diffuse? */
533 +                projSA[0] = M_PI;
534 +                if (qflags == SDqueryMin+SDqueryMax)
535 +                        projSA[1] = M_PI;
536 +        }
537 +        return SDEnone;
538 + }
539 +
540 + /* Return BSDF for the given incident and scattered ray vectors */
541 + SDError
542 + SDevalBSDF(SDValue *sv, const FVECT outVec, const FVECT inVec, const SDData *sd)
543 + {
544 +        int             inFront, outFront;
545 +        SDSpectralDF    *sdf;
546 +        float           coef[SDmaxCh];
547 +        int             nch, i;
548 +                                        /* check arguments */
549 +        if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL))
550 +                return SDEargument;
551 +                                        /* whose side are we on? */
552 +        inFront = (inVec[2] > 0);
553 +        outFront = (outVec[2] > 0);
554 +                                        /* start with diffuse portion */
555 +        if (inFront & outFront) {
556 +                *sv = sd->rLambFront;
557 +                sdf = sd->rf;
558 +        } else if (!(inFront | outFront)) {
559 +                *sv = sd->rLambBack;
560 +                sdf = sd->rb;
561 +        } else /* inFront ^ outFront */ {
562 +                *sv = sd->tLamb;
563 +                sdf = sd->tf;
564 +        }
565 +        sv->cieY *= 1./M_PI;
566 +                                        /* add non-diffuse components */
567 +        i = (sdf != NULL) ? sdf->ncomp : 0;
568 +        while (i-- > 0) {
569 +                nch = (*sdf->comp[i].func->getBSDFs)(coef, outVec, inVec,
570 +                                                        &sdf->comp[i]);
571 +                while (nch-- > 0) {
572 +                        c_cmix(&sv->spec, sv->cieY, &sv->spec,
573 +                                        coef[nch], &sdf->comp[i].cspec[nch]);
574 +                        sv->cieY += coef[nch];
575 +                }
576 +        }
577 +                                        /* make sure everything is set */
578 +        c_ccvt(&sv->spec, C_CSXY+C_CSSPEC);
579 +        return SDEnone;
580 + }
581 +
582 + /* Compute directional hemispherical scattering at this incident angle */
583 + double
584 + SDdirectHemi(const FVECT inVec, int sflags, const SDData *sd)
585 + {
586 +        double          hsum;
587 +        SDSpectralDF    *rdf;
588 +        const SDCDst    *cd;
589 +        int             i;
590 +                                        /* check arguments */
591 +        if ((inVec == NULL) | (sd == NULL))
592 +                return .0;
593 +                                        /* gather diffuse components */
594 +        if (inVec[2] > 0) {
595 +                hsum = sd->rLambFront.cieY;
596 +                rdf = sd->rf;
597 +        } else /* !inFront */ {
598 +                hsum = sd->rLambBack.cieY;
599 +                rdf = sd->rb;
600 +        }
601 +        if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
602 +                hsum = .0;
603 +        if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
604 +                hsum += sd->tLamb.cieY;
605 +                                        /* gather non-diffuse components */
606 +        i = ((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR &&
607 +                        rdf != NULL) ? rdf->ncomp : 0;
608 +        while (i-- > 0) {               /* non-diffuse reflection */
609 +                cd = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
610 +                if (cd != NULL)
611 +                        hsum += cd->cTotal;
612 +        }
613 +        i = ((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT &&
614 +                        sd->tf != NULL) ? sd->tf->ncomp : 0;
615 +        while (i-- > 0) {               /* non-diffuse transmission */
616 +                cd = (*sd->tf->comp[i].func->getCDist)(inVec, &sd->tf->comp[i]);
617 +                if (cd != NULL)
618 +                        hsum += cd->cTotal;
619 +        }
620 +        return hsum;
621 + }
622 +
623 + /* Sample BSDF direction based on the given random variable */
624 + SDError
625 + SDsampBSDF(SDValue *sv, FVECT ioVec, double randX, int sflags, const SDData *sd)
626 + {
627 +        SDError         ec;
628 +        FVECT           inVec;
629 +        int             inFront;
630 +        SDSpectralDF    *rdf;
631 +        double          rdiff;
632 +        float           coef[SDmaxCh];
633 +        int             i, j, n, nr;
634 +        SDComponent     *sdc;
635 +        const SDCDst    **cdarr = NULL;
636 +                                        /* check arguments */
637 +        if ((sv == NULL) | (ioVec == NULL) | (sd == NULL) |
638 +                        (randX < 0) | (randX >= 1.))
639 +                return SDEargument;
640 +                                        /* whose side are we on? */
641 +        VCOPY(inVec, ioVec);
642 +        inFront = (inVec[2] > 0);
643 +                                        /* remember diffuse portions */
644 +        if (inFront) {
645 +                *sv = sd->rLambFront;
646 +                rdf = sd->rf;
647 +        } else /* !inFront */ {
648 +                *sv = sd->rLambBack;
649 +                rdf = sd->rb;
650 +        }
651 +        if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR)
652 +                sv->cieY = .0;
653 +        rdiff = sv->cieY;
654 +        if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT)
655 +                sv->cieY += sd->tLamb.cieY;
656 +                                        /* gather non-diffuse components */
657 +        i = nr = ((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR &&
658 +                        rdf != NULL) ? rdf->ncomp : 0;
659 +        j = ((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT &&
660 +                        sd->tf != NULL) ? sd->tf->ncomp : 0;
661 +        n = i + j;
662 +        if (n > 0 && (cdarr = (const SDCDst **)malloc(n*sizeof(SDCDst *))) == NULL)
663 +                return SDEmemory;
664 +        while (j-- > 0) {               /* non-diffuse transmission */
665 +                cdarr[i+j] = (*sd->tf->comp[j].func->getCDist)(inVec, &sd->tf->comp[j]);
666 +                if (cdarr[i+j] == NULL) {
667 +                        free(cdarr);
668 +                        return SDEmemory;
669 +                }
670 +                sv->cieY += cdarr[i+j]->cTotal;
671 +        }
672 +        while (i-- > 0) {               /* non-diffuse reflection */
673 +                cdarr[i] = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]);
674 +                if (cdarr[i] == NULL) {
675 +                        free(cdarr);
676 +                        return SDEmemory;
677 +                }
678 +                sv->cieY += cdarr[i]->cTotal;
679 +        }
680 +        if (sv->cieY <= 1e-7) {         /* anything to sample? */
681 +                sv->cieY = .0;
682 +                memset(ioVec, 0, 3*sizeof(double));
683 +                return SDEnone;
684 +        }
685 +                                        /* scale random variable */
686 +        randX *= sv->cieY;
687 +                                        /* diffuse reflection? */
688 +        if (randX < rdiff) {
689 +                SDdiffuseSamp(ioVec, inFront, randX/rdiff);
690 +                goto done;
691 +        }
692 +        randX -= rdiff;
693 +                                        /* diffuse transmission? */
694 +        if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) {
695 +                if (randX < sd->tLamb.cieY) {
696 +                        sv->spec = sd->tLamb.spec;
697 +                        SDdiffuseSamp(ioVec, !inFront, randX/sd->tLamb.cieY);
698 +                        goto done;
699 +                }
700 +                randX -= sd->tLamb.cieY;
701 +        }
702 +                                        /* else one of cumulative dist. */
703 +        for (i = 0; i < n && randX < cdarr[i]->cTotal; i++)
704 +                randX -= cdarr[i]->cTotal;
705 +        if (i >= n)
706 +                return SDEinternal;
707 +                                        /* compute sample direction */
708 +        sdc = (i < nr) ? &rdf->comp[i] : &sd->tf->comp[i-nr];
709 +        ec = (*sdc->func->sampCDist)(ioVec, randX/cdarr[i]->cTotal, cdarr[i]);
710 +        if (ec)
711 +                return ec;
712 +                                        /* compute color */
713 +        j = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc);
714 +        if (j <= 0) {
715 +                sprintf(SDerrorDetail, "BSDF \"%s\" sampling value error",
716 +                                sd->name);
717 +                return SDEinternal;
718 +        }
719 +        sv->spec = sdc->cspec[0];
720 +        rdiff = coef[0];
721 +        while (--j) {
722 +                c_cmix(&sv->spec, rdiff, &sv->spec, coef[j], &sdc->cspec[j]);
723 +                rdiff += coef[j];
724 +        }
725 + done:
726 +        if (cdarr != NULL)
727 +                free(cdarr);
728 +                                        /* make sure everything is set */
729 +        c_ccvt(&sv->spec, C_CSXY+C_CSSPEC);
730 +        return SDEnone;
731 + }
732 +
733 + /* Compute World->BSDF transform from surface normal and up (Y) vector */
734 + SDError
735 + SDcompXform(RREAL vMtx[3][3], const FVECT sNrm, const FVECT uVec)
736 + {
737 +        if ((vMtx == NULL) | (sNrm == NULL) | (uVec == NULL))
738 +                return SDEargument;
739 +        VCOPY(vMtx[2], sNrm);
740 +        if (normalize(vMtx[2]) == 0)
741 +                return SDEargument;
742 +        fcross(vMtx[0], uVec, vMtx[2]);
743 +        if (normalize(vMtx[0]) == 0)
744 +                return SDEargument;
745 +        fcross(vMtx[1], vMtx[2], vMtx[0]);
746 +        return SDEnone;
747 + }
748 +
749 + /* Compute inverse transform */
750 + SDError
751 + SDinvXform(RREAL iMtx[3][3], RREAL vMtx[3][3])
752 + {
753 +        RREAL   mTmp[3][3];
754 +        double  d;
755 +
756 +        if ((iMtx == NULL) | (vMtx == NULL))
757 +                return SDEargument;
758 +                                        /* compute determinant */
759 +        mTmp[0][0] = vMtx[2][2]*vMtx[1][1] - vMtx[2][1]*vMtx[1][2];
760 +        mTmp[0][1] = vMtx[2][1]*vMtx[0][2] - vMtx[2][2]*vMtx[0][1];
761 +        mTmp[0][2] = vMtx[1][2]*vMtx[0][1] - vMtx[1][1]*vMtx[0][2];
762 +        d = vMtx[0][0]*mTmp[0][0] + vMtx[1][0]*mTmp[0][1] + vMtx[2][0]*mTmp[0][2];
763 +        if (d == 0) {
764 +                strcpy(SDerrorDetail, "Zero determinant in matrix inversion");
765 +                return SDEargument;
766 +        }
767 +        d = 1./d;                       /* invert matrix */
768 +        mTmp[0][0] *= d; mTmp[0][1] *= d; mTmp[0][2] *= d;
769 +        mTmp[1][0] = d*(vMtx[2][0]*vMtx[1][2] - vMtx[2][2]*vMtx[1][0]);
770 +        mTmp[1][1] = d*(vMtx[2][2]*vMtx[0][0] - vMtx[2][0]*vMtx[0][2]);
771 +        mTmp[1][2] = d*(vMtx[1][0]*vMtx[0][2] - vMtx[1][2]*vMtx[0][0]);
772 +        mTmp[2][0] = d*(vMtx[2][1]*vMtx[1][0] - vMtx[2][0]*vMtx[1][1]);
773 +        mTmp[2][1] = d*(vMtx[2][0]*vMtx[0][1] - vMtx[2][1]*vMtx[0][0]);
774 +        mTmp[2][2] = d*(vMtx[1][1]*vMtx[0][0] - vMtx[1][0]*vMtx[0][1]);
775 +        memcpy(iMtx, mTmp, sizeof(mTmp));
776 +        return SDEnone;
777 + }
778 +
779 + /* Transform and normalize direction (column) vector */
780 + SDError
781 + SDmapDir(FVECT resVec, RREAL vMtx[3][3], const FVECT inpVec)
782 + {
783 +        FVECT   vTmp;
784 +
785 +        if ((resVec == NULL) | (inpVec == NULL))
786 +                return SDEargument;
787 +        if (vMtx == NULL) {             /* assume they just want to normalize */
788 +                if (resVec != inpVec)
789 +                        VCOPY(resVec, inpVec);
790 +                return (normalize(resVec) > 0) ? SDEnone : SDEargument;
791 +        }
792 +        vTmp[0] = DOT(vMtx[0], inpVec);
793 +        vTmp[1] = DOT(vMtx[1], inpVec);
794 +        vTmp[2] = DOT(vMtx[2], inpVec);
795 +        if (normalize(vTmp) == 0)
796 +                return SDEargument;
797 +        VCOPY(resVec, vTmp);
798 +        return SDEnone;
799 + }
800 +
801 + /*################################################################*/
802 + /*######### DEPRECATED ROUTINES AWAITING PERMANENT REMOVAL #######*/
803 +
804 + /*
805   * Routines for handling BSDF data
806   */
807  
808   #include "standard.h"
9 #include "bsdf.h"
809   #include "paths.h"
11 #include "ezxml.h"
810   #include <ctype.h>
811  
812   #define MAXLATS         46              /* maximum number of latitudes */
# Line 66 | Line 864 | static int     nabases = 3;    /* current number of defined b
864   static int
865   fequal(double a, double b)
866   {
867 <        if (b != .0)
867 >        if (b != 0)
868                  a = a/b - 1.;
869          return((a <= 1e-6) & (a >= -1e-6));
870   }
871  
872 < // returns the name of the given tag
872 > /* Returns the name of the given tag */
873   #ifdef ezxml_name
874   #undef ezxml_name
875   static char *
# Line 83 | Line 881 | ezxml_name(ezxml_t xml)
881   }
882   #endif
883  
884 < // returns the given tag's character content or empty string if none
884 > /* Returns the given tag's character content or empty string if none */
885   #ifdef ezxml_txt
886   #undef ezxml_txt
887   static char *
# Line 247 | Line 1045 | load_angle_basis(      /* load custom BSDF angle basis */
1045   }
1046  
1047  
250 static double
251 to_meters(              /* return factor to convert given unit to meters */
252        const char *unit
253 )
254 {
255        if (unit == NULL) return(1.);           /* safe assumption? */
256        if (!strcasecmp(unit, "Meter")) return(1.);
257        if (!strcasecmp(unit, "Foot")) return(.3048);
258        if (!strcasecmp(unit, "Inch")) return(.0254);
259        if (!strcasecmp(unit, "Centimeter")) return(.01);
260        if (!strcasecmp(unit, "Millimeter")) return(.001);
261        sprintf(errmsg, "unknown dimensional unit '%s'", unit);
262        error(USER, errmsg);
263 }
264
265
1048   static void
1049   load_geometry(          /* load geometric dimensions and description (if any) */
1050          struct BSDF_data *dp,
# Line 343 | Line 1125 | load_bsdf_data(                /* load BSDF distribution for this wa
1125                          break;
1126                  }
1127          if (i < 0) {
1128 <                sprintf(errmsg, "undefined RowAngleBasis '%s'", cbasis);
1128 >                sprintf(errmsg, "undefined RowAngleBasis '%s'", rbasis);
1129                  error(WARNING, errmsg);
1130                  return;
1131          }
# Line 400 | Line 1182 | check_bsdf_data(       /* check that BSDF data is sane */
1182          hemi_total = .0;
1183          for (i = dp->ninc; i--; ) {
1184                  dom = getBSDF_incohm(dp,i);
1185 <                if (dom <= .0) {
1185 >                if (dom <= 0) {
1186                          error(WARNING, "zero/negative incoming solid angle");
1187                          continue;
1188                  }
# Line 423 | Line 1205 | check_bsdf_data(       /* check that BSDF data is sane */
1205          hemi_total = .0;
1206          for (o = dp->nout; o--; ) {
1207                  dom = getBSDF_outohm(dp,o);
1208 <                if (dom <= .0) {
1208 >                if (dom <= 0) {
1209                          error(WARNING, "zero/negative outgoing solid angle");
1210                          continue;
1211                  }
# Line 447 | Line 1229 | check_bsdf_data(       /* check that BSDF data is sane */
1229                  hemi_total = .0;
1230                  for (o = dp->nout; o--; ) {
1231                          double  f = BSDF_value(dp,i,o);
1232 <                        if (f >= .0)
1232 >                        if (f >= 0)
1233                                  hemi_total += f*omega_oarr[o];
1234                          else {
1235                                  nneg += (f < -FTINY);
# Line 542 | Line 1324 | load_BSDF(             /* load BSDF data from file */
1324          load_geometry(dp, ezxml_child(wtl, "Material"));
1325          for (wld = ezxml_child(wtl, "WavelengthData");
1326                                  wld != NULL; wld = wld->next) {
1327 <                if (strcasecmp(ezxml_txt(ezxml_child(wld,"Wavelength")), "Visible"))
1327 >                if (strcasecmp(ezxml_txt(ezxml_child(wld,"Wavelength")),
1328 >                                "Visible"))
1329                          continue;
1330 <                wdb = ezxml_child(wld, "WavelengthDataBlock");
1331 <                if (wdb == NULL) continue;
1332 <                if (strcasecmp(ezxml_txt(ezxml_child(wdb,"WavelengthDataDirection")),
1330 >                for (wdb = ezxml_child(wld, "WavelengthDataBlock");
1331 >                                wdb != NULL; wdb = wdb->next)
1332 >                        if (!strcasecmp(ezxml_txt(ezxml_child(wdb,
1333 >                                        "WavelengthDataDirection")),
1334                                          "Transmission Front"))
1335 <                        continue;
1336 <                load_bsdf_data(dp, wdb);        /* load front BTDF */
1337 <                break;                          /* ignore the rest */
1335 >                                break;
1336 >                if (wdb != NULL) {              /* load front BTDF */
1337 >                        load_bsdf_data(dp, wdb);
1338 >                        break;                  /* ignore the rest */
1339 >                }
1340          }
1341          ezxml_free(fl);                         /* done with XML file */
1342          if (!check_bsdf_data(dp)) {
# Line 726 | Line 1512 | getBSDF_xfm(           /* compute BSDF orient. -> world orient.
1512          }
1513          return(1);
1514   }
1515 +
1516 + /*######### END DEPRECATED ROUTINES #######*/
1517 + /*################################################################*/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines