diff --git a/pmapdump.c b/pmapdump.c index 15477c9..0bad469 100644 --- a/pmapdump.c +++ b/pmapdump.c @@ -1,545 +1,545 @@ #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 $ */ /* TODO: - UPDATE TO HANDLE PRECOMPUTED CONTRIBUTION PMAPS! - NORMALISATION OPTION! */ #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 /* 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" }, #ifdef PMAP_PHOTONFLOW { "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" }, #endif { "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}, #ifdef PMAP_PHOTONFLOW {0.1, 1, 0.1}, {1, 1, 1}, #endif {1, 1, 0.1} }; 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, radScale = RADSCALE, extent, dumpRatio, fluxNorm = 0, fluxMax = 0; unsigned arg, j, ptype, dim, fluxCol = 0, points = 0, normals = 0, aux = 0, normalise = 0; long numSpheres = NSPHERES, photonOffset; unsigned long photonCnt; COLOR col = {0, 0, 0}; FILE *pmapFile; PhotonMap pm; Photon p; #ifdef PMAP_OOC char leafFname [1024]; #endif #define BOOLOPTSTR(v) ((v) ? "+" : "-") void printdefaults () { 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 (if -a+)\n", + 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 (if -a+)\n", + printf("-N%s\t\t\t\t# %sdump normals (with -a+)\n", BOOLOPTSTR(normals), normals ? "" : "don't " ); - printf("-O %.3f\t\t\t# photon flux normalisation factor (if -f+)\n", - fluxNorm + 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 (if -a-)\n", radScale); + printf("-r %.3f\t\t\t# sphere radius scale (with -a-)\n", radScale); } if (argc < 2) { puts("Dump photon maps as RADIANCE scene description " "or ASCII point list\n" ); printf("Usage:\n" - "%s [-a [-N][-A|-P]] [-r radscale1] [-n num1] " + "%s [-a [-N][-A]] [-r radscale1] [-n num1] " "[-f [-O norm1] | -c r1 g1 b1] pmap1\n" - "\t[-a [-N][-A|-P]] [-r radscale2] [-n num2] " + "\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); /* 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; }