19 |
|
#include "bsdf.h" |
20 |
|
#include "bsdf_m.h" |
21 |
|
|
22 |
– |
#ifndef FTINY |
23 |
– |
#define FTINY 1e-6 |
24 |
– |
#endif |
25 |
– |
|
22 |
|
/* Function return codes */ |
23 |
|
#define RC_GOOD 1 |
24 |
|
#define RC_FAIL 0 |
346 |
|
} |
347 |
|
free(ohma); |
348 |
|
/* need incoming solid angles, too? */ |
349 |
< |
if (dp->ninc < dp->nout || dp->ib_ohm != dp->ob_ohm || |
354 |
< |
dp->ib_priv != dp->ob_priv) { |
349 |
> |
if ((dp->ib_ohm != dp->ob_ohm) | (dp->ib_priv != dp->ob_priv)) { |
350 |
|
double ohm; |
351 |
|
for (i = dp->ninc; i--; ) |
352 |
|
if ((ohm = mBSDF_incohm(dp,i)) < df->minProjSA) |
359 |
|
static int |
360 |
|
load_bsdf_data(SDData *sd, ezxml_t wdb, int rowinc) |
361 |
|
{ |
367 |
– |
char *cbasis = ezxml_txt(ezxml_child(wdb,"ColumnAngleBasis")); |
368 |
– |
char *rbasis = ezxml_txt(ezxml_child(wdb,"RowAngleBasis")); |
362 |
|
SDSpectralDF *df; |
363 |
|
SDMat *dp; |
364 |
|
char *sdata; |
365 |
|
int inbi, outbi; |
366 |
|
int i; |
374 |
– |
|
375 |
– |
if ((!cbasis || !*cbasis) | (!rbasis || !*rbasis)) { |
376 |
– |
sprintf(SDerrorDetail, "Missing column/row basis for BSDF '%s'", |
377 |
– |
sd->name); |
378 |
– |
return RC_FORMERR; |
379 |
– |
} |
367 |
|
/* allocate BSDF component */ |
368 |
|
sdata = ezxml_txt(ezxml_child(wdb, "WavelengthDataDirection")); |
369 |
< |
if (!strcasecmp(sdata, "Transmission Front")) { |
369 |
> |
if (!strcasecmp(sdata, "Transmission Back") || (sd->tf == NULL && |
370 |
> |
!strcasecmp(sdata, "Transmission Front"))) { |
371 |
|
if (sd->tf != NULL) |
372 |
|
SDfreeSpectralDF(sd->tf); |
373 |
|
if ((sd->tf = SDnewSpectralDF(1)) == NULL) |
374 |
|
return RC_MEMERR; |
375 |
|
df = sd->tf; |
376 |
|
} else if (!strcasecmp(sdata, "Reflection Front")) { |
377 |
< |
if (sd->rf != NULL) |
390 |
< |
SDfreeSpectralDF(sd->rf); |
391 |
< |
if ((sd->rf = SDnewSpectralDF(1)) == NULL) |
392 |
< |
return RC_MEMERR; |
393 |
< |
df = sd->rf; |
394 |
< |
} else if (!strcasecmp(sdata, "Reflection Back")) { |
395 |
< |
if (sd->rb != NULL) |
377 |
> |
if (sd->rb != NULL) /* note back-front reversal */ |
378 |
|
SDfreeSpectralDF(sd->rb); |
379 |
|
if ((sd->rb = SDnewSpectralDF(1)) == NULL) |
380 |
|
return RC_MEMERR; |
381 |
|
df = sd->rb; |
382 |
+ |
} else if (!strcasecmp(sdata, "Reflection Back")) { |
383 |
+ |
if (sd->rf != NULL) /* note front-back reversal */ |
384 |
+ |
SDfreeSpectralDF(sd->rf); |
385 |
+ |
if ((sd->rf = SDnewSpectralDF(1)) == NULL) |
386 |
+ |
return RC_MEMERR; |
387 |
+ |
df = sd->rf; |
388 |
|
} else |
389 |
|
return RC_FAIL; |
390 |
+ |
/* XXX should also check "ScatteringDataType" for consistency? */ |
391 |
|
/* get angle bases */ |
392 |
|
sdata = ezxml_txt(ezxml_child(wdb,"ColumnAngleBasis")); |
393 |
|
if (!sdata || !*sdata) { |
396 |
|
return RC_FORMERR; |
397 |
|
} |
398 |
|
for (inbi = nabases; inbi--; ) |
399 |
< |
if (!strcasecmp(cbasis, abase_list[inbi].name)) |
399 |
> |
if (!strcasecmp(sdata, abase_list[inbi].name)) |
400 |
|
break; |
401 |
|
if (inbi < 0) { |
402 |
< |
sprintf(SDerrorDetail, "Undefined ColumnAngleBasis '%s'", |
414 |
< |
cbasis); |
402 |
> |
sprintf(SDerrorDetail, "Undefined ColumnAngleBasis '%s'", sdata); |
403 |
|
return RC_FORMERR; |
404 |
|
} |
405 |
|
sdata = ezxml_txt(ezxml_child(wdb,"RowAngleBasis")); |
409 |
|
return RC_FORMERR; |
410 |
|
} |
411 |
|
for (outbi = nabases; outbi--; ) |
412 |
< |
if (!strcasecmp(rbasis, abase_list[outbi].name)) |
412 |
> |
if (!strcasecmp(sdata, abase_list[outbi].name)) |
413 |
|
break; |
414 |
|
if (outbi < 0) { |
415 |
< |
sprintf(SDerrorDetail, "Undefined RowAngleBasis '%s'", cbasis); |
415 |
> |
sprintf(SDerrorDetail, "Undefined RowAngleBasis '%s'", sdata); |
416 |
|
return RC_FORMERR; |
417 |
|
} |
418 |
|
/* allocate BSDF matrix */ |
419 |
|
dp = SDnewMatrix(abase_list[inbi].nangles, abase_list[outbi].nangles); |
420 |
|
if (dp == NULL) |
421 |
|
return RC_MEMERR; |
422 |
< |
dp->ib_priv = (void *)&abase_list[inbi]; |
423 |
< |
dp->ob_priv = (void *)&abase_list[outbi]; |
422 |
> |
dp->ib_priv = &abase_list[inbi]; |
423 |
> |
dp->ob_priv = &abase_list[outbi]; |
424 |
|
if (df == sd->tf) { |
425 |
< |
dp->ib_vec = ab_getvecR; |
426 |
< |
dp->ib_ndx = ab_getndxR; |
427 |
< |
dp->ob_vec = ab_getvec; |
428 |
< |
dp->ob_ndx = ab_getndx; |
425 |
> |
dp->ib_vec = &ab_getvecR; |
426 |
> |
dp->ib_ndx = &ab_getndxR; |
427 |
> |
dp->ob_vec = &ab_getvec; |
428 |
> |
dp->ob_ndx = &ab_getndx; |
429 |
|
} else if (df == sd->rf) { |
430 |
< |
dp->ib_vec = ab_getvec; |
431 |
< |
dp->ib_ndx = ab_getndx; |
432 |
< |
dp->ob_vec = ab_getvec; |
433 |
< |
dp->ob_ndx = ab_getndx; |
430 |
> |
dp->ib_vec = &ab_getvec; |
431 |
> |
dp->ib_ndx = &ab_getndx; |
432 |
> |
dp->ob_vec = &ab_getvec; |
433 |
> |
dp->ob_ndx = &ab_getndx; |
434 |
|
} else /* df == sd->rb */ { |
435 |
< |
dp->ib_vec = ab_getvecR; |
436 |
< |
dp->ib_ndx = ab_getndxR; |
437 |
< |
dp->ob_vec = ab_getvecR; |
438 |
< |
dp->ob_ndx = ab_getndxR; |
435 |
> |
dp->ib_vec = &ab_getvecR; |
436 |
> |
dp->ib_ndx = &ab_getndxR; |
437 |
> |
dp->ob_vec = &ab_getvecR; |
438 |
> |
dp->ob_ndx = &ab_getndxR; |
439 |
|
} |
440 |
< |
dp->ib_ohm = ab_getohm; |
441 |
< |
dp->ob_ohm = ab_getohm; |
440 |
> |
dp->ib_ohm = &ab_getohm; |
441 |
> |
dp->ob_ohm = &ab_getohm; |
442 |
|
df->comp[0].cspec[0] = c_dfcolor; /* XXX monochrome for now */ |
443 |
|
df->comp[0].dist = dp; |
444 |
|
df->comp[0].func = &SDhandleMtx; |
507 |
|
c_cmix(&dv->spec, dv->cieY, &dv->spec, ymin, &df->comp[n].cspec[0]); |
508 |
|
dv->cieY += ymin; |
509 |
|
} |
510 |
< |
df->maxHemi -= dv->cieY; /* correct minimum hemispherical */ |
511 |
< |
dv->spec.clock++; /* make sure everything is set */ |
510 |
> |
df->maxHemi -= dv->cieY; /* adjust minimum hemispherical */ |
511 |
> |
/* make sure everything is set */ |
512 |
|
c_ccvt(&dv->spec, C_CSXY+C_CSSPEC); |
513 |
|
} |
514 |
|
|
515 |
|
/* Load a BSDF matrix from an open XML file */ |
516 |
|
SDError |
517 |
< |
SDloadMtx(SDData *sd, ezxml_t fl) |
517 |
> |
SDloadMtx(SDData *sd, ezxml_t wtl) |
518 |
|
{ |
519 |
< |
ezxml_t wtl, wld, wdb; |
519 |
> |
ezxml_t wld, wdb; |
520 |
|
int rowIn; |
521 |
|
struct BSDF_data *dp; |
522 |
|
char *txt; |
523 |
|
int rval; |
524 |
|
|
525 |
< |
if (strcmp(ezxml_name(fl), "WindowElement")) { |
525 |
> |
txt = ezxml_txt(ezxml_child(ezxml_child(wtl, |
526 |
> |
"DataDefinition"), "IncidentDataStructure")); |
527 |
> |
if (txt == NULL || !*txt) { |
528 |
|
sprintf(SDerrorDetail, |
529 |
< |
"BSDF \"%s\": top level node not 'WindowElement'", |
529 |
> |
"BSDF \"%s\": missing IncidentDataStructure", |
530 |
|
sd->name); |
531 |
|
return SDEformat; |
532 |
|
} |
543 |
– |
wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer"); |
544 |
– |
txt = ezxml_txt(ezxml_child(ezxml_child(wtl, |
545 |
– |
"DataDefinition"), "IncidentDataStructure")); |
533 |
|
if (!strcasecmp(txt, "Rows")) |
534 |
|
rowIn = 1; |
535 |
|
else if (!strcasecmp(txt, "Columns")) |
544 |
|
rval = load_angle_basis(ezxml_child(ezxml_child(wtl, |
545 |
|
"DataDefinition"), "AngleBasis")); |
546 |
|
if (rval < 0) |
547 |
< |
goto err_return; |
547 |
> |
return convert_errcode(rval); |
548 |
|
/* load BSDF components */ |
549 |
|
for (wld = ezxml_child(wtl, "WavelengthData"); |
550 |
|
wld != NULL; wld = wld->next) { |
554 |
|
for (wdb = ezxml_child(wld, "WavelengthDataBlock"); |
555 |
|
wdb != NULL; wdb = wdb->next) |
556 |
|
if ((rval = load_bsdf_data(sd, wdb, rowIn)) < 0) |
557 |
< |
goto err_return; |
557 |
> |
return convert_errcode(rval); |
558 |
|
} |
559 |
|
/* separate diffuse components */ |
560 |
|
extract_diffuse(&sd->rLambFront, sd->rf); |
562 |
|
extract_diffuse(&sd->tLamb, sd->tf); |
563 |
|
/* return success */ |
564 |
|
return SDEnone; |
578 |
– |
err_return: /* jump here on failure */ |
579 |
– |
if (sd->rf != NULL) { |
580 |
– |
SDfreeSpectralDF(sd->rf); |
581 |
– |
sd->rf = NULL; |
582 |
– |
} |
583 |
– |
if (sd->rb != NULL) { |
584 |
– |
SDfreeSpectralDF(sd->rb); |
585 |
– |
sd->rb = NULL; |
586 |
– |
} |
587 |
– |
if (sd->tf != NULL) { |
588 |
– |
SDfreeSpectralDF(sd->tf); |
589 |
– |
sd->tf = NULL; |
590 |
– |
} |
591 |
– |
return convert_errcode(rval); |
565 |
|
} |
566 |
|
|
567 |
|
/* Get Matrix BSDF value */ |
590 |
|
SDqueryMtxProjSA(double *psa, const FVECT vec, int qflags, const void *dist) |
591 |
|
{ |
592 |
|
const SDMat *dp = (const SDMat *)dist; |
593 |
< |
|
594 |
< |
if (!(qflags & SDqueryInc+SDqueryOut)) |
593 |
> |
double inc_psa, out_psa; |
594 |
> |
/* check arguments */ |
595 |
> |
if ((psa == NULL) | (vec == NULL) | (dp == NULL)) |
596 |
|
return SDEargument; |
597 |
< |
if (qflags & SDqueryInc) { |
598 |
< |
double inc_psa = mBSDF_incohm(dp, mBSDF_incndx(dp, vec)); |
599 |
< |
if (inc_psa < .0) |
600 |
< |
return SDEinternal; |
601 |
< |
switch (qflags & SDqueryMin+SDqueryMax) { |
602 |
< |
case SDqueryMax: |
603 |
< |
if (inc_psa > psa[0]) |
604 |
< |
psa[0] = inc_psa; |
605 |
< |
break; |
606 |
< |
case SDqueryMin+SDqueryMax: |
633 |
< |
if (inc_psa > psa[1]) |
634 |
< |
psa[1] = inc_psa; |
635 |
< |
/* fall through */ |
636 |
< |
case SDqueryMin: |
637 |
< |
if (inc_psa < psa[0]) |
638 |
< |
psa[0] = inc_psa; |
639 |
< |
break; |
640 |
< |
case 0: |
597 |
> |
/* get projected solid angles */ |
598 |
> |
inc_psa = mBSDF_incohm(dp, mBSDF_incndx(dp, vec)); |
599 |
> |
out_psa = mBSDF_outohm(dp, mBSDF_outndx(dp, vec)); |
600 |
> |
|
601 |
> |
switch (qflags) { /* record based on flag settings */ |
602 |
> |
case SDqueryVal: |
603 |
> |
psa[0] = .0; |
604 |
> |
/* fall through */ |
605 |
> |
case SDqueryMax: |
606 |
> |
if (inc_psa > psa[0]) |
607 |
|
psa[0] = inc_psa; |
608 |
< |
break; |
609 |
< |
} |
608 |
> |
if (out_psa > psa[0]) |
609 |
> |
psa[0] = out_psa; |
610 |
> |
break; |
611 |
> |
case SDqueryMin+SDqueryMax: |
612 |
> |
if (inc_psa > psa[0]) |
613 |
> |
psa[1] = inc_psa; |
614 |
> |
if (out_psa > psa[0]) |
615 |
> |
psa[1] = out_psa; |
616 |
> |
/* fall through */ |
617 |
> |
case SDqueryMin: |
618 |
> |
if ((inc_psa > .0) & (inc_psa < psa[0])) |
619 |
> |
psa[0] = inc_psa; |
620 |
> |
if ((out_psa > .0) & (out_psa < psa[0])) |
621 |
> |
psa[0] = out_psa; |
622 |
> |
break; |
623 |
|
} |
624 |
< |
if (qflags & SDqueryOut) { |
625 |
< |
double out_psa = mBSDF_outohm(dp, mBSDF_outndx(dp, vec)); |
647 |
< |
if (out_psa < .0) |
648 |
< |
return SDEinternal; |
649 |
< |
switch (qflags & SDqueryMin+SDqueryMax) { |
650 |
< |
case SDqueryMax: |
651 |
< |
if (out_psa > psa[0]) |
652 |
< |
psa[0] = out_psa; |
653 |
< |
break; |
654 |
< |
case SDqueryMin+SDqueryMax: |
655 |
< |
if (out_psa > psa[1]) |
656 |
< |
psa[1] = out_psa; |
657 |
< |
/* fall through */ |
658 |
< |
case SDqueryMin: |
659 |
< |
if (out_psa < psa[0]) |
660 |
< |
psa[0] = out_psa; |
661 |
< |
break; |
662 |
< |
case 0: |
663 |
< |
psa[(qflags&SDqueryInc)!=0] = out_psa; |
664 |
< |
break; |
665 |
< |
} |
666 |
< |
} |
667 |
< |
return SDEnone; |
624 |
> |
/* make sure it's legal */ |
625 |
> |
return (psa[0] <= .0) ? SDEinternal : SDEnone; |
626 |
|
} |
627 |
|
|
628 |
|
/* Compute new cumulative distribution from BSDF */ |
664 |
|
int reverse; |
665 |
|
SDMatCDst myCD; |
666 |
|
SDMatCDst *cd, *cdlast; |
667 |
< |
|
668 |
< |
if (dp == NULL) |
667 |
> |
/* check arguments */ |
668 |
> |
if ((inVec == NULL) | (dp == NULL)) |
669 |
|
return NULL; |
670 |
|
memset(&myCD, 0, sizeof(myCD)); |
671 |
|
myCD.indx = mBSDF_incndx(dp, inVec); |
720 |
|
const SDMatCDst *mcd = (const SDMatCDst *)cdp; |
721 |
|
const unsigned target = randX*maxval; |
722 |
|
int i, iupper, ilower; |
723 |
+ |
/* check arguments */ |
724 |
+ |
if ((outVec == NULL) | (mcd == NULL)) |
725 |
+ |
return SDEargument; |
726 |
|
/* binary search to find index */ |
727 |
|
ilower = 0; iupper = mcd->calen; |
728 |
|
while ((i = (iupper + ilower) >> 1) != ilower) |