10 |
|
* |
11 |
|
*/ |
12 |
|
|
13 |
+ |
#define _USE_MATH_DEFINES |
14 |
|
#include <stdio.h> |
15 |
|
#include <stdlib.h> |
16 |
+ |
#include <string.h> |
17 |
|
#include <math.h> |
18 |
+ |
#include <ctype.h> |
19 |
|
#include "ezxml.h" |
20 |
|
#include "hilbert.h" |
21 |
|
#include "bsdf.h" |
35 |
|
"Unknown error" |
36 |
|
}; |
37 |
|
|
38 |
+ |
/* Pointer to error list in preferred language */ |
39 |
+ |
const char **SDerrorList = SDerrorEnglish; |
40 |
+ |
|
41 |
|
/* Additional information on last error (ASCII English) */ |
42 |
|
char SDerrorDetail[256]; |
43 |
|
|
44 |
+ |
/* Empty distribution for getCDist() calls that fail for some reason */ |
45 |
+ |
const SDCDst SDemptyCD; |
46 |
+ |
|
47 |
|
/* Cache of loaded BSDFs */ |
48 |
|
struct SDCache_s *SDcacheList = NULL; |
49 |
|
|
50 |
|
/* Retain BSDFs in cache list */ |
51 |
|
int SDretainSet = SDretainNone; |
52 |
|
|
53 |
< |
/* Report any error to the indicated stream (in English) */ |
53 |
> |
/* Report any error to the indicated stream */ |
54 |
|
SDError |
55 |
< |
SDreportEnglish(SDError ec, FILE *fp) |
55 |
> |
SDreportError(SDError ec, FILE *fp) |
56 |
|
{ |
48 |
– |
if (fp == NULL) |
49 |
– |
return ec; |
57 |
|
if (!ec) |
58 |
|
return SDEnone; |
59 |
< |
fputs(SDerrorEnglish[ec], fp); |
59 |
> |
if ((ec < SDEnone) | (ec > SDEunknown)) { |
60 |
> |
SDerrorDetail[0] = '\0'; |
61 |
> |
ec = SDEunknown; |
62 |
> |
} |
63 |
> |
if (fp == NULL) |
64 |
> |
return ec; |
65 |
> |
fputs(SDerrorList[ec], fp); |
66 |
|
if (SDerrorDetail[0]) { |
67 |
|
fputs(": ", fp); |
68 |
|
fputs(SDerrorDetail, fp); |
90 |
|
|
91 |
|
/* Load geometric dimensions and description (if any) */ |
92 |
|
static SDError |
93 |
< |
SDloadGeometry(SDData *sd, ezxml_t wdb) |
93 |
> |
SDloadGeometry(SDData *sd, ezxml_t wtl) |
94 |
|
{ |
95 |
< |
ezxml_t geom; |
95 |
> |
ezxml_t node, matl, geom; |
96 |
|
double cfact; |
97 |
< |
const char *fmt, *mgfstr; |
97 |
> |
const char *fmt = NULL, *mgfstr; |
98 |
|
|
99 |
< |
if (wdb == NULL) /* no geometry section? */ |
99 |
> |
SDerrorDetail[0] = '\0'; |
100 |
> |
sd->matn[0] = '\0'; sd->makr[0] = '\0'; |
101 |
> |
sd->dim[0] = sd->dim[1] = sd->dim[2] = 0; |
102 |
> |
matl = ezxml_child(wtl, "Material"); |
103 |
> |
if (matl != NULL) { /* get material info. */ |
104 |
> |
if ((node = ezxml_child(matl, "Name")) != NULL) { |
105 |
> |
strncpy(sd->matn, ezxml_txt(node), SDnameLn); |
106 |
> |
if (sd->matn[SDnameLn-1]) |
107 |
> |
strcpy(sd->matn+(SDnameLn-4), "..."); |
108 |
> |
} |
109 |
> |
if ((node = ezxml_child(matl, "Manufacturer")) != NULL) { |
110 |
> |
strncpy(sd->makr, ezxml_txt(node), SDnameLn); |
111 |
> |
if (sd->makr[SDnameLn-1]) |
112 |
> |
strcpy(sd->makr+(SDnameLn-4), "..."); |
113 |
> |
} |
114 |
> |
if ((node = ezxml_child(matl, "Width")) != NULL) |
115 |
> |
sd->dim[0] = atof(ezxml_txt(node)) * |
116 |
> |
to_meters(ezxml_attr(node, "unit")); |
117 |
> |
if ((node = ezxml_child(matl, "Height")) != NULL) |
118 |
> |
sd->dim[1] = atof(ezxml_txt(node)) * |
119 |
> |
to_meters(ezxml_attr(node, "unit")); |
120 |
> |
if ((node = ezxml_child(matl, "Thickness")) != NULL) |
121 |
> |
sd->dim[2] = atof(ezxml_txt(node)) * |
122 |
> |
to_meters(ezxml_attr(node, "unit")); |
123 |
> |
if ((sd->dim[0] < 0) | (sd->dim[1] < 0) | (sd->dim[2] < 0)) { |
124 |
> |
if (!SDerrorDetail[0]) |
125 |
> |
sprintf(SDerrorDetail, "Negative dimension in \"%s\"", |
126 |
> |
sd->name); |
127 |
> |
return SDEdata; |
128 |
> |
} |
129 |
> |
} |
130 |
> |
sd->mgf = NULL; |
131 |
> |
geom = ezxml_child(wtl, "Geometry"); |
132 |
> |
if (geom == NULL) /* no actual geometry? */ |
133 |
|
return SDEnone; |
134 |
< |
sprintf(SDerrorDetail, "Negative size in \"%s\"", sd->name); |
135 |
< |
sd->dim[0] = sd->dim[1] = sd->dim[2] = .0; |
90 |
< |
if ((geom = ezxml_child(wdb, "Width")) != NULL) |
91 |
< |
sd->dim[0] = atof(ezxml_txt(geom)) * |
92 |
< |
to_meters(ezxml_attr(geom, "unit")); |
93 |
< |
if ((geom = ezxml_child(wdb, "Height")) != NULL) |
94 |
< |
sd->dim[1] = atof(ezxml_txt(geom)) * |
95 |
< |
to_meters(ezxml_attr(geom, "unit")); |
96 |
< |
if ((geom = ezxml_child(wdb, "Thickness")) != NULL) |
97 |
< |
sd->dim[2] = atof(ezxml_txt(geom)) * |
98 |
< |
to_meters(ezxml_attr(geom, "unit")); |
99 |
< |
if ((sd->dim[0] < .0) | (sd->dim[1] < .0) | (sd->dim[2] < .0)) |
100 |
< |
return SDEdata; |
101 |
< |
if ((geom = ezxml_child(wdb, "Geometry")) == NULL || |
102 |
< |
(mgfstr = ezxml_txt(geom)) == NULL) |
103 |
< |
return SDEnone; |
104 |
< |
if ((fmt = ezxml_attr(geom, "format")) != NULL && |
105 |
< |
strcasecmp(fmt, "MGF")) { |
134 |
> |
fmt = ezxml_attr(geom, "format"); |
135 |
> |
if (fmt != NULL && strcasecmp(fmt, "MGF")) { |
136 |
|
sprintf(SDerrorDetail, |
137 |
|
"Unrecognized geometry format '%s' in \"%s\"", |
138 |
|
fmt, sd->name); |
139 |
|
return SDEsupport; |
140 |
|
} |
141 |
< |
cfact = to_meters(ezxml_attr(geom, "unit")); |
141 |
> |
if ((node = ezxml_child(geom, "MGFblock")) == NULL || |
142 |
> |
(mgfstr = ezxml_txt(node)) == NULL) |
143 |
> |
return SDEnone; |
144 |
> |
while (isspace(*mgfstr)) |
145 |
> |
++mgfstr; |
146 |
> |
if (!*mgfstr) |
147 |
> |
return SDEnone; |
148 |
> |
cfact = to_meters(ezxml_attr(node, "unit")); |
149 |
> |
if (cfact <= 0) |
150 |
> |
return SDEformat; |
151 |
|
sd->mgf = (char *)malloc(strlen(mgfstr)+32); |
152 |
|
if (sd->mgf == NULL) { |
153 |
|
strcpy(SDerrorDetail, "Out of memory in SDloadGeometry"); |
189 |
|
ezxml_free(fl); |
190 |
|
return SDEformat; |
191 |
|
} |
192 |
+ |
wtl = ezxml_child(fl, "FileType"); |
193 |
+ |
if (wtl != NULL && strcmp(ezxml_txt(wtl), "BSDF")) { |
194 |
+ |
sprintf(SDerrorDetail, |
195 |
+ |
"XML \"%s\": wrong FileType (must be 'BSDF')", |
196 |
+ |
sd->name); |
197 |
+ |
ezxml_free(fl); |
198 |
+ |
return SDEformat; |
199 |
+ |
} |
200 |
|
wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer"); |
201 |
|
if (wtl == NULL) { |
202 |
< |
sprintf(SDerrorDetail, "BSDF \"%s\": no optical layer'", |
202 |
> |
sprintf(SDerrorDetail, "BSDF \"%s\": no optical layers'", |
203 |
|
sd->name); |
204 |
|
ezxml_free(fl); |
205 |
|
return SDEformat; |
206 |
|
} |
207 |
|
/* load geometry if present */ |
208 |
< |
lastErr = SDloadGeometry(sd, ezxml_child(wtl, "Material")); |
209 |
< |
if (lastErr) |
208 |
> |
lastErr = SDloadGeometry(sd, wtl); |
209 |
> |
if (lastErr) { |
210 |
> |
ezxml_free(fl); |
211 |
|
return lastErr; |
212 |
+ |
} |
213 |
|
/* try loading variable resolution data */ |
214 |
|
lastErr = SDloadTre(sd, wtl); |
215 |
|
/* check our result */ |
216 |
< |
switch (lastErr) { |
168 |
< |
case SDEformat: |
169 |
< |
case SDEdata: |
170 |
< |
case SDEsupport: /* possibly we just tried the wrong format */ |
216 |
> |
if (lastErr == SDEsupport) /* try matrix BSDF if not tree data */ |
217 |
|
lastErr = SDloadMtx(sd, wtl); |
218 |
< |
break; |
173 |
< |
default: /* variable res. OK else serious error */ |
174 |
< |
break; |
175 |
< |
} |
218 |
> |
|
219 |
|
/* done with XML file */ |
220 |
|
ezxml_free(fl); |
221 |
|
|
233 |
|
if (sd->tf != NULL && sd->tf->maxHemi <= .001) { |
234 |
|
SDfreeSpectralDF(sd->tf); sd->tf = NULL; |
235 |
|
} |
236 |
+ |
if (sd->tb != NULL && sd->tb->maxHemi <= .001) { |
237 |
+ |
SDfreeSpectralDF(sd->tb); sd->tb = NULL; |
238 |
+ |
} |
239 |
|
/* return success */ |
240 |
|
return SDEnone; |
241 |
|
} |
264 |
|
return df; |
265 |
|
} |
266 |
|
|
267 |
+ |
/* Add component(s) to spectral distribution function */ |
268 |
+ |
SDSpectralDF * |
269 |
+ |
SDaddComponent(SDSpectralDF *odf, int nadd) |
270 |
+ |
{ |
271 |
+ |
SDSpectralDF *df; |
272 |
+ |
|
273 |
+ |
if (odf == NULL) |
274 |
+ |
return SDnewSpectralDF(nadd); |
275 |
+ |
if (nadd <= 0) |
276 |
+ |
return odf; |
277 |
+ |
df = (SDSpectralDF *)realloc(odf, sizeof(SDSpectralDF) + |
278 |
+ |
(odf->ncomp+nadd-1)*sizeof(SDComponent)); |
279 |
+ |
if (df == NULL) { |
280 |
+ |
sprintf(SDerrorDetail, |
281 |
+ |
"Cannot add %d component(s) to spectral DF", nadd); |
282 |
+ |
SDfreeSpectralDF(odf); |
283 |
+ |
return NULL; |
284 |
+ |
} |
285 |
+ |
memset(df->comp+df->ncomp, 0, nadd*sizeof(SDComponent)); |
286 |
+ |
df->ncomp += nadd; |
287 |
+ |
return df; |
288 |
+ |
} |
289 |
+ |
|
290 |
|
/* Free cached cumulative distributions for BSDF component */ |
291 |
|
void |
292 |
|
SDfreeCumulativeCache(SDSpectralDF *df) |
313 |
|
return; |
314 |
|
SDfreeCumulativeCache(df); |
315 |
|
for (n = df->ncomp; n-- > 0; ) |
316 |
< |
(*df->comp[n].func->freeSC)(df->comp[n].dist); |
316 |
> |
if (df->comp[n].dist != NULL) |
317 |
> |
(*df->comp[n].func->freeSC)(df->comp[n].dist); |
318 |
|
free(df); |
319 |
|
} |
320 |
|
|
338 |
|
|
339 |
|
/* Initialize an unused BSDF struct (simply clears to zeroes) */ |
340 |
|
void |
341 |
< |
SDclearBSDF(SDData *sd) |
341 |
> |
SDclearBSDF(SDData *sd, const char *fname) |
342 |
|
{ |
343 |
< |
if (sd != NULL) |
344 |
< |
memset(sd, 0, sizeof(SDData)); |
343 |
> |
if (sd == NULL) |
344 |
> |
return; |
345 |
> |
memset(sd, 0, sizeof(SDData)); |
346 |
> |
if (fname == NULL) |
347 |
> |
return; |
348 |
> |
SDclipName(sd->name, fname); |
349 |
|
} |
350 |
|
|
351 |
|
/* Free data associated with BSDF struct */ |
370 |
|
SDfreeSpectralDF(sd->tf); |
371 |
|
sd->tf = NULL; |
372 |
|
} |
373 |
+ |
if (sd->tb != NULL) { |
374 |
+ |
SDfreeSpectralDF(sd->tb); |
375 |
+ |
sd->tb = NULL; |
376 |
+ |
} |
377 |
|
sd->rLambFront.cieY = .0; |
378 |
|
sd->rLambFront.spec.flags = 0; |
379 |
|
sd->rLambBack.cieY = .0; |
407 |
|
sdl->next = SDcacheList; |
408 |
|
SDcacheList = sdl; |
409 |
|
|
410 |
< |
sdl->refcnt++; |
410 |
> |
sdl->refcnt = 1; |
411 |
|
return &sdl->bsdf; |
412 |
|
} |
413 |
|
|
423 |
|
return NULL; |
424 |
|
SDerrorDetail[0] = '\0'; |
425 |
|
if ((sd = SDgetCache(fname)) == NULL) { |
426 |
< |
SDreportEnglish(SDEmemory, stderr); |
426 |
> |
SDreportError(SDEmemory, stderr); |
427 |
|
return NULL; |
428 |
|
} |
429 |
|
if (!SDisLoaded(sd) && (ec = SDloadFile(sd, fname))) { |
430 |
< |
SDreportEnglish(ec, stderr); |
430 |
> |
SDreportError(ec, stderr); |
431 |
|
SDfreeCache(sd); |
432 |
|
return NULL; |
433 |
|
} |
451 |
|
for (sdl = SDcacheList; sdl != NULL; sdl = (sdLast=sdl)->next) |
452 |
|
if (&sdl->bsdf == sd) |
453 |
|
break; |
454 |
< |
if (sdl == NULL || --sdl->refcnt) |
454 |
> |
if (sdl == NULL || (sdl->refcnt -= (sdl->refcnt > 0))) |
455 |
|
return; /* missing or still in use */ |
456 |
|
/* keep unreferenced data? */ |
457 |
|
if (SDisLoaded(sd) && SDretainSet) { |
461 |
|
SDfreeCumulativeCache(sd->rf); |
462 |
|
SDfreeCumulativeCache(sd->rb); |
463 |
|
SDfreeCumulativeCache(sd->tf); |
464 |
+ |
SDfreeCumulativeCache(sd->tb); |
465 |
|
return; |
466 |
|
} |
467 |
|
/* remove from list and free */ |
475 |
|
|
476 |
|
/* Sample an individual BSDF component */ |
477 |
|
SDError |
478 |
< |
SDsampComponent(SDValue *sv, FVECT outVec, const FVECT inVec, |
400 |
< |
double randX, SDComponent *sdc) |
478 |
> |
SDsampComponent(SDValue *sv, FVECT ioVec, double randX, SDComponent *sdc) |
479 |
|
{ |
480 |
|
float coef[SDmaxCh]; |
481 |
|
SDError ec; |
482 |
+ |
FVECT inVec; |
483 |
|
const SDCDst *cd; |
484 |
|
double d; |
485 |
|
int n; |
486 |
|
/* check arguments */ |
487 |
< |
if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sdc == NULL)) |
487 |
> |
if ((sv == NULL) | (ioVec == NULL) | (sdc == NULL)) |
488 |
|
return SDEargument; |
489 |
|
/* get cumulative distribution */ |
490 |
+ |
VCOPY(inVec, ioVec); |
491 |
+ |
sv->cieY = 0; |
492 |
|
cd = (*sdc->func->getCDist)(inVec, sdc); |
493 |
< |
if (cd == NULL) |
494 |
< |
return SDEmemory; |
495 |
< |
if (cd->cTotal <= 1e-7) { /* anything to sample? */ |
493 |
> |
if (cd != NULL) |
494 |
> |
sv->cieY = cd->cTotal; |
495 |
> |
if (sv->cieY <= 1e-6) { /* nothing to sample? */ |
496 |
|
sv->spec = c_dfcolor; |
497 |
< |
sv->cieY = .0; |
417 |
< |
memset(outVec, 0, 3*sizeof(double)); |
497 |
> |
memset(ioVec, 0, 3*sizeof(double)); |
498 |
|
return SDEnone; |
499 |
|
} |
420 |
– |
sv->cieY = cd->cTotal; |
500 |
|
/* compute sample direction */ |
501 |
< |
ec = (*sdc->func->sampCDist)(outVec, randX, cd); |
501 |
> |
ec = (*sdc->func->sampCDist)(ioVec, randX, cd); |
502 |
|
if (ec) |
503 |
|
return ec; |
504 |
|
/* get BSDF color */ |
505 |
< |
n = (*sdc->func->getBSDFs)(coef, outVec, inVec, sdc->dist); |
505 |
> |
n = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc); |
506 |
|
if (n <= 0) { |
507 |
|
strcpy(SDerrorDetail, "BSDF sample value error"); |
508 |
|
return SDEinternal; |
527 |
|
unsigned nBits; |
528 |
|
double scale; |
529 |
|
bitmask_t ndx, coord[MS_MAXDIM]; |
530 |
< |
|
530 |
> |
|
531 |
> |
if (n <= 0) /* check corner cases */ |
532 |
> |
return; |
533 |
> |
if (randX < 0) randX = 0; |
534 |
> |
else if (randX >= 1.) randX = 0.999999999999999; |
535 |
> |
if (n == 1) { |
536 |
> |
t[0] = randX; |
537 |
> |
return; |
538 |
> |
} |
539 |
|
while (n > MS_MAXDIM) /* punt for higher dimensions */ |
540 |
< |
t[--n] = drand48(); |
540 |
> |
t[--n] = rand()*(1./(RAND_MAX+.5)); |
541 |
|
nBits = (8*sizeof(bitmask_t) - 1) / n; |
542 |
|
ndx = randX * (double)((bitmask_t)1 << (nBits*n)); |
543 |
|
/* get coordinate on Hilbert curve */ |
545 |
|
/* convert back to [0,1) range */ |
546 |
|
scale = 1. / (double)((bitmask_t)1 << nBits); |
547 |
|
while (n--) |
548 |
< |
t[n] = scale * ((double)coord[n] + drand48()); |
548 |
> |
t[n] = scale * ((double)coord[n] + rand()*(1./(RAND_MAX+.5))); |
549 |
|
} |
550 |
|
|
551 |
|
#undef MS_MAXDIM |
558 |
|
SDmultiSamp(outVec, 2, randX); |
559 |
|
SDsquare2disk(outVec, outVec[0], outVec[1]); |
560 |
|
outVec[2] = 1. - outVec[0]*outVec[0] - outVec[1]*outVec[1]; |
561 |
< |
if (outVec[2] > .0) /* a bit of paranoia */ |
561 |
> |
if (outVec[2] > 0) /* a bit of paranoia */ |
562 |
|
outVec[2] = sqrt(outVec[2]); |
563 |
|
if (!outFront) /* going out back? */ |
564 |
|
outVec[2] = -outVec[2]; |
566 |
|
|
567 |
|
/* Query projected solid angle coverage for non-diffuse BSDF direction */ |
568 |
|
SDError |
569 |
< |
SDsizeBSDF(double *projSA, const FVECT vec, int qflags, const SDData *sd) |
569 |
> |
SDsizeBSDF(double *projSA, const FVECT v1, const RREAL *v2, |
570 |
> |
int qflags, const SDData *sd) |
571 |
|
{ |
572 |
< |
SDSpectralDF *rdf; |
572 |
> |
SDSpectralDF *rdf, *tdf; |
573 |
|
SDError ec; |
574 |
|
int i; |
575 |
|
/* check arguments */ |
576 |
< |
if ((projSA == NULL) | (vec == NULL) | (sd == NULL)) |
576 |
> |
if ((projSA == NULL) | (v1 == NULL) | (sd == NULL)) |
577 |
|
return SDEargument; |
578 |
|
/* initialize extrema */ |
579 |
< |
switch (qflags & SDqueryMin+SDqueryMax) { |
579 |
> |
switch (qflags) { |
580 |
|
case SDqueryMax: |
581 |
|
projSA[0] = .0; |
582 |
|
break; |
589 |
|
case 0: |
590 |
|
return SDEargument; |
591 |
|
} |
592 |
< |
if (vec[2] > .0) /* front surface query? */ |
592 |
> |
if (v1[2] > 0) { /* front surface query? */ |
593 |
|
rdf = sd->rf; |
594 |
< |
else |
594 |
> |
tdf = (sd->tf != NULL) ? sd->tf : sd->tb; |
595 |
> |
} else { |
596 |
|
rdf = sd->rb; |
597 |
+ |
tdf = (sd->tb != NULL) ? sd->tb : sd->tf; |
598 |
+ |
} |
599 |
+ |
if (v2 != NULL) /* bidirectional? */ |
600 |
+ |
if (v1[2] > 0 ^ v2[2] > 0) |
601 |
+ |
rdf = NULL; |
602 |
+ |
else |
603 |
+ |
tdf = NULL; |
604 |
|
ec = SDEdata; /* run through components */ |
605 |
|
for (i = (rdf==NULL) ? 0 : rdf->ncomp; i--; ) { |
606 |
< |
ec = (*rdf->comp[i].func->queryProjSA)(projSA, vec, qflags, |
607 |
< |
rdf->comp[i].dist); |
606 |
> |
ec = (*rdf->comp[i].func->queryProjSA)(projSA, v1, v2, |
607 |
> |
qflags, &rdf->comp[i]); |
608 |
|
if (ec) |
609 |
|
return ec; |
610 |
|
} |
611 |
< |
for (i = (sd->tf==NULL) ? 0 : sd->tf->ncomp; i--; ) { |
612 |
< |
ec = (*sd->tf->comp[i].func->queryProjSA)(projSA, vec, qflags, |
613 |
< |
sd->tf->comp[i].dist); |
611 |
> |
for (i = (tdf==NULL) ? 0 : tdf->ncomp; i--; ) { |
612 |
> |
ec = (*tdf->comp[i].func->queryProjSA)(projSA, v1, v2, |
613 |
> |
qflags, &tdf->comp[i]); |
614 |
|
if (ec) |
615 |
|
return ec; |
616 |
|
} |
617 |
< |
return ec; |
617 |
> |
if (ec) { /* all diffuse? */ |
618 |
> |
projSA[0] = M_PI; |
619 |
> |
if (qflags == SDqueryMin+SDqueryMax) |
620 |
> |
projSA[1] = M_PI; |
621 |
> |
} |
622 |
> |
return SDEnone; |
623 |
|
} |
624 |
|
|
625 |
|
/* Return BSDF for the given incident and scattered ray vectors */ |
634 |
|
if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL)) |
635 |
|
return SDEargument; |
636 |
|
/* whose side are we on? */ |
637 |
< |
inFront = (inVec[2] > .0); |
638 |
< |
outFront = (outVec[2] > .0); |
637 |
> |
inFront = (inVec[2] > 0); |
638 |
> |
outFront = (outVec[2] > 0); |
639 |
|
/* start with diffuse portion */ |
640 |
|
if (inFront & outFront) { |
641 |
|
*sv = sd->rLambFront; |
643 |
|
} else if (!(inFront | outFront)) { |
644 |
|
*sv = sd->rLambBack; |
645 |
|
sdf = sd->rb; |
646 |
< |
} else /* inFront ^ outFront */ { |
646 |
> |
} else if (inFront) { |
647 |
|
*sv = sd->tLamb; |
648 |
< |
sdf = sd->tf; |
648 |
> |
sdf = (sd->tf != NULL) ? sd->tf : sd->tb; |
649 |
> |
} else /* inBack */ { |
650 |
> |
*sv = sd->tLamb; |
651 |
> |
sdf = (sd->tb != NULL) ? sd->tb : sd->tf; |
652 |
|
} |
653 |
|
sv->cieY *= 1./M_PI; |
654 |
|
/* add non-diffuse components */ |
655 |
|
i = (sdf != NULL) ? sdf->ncomp : 0; |
656 |
|
while (i-- > 0) { |
657 |
|
nch = (*sdf->comp[i].func->getBSDFs)(coef, outVec, inVec, |
658 |
< |
sdf->comp[i].dist); |
658 |
> |
&sdf->comp[i]); |
659 |
|
while (nch-- > 0) { |
660 |
|
c_cmix(&sv->spec, sv->cieY, &sv->spec, |
661 |
|
coef[nch], &sdf->comp[i].cspec[nch]); |
672 |
|
SDdirectHemi(const FVECT inVec, int sflags, const SDData *sd) |
673 |
|
{ |
674 |
|
double hsum; |
675 |
< |
SDSpectralDF *rdf; |
675 |
> |
SDSpectralDF *rdf, *tdf; |
676 |
|
const SDCDst *cd; |
677 |
|
int i; |
678 |
|
/* check arguments */ |
679 |
|
if ((inVec == NULL) | (sd == NULL)) |
680 |
|
return .0; |
681 |
|
/* gather diffuse components */ |
682 |
< |
if (inVec[2] > .0) { |
682 |
> |
if (inVec[2] > 0) { |
683 |
|
hsum = sd->rLambFront.cieY; |
684 |
|
rdf = sd->rf; |
685 |
+ |
tdf = (sd->tf != NULL) ? sd->tf : sd->tb; |
686 |
|
} else /* !inFront */ { |
687 |
|
hsum = sd->rLambBack.cieY; |
688 |
|
rdf = sd->rb; |
689 |
+ |
tdf = (sd->tb != NULL) ? sd->tb : sd->tf; |
690 |
|
} |
691 |
|
if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR) |
692 |
|
hsum = .0; |
693 |
|
if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) |
694 |
|
hsum += sd->tLamb.cieY; |
695 |
|
/* gather non-diffuse components */ |
696 |
< |
i = ((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR && |
697 |
< |
rdf != NULL) ? rdf->ncomp : 0; |
696 |
> |
i = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) & |
697 |
> |
(rdf != NULL)) ? rdf->ncomp : 0; |
698 |
|
while (i-- > 0) { /* non-diffuse reflection */ |
699 |
|
cd = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]); |
700 |
|
if (cd != NULL) |
701 |
|
hsum += cd->cTotal; |
702 |
|
} |
703 |
< |
i = ((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT && |
704 |
< |
sd->tf != NULL) ? sd->tf->ncomp : 0; |
703 |
> |
i = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) & |
704 |
> |
(tdf != NULL)) ? tdf->ncomp : 0; |
705 |
|
while (i-- > 0) { /* non-diffuse transmission */ |
706 |
< |
cd = (*sd->tf->comp[i].func->getCDist)(inVec, &sd->tf->comp[i]); |
706 |
> |
cd = (*tdf->comp[i].func->getCDist)(inVec, &tdf->comp[i]); |
707 |
|
if (cd != NULL) |
708 |
|
hsum += cd->cTotal; |
709 |
|
} |
712 |
|
|
713 |
|
/* Sample BSDF direction based on the given random variable */ |
714 |
|
SDError |
715 |
< |
SDsampBSDF(SDValue *sv, FVECT outVec, const FVECT inVec, |
610 |
< |
double randX, int sflags, const SDData *sd) |
715 |
> |
SDsampBSDF(SDValue *sv, FVECT ioVec, double randX, int sflags, const SDData *sd) |
716 |
|
{ |
717 |
|
SDError ec; |
718 |
+ |
FVECT inVec; |
719 |
|
int inFront; |
720 |
< |
SDSpectralDF *rdf; |
720 |
> |
SDSpectralDF *rdf, *tdf; |
721 |
|
double rdiff; |
722 |
|
float coef[SDmaxCh]; |
723 |
|
int i, j, n, nr; |
724 |
|
SDComponent *sdc; |
725 |
|
const SDCDst **cdarr = NULL; |
726 |
|
/* check arguments */ |
727 |
< |
if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL) | |
728 |
< |
(randX < .0) | (randX >= 1.)) |
727 |
> |
if ((sv == NULL) | (ioVec == NULL) | (sd == NULL) | |
728 |
> |
(randX < 0) | (randX >= 1.)) |
729 |
|
return SDEargument; |
730 |
|
/* whose side are we on? */ |
731 |
< |
inFront = (inVec[2] > .0); |
731 |
> |
VCOPY(inVec, ioVec); |
732 |
> |
inFront = (inVec[2] > 0); |
733 |
|
/* remember diffuse portions */ |
734 |
|
if (inFront) { |
735 |
|
*sv = sd->rLambFront; |
736 |
|
rdf = sd->rf; |
737 |
+ |
tdf = (sd->tf != NULL) ? sd->tf : sd->tb; |
738 |
|
} else /* !inFront */ { |
739 |
|
*sv = sd->rLambBack; |
740 |
|
rdf = sd->rb; |
741 |
+ |
tdf = (sd->tb != NULL) ? sd->tb : sd->tf; |
742 |
|
} |
743 |
|
if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR) |
744 |
|
sv->cieY = .0; |
746 |
|
if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) |
747 |
|
sv->cieY += sd->tLamb.cieY; |
748 |
|
/* gather non-diffuse components */ |
749 |
< |
i = nr = ((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR && |
750 |
< |
rdf != NULL) ? rdf->ncomp : 0; |
751 |
< |
j = ((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT && |
752 |
< |
sd->tf != NULL) ? sd->tf->ncomp : 0; |
749 |
> |
i = nr = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) & |
750 |
> |
(rdf != NULL)) ? rdf->ncomp : 0; |
751 |
> |
j = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) & |
752 |
> |
(tdf != NULL)) ? tdf->ncomp : 0; |
753 |
|
n = i + j; |
754 |
|
if (n > 0 && (cdarr = (const SDCDst **)malloc(n*sizeof(SDCDst *))) == NULL) |
755 |
|
return SDEmemory; |
756 |
|
while (j-- > 0) { /* non-diffuse transmission */ |
757 |
< |
cdarr[i+j] = (*sd->tf->comp[j].func->getCDist)(inVec, &sd->tf->comp[j]); |
758 |
< |
if (cdarr[i+j] == NULL) { |
759 |
< |
free(cdarr); |
651 |
< |
return SDEmemory; |
652 |
< |
} |
757 |
> |
cdarr[i+j] = (*tdf->comp[j].func->getCDist)(inVec, &tdf->comp[j]); |
758 |
> |
if (cdarr[i+j] == NULL) |
759 |
> |
cdarr[i+j] = &SDemptyCD; |
760 |
|
sv->cieY += cdarr[i+j]->cTotal; |
761 |
|
} |
762 |
|
while (i-- > 0) { /* non-diffuse reflection */ |
763 |
|
cdarr[i] = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]); |
764 |
< |
if (cdarr[i] == NULL) { |
765 |
< |
free(cdarr); |
766 |
< |
return SDEmemory; |
767 |
< |
} |
661 |
< |
sv->cieY += cdarr[i]->cTotal; |
764 |
> |
if (cdarr[i] == NULL) |
765 |
> |
cdarr[i] = &SDemptyCD; |
766 |
> |
else |
767 |
> |
sv->cieY += cdarr[i]->cTotal; |
768 |
|
} |
769 |
< |
if (sv->cieY <= 1e-7) { /* anything to sample? */ |
769 |
> |
if (sv->cieY <= 1e-6) { /* anything to sample? */ |
770 |
|
sv->cieY = .0; |
771 |
< |
memset(outVec, 0, 3*sizeof(double)); |
771 |
> |
memset(ioVec, 0, 3*sizeof(double)); |
772 |
|
return SDEnone; |
773 |
|
} |
774 |
|
/* scale random variable */ |
775 |
|
randX *= sv->cieY; |
776 |
|
/* diffuse reflection? */ |
777 |
|
if (randX < rdiff) { |
778 |
< |
SDdiffuseSamp(outVec, inFront, randX/rdiff); |
778 |
> |
SDdiffuseSamp(ioVec, inFront, randX/rdiff); |
779 |
|
goto done; |
780 |
|
} |
781 |
|
randX -= rdiff; |
783 |
|
if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) { |
784 |
|
if (randX < sd->tLamb.cieY) { |
785 |
|
sv->spec = sd->tLamb.spec; |
786 |
< |
SDdiffuseSamp(outVec, !inFront, randX/sd->tLamb.cieY); |
786 |
> |
SDdiffuseSamp(ioVec, !inFront, randX/sd->tLamb.cieY); |
787 |
|
goto done; |
788 |
|
} |
789 |
|
randX -= sd->tLamb.cieY; |
794 |
|
if (i >= n) |
795 |
|
return SDEinternal; |
796 |
|
/* compute sample direction */ |
797 |
< |
sdc = (i < nr) ? &rdf->comp[i] : &sd->tf->comp[i-nr]; |
798 |
< |
ec = (*sdc->func->sampCDist)(outVec, randX/cdarr[i]->cTotal, cdarr[i]); |
797 |
> |
sdc = (i < nr) ? &rdf->comp[i] : &tdf->comp[i-nr]; |
798 |
> |
ec = (*sdc->func->sampCDist)(ioVec, randX/cdarr[i]->cTotal, cdarr[i]); |
799 |
|
if (ec) |
800 |
|
return ec; |
801 |
|
/* compute color */ |
802 |
< |
j = (*sdc->func->getBSDFs)(coef, outVec, inVec, sdc->dist); |
802 |
> |
j = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc); |
803 |
|
if (j <= 0) { |
804 |
|
sprintf(SDerrorDetail, "BSDF \"%s\" sampling value error", |
805 |
|
sd->name); |
826 |
|
if ((vMtx == NULL) | (sNrm == NULL) | (uVec == NULL)) |
827 |
|
return SDEargument; |
828 |
|
VCOPY(vMtx[2], sNrm); |
829 |
< |
if (normalize(vMtx[2]) == .0) |
829 |
> |
if (normalize(vMtx[2]) == 0) |
830 |
|
return SDEargument; |
831 |
|
fcross(vMtx[0], uVec, vMtx[2]); |
832 |
< |
if (normalize(vMtx[0]) == .0) |
832 |
> |
if (normalize(vMtx[0]) == 0) |
833 |
|
return SDEargument; |
834 |
|
fcross(vMtx[1], vMtx[2], vMtx[0]); |
835 |
|
return SDEnone; |
849 |
|
mTmp[0][1] = vMtx[2][1]*vMtx[0][2] - vMtx[2][2]*vMtx[0][1]; |
850 |
|
mTmp[0][2] = vMtx[1][2]*vMtx[0][1] - vMtx[1][1]*vMtx[0][2]; |
851 |
|
d = vMtx[0][0]*mTmp[0][0] + vMtx[1][0]*mTmp[0][1] + vMtx[2][0]*mTmp[0][2]; |
852 |
< |
if (d == .0) { |
852 |
> |
if (d == 0) { |
853 |
|
strcpy(SDerrorDetail, "Zero determinant in matrix inversion"); |
854 |
|
return SDEargument; |
855 |
|
} |
876 |
|
if (vMtx == NULL) { /* assume they just want to normalize */ |
877 |
|
if (resVec != inpVec) |
878 |
|
VCOPY(resVec, inpVec); |
879 |
< |
return (normalize(resVec) > .0) ? SDEnone : SDEargument; |
879 |
> |
return (normalize(resVec) > 0) ? SDEnone : SDEargument; |
880 |
|
} |
881 |
|
vTmp[0] = DOT(vMtx[0], inpVec); |
882 |
|
vTmp[1] = DOT(vMtx[1], inpVec); |
883 |
|
vTmp[2] = DOT(vMtx[2], inpVec); |
884 |
< |
if (normalize(vTmp) == .0) |
884 |
> |
if (normalize(vTmp) == 0) |
885 |
|
return SDEargument; |
886 |
|
VCOPY(resVec, vTmp); |
887 |
|
return SDEnone; |
888 |
|
} |
783 |
– |
|
784 |
– |
/*################################################################*/ |
785 |
– |
/*######### DEPRECATED ROUTINES AWAITING PERMANENT REMOVAL #######*/ |
786 |
– |
|
787 |
– |
/* |
788 |
– |
* Routines for handling BSDF data |
789 |
– |
*/ |
790 |
– |
|
791 |
– |
#include "standard.h" |
792 |
– |
#include "paths.h" |
793 |
– |
#include <ctype.h> |
794 |
– |
|
795 |
– |
#define MAXLATS 46 /* maximum number of latitudes */ |
796 |
– |
|
797 |
– |
/* BSDF angle specification */ |
798 |
– |
typedef struct { |
799 |
– |
char name[64]; /* basis name */ |
800 |
– |
int nangles; /* total number of directions */ |
801 |
– |
struct { |
802 |
– |
float tmin; /* starting theta */ |
803 |
– |
short nphis; /* number of phis (0 term) */ |
804 |
– |
} lat[MAXLATS+1]; /* latitudes */ |
805 |
– |
} ANGLE_BASIS; |
806 |
– |
|
807 |
– |
#define MAXABASES 7 /* limit on defined bases */ |
808 |
– |
|
809 |
– |
static ANGLE_BASIS abase_list[MAXABASES] = { |
810 |
– |
{ |
811 |
– |
"LBNL/Klems Full", 145, |
812 |
– |
{ {-5., 1}, |
813 |
– |
{5., 8}, |
814 |
– |
{15., 16}, |
815 |
– |
{25., 20}, |
816 |
– |
{35., 24}, |
817 |
– |
{45., 24}, |
818 |
– |
{55., 24}, |
819 |
– |
{65., 16}, |
820 |
– |
{75., 12}, |
821 |
– |
{90., 0} } |
822 |
– |
}, { |
823 |
– |
"LBNL/Klems Half", 73, |
824 |
– |
{ {-6.5, 1}, |
825 |
– |
{6.5, 8}, |
826 |
– |
{19.5, 12}, |
827 |
– |
{32.5, 16}, |
828 |
– |
{46.5, 20}, |
829 |
– |
{61.5, 12}, |
830 |
– |
{76.5, 4}, |
831 |
– |
{90., 0} } |
832 |
– |
}, { |
833 |
– |
"LBNL/Klems Quarter", 41, |
834 |
– |
{ {-9., 1}, |
835 |
– |
{9., 8}, |
836 |
– |
{27., 12}, |
837 |
– |
{46., 12}, |
838 |
– |
{66., 8}, |
839 |
– |
{90., 0} } |
840 |
– |
} |
841 |
– |
}; |
842 |
– |
|
843 |
– |
static int nabases = 3; /* current number of defined bases */ |
844 |
– |
|
845 |
– |
#define FEQ(a,b) ((a)-(b) <= 1e-6 && (b)-(a) <= 1e-6) |
846 |
– |
|
847 |
– |
static int |
848 |
– |
fequal(double a, double b) |
849 |
– |
{ |
850 |
– |
if (b != .0) |
851 |
– |
a = a/b - 1.; |
852 |
– |
return((a <= 1e-6) & (a >= -1e-6)); |
853 |
– |
} |
854 |
– |
|
855 |
– |
/* Returns the name of the given tag */ |
856 |
– |
#ifdef ezxml_name |
857 |
– |
#undef ezxml_name |
858 |
– |
static char * |
859 |
– |
ezxml_name(ezxml_t xml) |
860 |
– |
{ |
861 |
– |
if (xml == NULL) |
862 |
– |
return(NULL); |
863 |
– |
return(xml->name); |
864 |
– |
} |
865 |
– |
#endif |
866 |
– |
|
867 |
– |
/* Returns the given tag's character content or empty string if none */ |
868 |
– |
#ifdef ezxml_txt |
869 |
– |
#undef ezxml_txt |
870 |
– |
static char * |
871 |
– |
ezxml_txt(ezxml_t xml) |
872 |
– |
{ |
873 |
– |
if (xml == NULL) |
874 |
– |
return(""); |
875 |
– |
return(xml->txt); |
876 |
– |
} |
877 |
– |
#endif |
878 |
– |
|
879 |
– |
|
880 |
– |
static int |
881 |
– |
ab_getvec( /* get vector for this angle basis index */ |
882 |
– |
FVECT v, |
883 |
– |
int ndx, |
884 |
– |
void *p |
885 |
– |
) |
886 |
– |
{ |
887 |
– |
ANGLE_BASIS *ab = (ANGLE_BASIS *)p; |
888 |
– |
int li; |
889 |
– |
double pol, azi, d; |
890 |
– |
|
891 |
– |
if ((ndx < 0) | (ndx >= ab->nangles)) |
892 |
– |
return(0); |
893 |
– |
for (li = 0; ndx >= ab->lat[li].nphis; li++) |
894 |
– |
ndx -= ab->lat[li].nphis; |
895 |
– |
pol = PI/180.*0.5*(ab->lat[li].tmin + ab->lat[li+1].tmin); |
896 |
– |
azi = 2.*PI*ndx/ab->lat[li].nphis; |
897 |
– |
v[2] = d = cos(pol); |
898 |
– |
d = sqrt(1. - d*d); /* sin(pol) */ |
899 |
– |
v[0] = cos(azi)*d; |
900 |
– |
v[1] = sin(azi)*d; |
901 |
– |
return(1); |
902 |
– |
} |
903 |
– |
|
904 |
– |
|
905 |
– |
static int |
906 |
– |
ab_getndx( /* get index corresponding to the given vector */ |
907 |
– |
FVECT v, |
908 |
– |
void *p |
909 |
– |
) |
910 |
– |
{ |
911 |
– |
ANGLE_BASIS *ab = (ANGLE_BASIS *)p; |
912 |
– |
int li, ndx; |
913 |
– |
double pol, azi, d; |
914 |
– |
|
915 |
– |
if ((v[2] < -1.0) | (v[2] > 1.0)) |
916 |
– |
return(-1); |
917 |
– |
pol = 180.0/PI*acos(v[2]); |
918 |
– |
azi = 180.0/PI*atan2(v[1], v[0]); |
919 |
– |
if (azi < 0.0) azi += 360.0; |
920 |
– |
for (li = 1; ab->lat[li].tmin <= pol; li++) |
921 |
– |
if (!ab->lat[li].nphis) |
922 |
– |
return(-1); |
923 |
– |
--li; |
924 |
– |
ndx = (int)((1./360.)*azi*ab->lat[li].nphis + 0.5); |
925 |
– |
if (ndx >= ab->lat[li].nphis) ndx = 0; |
926 |
– |
while (li--) |
927 |
– |
ndx += ab->lat[li].nphis; |
928 |
– |
return(ndx); |
929 |
– |
} |
930 |
– |
|
931 |
– |
|
932 |
– |
static double |
933 |
– |
ab_getohm( /* get solid angle for this angle basis index */ |
934 |
– |
int ndx, |
935 |
– |
void *p |
936 |
– |
) |
937 |
– |
{ |
938 |
– |
ANGLE_BASIS *ab = (ANGLE_BASIS *)p; |
939 |
– |
int li; |
940 |
– |
double theta, theta1; |
941 |
– |
|
942 |
– |
if ((ndx < 0) | (ndx >= ab->nangles)) |
943 |
– |
return(0); |
944 |
– |
for (li = 0; ndx >= ab->lat[li].nphis; li++) |
945 |
– |
ndx -= ab->lat[li].nphis; |
946 |
– |
theta1 = PI/180. * ab->lat[li+1].tmin; |
947 |
– |
if (ab->lat[li].nphis == 1) { /* special case */ |
948 |
– |
if (ab->lat[li].tmin > FTINY) |
949 |
– |
error(USER, "unsupported BSDF coordinate system"); |
950 |
– |
return(2.*PI*(1. - cos(theta1))); |
951 |
– |
} |
952 |
– |
theta = PI/180. * ab->lat[li].tmin; |
953 |
– |
return(2.*PI*(cos(theta) - cos(theta1))/(double)ab->lat[li].nphis); |
954 |
– |
} |
955 |
– |
|
956 |
– |
|
957 |
– |
static int |
958 |
– |
ab_getvecR( /* get reverse vector for this angle basis index */ |
959 |
– |
FVECT v, |
960 |
– |
int ndx, |
961 |
– |
void *p |
962 |
– |
) |
963 |
– |
{ |
964 |
– |
if (!ab_getvec(v, ndx, p)) |
965 |
– |
return(0); |
966 |
– |
|
967 |
– |
v[0] = -v[0]; |
968 |
– |
v[1] = -v[1]; |
969 |
– |
v[2] = -v[2]; |
970 |
– |
|
971 |
– |
return(1); |
972 |
– |
} |
973 |
– |
|
974 |
– |
|
975 |
– |
static int |
976 |
– |
ab_getndxR( /* get index corresponding to the reverse vector */ |
977 |
– |
FVECT v, |
978 |
– |
void *p |
979 |
– |
) |
980 |
– |
{ |
981 |
– |
FVECT v2; |
982 |
– |
|
983 |
– |
v2[0] = -v[0]; |
984 |
– |
v2[1] = -v[1]; |
985 |
– |
v2[2] = -v[2]; |
986 |
– |
|
987 |
– |
return ab_getndx(v2, p); |
988 |
– |
} |
989 |
– |
|
990 |
– |
|
991 |
– |
static void |
992 |
– |
load_angle_basis( /* load custom BSDF angle basis */ |
993 |
– |
ezxml_t wab |
994 |
– |
) |
995 |
– |
{ |
996 |
– |
char *abname = ezxml_txt(ezxml_child(wab, "AngleBasisName")); |
997 |
– |
ezxml_t wbb; |
998 |
– |
int i; |
999 |
– |
|
1000 |
– |
if (!abname || !*abname) |
1001 |
– |
return; |
1002 |
– |
for (i = nabases; i--; ) |
1003 |
– |
if (!strcasecmp(abname, abase_list[i].name)) |
1004 |
– |
return; /* assume it's the same */ |
1005 |
– |
if (nabases >= MAXABASES) |
1006 |
– |
error(INTERNAL, "too many angle bases"); |
1007 |
– |
strcpy(abase_list[nabases].name, abname); |
1008 |
– |
abase_list[nabases].nangles = 0; |
1009 |
– |
for (i = 0, wbb = ezxml_child(wab, "AngleBasisBlock"); |
1010 |
– |
wbb != NULL; i++, wbb = wbb->next) { |
1011 |
– |
if (i >= MAXLATS) |
1012 |
– |
error(INTERNAL, "too many latitudes in custom basis"); |
1013 |
– |
abase_list[nabases].lat[i+1].tmin = atof(ezxml_txt( |
1014 |
– |
ezxml_child(ezxml_child(wbb, |
1015 |
– |
"ThetaBounds"), "UpperTheta"))); |
1016 |
– |
if (!i) |
1017 |
– |
abase_list[nabases].lat[i].tmin = |
1018 |
– |
-abase_list[nabases].lat[i+1].tmin; |
1019 |
– |
else if (!fequal(atof(ezxml_txt(ezxml_child(ezxml_child(wbb, |
1020 |
– |
"ThetaBounds"), "LowerTheta"))), |
1021 |
– |
abase_list[nabases].lat[i].tmin)) |
1022 |
– |
error(WARNING, "theta values disagree in custom basis"); |
1023 |
– |
abase_list[nabases].nangles += |
1024 |
– |
abase_list[nabases].lat[i].nphis = |
1025 |
– |
atoi(ezxml_txt(ezxml_child(wbb, "nPhis"))); |
1026 |
– |
} |
1027 |
– |
abase_list[nabases++].lat[i].nphis = 0; |
1028 |
– |
} |
1029 |
– |
|
1030 |
– |
|
1031 |
– |
static void |
1032 |
– |
load_geometry( /* load geometric dimensions and description (if any) */ |
1033 |
– |
struct BSDF_data *dp, |
1034 |
– |
ezxml_t wdb |
1035 |
– |
) |
1036 |
– |
{ |
1037 |
– |
ezxml_t geom; |
1038 |
– |
double cfact; |
1039 |
– |
const char *fmt, *mgfstr; |
1040 |
– |
|
1041 |
– |
dp->dim[0] = dp->dim[1] = dp->dim[2] = 0; |
1042 |
– |
dp->mgf = NULL; |
1043 |
– |
if ((geom = ezxml_child(wdb, "Width")) != NULL) |
1044 |
– |
dp->dim[0] = atof(ezxml_txt(geom)) * |
1045 |
– |
to_meters(ezxml_attr(geom, "unit")); |
1046 |
– |
if ((geom = ezxml_child(wdb, "Height")) != NULL) |
1047 |
– |
dp->dim[1] = atof(ezxml_txt(geom)) * |
1048 |
– |
to_meters(ezxml_attr(geom, "unit")); |
1049 |
– |
if ((geom = ezxml_child(wdb, "Thickness")) != NULL) |
1050 |
– |
dp->dim[2] = atof(ezxml_txt(geom)) * |
1051 |
– |
to_meters(ezxml_attr(geom, "unit")); |
1052 |
– |
if ((geom = ezxml_child(wdb, "Geometry")) == NULL || |
1053 |
– |
(mgfstr = ezxml_txt(geom)) == NULL) |
1054 |
– |
return; |
1055 |
– |
if ((fmt = ezxml_attr(geom, "format")) != NULL && |
1056 |
– |
strcasecmp(fmt, "MGF")) { |
1057 |
– |
sprintf(errmsg, "unrecognized geometry format '%s'", fmt); |
1058 |
– |
error(WARNING, errmsg); |
1059 |
– |
return; |
1060 |
– |
} |
1061 |
– |
cfact = to_meters(ezxml_attr(geom, "unit")); |
1062 |
– |
dp->mgf = (char *)malloc(strlen(mgfstr)+32); |
1063 |
– |
if (dp->mgf == NULL) |
1064 |
– |
error(SYSTEM, "out of memory in load_geometry"); |
1065 |
– |
if (cfact < 0.99 || cfact > 1.01) |
1066 |
– |
sprintf(dp->mgf, "xf -s %.5f\n%s\nxf\n", cfact, mgfstr); |
1067 |
– |
else |
1068 |
– |
strcpy(dp->mgf, mgfstr); |
1069 |
– |
} |
1070 |
– |
|
1071 |
– |
|
1072 |
– |
static void |
1073 |
– |
load_bsdf_data( /* load BSDF distribution for this wavelength */ |
1074 |
– |
struct BSDF_data *dp, |
1075 |
– |
ezxml_t wdb |
1076 |
– |
) |
1077 |
– |
{ |
1078 |
– |
char *cbasis = ezxml_txt(ezxml_child(wdb,"ColumnAngleBasis")); |
1079 |
– |
char *rbasis = ezxml_txt(ezxml_child(wdb,"RowAngleBasis")); |
1080 |
– |
char *sdata; |
1081 |
– |
int i; |
1082 |
– |
|
1083 |
– |
if ((!cbasis || !*cbasis) | (!rbasis || !*rbasis)) { |
1084 |
– |
error(WARNING, "missing column/row basis for BSDF"); |
1085 |
– |
return; |
1086 |
– |
} |
1087 |
– |
for (i = nabases; i--; ) |
1088 |
– |
if (!strcasecmp(cbasis, abase_list[i].name)) { |
1089 |
– |
dp->ninc = abase_list[i].nangles; |
1090 |
– |
dp->ib_priv = (void *)&abase_list[i]; |
1091 |
– |
dp->ib_vec = ab_getvecR; |
1092 |
– |
dp->ib_ndx = ab_getndxR; |
1093 |
– |
dp->ib_ohm = ab_getohm; |
1094 |
– |
break; |
1095 |
– |
} |
1096 |
– |
if (i < 0) { |
1097 |
– |
sprintf(errmsg, "undefined ColumnAngleBasis '%s'", cbasis); |
1098 |
– |
error(WARNING, errmsg); |
1099 |
– |
return; |
1100 |
– |
} |
1101 |
– |
for (i = nabases; i--; ) |
1102 |
– |
if (!strcasecmp(rbasis, abase_list[i].name)) { |
1103 |
– |
dp->nout = abase_list[i].nangles; |
1104 |
– |
dp->ob_priv = (void *)&abase_list[i]; |
1105 |
– |
dp->ob_vec = ab_getvec; |
1106 |
– |
dp->ob_ndx = ab_getndx; |
1107 |
– |
dp->ob_ohm = ab_getohm; |
1108 |
– |
break; |
1109 |
– |
} |
1110 |
– |
if (i < 0) { |
1111 |
– |
sprintf(errmsg, "undefined RowAngleBasis '%s'", rbasis); |
1112 |
– |
error(WARNING, errmsg); |
1113 |
– |
return; |
1114 |
– |
} |
1115 |
– |
/* read BSDF data */ |
1116 |
– |
sdata = ezxml_txt(ezxml_child(wdb,"ScatteringData")); |
1117 |
– |
if (!sdata || !*sdata) { |
1118 |
– |
error(WARNING, "missing BSDF ScatteringData"); |
1119 |
– |
return; |
1120 |
– |
} |
1121 |
– |
dp->bsdf = (float *)malloc(sizeof(float)*dp->ninc*dp->nout); |
1122 |
– |
if (dp->bsdf == NULL) |
1123 |
– |
error(SYSTEM, "out of memory in load_bsdf_data"); |
1124 |
– |
for (i = 0; i < dp->ninc*dp->nout; i++) { |
1125 |
– |
char *sdnext = fskip(sdata); |
1126 |
– |
if (sdnext == NULL) { |
1127 |
– |
error(WARNING, "bad/missing BSDF ScatteringData"); |
1128 |
– |
free(dp->bsdf); dp->bsdf = NULL; |
1129 |
– |
return; |
1130 |
– |
} |
1131 |
– |
while (*sdnext && isspace(*sdnext)) |
1132 |
– |
sdnext++; |
1133 |
– |
if (*sdnext == ',') sdnext++; |
1134 |
– |
dp->bsdf[i] = atof(sdata); |
1135 |
– |
sdata = sdnext; |
1136 |
– |
} |
1137 |
– |
while (isspace(*sdata)) |
1138 |
– |
sdata++; |
1139 |
– |
if (*sdata) { |
1140 |
– |
sprintf(errmsg, "%d extra characters after BSDF ScatteringData", |
1141 |
– |
(int)strlen(sdata)); |
1142 |
– |
error(WARNING, errmsg); |
1143 |
– |
} |
1144 |
– |
} |
1145 |
– |
|
1146 |
– |
|
1147 |
– |
static int |
1148 |
– |
check_bsdf_data( /* check that BSDF data is sane */ |
1149 |
– |
struct BSDF_data *dp |
1150 |
– |
) |
1151 |
– |
{ |
1152 |
– |
double *omega_iarr, *omega_oarr; |
1153 |
– |
double dom, contrib, hemi_total, full_total; |
1154 |
– |
int nneg; |
1155 |
– |
FVECT v; |
1156 |
– |
int i, o; |
1157 |
– |
|
1158 |
– |
if (dp == NULL || dp->bsdf == NULL) |
1159 |
– |
return(0); |
1160 |
– |
omega_iarr = (double *)calloc(dp->ninc, sizeof(double)); |
1161 |
– |
omega_oarr = (double *)calloc(dp->nout, sizeof(double)); |
1162 |
– |
if ((omega_iarr == NULL) | (omega_oarr == NULL)) |
1163 |
– |
error(SYSTEM, "out of memory in check_bsdf_data"); |
1164 |
– |
/* incoming projected solid angles */ |
1165 |
– |
hemi_total = .0; |
1166 |
– |
for (i = dp->ninc; i--; ) { |
1167 |
– |
dom = getBSDF_incohm(dp,i); |
1168 |
– |
if (dom <= .0) { |
1169 |
– |
error(WARNING, "zero/negative incoming solid angle"); |
1170 |
– |
continue; |
1171 |
– |
} |
1172 |
– |
if (!getBSDF_incvec(v,dp,i) || v[2] > FTINY) { |
1173 |
– |
error(WARNING, "illegal incoming BSDF direction"); |
1174 |
– |
free(omega_iarr); free(omega_oarr); |
1175 |
– |
return(0); |
1176 |
– |
} |
1177 |
– |
hemi_total += omega_iarr[i] = dom * -v[2]; |
1178 |
– |
} |
1179 |
– |
if ((hemi_total > 1.02*PI) | (hemi_total < 0.98*PI)) { |
1180 |
– |
sprintf(errmsg, "incoming BSDF hemisphere off by %.1f%%", |
1181 |
– |
100.*(hemi_total/PI - 1.)); |
1182 |
– |
error(WARNING, errmsg); |
1183 |
– |
} |
1184 |
– |
dom = PI / hemi_total; /* fix normalization */ |
1185 |
– |
for (i = dp->ninc; i--; ) |
1186 |
– |
omega_iarr[i] *= dom; |
1187 |
– |
/* outgoing projected solid angles */ |
1188 |
– |
hemi_total = .0; |
1189 |
– |
for (o = dp->nout; o--; ) { |
1190 |
– |
dom = getBSDF_outohm(dp,o); |
1191 |
– |
if (dom <= .0) { |
1192 |
– |
error(WARNING, "zero/negative outgoing solid angle"); |
1193 |
– |
continue; |
1194 |
– |
} |
1195 |
– |
if (!getBSDF_outvec(v,dp,o) || v[2] < -FTINY) { |
1196 |
– |
error(WARNING, "illegal outgoing BSDF direction"); |
1197 |
– |
free(omega_iarr); free(omega_oarr); |
1198 |
– |
return(0); |
1199 |
– |
} |
1200 |
– |
hemi_total += omega_oarr[o] = dom * v[2]; |
1201 |
– |
} |
1202 |
– |
if ((hemi_total > 1.02*PI) | (hemi_total < 0.98*PI)) { |
1203 |
– |
sprintf(errmsg, "outgoing BSDF hemisphere off by %.1f%%", |
1204 |
– |
100.*(hemi_total/PI - 1.)); |
1205 |
– |
error(WARNING, errmsg); |
1206 |
– |
} |
1207 |
– |
dom = PI / hemi_total; /* fix normalization */ |
1208 |
– |
for (o = dp->nout; o--; ) |
1209 |
– |
omega_oarr[o] *= dom; |
1210 |
– |
nneg = 0; /* check outgoing totals */ |
1211 |
– |
for (i = 0; i < dp->ninc; i++) { |
1212 |
– |
hemi_total = .0; |
1213 |
– |
for (o = dp->nout; o--; ) { |
1214 |
– |
double f = BSDF_value(dp,i,o); |
1215 |
– |
if (f >= .0) |
1216 |
– |
hemi_total += f*omega_oarr[o]; |
1217 |
– |
else { |
1218 |
– |
nneg += (f < -FTINY); |
1219 |
– |
BSDF_value(dp,i,o) = .0f; |
1220 |
– |
} |
1221 |
– |
} |
1222 |
– |
if (hemi_total > 1.01) { |
1223 |
– |
sprintf(errmsg, |
1224 |
– |
"incoming BSDF direction %d passes %.1f%% of light", |
1225 |
– |
i, 100.*hemi_total); |
1226 |
– |
error(WARNING, errmsg); |
1227 |
– |
} |
1228 |
– |
} |
1229 |
– |
if (nneg) { |
1230 |
– |
sprintf(errmsg, "%d negative BSDF values (ignored)", nneg); |
1231 |
– |
error(WARNING, errmsg); |
1232 |
– |
} |
1233 |
– |
full_total = .0; /* reverse roles and check again */ |
1234 |
– |
for (o = 0; o < dp->nout; o++) { |
1235 |
– |
hemi_total = .0; |
1236 |
– |
for (i = dp->ninc; i--; ) |
1237 |
– |
hemi_total += BSDF_value(dp,i,o) * omega_iarr[i]; |
1238 |
– |
|
1239 |
– |
if (hemi_total > 1.01) { |
1240 |
– |
sprintf(errmsg, |
1241 |
– |
"outgoing BSDF direction %d collects %.1f%% of light", |
1242 |
– |
o, 100.*hemi_total); |
1243 |
– |
error(WARNING, errmsg); |
1244 |
– |
} |
1245 |
– |
full_total += hemi_total*omega_oarr[o]; |
1246 |
– |
} |
1247 |
– |
full_total /= PI; |
1248 |
– |
if (full_total > 1.00001) { |
1249 |
– |
sprintf(errmsg, "BSDF transfers %.4f%% of light", |
1250 |
– |
100.*full_total); |
1251 |
– |
error(WARNING, errmsg); |
1252 |
– |
} |
1253 |
– |
free(omega_iarr); free(omega_oarr); |
1254 |
– |
return(1); |
1255 |
– |
} |
1256 |
– |
|
1257 |
– |
|
1258 |
– |
struct BSDF_data * |
1259 |
– |
load_BSDF( /* load BSDF data from file */ |
1260 |
– |
char *fname |
1261 |
– |
) |
1262 |
– |
{ |
1263 |
– |
char *path; |
1264 |
– |
ezxml_t fl, wtl, wld, wdb; |
1265 |
– |
struct BSDF_data *dp; |
1266 |
– |
|
1267 |
– |
path = getpath(fname, getrlibpath(), R_OK); |
1268 |
– |
if (path == NULL) { |
1269 |
– |
sprintf(errmsg, "cannot find BSDF file \"%s\"", fname); |
1270 |
– |
error(WARNING, errmsg); |
1271 |
– |
return(NULL); |
1272 |
– |
} |
1273 |
– |
fl = ezxml_parse_file(path); |
1274 |
– |
if (fl == NULL) { |
1275 |
– |
sprintf(errmsg, "cannot open BSDF \"%s\"", path); |
1276 |
– |
error(WARNING, errmsg); |
1277 |
– |
return(NULL); |
1278 |
– |
} |
1279 |
– |
if (ezxml_error(fl)[0]) { |
1280 |
– |
sprintf(errmsg, "BSDF \"%s\" %s", path, ezxml_error(fl)); |
1281 |
– |
error(WARNING, errmsg); |
1282 |
– |
ezxml_free(fl); |
1283 |
– |
return(NULL); |
1284 |
– |
} |
1285 |
– |
if (strcmp(ezxml_name(fl), "WindowElement")) { |
1286 |
– |
sprintf(errmsg, |
1287 |
– |
"BSDF \"%s\": top level node not 'WindowElement'", |
1288 |
– |
path); |
1289 |
– |
error(WARNING, errmsg); |
1290 |
– |
ezxml_free(fl); |
1291 |
– |
return(NULL); |
1292 |
– |
} |
1293 |
– |
wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer"); |
1294 |
– |
if (strcasecmp(ezxml_txt(ezxml_child(ezxml_child(wtl, |
1295 |
– |
"DataDefinition"), "IncidentDataStructure")), |
1296 |
– |
"Columns")) { |
1297 |
– |
sprintf(errmsg, |
1298 |
– |
"BSDF \"%s\": unsupported IncidentDataStructure", |
1299 |
– |
path); |
1300 |
– |
error(WARNING, errmsg); |
1301 |
– |
ezxml_free(fl); |
1302 |
– |
return(NULL); |
1303 |
– |
} |
1304 |
– |
load_angle_basis(ezxml_child(ezxml_child(wtl, |
1305 |
– |
"DataDefinition"), "AngleBasis")); |
1306 |
– |
dp = (struct BSDF_data *)calloc(1, sizeof(struct BSDF_data)); |
1307 |
– |
load_geometry(dp, ezxml_child(wtl, "Material")); |
1308 |
– |
for (wld = ezxml_child(wtl, "WavelengthData"); |
1309 |
– |
wld != NULL; wld = wld->next) { |
1310 |
– |
if (strcasecmp(ezxml_txt(ezxml_child(wld,"Wavelength")), |
1311 |
– |
"Visible")) |
1312 |
– |
continue; |
1313 |
– |
for (wdb = ezxml_child(wld, "WavelengthDataBlock"); |
1314 |
– |
wdb != NULL; wdb = wdb->next) |
1315 |
– |
if (!strcasecmp(ezxml_txt(ezxml_child(wdb, |
1316 |
– |
"WavelengthDataDirection")), |
1317 |
– |
"Transmission Front")) |
1318 |
– |
break; |
1319 |
– |
if (wdb != NULL) { /* load front BTDF */ |
1320 |
– |
load_bsdf_data(dp, wdb); |
1321 |
– |
break; /* ignore the rest */ |
1322 |
– |
} |
1323 |
– |
} |
1324 |
– |
ezxml_free(fl); /* done with XML file */ |
1325 |
– |
if (!check_bsdf_data(dp)) { |
1326 |
– |
sprintf(errmsg, "bad/missing BTDF data in \"%s\"", path); |
1327 |
– |
error(WARNING, errmsg); |
1328 |
– |
free_BSDF(dp); |
1329 |
– |
dp = NULL; |
1330 |
– |
} |
1331 |
– |
return(dp); |
1332 |
– |
} |
1333 |
– |
|
1334 |
– |
|
1335 |
– |
void |
1336 |
– |
free_BSDF( /* free BSDF data structure */ |
1337 |
– |
struct BSDF_data *b |
1338 |
– |
) |
1339 |
– |
{ |
1340 |
– |
if (b == NULL) |
1341 |
– |
return; |
1342 |
– |
if (b->mgf != NULL) |
1343 |
– |
free(b->mgf); |
1344 |
– |
if (b->bsdf != NULL) |
1345 |
– |
free(b->bsdf); |
1346 |
– |
free(b); |
1347 |
– |
} |
1348 |
– |
|
1349 |
– |
|
1350 |
– |
int |
1351 |
– |
r_BSDF_incvec( /* compute random input vector at given location */ |
1352 |
– |
FVECT v, |
1353 |
– |
struct BSDF_data *b, |
1354 |
– |
int i, |
1355 |
– |
double rv, |
1356 |
– |
MAT4 xm |
1357 |
– |
) |
1358 |
– |
{ |
1359 |
– |
FVECT pert; |
1360 |
– |
double rad; |
1361 |
– |
int j; |
1362 |
– |
|
1363 |
– |
if (!getBSDF_incvec(v, b, i)) |
1364 |
– |
return(0); |
1365 |
– |
rad = sqrt(getBSDF_incohm(b, i) / PI); |
1366 |
– |
multisamp(pert, 3, rv); |
1367 |
– |
for (j = 0; j < 3; j++) |
1368 |
– |
v[j] += rad*(2.*pert[j] - 1.); |
1369 |
– |
if (xm != NULL) |
1370 |
– |
multv3(v, v, xm); |
1371 |
– |
return(normalize(v) != 0.0); |
1372 |
– |
} |
1373 |
– |
|
1374 |
– |
|
1375 |
– |
int |
1376 |
– |
r_BSDF_outvec( /* compute random output vector at given location */ |
1377 |
– |
FVECT v, |
1378 |
– |
struct BSDF_data *b, |
1379 |
– |
int o, |
1380 |
– |
double rv, |
1381 |
– |
MAT4 xm |
1382 |
– |
) |
1383 |
– |
{ |
1384 |
– |
FVECT pert; |
1385 |
– |
double rad; |
1386 |
– |
int j; |
1387 |
– |
|
1388 |
– |
if (!getBSDF_outvec(v, b, o)) |
1389 |
– |
return(0); |
1390 |
– |
rad = sqrt(getBSDF_outohm(b, o) / PI); |
1391 |
– |
multisamp(pert, 3, rv); |
1392 |
– |
for (j = 0; j < 3; j++) |
1393 |
– |
v[j] += rad*(2.*pert[j] - 1.); |
1394 |
– |
if (xm != NULL) |
1395 |
– |
multv3(v, v, xm); |
1396 |
– |
return(normalize(v) != 0.0); |
1397 |
– |
} |
1398 |
– |
|
1399 |
– |
|
1400 |
– |
static int |
1401 |
– |
addrot( /* compute rotation (x,y,z) => (xp,yp,zp) */ |
1402 |
– |
char *xfarg[], |
1403 |
– |
FVECT xp, |
1404 |
– |
FVECT yp, |
1405 |
– |
FVECT zp |
1406 |
– |
) |
1407 |
– |
{ |
1408 |
– |
static char bufs[3][16]; |
1409 |
– |
int bn = 0; |
1410 |
– |
char **xfp = xfarg; |
1411 |
– |
double theta; |
1412 |
– |
|
1413 |
– |
if (yp[2]*yp[2] + zp[2]*zp[2] < 2.*FTINY*FTINY) { |
1414 |
– |
/* Special case for X' along Z-axis */ |
1415 |
– |
theta = -atan2(yp[0], yp[1]); |
1416 |
– |
*xfp++ = "-ry"; |
1417 |
– |
*xfp++ = xp[2] < 0.0 ? "90" : "-90"; |
1418 |
– |
*xfp++ = "-rz"; |
1419 |
– |
sprintf(bufs[bn], "%f", theta*(180./PI)); |
1420 |
– |
*xfp++ = bufs[bn++]; |
1421 |
– |
return(xfp - xfarg); |
1422 |
– |
} |
1423 |
– |
theta = atan2(yp[2], zp[2]); |
1424 |
– |
if (!FEQ(theta,0.0)) { |
1425 |
– |
*xfp++ = "-rx"; |
1426 |
– |
sprintf(bufs[bn], "%f", theta*(180./PI)); |
1427 |
– |
*xfp++ = bufs[bn++]; |
1428 |
– |
} |
1429 |
– |
theta = asin(-xp[2]); |
1430 |
– |
if (!FEQ(theta,0.0)) { |
1431 |
– |
*xfp++ = "-ry"; |
1432 |
– |
sprintf(bufs[bn], " %f", theta*(180./PI)); |
1433 |
– |
*xfp++ = bufs[bn++]; |
1434 |
– |
} |
1435 |
– |
theta = atan2(xp[1], xp[0]); |
1436 |
– |
if (!FEQ(theta,0.0)) { |
1437 |
– |
*xfp++ = "-rz"; |
1438 |
– |
sprintf(bufs[bn], "%f", theta*(180./PI)); |
1439 |
– |
*xfp++ = bufs[bn++]; |
1440 |
– |
} |
1441 |
– |
*xfp = NULL; |
1442 |
– |
return(xfp - xfarg); |
1443 |
– |
} |
1444 |
– |
|
1445 |
– |
|
1446 |
– |
int |
1447 |
– |
getBSDF_xfm( /* compute BSDF orient. -> world orient. transform */ |
1448 |
– |
MAT4 xm, |
1449 |
– |
FVECT nrm, |
1450 |
– |
UpDir ud, |
1451 |
– |
char *xfbuf |
1452 |
– |
) |
1453 |
– |
{ |
1454 |
– |
char *xfargs[7]; |
1455 |
– |
XF myxf; |
1456 |
– |
FVECT updir, xdest, ydest; |
1457 |
– |
int i; |
1458 |
– |
|
1459 |
– |
updir[0] = updir[1] = updir[2] = 0.; |
1460 |
– |
switch (ud) { |
1461 |
– |
case UDzneg: |
1462 |
– |
updir[2] = -1.; |
1463 |
– |
break; |
1464 |
– |
case UDyneg: |
1465 |
– |
updir[1] = -1.; |
1466 |
– |
break; |
1467 |
– |
case UDxneg: |
1468 |
– |
updir[0] = -1.; |
1469 |
– |
break; |
1470 |
– |
case UDxpos: |
1471 |
– |
updir[0] = 1.; |
1472 |
– |
break; |
1473 |
– |
case UDypos: |
1474 |
– |
updir[1] = 1.; |
1475 |
– |
break; |
1476 |
– |
case UDzpos: |
1477 |
– |
updir[2] = 1.; |
1478 |
– |
break; |
1479 |
– |
case UDunknown: |
1480 |
– |
return(0); |
1481 |
– |
} |
1482 |
– |
fcross(xdest, updir, nrm); |
1483 |
– |
if (normalize(xdest) == 0.0) |
1484 |
– |
return(0); |
1485 |
– |
fcross(ydest, nrm, xdest); |
1486 |
– |
xf(&myxf, addrot(xfargs, xdest, ydest, nrm), xfargs); |
1487 |
– |
copymat4(xm, myxf.xfm); |
1488 |
– |
if (xfbuf == NULL) |
1489 |
– |
return(1); |
1490 |
– |
/* return xf arguments as well */ |
1491 |
– |
for (i = 0; xfargs[i] != NULL; i++) { |
1492 |
– |
*xfbuf++ = ' '; |
1493 |
– |
strcpy(xfbuf, xfargs[i]); |
1494 |
– |
while (*xfbuf) ++xfbuf; |
1495 |
– |
} |
1496 |
– |
return(1); |
1497 |
– |
} |
1498 |
– |
|
1499 |
– |
/*######### END DEPRECATED ROUTINES #######*/ |
1500 |
– |
/*################################################################*/ |