Page MenuHomec4science

pmcontrib4.c
No OneTemporary

File Metadata

Created
Wed, May 29, 08:24

pmcontrib4.c

#ifndef lint
static const char RCSid[] = "$Id: pmcontrib2.c,v 2.5 2018/11/08 00:54:07 greg Exp $";
#endif
/*
=========================================================================
Photon map routines for precomputed light source contributions.
These routines interface to rcontrib.
Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
(c) Lucerne University of Applied Sciences and Arts,
supported by the Swiss National Science Foundation
(SNSF #147053, "Daylight Redirecting Components",
SNSF #179067, "Light Fields for Spatio-Temporal Glare Assessment")
=========================================================================
$Id$
*/
#include "pmapcontrib.h"
#ifdef PMAP_CONTRIB
#include "pmapmat.h"
#include "pmapsrc.h"
#include "pmaprand.h"
#include "pmapio.h"
#include "pmapdiag.h"
#include "otypes.h"
#include "otspecial.h"
#include "oocnn.h"
#include "random.h"
void initPmapContribTab (LUTAB *contribTab, int *contribMode)
/* Set contribution table (interface to rcmain.c) */
{
pmapContribTab = contribTab;
pmapContribMode = contribMode;
}
/* ------------------ CONTRIB PMAP LOADING STUFF --------------------- */
static int checkContribModifier (const LUENT *modContNode, void *p)
/* Check for valid modifier in LUT entry */
{
MODCONT *modCont = (MODCONT*)modContNode -> data;
char *modName = (char*)modCont -> modname;
OBJECT modObj = modifier(modName);
const PhotonMap *pmap = (PhotonMap*)p;
if (modObj == OVOID) {
sprintf(errmsg, "invalid modifier %s", modName);
error(USER, errmsg);
}
if (!islight(objptr(modObj) -> otype)) {
sprintf(errmsg, "%s is not a light source modifier", modName);
error(USER, errmsg);
}
/* Warn if number of bins exceeds lookup bandwidth (as this is
* guaranteed to result in empty bins!) */
if (pmap -> maxGather < modCont -> nbins) {
sprintf(errmsg, "contribution photon lookup bandwidth too low "
"for modifier %s, some bins will be zero", modName
);
error(WARNING, errmsg);
}
return 0;
}
void initPmapContrib (PhotonMap *pmap)
/* Set up photon map contributions and get binning parameters */
{
unsigned t;
/* Avoid incompatible photon maps */
for (t = 0; t < NUM_PMAP_TYPES; t++)
if (photonMaps [t] && t != PMAP_TYPE_CONTRIB) {
sprintf(errmsg, "%s photon map does not support contributions",
pmapName [t]
);
error(USER, errmsg);
}
if (!pmapContribTab)
error(INTERNAL,
"contribution LUT not initialised in initPmapContrib()"
);
/* Check for valid contribution modifiers */
lu_doall(pmapContribTab, checkContribModifier, pmap);
/* Set callback to collect contributions */
if (pmap) {
pmap -> lookup = getPreCompPhotonContrib;
/* XXX: Need tighter tolerance for contribution photon lookups? */
pmap -> gatherTolerance = 1.0;
}
}
static int loadPreCompContrib (const LUENT *modContNode, void *p)
/* Load child contribution photon map for current contrib modifier entry */
{
MODCONT *modCont = (MODCONT*)modContNode -> data;
char *modName = (char*)modCont -> modname;
PhotonMap *preCompContribPmap;
const PhotonMap *parentPmap = (const PhotonMap*)p;
LUENT *preCompContribNode;
PreComputedContrib *preCompContrib;
char dirName [1024];
/* Allocate new LUT entry for child precomputed contribution pmap */
preCompContribNode = lu_find(parentPmap -> preCompContribTab, modName);
if (!preCompContribNode)
error(SYSTEM,
"out of memory allocating LUT entry in loadPreCompContrib()"
);
preCompContribNode -> key = modName;
/* Allocate child precomputed contribution photon map */
preCompContribNode -> data = (char*)(
preCompContribPmap = malloc(sizeof(PhotonMap))
);
if (!preCompContribPmap)
error(SYSTEM, "out of memory allocating precomputed contribution "
"photon map in loadPreCompContrib()"
);
/* Set subdirectory from parent photon map's filename */
snprintf(dirName, sizeof(dirName), PMAP_CONTRIB_DIR,
parentPmap -> fileName
);
/* Allocate & set child pmap's filename from subdir and modifier */
preCompContribPmap -> fileName = malloc(strlen(parentPmap -> fileName) +
strlen(modName) + strlen(PMAP_CONTRIB_FILE)
);
if (!preCompContribPmap -> fileName)
error(SYSTEM, "out of memory allocating filename in "
"loadPreCompContrib()"
);
snprintf(preCompContribPmap -> fileName, sizeof(dirName) + 4,
PMAP_CONTRIB_FILE, dirName, modName
);
/* Load child precomp contrib photon map for current modifier;
loadPhotonMap() will not recurse and call loadContribPhotonMap()
again because preCompContribPmap -> preCompContribTab == NULL */
loadPhotonMap(preCompContribPmap, preCompContribPmap -> fileName);
/* Pass parent pmap's lookup parameters on to child */
preCompContribPmap -> minGather = parentPmap -> minGather;
preCompContribPmap -> maxGather = parentPmap -> maxGather;
preCompContribPmap -> gatherTolerance = parentPmap -> gatherTolerance;
/* Override contrib/coeff mode if it doesn't match precomputed */
if (*pmapContribMode != preCompContribPmap -> contribMode) {
sprintf(errmsg,
"photon map contains precomputed %s, overriding rcontrib setting",
preCompContribPmap -> contribMode ? "contributions" : "coefficients"
);
error(WARNING, errmsg);
*pmapContribMode = preCompContribPmap -> contribMode;
}
/* NOTE: preCompContribPmap -> preCompContrib is initialised by
* loadPhotonMap() */
preCompContrib = (PreComputedContrib*)(
preCompContribPmap -> preCompContrib
);
/* Set bins per dimension and number of bins */
preCompContrib -> scDim = PMAP_CONTRIB_SCDIM(preCompContrib -> l);
preCompContrib -> nBins = preCompContrib -> scDim *
preCompContrib -> scDim;
/* Set wavelet coeff filename */
preCompContrib -> waveletFname = malloc(strlen(dirName) +
strlen(modName) + strlen(PMAP_CONTRIB_WAVELETFILE)
);
if (!preCompContribPmap -> fileName)
error(SYSTEM, "out of memory allocating filename in "
"loadPreCompContrib()"
);
snprintf(preCompContrib -> waveletFname, sizeof(dirName) + 5,
PMAP_CONTRIB_WAVELETFILE, dirName, modName
);
return 0;
}
void loadContribPhotonMap (PhotonMap *pmap, const char *fname)
/* Load contribution pmap and its per-modifier precomputed children */
{
LUTAB lutInit = LU_SINIT(NULL, freePreCompContribNode);
/* Allocate & init LUT containing per-modifier child contrib pmaps */
if (!(pmap -> preCompContribTab = malloc(sizeof(lutInit))))
error(SYSTEM, "out of memory allocating precomputed contribution "
"LUT in loadContribPhotonMap()"
);
memcpy(pmap -> preCompContribTab, &lutInit, sizeof(lutInit));
/* NOTE: filename already set in loadPhotonMap()
pmap -> fileName = (char*)fname; */
/* Init contrib photon map for light source contributions */
initPmapContrib(pmap);
/* Load child contribution photon maps for each modifier in contribTab */
lu_doall(pmapContribTab, loadPreCompContrib, pmap);
}
static int decodeContribs (PreComputedContrib *preCompContrib,
DCOLOR *binnedContribs
)
/* Decode and decompress mRGBE-encoded wavelet coefficients in
preCompContrib -> mrgbeCoeffs, apply inverse wavelet transform and return
decoded contributions in binnedContribs.
NOTE: The average wavelet coefficient is expected as input in the upper
left of the wavelet matrix (i.e. preCompContrib->waveletMatrix [0][0]).
Returns 0 on success, else -1. */
{
unsigned b, i, coeffBin;
mRGBERange *mrgbeRange;
mRGBE *mrgbeCoeffs;
DCOLOR fCoeff;
#ifdef PMAP_CONTRIB_DBG
unsigned x, y;
#endif
if (!binnedContribs || !preCompContrib || !binnedContribs ||
!preCompContrib -> mrgbeCoeffs
)
/* Should be initialised by caller! */
return -1;
mrgbeCoeffs = preCompContrib -> mrgbeCoeffs;
mrgbeRange = &preCompContrib -> mrgbeRange;
/* Init mRGBE coefficient normalisation from range */
if (!mRGBEinit(mrgbeRange, mrgbeRange -> min, mrgbeRange -> max))
return -1;
/* Zero all detail coeffs (omitting average at binnedContribs [0]) */
for (b = 1; b < preCompContrib -> nBins; b++) {
#ifdef PMAP_CONTRIB_ZEROJITTER
/* Add +/- jitter to zeros; this will replace the thresholded
* coefficients. The intent is to break up compression artefacts in
* the reconstructed contributions. In practice, it just adds high
* frequency noise and doesn't help at all! */
copycolor(binnedContribs [b], mrgbeRange -> min);
scalecolor(binnedContribs [b], (frandom() - 0.5));
#else
setcolor(binnedContribs [b], 0, 0, 0);
#endif
}
/* Decode mRGBE to wavelet detail coefficients, omitting average at
* binnedContrib [0].
* NOTE: The encoded bin preserves the offset for the omitted 1st
* coefficient (i.e. the average), so no shifting done on decoding. */
for (b = 0; b < preCompContrib -> nCompressedBins; b++) {
coeffBin = mRGBEdecode(mrgbeCoeffs [b], mrgbeRange, fCoeff);
#ifdef PMAP_CONTRIB_DBG
/* Check for invalid decoded bins */
if (coeffBin < 1 || coeffBin >= preCompContrib -> nBins)
error(CONSISTENCY, "bin out of range in decodeContribs()");
#endif
copycolor(binnedContribs [coeffBin], fCoeff);
}
/* Do inverse 2D wavelet transform on preCompContrib -> waveletMatrix
(which actually maps to binnedContribs) */
if (waveletInvXform2(preCompContrib -> waveletMatrix,
preCompContrib -> tWaveletMatrix, preCompContrib -> l
) < 0
)
error(INTERNAL,
"failed inverse wavelet transform in decodeContribs()"
);
#ifdef PMAP_CONTRIB_DBG
for (b = 0; b < preCompContrib -> nBins; b++) {
/* Warn if coefficient is negative; this _can_ happen due to loss of
* precision by the mRGBE encoding */
if (min(min(binnedContribs [b][0], binnedContribs [b][1]),
binnedContribs [b][2]
) < 0
)
error(WARNING, "negative contributions in getPreCompContrib()");
}
for (x = 0; x < preCompContrib -> scDim; x++) {
for (y = 0; y < preCompContrib -> scDim; y++)
/* Dump decoded contribs for debugging */
printf("% 7.3g\t",
colorAvg(preCompContrib -> waveletMatrix [x][y])
);
putchar('\n');
}
putchar('\n');
#endif
return 0;
}
static void getPreCompContribByPhoton (const Photon *photon,
OOC_DataIdx photonIdx, PreComputedContrib *preCompContrib,
DCOLOR *binnedContribs
)
/* Fetch and decode mRGBE-encoded wavelet coefficients for given photon
from wavelet file at offset photonIdx, perform inverse wavelet xform,
and return the reconstructed binned contributions in binnedContribs
(which is assumed to be variable). Returns 0 on success, else -1. */
/* XXX: HERE binnedContribs POINTS TO CACHE PAGE DATA! */
{
unsigned b;
COLR mrgbeRange32 [2];
COLOR fCoeff;
if (!binnedContribs || !preCompContrib)
/* Shouldn't happen */
error(INTERNAL,
"contributions not initialised in getPreCompContrib()"
);
if (preCompContrib -> nBins <= 1)
/* No binning, so nothing to decode */
return;
/* Lazily initialise preCompContrib */
if (!preCompContrib -> waveletFile) {
/* Lazily open wavelet coefficient file */
preCompContrib -> waveletFile = fopen(
preCompContrib -> waveletFname, "rb"
);
if (!preCompContrib -> waveletFile) {
sprintf(errmsg, "failed to open wavelet coefficient file %s "
"in getPreCompContrib()", preCompContrib -> waveletFname
);
error(SYSTEM, errmsg);
}
/* Set record size of encoded coeffs in wavelet file */
preCompContrib -> contribSize = PMAP_CONTRIB_ENCSIZE(
preCompContrib -> nCompressedBins
);
/* Lazily allocate buffer for mRGBE wavelet coeffs */
preCompContrib -> mrgbeCoeffs = calloc(
preCompContrib -> nCompressedBins, sizeof(mRGBE)
);
if (!preCompContrib -> mrgbeCoeffs)
error(SYSTEM, "out of memory allocating decoded coefficients "
"in getPreCompContrib()"
);
/* Lazily allocate primary wavelet matrix rows */
preCompContrib -> waveletMatrix = calloc(
preCompContrib -> scDim, sizeof(WaveletCoeff3*)
);
if (!preCompContrib -> waveletMatrix)
error(SYSTEM, "out of memory allocating primary wavelet "
"matrix in getPreCompContrib()"
);
/* Lazily allocate transposed wavelet matrix */
preCompContrib -> tWaveletMatrix =
allocWaveletMatrix2(preCompContrib -> l);
if (!preCompContrib -> tWaveletMatrix)
error(SYSTEM, "out of memory allocating transposed wavelet "
" matrix in getPreCompContrib()"
);
}
/* Seek to photon index in wavelet file and read mRGBE normalisation
and encoded bins */
if (fseek(preCompContrib -> waveletFile,
photonIdx * preCompContrib -> contribSize, SEEK_SET
) < 0
)
error(SYSTEM, "failed seek in wavelet coefficient file "
"in getPreCompContrib()"
);
/* Read only mRGBE maximum if 1 compressed bin (maximum compression),
* since minimum is implicitly zero and therefore omitted */
getbinary(mrgbeRange32, sizeof(COLR),
1 + (preCompContrib -> nCompressedBins > 1),
preCompContrib -> waveletFile
);
if (getbinary(preCompContrib -> mrgbeCoeffs, sizeof(mRGBE),
preCompContrib -> nCompressedBins, preCompContrib -> waveletFile
) != preCompContrib -> nCompressedBins
)
error(SYSTEM, "failed reading from wavelet coefficient file "
"in getPreCompContrib()"
);
/* Get mRGBE range (NOTE min/max are reversed in the coeff file) */
colr_color(fCoeff, mrgbeRange32 [0]);
copycolor(preCompContrib -> mrgbeRange.max, fCoeff);
if (preCompContrib -> nCompressedBins > 1) {
colr_color(fCoeff, mrgbeRange32 [1]);
copycolor(preCompContrib -> mrgbeRange.min, fCoeff);
}
else {
/* Single compressed bin; mRGBE minimum is implicitly zero */
setcolor(preCompContrib -> mrgbeRange.min, 0, 0, 0);
}
/* Map primary wavelet matrix rows to linear output array
binnedContribs; do this every time under the assumption that
binnedContribs is not static! */
for (b = 0; b < preCompContrib -> scDim; b++)
/* Point to each row in cache linear output array */
preCompContrib -> waveletMatrix [b] =
&binnedContribs [b * preCompContrib -> scDim];
/* Set average wavelet coefficient from photon flux; this coefficient
is expected by the inverse transform routine in upper left of the
wavelet matrix (i.e. preCompContrib -> waveletMatrix [0][0] =
binnedContribs [0]) */
getPhotonFlux((Photon*)photon, fCoeff);
copycolor(preCompContrib -> waveletMatrix [0][0], fCoeff);
/* All set, now decode mRGBE coeffs and invert wavelet transform */
if (decodeContribs(preCompContrib, binnedContribs))
error(INTERNAL, "failed contribution decoding/decompression "
"in getPreCompContrib()"
);
}
typedef struct {
const RAY *ray;
RREAL rayCoeff [3];
COLORV *totalContrib;
} PreCompContribRCData;
static int getPreCompContribByMod (const LUENT *preCompContribTabNode,
void *p
)
/* Fetch and decode precomputed contributions from closest photon in pmap
* referenced by preCompContribTabNode, and accumulate in pmapContribTab
* for the corresponding modifier */
{
PhotonMap *preCompContribPmap = (PhotonMap*)(
preCompContribTabNode -> data
);
PreComputedContrib *preCompContrib;
const char *modName = preCompContribTabNode -> key;
MODCONT *modCont;
PreCompContribRCData *rcData = (PreCompContribRCData*)p;
LUENT *contribTabNode;
Photon photon;
OOC_DataIdx photonIdx;
COLOR photonContrib;
unsigned b;
if (!preCompContribPmap || !rcData ||
!(preCompContrib = (PreComputedContrib*)(
preCompContribPmap -> preCompContrib
))
) {
sprintf(errmsg, "missing precomputed contributions for modifier %s",
modName
);
error(INTERNAL, errmsg);
}
/* Find rcontrib's LUT entry for this modifier */
contribTabNode = lu_find(pmapContribTab, modName);
if (!contribTabNode || !contribTabNode -> key ||
!(modCont = (MODCONT*)contribTabNode -> data)
) {
sprintf(errmsg, "missing contribution LUT entry for modifier %s",
modName
);
error(INTERNAL, errmsg);
}
/* Reallocate rcontrib bins if they don't match precomputed */
if (modCont -> nbins != preCompContrib -> nBins) {
contribTabNode -> data = realloc(modCont,
sizeof(MODCONT) + sizeof(DCOLOR) * (preCompContrib -> nBins - 1)
);
if (!contribTabNode -> data) {
sprintf(errmsg, "out of memory reallocating contribution bins "
"for modifier %s", modCont -> modname
);
error(SYSTEM, errmsg);
}
else {
sprintf(errmsg, "bin count mismatch for modifier %s, resizing",
modCont -> modname
);
error(WARNING, errmsg);
}
modCont = (MODCONT*)contribTabNode -> data;
modCont -> nbins = preCompContrib -> nBins;
memset(modCont -> cbin, 0, sizeof(DCOLOR) * modCont -> nbins);
}
/* Fetch closest photon and its contributions */
if (find1Photon(preCompContribPmap, rcData -> ray, &photon,
&photonIdx
)) {
/* XXX: TEMPORARY BUFFER FOR DECODED BINS, PENDING CONTRIB CACHE */
DCOLOR binnedContribs [preCompContrib -> nBins];
if (preCompContrib -> nBins > 1) {
/* BINNING ENABLED; fetch and decode bins for found photon */
getPreCompContribByPhoton(&photon, photonIdx, preCompContrib,
binnedContribs
);
}
else {
/* NO BINNING; get single contribution directly from photon */
getPhotonFlux(&photon, photonContrib);
copycolor(binnedContribs [0], photonContrib);
}
for (b = 0; b < preCompContrib -> nBins; b++) {
/* Accumulate binned contributions into rcontrib's LUT entry for
this modifier, weighted by ray coefficient. Also sum total
contributions. */
multcolor(binnedContribs [b], rcData -> rayCoeff);
addcolor(modCont -> cbin [b], binnedContribs [b]);
addcolor(rcData -> totalContrib, binnedContribs [b]);
}
}
return 0;
}
void getPreCompPhotonContrib (PhotonMap *pmap, RAY *ray,
COLOR totalContrib
)
/* Get precomputed light source contributions at ray -> rop for all
specified modifiers and accumulate them in pmapContribTab (which maps
to rcontrib's binning LUT). Also return total precomputed contribs. */
{
PreCompContribRCData rcData;
/* Ignore sources */
if (ray -> ro && islight(objptr(ray -> ro -> omod) -> otype))
return;
/* Get cumulative path coefficient up to photon lookup point */
raycontrib(rcData.rayCoeff, ray, PRIMARY);
setcolor(totalContrib, 0, 0, 0);
/* Rcontrib results passed to per-modifier child contrib pmaps */
rcData.ray = ray;
rcData.totalContrib = totalContrib;
/* Iterate over child contribution photon maps for each modifier and
* collect their contributions */
lu_doall(pmap -> preCompContribTab, getPreCompContribByMod, &rcData);
}
#endif /* PMAP_CONTRIB */

Event Timeline