Page MenuHomec4science

pmapdump.c
No OneTemporary

File Metadata

Created
Sun, May 12, 04:42

pmapdump.c

#ifndef lint
static const char RCSid[] = "$Id: pmapdump.c,v 2.18 2021/02/18 17:08:50 rschregle Exp $";
#endif
/*
======================================================================
Dump photon maps as RADIANCE scene description or ASCII point list
to stdout
Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
(c) Fraunhofer Institute for Solar Energy Systems,
supported by the German Research Foundation
(DFG LU-204/10-2, "Fassadenintegrierte Regelsysteme FARESYS")
(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")
(c) Tokyo University of Science,
supported by the JSPS Grants-in-Aid for Scientific Research
(KAKENHI JP19KK0115, "Three-Dimensional Light Flow")
======================================================================
$Id: pmapdump.c,v 2.18 2021/02/18 17:08:50 rschregle Exp $
*/
#include "pmap.h"
#include "pmapio.h"
#include "rtio.h"
#include "resolu.h"
#include "random.h"
#include "math.h"
/* Defaults */
/* Sphere radius as fraction of avg. intersphere dist */
/* Relative scale for sphere radius (fudge factor) */
/* Number of spheres */
#define RADCOEFF 0.05
#define RADSCALE 1.0
#define NSPHERES 10000
/* Formats for ASCII output as XYZ RGB points plus optional normals
(incident directions for volume photons) and path IDs */
#define POINTFMT "%g\t%g\t%g\t%g\t%g\t%g"
#define NORMFMT "\t%f\t%f\t%f"
#define PATHFMT "\t%u:%lu"
#define SRCFMT "\t%u"
#define TIMEFMT "\t%g"
/* Seed for random number generator; this determines the sequence of
* photons extracted. TODO: Make accessible as option??? */
#define RANDSEED 0
#define BOOLOPTSTR(v) ((v) ? "+" : "-")
/* RADIANCE material and object defs for each photon type */
typedef struct {
char *mat, *obj;
} RadianceDef;
extern char VersionID [];
const RadianceDef radDefs [] = {
{ "void glow mat.global\n0\n0\n4 %g %g %g 0\n",
"mat.global sphere obj.global\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.pglobal\n0\n0\n4 %g %g %g 0\n",
"mat.pglobal sphere obj.pglobal\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.caustic\n0\n0\n4 %g %g %g 0\n",
"mat.caustic sphere obj.caustic\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.volume\n0\n0\n4 %g %g %g 0\n",
"mat.volume sphere obj.volume\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.direct\n0\n0\n4 %g %g %g 0\n",
"mat.direct sphere obj.direct\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.contrib\n0\n0\n4 %g %g %g 0\n",
"mat.contrib sphere obj.contrib\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.transient\n0\n0\n4 %g %g %g 0\n",
"mat.transient sphere obj.transient\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.lightflow\n0\n0\n4 %g %g %g 0\n",
"mat.lightflow sphere obj.lightflow\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.tlightflow\n0\n0\n4 %g %g %g 0\n",
"mat.tlightflow sphere obj.tlightflow\n0\n0\n4 %g %g %g %g\n"
},
{ "void glow mat.contrib\n0\n0\n4 %g %g %g 0\n",
"mat.contrib sphere obj.contrib\n0\n0\n4 %g %g %g %g\n"
}
};
/* Default colour codes are as follows: global = blue
precomp global = cyan
caustic = red
transient = white
volume/lightflow = green
direct = magenta
contrib = yellow */
const COLOR colDefs [] = {
{0.25, 0.25, 2}, {0.1, 1, 1}, {1, 0.1, 0.1},
{0.1, 1, 0.1}, {1, 0.1, 1}, {1, 1, 0.1}, {1, 1, 1},
{0.1, 1, 0.1}, {1, 1, 1},
{1, 1, 0.1}
};
/* Option defaults */
unsigned fluxCol = 0, points = 0, normals = 0, aux = 0, normalise = 0;
long numSpheres = NSPHERES;
RREAL fluxNorm = 0, radScale = RADSCALE;
void printdefaults ()
{
unsigned ptype;
printf("-a%s\t\t\t\t# dump %s\n", BOOLOPTSTR(points),
points ? "ASCII point list" : "RADIANCE scene description"
);
printf("-A%s\t\t\t\t# %sdump auxiliary data (with -a+)\n",
BOOLOPTSTR(aux), aux ? "" : "don't "
);
for (ptype = 0; ptype < NUM_PMAP_TYPES; ptype++)
printf("-c %.3f %.3f %.3f\t\t# %s photon colour\n",
colDefs [ptype][0], colDefs [ptype][1], colDefs [ptype][2],
pmapName [ptype]
);
printf("-f%s\t\t\t\t# %sdump photon flux\n",
BOOLOPTSTR(fluxCol), fluxCol ? "" : "don't "
);
printf("-n %lu\t\t\t# number of points/spheres\n", numSpheres);
printf("-N%s\t\t\t\t# %sdump normals (with -a+)\n",
BOOLOPTSTR(normals), normals ? "" : "don't "
);
printf("-O %.3f\t\t\t# %snormalise photon flux (with -f+)\n",
fluxNorm, fluxNorm > FTINY ? " " : "don't "
);
printf("-P%s\t\t\t\t# %sdump photon path IDs (same as -A%s)\n",
BOOLOPTSTR(aux), aux ? "" : "don't ", BOOLOPTSTR(aux)
);
printf("-r %.3f\t\t\t# sphere radius scale (with -a-)\n", radScale);
}
static int setBool(char *str, unsigned pos, unsigned *var)
{
switch ((str) [pos]) {
case '\0':
*var = !*var;
break;
case 'y': case 'Y': case 't': case 'T': case '+': case '1':
*var = 1;
break;
case 'n': case 'N': case 'f': case 'F': case '-': case '0':
*var = 0;
break;
default:
return 0;
}
return 1;
}
int main (int argc, char** argv)
{
char format [MAXFMTLEN];
RREAL rad, extent, dumpRatio, fluxMax = 0;
unsigned arg, j, ptype, dim;
long photonOffset;
unsigned long photonCnt;
COLOR col = {0, 0, 0};
FILE *pmapFile;
PhotonMap pm;
Photon p;
#ifdef PMAP_OOC
char leafFname [1024];
#endif
#ifdef PMAP_CONTRIB
PreComputedContrib pcContrib;
#endif
if (argc < 2) {
puts("Dump photon maps as RADIANCE scene description "
"or ASCII point list\n"
);
printf("Usage:\n"
"%s [-a [-N][-A]] [-r radscale1] [-n num1] "
"[-f [-O norm1] | -c r1 g1 b1] pmap1\n"
"\t[-a [-N][-A]] [-r radscale2] [-n num2] "
"[-f [-O norm2] | -c r2 g2 b2] pmap2 ...\n",
argv [0]
);
return 1;
}
/* Parse options */
for (arg = 1; arg < argc; arg++) {
if (!strcmp(argv [arg], "-version")) {
puts(VersionID);
quit(0);
}
if (!strcmp(argv [arg], "-defaults") ||
!strcmp(argv [arg], "-help")
) {
printdefaults();
quit(0);
}
if (argv [arg][0] == '-') {
switch (argv [arg][1]) {
case 'a':
if (!setBool(argv [arg], 2, &points))
error(USER, "invalid option syntax at -a");
break;
case 'A': /* Dump type-specific auxiliary data */
case 'P': /* Dump paths; kept for backward compatibility */
if (!setBool(argv [arg], 2, &aux))
error(USER, "invalid option syntax at -A / -P");
break;
case 'c':
if (fluxCol)
error(USER, "-f and -c are mutually exclusive");
if (badarg(argc - arg - 1, &argv [arg + 1], "fff"))
error(USER, "invalid RGB colour");
for (j = 0; j < 3; j++)
col [j] = atof(argv [++arg]);
break;
case 'f':
if (intens(col) > 0)
error(USER, "-f and -c are mutually exclusive");
if (!setBool(argv [arg], 2, &fluxCol))
error(USER, "invalid option syntax at -f");
break;
case 'n':
if ((numSpheres = parseMultiplier(argv [++arg])) <= 0)
error(USER, "invalid number of points/spheres");
break;
case 'N':
if (!setBool(argv [arg], 2, &normals))
error(USER, "invalid option syntax at -N");
break;
case 'O':
if ((fluxNorm = atof(argv [++arg])) <= FTINY)
error(USER, "invalid normalisation factor");
break;
case 'r':
if ((radScale = atof(argv [++arg])) <= FTINY)
error(USER, "invalid radius scale");
break;
default:
sprintf(errmsg, "unknown option %s", argv [arg]);
error(USER, errmsg);
return -1;
}
continue;
}
/* Check for compatible options */
if ((normals || aux) && !points)
error(USER, "options -A, -N, -P require -a+");
if (fluxNorm > FTINY && !fluxCol)
error(USER, "photon flux normalisation requires -f+");
/* Open next photon map file */
if (!(pmapFile = fopen(argv [arg], "rb"))) {
sprintf(errmsg, "can't open %s", argv [arg]);
error(SYSTEM, errmsg);
}
/* Get format string */
strcpy(format, PMAP_FORMAT_GLOB);
if (checkheader(pmapFile, format, NULL) != 1) {
sprintf(errmsg, "photon map file %s has an unknown format",
argv [arg]
);
error(USER, errmsg);
}
/* Identify photon map type from format string */
for (ptype = 0;
ptype < NUM_PMAP_TYPES && strcmp(pmapFormat [ptype], format);
ptype++
);
if (!validPmapType(ptype)) {
sprintf(errmsg, "file %s contains an unknown photon map type",
argv [arg]
);
error(USER, errmsg);
}
/* Get file format version and check for compatibility */
if (strcmp(getstr(format, pmapFile), PMAP_FILEVER))
error(USER, "incompatible photon map file format");
if (!points) {
/* Dump command line as comment */
fputs("# ", stdout);
printargs(argc, argv, stdout);
fputc('\n', stdout);
}
/* Set point/sphere colour if independent of photon flux,
output RADIANCE material def if required */
if (!fluxCol) {
if (intens(col) <= 0)
copycolor(col, colDefs [ptype]);
if (!points) {
printf(radDefs [ptype].mat, col [0], col [1], col [2]);
fputc('\n', stdout);
}
}
/* Get number of photons as fixed size, which possibly results in
* padding of MSB with 0 on some platforms. Unlike sizeof() however,
* this ensures portability since this value may span 32 or 64 bits
* depending on platform. */
pm.numPhotons = getint(PMAP_LONGSIZE, pmapFile);
/* Skip avg photon flux */
for (j = 0; j < 3; j++)
getflt(pmapFile);
/* Get distribution extent (min & max photon positions) */
for (j = 0; j < 3; j++) {
pm.minPos [j] = getflt(pmapFile);
pm.maxPos [j] = getflt(pmapFile);
}
/* Skip centre of gravity, and avg photon dist to it */
for (j = 0; j < 4; j++)
getflt(pmapFile);
/* Get photon velocity, skip min/max/avg path length */
pm.velocity = getflt(pmapFile);
for (j = 0; j < 3; j++)
getflt(pmapFile);
#ifdef PMAP_CONTRIB
if (ptype == PMAP_TYPE_CONTRIB_CHILD) {
/* Precomputed contribution photon map; skip mode and num bins */
getint(sizeof(pmap -> contribMode), pmapFile);
getint(sizeof(pcContrib.l), pmapFile);
getint(sizeof(pcContrib.nCompressedBins), pmapFile);
}
#endif
/* Sphere radius based on avg intersphere dist depending on
whether the distribution occupies a 1D line (!), a 2D plane,
or 3D volume (= sphere distrib density ^-1/d, where d is the
dimensionality of the distribution) */
for (j = 0, extent = 1.0, dim = 0; j < 3; j++) {
rad = pm.maxPos [j] - pm.minPos [j];
if (rad > FTINY) {
dim++;
extent *= rad;
}
}
rad = radScale * RADCOEFF * pow(extent / numSpheres, 1./dim);
/* Photon dump probability to satisfy target sphere count */
dumpRatio = min(1, (float)numSpheres / pm.numPhotons);
#ifdef PMAP_OOC
/* Open leaf file with filename derived from pmap, replace pmapFile
* (which is currently the node file) */
strncpy(leafFname, argv [arg], sizeof(leafFname) - 1);
strncat(leafFname, PMAP_OOC_LEAFSUFFIX, sizeof(leafFname) - 1);
fclose(pmapFile);
if (!(pmapFile = fopen(leafFname, "rb"))) {
sprintf(errmsg, "cannot open leaf file %s", leafFname);
error(SYSTEM, errmsg);
}
#endif
/* Remember start of photons in file (if we need to reiterate with
* normalisation) */
photonOffset = ftell(pmapFile);
/* Set flux normalisation flag for reiteration in loop below */
normalise = fluxNorm > FTINY;
do {
/* Set random number seed to obtain identical sequence of
* photons if reiterating with normalisation */
srandom(RANDSEED);
/* Reset file pointer to start of photons (if reiterating with
* normalisation) */
if (fseek(pmapFile, photonOffset, SEEK_SET) < 0)
error(SYSTEM, "failed seek in photon map file");
/* Read photons */
for (photonCnt = pm.numPhotons; photonCnt; photonCnt--) {
#ifdef PMAP_OOC
/* Get entire photon record from ooC octree leaf file
!!! OOC PMAP FILES CURRENTLY DON'T USE PORTABLE I/O !!! */
if (!fread(&p, sizeof(p), 1, pmapFile)) {
sprintf(errmsg, "error reading OOC leaf file %s", leafFname);
error(SYSTEM, errmsg);
}
#else /* kd-tree */
/* Get photon position */
for (j = 0; j < 3; j++)
p.pos [j] = getflt(pmapFile);
/* Get photon normal (dumped with -N) */
for (j = 0; j < 3; j++)
p.norm [j] = getint(1, pmapFile);
/* Get photon flux */
#ifdef PMAP_FLOAT_FLUX
for (j = 0; j < 3; j++)
p.flux [j] = getflt(pmapFile);
#else
for (j = 0; j < 4; j++)
p.flux [j] = getint(1, pmapFile);
#endif
/* Get photon's auxiliary field (dumped with -A/-P) */
getbinary(&p.aux, 1, sizeof(p.aux), pmapFile);
/* Get photon's flags (includes subprocess prefix for path ID */
p.flags = getint(sizeof(p.flags), pmapFile);
#endif
/* Dump photon probabilistically acc. to target sphere count */
if (frandom() <= dumpRatio) {
if (fluxCol) {
/* Get photon flux */
getPhotonFlux(&p, col);
/* Scale by dumpRatio for energy conservation */
scalecolor(col, 1.0 / dumpRatio);
}
if (!normalise) {
if (fluxNorm > FTINY) {
/* Normalise photon flux (with maximum flux found in
* previous iteration) and output */
scalecolor(col,
fluxMax > FTINY ? fluxNorm / fluxMax : 0
);
}
if (!points) {
if (fluxCol) {
/* Dump material def if variable (depends on flux) */
printf(radDefs [ptype].mat, col [0], col [1], col [2]);
fputc('\n', stdout);
}
printf(radDefs [ptype].obj,
p.pos [0], p.pos [1], p.pos [2], rad
);
fputc('\n', stdout);
}
else {
/* Dump as XYZ RGB point */
printf(POINTFMT, p.pos [0], p.pos [1], p.pos [2],
col [0], col [1] ,col [2]
);
if (normals)
printf(NORMFMT, p.norm [0] / 127.,
p.norm [1] / 127., p.norm [2] / 127.
);
if (aux)
/* Interpret auxiliary data depending on photon type */
switch (ptype) {
case PMAP_TYPE_TRANSIENT:
#ifdef PMAP_PHOTONFLOW
case PMAP_TYPE_TRANSLIGHTFLOW:
#endif
/* Aux field contains path length; calculate
photon's time of flight */
printf(TIMEFMT, p.aux.pathLen / pm.velocity);
break;
case PMAP_TYPE_CONTRIB:
/* Aux field contains photon's emitting light
source index */
printf(SRCFMT, p.aux.contribSrc);
break;
default:
/* Aux field contains photon's path ID; prefix with
photon's process index for uniqueness */
printf(PATHFMT, p.proc, (unsigned long)p.aux.pathID);
}
fputc('\n', stdout);
}
}
else {
/* normalise == 1; find max flux for normalisation in next
iteration */
if (intens(col) > fluxMax)
fluxMax = intens(col);
}
}
if (ferror(pmapFile) || feof(pmapFile)) {
sprintf(errmsg, "error reading %s", argv [arg]);
error(USER, errmsg);
}
}
} while (!(normalise ^= 1));
fclose(pmapFile);
/* Reset defaults for next dump */
radScale = RADSCALE;
numSpheres = NSPHERES;
col [0] = col [1] = col [2] = fluxNorm = fluxMax = 0.;
fluxCol = points = aux = 0;
}
return 0;
}

Event Timeline