diff --git a/mkpmap.c b/mkpmap.c index 3150e87..70cc6ce 100644 --- a/mkpmap.c +++ b/mkpmap.c @@ -1,837 +1,864 @@ #ifndef lint static const char RCSid[] = "$Id: mkpmap.c,v 2.10 2020/08/07 01:21:13 rschregle Exp $"; #endif /* ========================================================================= Photon map generator 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: mkpmap.c,v 2.10 2020/08/07 01:21:13 rschregle Exp $ */ #include "pmap.h" #include "pmapmat.h" #include "pmapsrc.h" #include "pmapcontrib.h" #include "pmaprand.h" #include "paths.h" #include "ambient.h" #include "resolu.h" #include "source.h" #include #include #include /* Enable options for Ze Ekspertz only! */ #define PMAP_EKSPERTZ extern char VersionID []; char* progname; /* argv[0] */ int dimlist [MAXDIM]; /* sampling dimensions */ int ndims = 0; /* number of sampling dimensions */ char* octname = NULL; /* octree name */ CUBE thescene; /* scene top-level octree */ OBJECT nsceneobjs; /* number of objects in scene */ double srcsizerat = 0.01; /* source partition size ratio */ int backvis = 1; /* back face visibility */ int clobber = 0; /* overwrite output */ COLOR cextinction = BLKCOLOR; /* global extinction coefficient */ COLOR salbedo = BLKCOLOR; /* global scattering albedo */ double seccg = 0; /* global scattering eccentricity */ char *amblist [AMBLLEN + 1]; /* ambient include/exclude list */ int ambincl = -1; /* include == 1, exclude == 0 */ char *diagFile = NULL; /* diagnostics output file */ int rand_samp = 1; /* uncorrelated random sampling */ int nproc = 1; /* number of parallel processes */ #ifdef EVALDRC_HACK char *angsrcfile = NULL; /* angular source file for EvalDRC */ #endif /* Contribution photon map stuff: light source modifier lookup table and associated cleanup func */ static void chickenMcFreeNuggetz(void *contrib) { epfree((*(MODCONT *)contrib).binv); free(contrib); } LUTAB srcContrib = LU_SINIT(NULL, chickenMcFreeNuggetz); #if 0 char srcMod [MAXMODLIST]; #endif unsigned numSrcContrib = 0; /* The variables below interface with the RADIANCE suite merely as dummies to resolve external references and make the linker happy; most of them are unused. */ COLOR ambval = BLKCOLOR; double shadthresh = .05, ambacc = 0.2, shadcert = .5, minweight = 5e-3, ssampdist = 0, dstrsrc = 0.0, specthresh = 0.15, specjitter = 1.0, avgrefl = 0.5; int ambvwt = 0, ambssamp = 0, ambres = 32, ambounce = 0, directrelay = 1, directvis = 1, samplendx, do_irrad = 0, ambdiv = 128, vspretest = 512, maxdepth = 6; char *shm_boundary = NULL, *ambfile = NULL; char RCCONTEXT [] = "RC."; /* Evaluation context for contrib photon map */ void (*trace)() = NULL, (*addobjnotify [])() = {ambnotify, NULL}; void printdefaults() /* print default values to stdout */ { #ifdef EVALDRC_HACK /* EvalDRC support */ puts("-A\t\t\t\t# angular source file"); #endif puts("-ae mod\t\t\t\t# exclude modifier"); puts("-aE file\t\t\t\t# exclude modifiers from file"); puts("-ai mod\t\t\t\t# include mo difier"); puts("-aI file\t\t\t\t# include modifiers from file"); #ifdef PMAP_EKSPERTZ - puts("-api xmin ymin zmin xmax ymax zmax\t# region of interest"); + puts("-api xmin ymin zmin xmax ymax zmax\t# rectangular region of interest"); + puts("-apI xpos ypos zpos radius\t\t# spherical region of interest"); #endif puts("-apg file nPhotons\t\t\t# global photon map"); puts("-apc file nPhotons\t\t\t# caustic photon map"); puts("-apd file nPhotons\t\t\t# direct photon map"); puts("-app file nPhotons bwidth\t\t# precomputed global photon map"); puts("-apv file nPhotons\t\t\t# volume photon map"); puts("-apC file nPhotons\t\t\t# contribution photon map"); printf("-apD %f\t\t\t\t# predistribution factor\n", preDistrib); printf("-apM %d\t\t\t\t\t# max predistrib passes\n", maxPreDistrib); #if 1 /* Kept for backwards compat, will be gradually phased out by -ld, -lr */ printf("-apm %ld\t\t\t\t# limit photon bounces\n", photonMaxBounce); #endif puts("-apo+ mod\t\t\t\t# photon port modifier"); puts("-apO+ file\t\t\t\t# photon ports from file"); printf("-apP %f\t\t\t\t# precomputation factor\n", finalGather); printf("-apr %d\t\t\t\t\t# random seed\n", randSeed); puts("-aps mod\t\t\t\t# antimatter sensor modifier"); puts("-apS file\t\t\t\t# antimatter sensors from file"); printf(backvis ? "-bv+\t\t\t\t\t# back face visibility on\n": "-bv-\t\t\t\t\t# back face visibility off\n" ); printf("-dp %.1f\t\t\t\t# PDF samples / sr\n", pdfSamples); printf("-ds %f\t\t\t\t# source partition size ratio\n", srcsizerat); printf("-ef %s\t\t\t\t# diagnostics output file\n", diagFile); printf(clobber ? "-fo+\t\t\t\t\t# force overwrite\n" : "-fo-\t\t\t\t\t# do not overwrite\n" ); #ifdef PMAP_EKSPERTZ /* (Formerly) NU STUFF for Ze Exspertz! */ printf("-ld %.1f\t\t\t\t\t# limit photon distance\n", photonMaxDist); printf("-lr %ld\t\t\t\t# limit photon bounces\n", photonMaxBounce); #endif printf( "-ma %.2f %.2f %.2f\t\t\t# scattering albedo\n", colval(salbedo,RED), colval(salbedo,GRN), colval(salbedo,BLU) ); printf( "-me %.2e %.2e %.2e\t\t# extinction coefficient\n", colval(cextinction,RED), colval(cextinction,GRN), colval(cextinction,BLU) ); printf("-mg %.2f\t\t\t\t# scattering eccentricity\n", seccg); #if NIX /* Multiprocessing on NIX only; so tuff luck, Windoze Weenies! */ printf("-n %d\t\t\t\t\t# number of parallel processes\n", nproc); #endif printf("-t %-9d\t\t\t\t# time between reports\n", photonRepTime); printf(verbose ? "-v+\t\t\t\t\t# verbose console output\n" : "-v-\t\t\t\t\t# terse console output\n" ); #if 0 printf( "-V%c\t\t\t\t# precompute %s\n", contrib ? '+' : '-', contrib ? "contributions" : "coefficients" ); #endif } #if 0 /* Disabled for now; will attempt to extract bins from photon map header */ static char *appendParams (char *accParams, char **params, int n) /* Accumulate n next parameter strings from params to string accParams. Returns new accmulated string. */ { int i; if (params && n) for (i = 0; i < n; i++) if (params [i] && strlen(params [i])) /* Reallocate and factor in delimiter (space) and terminator */ if (accParams = realloc( accParams, strlen(accParams) + strlen(params [i]) + 2 )) { strcat(accParams, " "); strcat(accParams, params [i]); } else error( SYSTEM, "cannot append binning parameters for contrib photon map" ); return accParams; } #endif int main (int argc, char* argv []) { #define check(ol, al) if ( \ argv [i][ol] || badarg(argc - i - 1,argv + i + 1, al) \ ) goto badopt /* Evaluate boolean option, setting var accordingly */ #define check_bool(olen, var) switch (argv [i][olen]) { \ 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: \ goto badopt; \ } /* Evaluate trinary option, setting bits v1 and v2 in var accordingly */ #define check_tri(olen, v1, v2, var) switch (argv [i][olen]) { \ case '\0': case '+': \ var = v1; break; \ case '-': \ var = v2; break;\ case '0': \ var = v1 | v2; break; \ default: \ goto badopt; \ } /* Check target number of photons with optional multiplier suffix begins with a digit */ #define checkNumPhotons(i) if (!isdigit(argv [i][0])) \ if (argv [i][0] == '-') \ goto badopt; \ else { \ sprintf(errmsg, "invalid number of photons %s", argv [i]); \ error(USER, errmsg); \ } int loadflags = IO_CHECK | IO_SCENE | IO_TREE | IO_BOUNDS, rval, i, j, n, binCnt = 0; char **portLp = photonPortList, **sensLp = photonSensorList, **amblp = NULL, sbuf [MAXSTR], portFlags [2] = "\0\0", *binVal = NULL, *binParm = NULL; struct stat pmstat; /* Global program name */ progname = fixargv0(argv [0]); /* Initialize object types */ initotypes(); /* Initialize cal function routines for contrib photon binning */ initfunc(); setcontext(RCCONTEXT); #if defined(_WIN32) || defined(_WIN64) /* Increase file limit to maximum */ /* XXX: DO WE NEED THIS FOR CONTRIB PHOTONS? _setmaxstdio(2048); */ #endif /* Parse options */ for (i = 1; i < argc; i++) { /* Eggs-pand arguments */ while ((rval = expandarg(&argc, &argv, i))) if (rval < 0) { sprintf(errmsg, "cannot eggs-pand '%s'", argv [i]); error(SYSTEM, errmsg); } if (argv[i] == NULL) break; if (!strcmp(argv [i], "-version")) { puts(VersionID); quit(0); } if (!strcmp(argv [i], "-defaults") || !strcmp(argv [i], "-help")) { printdefaults(); quit(0); } /* Get octree */ if (i == argc - 1) { octname = argv [i]; break; } switch (argv [i][1]) { case 'a': /* Ambient */ switch (argv [i][2]) { case 'i': /* Ambient include */ case 'I': check(3, "s"); if (ambincl != 1) { ambincl = 1; amblp = amblist; } if (argv [i][2] == 'I') { /* Add modifiers from file */ rval = wordfile( amblp, AMBLLEN - (amblp - amblist), getpath(argv [++i], getrlibpath(), R_OK) ); if (rval < 0) { sprintf( errmsg, "cannot open ambient include file \"%s\"", argv [i] ); error(SYSTEM, errmsg); } amblp += rval; } else { /* Add modifier from next arg */ *amblp++ = savqstr(argv [++i]); *amblp = NULL; } break; case 'e': /* Ambient exclude */ case 'E': check(3, "s"); if (ambincl != 0) { ambincl = 0; amblp = amblist; } if (argv [i][2] == 'E') { /* Add modifiers from file */ rval = wordfile( amblp, AMBLLEN - (amblp - amblist), getpath(argv [++i], getrlibpath(), R_OK) ); if (rval < 0) { sprintf( errmsg, "cannot open ambient exclude file \"%s\"", argv [i] ); error(SYSTEM, errmsg); } amblp += rval; } else { /* Add modifier from next arg */ *amblp++ = savqstr(argv [++i]); *amblp = NULL; } break; case 'p': /* Pmap-specific */ switch (argv [i][3]) { case 'g': /* Global photon map */ checkNumPhotons(i + 2); check(4, "ss"); globalPmapParams.fileName = argv [++i]; globalPmapParams.distribTarget = parseMultiplier(argv [++i]); if (!globalPmapParams.distribTarget) goto badopt; globalPmapParams.minGather = globalPmapParams.maxGather = 0; break; case 'p': /* Precomputed global photon map */ checkNumPhotons(i + 2); check(4, "ssi"); preCompPmapParams.fileName = argv [++i]; preCompPmapParams.distribTarget = parseMultiplier(argv [++i]); if (!preCompPmapParams.distribTarget) goto badopt; preCompPmapParams.minGather = preCompPmapParams.maxGather = atoi(argv [++i]); if (!preCompPmapParams.maxGather) goto badopt; break; case 'c': /* Caustic photon map */ checkNumPhotons(i + 2); check(4, "ss"); causticPmapParams.fileName = argv [++i]; causticPmapParams.distribTarget = parseMultiplier(argv [++i]); if (!causticPmapParams.distribTarget) goto badopt; break; case 'v': /* Volume photon map */ checkNumPhotons(i + 2); check(4, "ss"); volumePmapParams.fileName = argv [++i]; volumePmapParams.distribTarget = parseMultiplier(argv [++i]); if (!volumePmapParams.distribTarget) goto badopt; break; case 'd': /* Direct photon map */ checkNumPhotons(i + 2); check(4, "ss"); directPmapParams.fileName = argv [++i]; directPmapParams.distribTarget = parseMultiplier(argv [++i]); if (!directPmapParams.distribTarget) goto badopt; break; case 'C': /* Precomputed contribution photon map */ checkNumPhotons(i + 2); check(4, "ssi"); contribPmapParams.fileName = argv [++i]; contribPmapParams.distribTarget = parseMultiplier(argv [++i]); if (!contribPmapParams.distribTarget) goto badopt; contribPmapParams.minGather = contribPmapParams.maxGather = atoi(argv [++i]); if (!contribPmapParams.maxGather) goto badopt; break; case 'D': /* Predistribution factor */ check(4, "f"); preDistrib = atof(argv [++i]); if (preDistrib <= 0) error(USER, "predistrib factor must be > 0"); break; case 'M': /* Max predistribution passes */ check(4, "i"); maxPreDistrib = atoi(argv [++i]); if (maxPreDistrib <= 0) error(USER, "max predistrib passes must be > 0"); break; #if 1 /* Kept for backwards compat, to be phased out by -lr */ case 'm': /* Max photon bounces */ check(4, "i"); photonMaxBounce = atol(argv [++i]); if (photonMaxBounce <= 0) error(USER, "max photon bounces must be > 0"); break; #endif #ifdef PMAP_EKSPERTZ - case 'i': /* Add region of interest */ - check(4, "ffffff"); + case 'i': /* Add rectangular region of interest */ + case 'I': /* Add spherical region of interest */ + check(4, isupper(argv [j=i][3]) ? "ffff" : "ffffff"); n = pmapNumROI; + pmapROI = realloc( pmapROI, ++pmapNumROI * sizeof(PhotonMapROI) ); if (!pmapROI) error(SYSTEM, "failed to allocate ROI"); - pmapROI [n].min [0] = atof(argv [++i]); - pmapROI [n].min [1] = atof(argv [++i]); - pmapROI [n].min [2] = atof(argv [++i]); - pmapROI [n].max [0] = atof(argv [++i]); - pmapROI [n].max [1] = atof(argv [++i]); - pmapROI [n].max [2] = atof(argv [++i]); - for (j = 0; j < 3; j++) - if (pmapROI [n].min [j] >= pmapROI [n].max [j]) + + pmapROI [n].pos [0] = atof(argv [++i]); + pmapROI [n].pos [1] = atof(argv [++i]); + pmapROI [n].pos [2] = atof(argv [++i]); + pmapROI [n].siz [0] = atof(argv [++i]); + + if (isupper(argv [j][3])) { + /* Spherical ROI; radius^2 */ + pmapROI [n].siz [0] *= pmapROI [n].siz [0]; + PMAP_ROI_SETSPHERE(pmapROI + n); + if (pmapROI [n].siz [0] <= FTINY) error( - USER, "invalid region of interest " - "(swapped min/max?)" + USER, + "region of interest has invalid radius" + ); + } + else { + /* Rectangular ROI */ + pmapROI [n].siz [1] = atof(argv [++i]); + pmapROI [n].siz [2] = atof(argv [++i]); + + for (j = 0; j < 3; j++) { + /* Pos at rectangle centre, siz symmetric */ + pmapROI [n].pos [j] = 0.5 * ( + pmapROI [n].pos [j] + pmapROI [n].siz [j] + ); + pmapROI [n].siz [j] = fabs( + pmapROI [n].siz [j] - pmapROI [n].pos [j] ); + if (pmapROI [n].siz [j] <= FTINY) + error( + USER, + "region of interest has invalid size" + ); + } + } break; #endif case 'P': /* Global photon precomp ratio */ check(4, "f"); finalGather = atof(argv [++i]); if (finalGather <= 0 || finalGather > 1) error( USER, "global photon precomputation ratio " "must be in range ]0, 1]" ); break; case 'o': /* Photon port */ case 'O': /* Check for bad arg and length, taking into account * default forward orientation if none specified, in * order to maintain previous behaviour */ check(argv [i][4] ? 5 : 4, "s"); /* Get port orientation flags */ check_tri( 4, PMAP_PORTFWD, PMAP_PORTBWD, portFlags [0] ); if (argv [i][3] == 'O') { /* Add port modifiers from file */ rval = wordfile( portLp, MAXSET - (portLp - photonPortList), getpath(argv [++i], getrlibpath(), R_OK) ); if (rval < 0) { sprintf( errmsg, "cannot open photon port file %s", argv [i] ); error(SYSTEM, errmsg); } /* HACK: append port orientation flags to every * modifier; note this requires reallocation */ for (; rval--; portLp++) { j = strlen(*portLp); if (!(*portLp = realloc(*portLp, j + 2))) { sprintf( errmsg, "cannot allocate photon port modifiers" " from file %s", argv [i] ); error(SYSTEM, errmsg); } strcat(*portLp, portFlags); } } else { /* Append port flags to port modifier, add to * port list and mark of end list with NULL */ strcpy(sbuf, argv [++i]); strcat(sbuf, portFlags); *portLp++ = savqstr(sbuf); *portLp = NULL; } break; case 'r': /* Random seed */ check(4, "i"); randSeed = atoi(argv [++i]); break; case 's': /* Antimatter sensor */ case 'S': check(4, "s"); if (argv[i][3] == 'S') { /* Add sensor modifiers from file */ rval = wordfile( sensLp, MAXSET - (sensLp - photonSensorList), getpath(argv [++i], getrlibpath(), R_OK) ); if (rval < 0) { sprintf( errmsg, "cannot open antimatter sensor file %s", argv [i] ); error(SYSTEM, errmsg); } sensLp += rval; } else { /* Append modifier to sensor list, mark end with * NULL */ *sensLp++ = savqstr(argv [++i]); *sensLp = NULL; } break; default: goto badopt; } break; default: goto badopt; } break; case 'b': switch (argv [i][2]) { case 'v': /* Back face visibility */ check_bool(3, backvis); break; case 'n': /* Binning count for precomputed contrib. photon map. Save params for export to file for rcontrib. */ check(3, "s"); /* binParams = appendParams(binParams, argv + i, 2); */ binCnt = (int)(eval(argv [++i]) + .5); break; default: /* Binning expression for precomp. contrib. photon map. Save params for export to file for rcontrib. */ check(2,"s"); /* binParams = appendParams(binParams, argv + i, 2); */ binVal = argv [++i]; } break; case 'd': /* Direct */ switch (argv [i][2]) { case 'p': /* PDF samples */ check(3, "f"); pdfSamples = atof(argv [++i]); break; case 's': /* Source partition size ratio */ check(3, "f"); srcsizerat = atof(argv [++i]); break; default: goto badopt; } break; case 'e': switch (argv [i][2]) { case 'f': /* Diagnostics file */ check(2, "s"); diagFile = argv [++i]; break; default: /* Functional expression for precomputed contrib. pmap */ check(2, "s"); /* binParams = appendParams(binParams, argv + i, 2); */ scompile(argv [++i], NULL, 0); } break; case 'f': switch (argv [i][2]) { case 'o': /* Force overwrite */ check_bool(3, clobber); break; default: /* Function file for precomputed contribution photon map */ check(2, "s"); /* binParams = appendParams(binParams, argv + i, 2); */ loadfunc(argv[++i]); } break; #ifdef PMAP_EKSPERTZ case 'l': /* Limits */ switch (argv [i][2]) { case 'd': /* Limit photon path distance */ check(3, "f"); photonMaxDist = atof(argv [++i]); if (photonMaxDist <= 0) error(USER, "max photon distance must be > 0"); break; case 'r': /* Limit photon bounces */ check(3, "i"); photonMaxBounce = atol(argv [++i]); if (photonMaxBounce <= 0) error(USER, "max photon bounces must be > 0"); break; default: goto badopt; } break; #endif case 'm': switch (argv[i][2]) { case 'a': /* Mist albedo */ check(3, "fff"); setcolor( salbedo, atof(argv [i + 1]), atof(argv [i + 2]), atof(argv [i + 3]) ); i += 3; break; case 'e': /* Mist eggs-tinction coefficient */ check(3, "fff"); setcolor( cextinction, atof(argv [i + 1]), atof(argv [i + 2]), atof(argv [i + 3]) ); i += 3; break; case 'g': /* Mist scattering eccentricity */ check(3, "f"); seccg = atof(argv [++i]); break; default: /* Modifier name for precomputed contrib. photon map */ check(2, "s"); /* binParams = appendParams(binParams, argv + i, 2); */ addSourceModifier( &srcContrib, &numSrcContrib, argv [++i], binParm, binVal, binCnt ); break; } break; case 'M': /* Modifier file for precomputed contribution photon map */ check(2, "s"); /* binParams = appendParams(binParams, argv + i, 2); */ addSourceModfile( &srcContrib, &numSrcContrib, argv [++i], binParm, binVal, binCnt ); break; #if NIX case 'n': /* Num parallel processes (NIX only) */ check(2, "i"); nproc = atoi(argv [++i]); if (nproc > PMAP_MAXPROC) { nproc = PMAP_MAXPROC; sprintf( errmsg, "too many parallel processes, clamping to %d\n", nproc ); error(WARNING, errmsg); } break; #endif case 'p': /* Parameter setting(s) for precomputed contrib. photon map */ check(2, "s"); /* binParams = appendParams(binParams, argv + i, 2); */ set_eparams(binParm = argv [++i]); break; case 't': /* Timer */ check(2, "i"); photonRepTime = atoi(argv [++i]); break; case 'v': /* Verbosity */ check_bool(2, verbose); break; #ifdef EVALDRC_HACK case 'A': /* Capt. B's (now historic) angular source file hack */ check(2,"s"); angsrcfile = argv[++i]; break; #endif default: goto badopt; } } /* Open diagnostics file */ if (diagFile) { if (!freopen(diagFile, "a", stderr)) quit(2); fprintf(stderr, "**************\n*** PID %5d: ", getpid()); printargs(argc, argv, stderr); putc('\n', stderr); fflush(stderr); } #ifdef NICE /* Lower priority */ nice(NICE); #endif if (octname == NULL) error(USER, "missing octree argument"); /* Allocate photon maps and set parameters */ for (i = 0; i < NUM_PMAP_TYPES; i++) { setPmapParam(photonMaps + i, pmapParams + i); /* Don't overwrite existing photon map unless clobbering enabled */ if ( photonMaps [i] && !stat(photonMaps [i] -> fileName, &pmstat) && !clobber ) { sprintf( errmsg, "photon map file %s exists, not overwritten", photonMaps [i] -> fileName ); error(USER, errmsg); } } for (i = 0; i < NUM_PMAP_TYPES && !photonMaps [i]; i++); if (i >= NUM_PMAP_TYPES) error(USER, "no photon maps specified"); readoct(octname, loadflags, &thescene, NULL); #ifdef EVALDRC_HACK if (angsrcfile) readobj(angsrcfile); /* load angular sources */ #endif nsceneobjs = nobjects; /* Get sources */ marksources(); /* Do forward pass and build photon maps */ if (contribPmap) /* Just build contrib pmap, ignore others */ distribPhotonContrib(contribPmap, &srcContrib, numSrcContrib, nproc); else distribPhotons(photonMaps, nproc); /* TODO: Where do we save binParams and wavelet coeffs? */ /* Save photon maps; no idea why GCC needs an explicit cast here... */ savePmaps((const PhotonMap**)photonMaps, argc, argv); cleanUpPmaps(photonMaps); quit(0); badopt: sprintf(errmsg, "command line error at '%s'", argv[i]); error(USER, errmsg); #undef check #undef check_bool return 0; } diff --git a/pmapdata.c b/pmapdata.c index 650cd5a..df75e4d 100644 --- a/pmapdata.c +++ b/pmapdata.c @@ -1,768 +1,772 @@ #ifndef lint static const char RCSid[] = "$Id: pmapdata.c,v 2.22 2020/04/08 15:14:21 rschregle Exp $"; #endif /* ========================================================================= Photon map types and high level interface to nearest neighbour lookups in underlying point cloud data structure. The default data structure is an in-core kd-tree (see pmapkdt.{h,c}). This can be overriden with the PMAP_OOC compiletime switch, which enables an out-of-core octree (see oococt.{h,c}). 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: pmapdata.c,v 2.22 2020/04/08 15:14:21 rschregle Exp $ */ #include "pmapdata.h" #include "pmapdens.h" #include "pmaprand.h" #include "pmapmat.h" #include "otypes.h" #include "random.h" PhotonMap *photonMaps [NUM_PMAP_TYPES] = { NULL, NULL, NULL, NULL, NULL, NULL }; /* Include routines to handle underlying point cloud data structure */ #ifdef PMAP_OOC #include "pmapooc.c" #else #include "pmapkdt.c" #endif /* Ambient include/exclude set (from ambient.c) */ #ifndef MAXASET #define MAXASET 4095 #endif extern OBJECT ambset [MAXASET+1]; /* Callback to print photon attributes acc. to user defined format */ int (*printPhoton)(RAY *r, Photon *p, PhotonMap *pm); /* PHOTON MAP BUILD ROUTINES ------------------------------------------ */ void initPhotonMap (PhotonMap *pmap, PhotonMapType t) /* Init photon map 'n' stuff... */ { if (!pmap) return; pmap -> numPhotons = 0; pmap -> biasCompHist = NULL; pmap -> maxPos [0] = pmap -> maxPos [1] = pmap -> maxPos [2] = -FHUGE; pmap -> minPos [0] = pmap -> minPos [1] = pmap -> minPos [2] = FHUGE; pmap -> minGathered = pmap -> maxGathered = pmap -> totalGathered = 0; pmap -> gatherTolerance = gatherTolerance; pmap -> minError = pmap -> maxError = pmap -> rmsError = 0; pmap -> numDensity = 0; pmap -> distribRatio = 1; pmap -> type = t; pmap -> squeue.node = NULL; pmap -> squeue.len = 0; #ifdef PMAP_PHOTONFLOW /* Init path lookup table and its key buffer */ pmap -> pathLUT = NULL; pmap -> pathLUTKeys = NULL; pmap -> numPathLUTKeys = 0; #endif /* Init local RNG state */ pmap -> randState [0] = 10243; pmap -> randState [1] = 39829; pmap -> randState [2] = 9433; pmapSeed(randSeed, pmap -> randState); /* Set up type-specific photon lookup callback */ pmap -> lookup = pmapLookup [t]; /* Mark photon contribution origin as unused */ pmap -> lastContribOrg.srcIdx = pmap -> lastContribOrg.srcBin = -1; pmap -> numContribOrg = 0; pmap -> contribOrg = NULL; /* Init storage */ pmap -> heap = NULL; pmap -> heapBuf = NULL; pmap -> heapBufLen = 0; #ifdef PMAP_OOC OOC_Null(&pmap -> store); #else kdT_Null(&pmap -> store); #endif } void initPhotonHeap (PhotonMap *pmap) { int fdFlags; if (!pmap) error(INTERNAL, "undefined photon map in initPhotonHeap"); if (!pmap -> heap) { /* Open heap file */ mktemp(strcpy(pmap -> heapFname, PMAP_TMPFNAME)); if (!(pmap -> heap = fopen(pmap -> heapFname, "w+b"))) error(SYSTEM, "failed opening heap file in initPhotonHeap"); #ifdef F_SETFL /* XXX is there an alternative needed for Windows? */ fdFlags = fcntl(fileno(pmap -> heap), F_GETFL); fcntl(fileno(pmap -> heap), F_SETFL, fdFlags | O_APPEND); #endif/* ftruncate(fileno(pmap -> heap), 0); */ } } void flushPhotonHeap (PhotonMap *pmap) { int fd; const unsigned long len = pmap -> heapBufLen * sizeof(Photon); if (!pmap) error(INTERNAL, "undefined photon map in flushPhotonHeap"); if (!pmap -> heap || !pmap -> heapBuf) { /* Silently ignore undefined heap error(INTERNAL, "undefined heap in flushPhotonHeap"); */ return; } /* Atomically seek and write block to heap */ /* !!! Unbuffered I/O via pwrite() avoids potential race conditions * !!! and buffer corruption which can occur with lseek()/fseek() * !!! followed by write()/fwrite(). */ fd = fileno(pmap -> heap); #ifdef DEBUG_PMAP sprintf( errmsg, "Proc %d: flushing %ld photons from pos %ld\n", getpid(), pmap -> heapBufLen, lseek(fd, 0, SEEK_END) / sizeof(Photon) ); eputs(errmsg); #endif /*if (pwrite(fd, pmap -> heapBuf, len, lseek(fd, 0, SEEK_END)) != len) */ if (write(fd, pmap -> heapBuf, len) != len) error(SYSTEM, "failed append to heap file in flushPhotonHeap"); #if NIX if (fsync(fd)) error(SYSTEM, "failed fsync in flushPhotonHeap"); #endif pmap -> heapBufLen = 0; } #ifdef DEBUG_PMAP static int checkPhotonHeap (FILE *file) /* Check heap for nonsensical or duplicate photons */ { Photon p, lastp; int i, dup; rewind(file); memset(&lastp, 0, sizeof(lastp)); while (fread(&p, sizeof(p), 1, file)) { dup = 1; for (i = 0; i <= 2; i++) { if ( p.pos [i] < thescene.cuorg [i] || p.pos [i] > thescene.cuorg [i] + thescene.cusize ) { sprintf( errmsg, "corrupt photon in heap at [%f, %f, %f]\n", p.pos [0], p.pos [1], p.pos [2] ); error(WARNING, errmsg); } dup &= p.pos [i] == lastp.pos [i]; } if (dup) { sprintf( errmsg, "consecutive duplicate photon in heap at [%f, %f, %f]\n", p.pos [0], p.pos [1], p.pos [2] ); error(WARNING, errmsg); } } return 0; } #endif int newPhoton (PhotonMap* pmap, const RAY* ray) { unsigned i; Photon photon; COLOR photonFlux; /* Account for distribution ratio */ if (!pmap || pmapRandom(pmap -> randState) > pmap -> distribRatio) return -1; /* Don't store on sources */ if (ray -> robj > -1 && islight(objptr(ray -> ro -> omod) -> otype)) return -1; /* Ignore photon if modifier in/outside exclude/include set */ if ( ambincl != -1 && ray -> ro && ambincl != inset(ambset, ray->ro->omod) ) return -1; - if (pmapNumROI && pmapROI) { + if (pmapNumROI && pmapROI) { unsigned inROI = 0; + FVECT photonDist; - /* Store photon if within a region of interest (for ze Ecksperts!) */ - for (i = 0; !inROI && i < pmapNumROI; i++) + /* Store photon if within a region of interest (for ze Ecksperts!) + Note size of spherical ROI is squared. */ + for (i = 0; !inROI && i < pmapNumROI; i++) { + VSUB(photonDist, ray -> rop, pmapROI [i].pos); + inROI = ( - ray -> rop [0] >= pmapROI [i].min [0] && - ray -> rop [0] <= pmapROI [i].max [0] && - ray -> rop [1] >= pmapROI [i].min [1] && - ray -> rop [1] <= pmapROI [i].max [1] && - ray -> rop [2] >= pmapROI [i].min [2] && - ray -> rop [2] <= pmapROI [i].max [2] + PMAP_ROI_ISSPHERE(pmapROI + i) + ? DOT(photonDist, photonDist) <= pmapROI [i].siz [0] + : fabs(photonDist [0]) <= pmapROI [i].siz [0] && + fabs(photonDist [1]) <= pmapROI [i].siz [1] && + fabs(photonDist [2]) <= pmapROI [i].siz [2] ); + } if (!inROI) return -1; } /* Adjust flux according to distribution ratio and ray weight */ copycolor(photonFlux, ray -> rcol); #if 0 /* Factored out ray -> rweight as deprecated (?) for pmap, and infact erroneously attenuates volume photon flux based on extinction, which is already factored in by photonParticipate() */ scalecolor( photonFlux, ray -> rweight / (pmap -> distribRatio ? pmap -> distribRatio : 1) ); #else scalecolor( photonFlux, 1.0 / (pmap -> distribRatio ? pmap -> distribRatio : 1) ); #endif setPhotonFlux(&photon, photonFlux); /* Set photon position and flags */ VCOPY(photon.pos, ray -> rop); photon.flags = 0; photon.caustic = PMAP_CAUSTICRAY(ray); if (isContribPmap(pmap)) { /* Set contrib photon's origin and subprocess index (the latter to * linearise the origin array after photon distribution is complete). * Also set origin's source index, thereby marking it as used. Note * the origin's bin has already been set by newPhotonOrigin(). */ photon.org = pmap -> numContribOrg; photon.proc = PMAP_GETRAYPROC(ray); pmap -> lastContribOrg.srcIdx = ray -> rsrc; } else { /* Set photon's path ID from ray serial num and subprocess index */ photon.org = ray -> rno; photon.proc = PMAP_GETRAYPROC(ray); } /* Set normal */ for (i = 0; i <= 2; i++) photon.norm [i] = 127.0 * ( isVolumePmap(pmap) ? ray -> rdir [i] : ray -> ron [i] ); if (!pmap -> heapBuf) { /* Lazily allocate heap buffa */ #if NIX /* Randomise buffa size to temporally decorellate flushes in * multiprocessing mode */ srandom(randSeed + getpid()); pmap -> heapBufSize = PMAP_HEAPBUFSIZE * (0.5 + frandom()); #else /* Randomisation disabled for single processes on WIN; also useful * for reproducability during debugging */ pmap -> heapBufSize = PMAP_HEAPBUFSIZE; #endif if (!(pmap -> heapBuf = calloc(pmap -> heapBufSize, sizeof(Photon)))) error(SYSTEM, "failed heap buffer allocation in newPhoton"); pmap -> heapBufLen = 0; } /* Photon initialised; now append to heap buffa */ memcpy(pmap -> heapBuf + pmap -> heapBufLen, &photon, sizeof(Photon)); if (++pmap -> heapBufLen >= pmap -> heapBufSize) /* Heap buffa full, flush to heap file */ flushPhotonHeap(pmap); pmap -> numPhotons++; /* Print photon attributes */ if (printPhoton) /* Non-const kludge */ printPhoton((RAY*)ray, &photon, pmap); return 0; } void buildPhotonMap ( PhotonMap *pmap, double *photonFlux, PhotonOrigin *originOfs, unsigned nproc ) { unsigned long n, nCheck = 0; unsigned i; Photon *p; COLOR flux; char nuHeapFname [sizeof(PMAP_TMPFNAME)]; FILE *nuHeap; /* Need double here to reduce summation errors */ double avgFlux [3] = {0, 0, 0}, CoG [3] = {0, 0, 0}, CoGdist = 0; FVECT d; if (!pmap) error(INTERNAL, "undefined photon map in buildPhotonMap"); /* Get number of photons from heapfile size */ if (fseek(pmap -> heap, 0, SEEK_END) < 0) error(SYSTEM, "failed seek to end of photon heap in buildPhotonMap"); pmap -> numPhotons = ftell(pmap -> heap) / sizeof(Photon); if (!pmap -> numPhotons) error(INTERNAL, "empty photon map in buildPhotonMap"); if (!pmap -> heap) error(INTERNAL, "no heap in buildPhotonMap"); #ifdef DEBUG_PMAP eputs("Checking photon heap consistency...\n"); checkPhotonHeap(pmap -> heap); sprintf(errmsg, "Heap contains %ld photons\n", pmap -> numPhotons); eputs(errmsg); #endif /* Allocate heap buffa */ if (!pmap -> heapBuf) { pmap -> heapBufSize = PMAP_HEAPBUFSIZE; pmap -> heapBuf = calloc(pmap -> heapBufSize, sizeof(Photon)); if (!pmap -> heapBuf) error( SYSTEM, "failed to allocate postprocessed photon heap in buildPhotonMap" ); } /* We REALLY don't need yet another @%&*! heap just to hold the scaled * photons, but can't think of a quicker fix... */ mktemp(strcpy(nuHeapFname, PMAP_TMPFNAME)); if (!(nuHeap = fopen(nuHeapFname, "w+b"))) error( SYSTEM, "failed to open postprocessed photon heap in buildPhotonMap" ); rewind(pmap -> heap); #ifdef DEBUG_PMAP eputs("Postprocessing photons...\n"); #endif while (!feof(pmap -> heap)) { #ifdef DEBUG_PMAP printf( "Reading %lu at %lu... ", pmap -> heapBufSize, ftell(pmap->heap) ); #endif pmap -> heapBufLen = fread( pmap -> heapBuf, sizeof(Photon), pmap -> heapBufSize, pmap -> heap ); #ifdef DEBUG_PMAP printf("Got %lu\n", pmap -> heapBufLen); #endif if (ferror(pmap -> heap)) error(SYSTEM, "failed to read photon heap in buildPhotonMap"); for (n = pmap -> heapBufLen, p = pmap -> heapBuf; n; n--, p++) { /* Update min and max pos and set photon flux */ for (i = 0; i <= 2; i++) { if (p -> pos [i] < pmap -> minPos [i]) pmap -> minPos [i] = p -> pos [i]; else if (p -> pos [i] > pmap -> maxPos [i]) pmap -> maxPos [i] = p -> pos [i]; /* Update centre of gravity with photon position */ CoG [i] += p -> pos [i]; } if (originOfs) /* Linearise contrib photon origin index from subprocess index * using the per-subprocess offsets in originOfs */ p -> org += originOfs [p -> proc]; /* Scale photon's flux (hitherto normalised to 1 over RGB); in * case of a contrib photon map, this is done per light source, * and photonFlux is assumed to be an array */ getPhotonFlux(p, flux); if (photonFlux) { scalecolor( flux, photonFlux [isContribPmap(pmap) ? photonSrcIdx(pmap, p) : 0] ); setPhotonFlux(p, flux); } /* Update average photon flux; need a double here */ addcolor(avgFlux, flux); } /* Write modified photons to new heap */ fwrite(pmap -> heapBuf, sizeof(Photon), pmap -> heapBufLen, nuHeap); if (ferror(nuHeap)) error( SYSTEM, "failed postprocessing photon flux in buildPhotonMap" ); nCheck += pmap -> heapBufLen; } #ifdef DEBUG_PMAP if (nCheck < pmap -> numPhotons) error(INTERNAL, "truncated photon heap in buildPhotonMap"); #endif /* Finalise average photon flux */ scalecolor(avgFlux, 1.0 / pmap -> numPhotons); copycolor(pmap -> photonFlux, avgFlux); /* Average photon positions to get centre of gravity */ for (i = 0; i < 3; i++) pmap -> CoG [i] = CoG [i] /= pmap -> numPhotons; rewind(pmap -> heap); /* Compute average photon distance to centre of gravity */ while (!feof(pmap -> heap)) { pmap -> heapBufLen = fread( pmap -> heapBuf, sizeof(Photon), pmap -> heapBufSize, pmap -> heap ); for (n = pmap -> heapBufLen, p = pmap -> heapBuf; n; n--, p++) { VSUB(d, p -> pos, CoG); CoGdist += DOT(d, d); } } pmap -> CoGdist = CoGdist /= pmap -> numPhotons; /* Swap heaps, discarding unscaled photons */ fclose(pmap -> heap); unlink(pmap -> heapFname); pmap -> heap = nuHeap; strcpy(pmap -> heapFname, nuHeapFname); #ifdef PMAP_OOC OOC_BuildPhotonMap(pmap, nproc); #else kdT_BuildPhotonMap(pmap); #endif /* Trash heap and its buffa */ free(pmap -> heapBuf); fclose(pmap -> heap); unlink(pmap -> heapFname); pmap -> heap = NULL; pmap -> heapBuf = NULL; } /* PHOTON MAP SEARCH ROUTINES ------------------------------------------ */ /* Dynamic max photon search radius increase and reduction factors */ #define PMAP_MAXDIST_INC 4 #define PMAP_MAXDIST_DEC 0.9 /* Num successful lookups before reducing in max search radius */ #define PMAP_MAXDIST_CNT 1000 /* Threshold below which we assume increasing max radius won't help */ #define PMAP_SHORT_LOOKUP_THRESH 1 /* Coefficient for adaptive maximum search radius */ #define PMAP_MAXDIST_COEFF 100 void findPhotons (PhotonMap* pmap, const RAY* ray) { int redo = 0; if (!pmap -> squeue.len) { /* Lazy init priority queue */ #ifdef PMAP_OOC OOC_InitFindPhotons(pmap); #else kdT_InitFindPhotons(pmap); #endif pmap -> minGathered = pmap -> maxGather; pmap -> maxGathered = pmap -> minGather; pmap -> totalGathered = 0; pmap -> numLookups = pmap -> numShortLookups = 0; pmap -> shortLookupPct = 0; pmap -> minError = FHUGE; pmap -> maxError = -FHUGE; pmap -> rmsError = 0; /* SQUARED max search radius limit is based on avg photon distance to * centre of gravity, unless fixed by user (maxDistFix > 0) */ pmap -> maxDist0 = pmap -> maxDist2Limit = maxDistFix > 0 ? maxDistFix * maxDistFix : PMAP_MAXDIST_COEFF * pmap -> squeue.len * pmap -> CoGdist / pmap -> numPhotons; } do { pmap -> squeue.tail = 0; pmap -> maxDist2 = pmap -> maxDist0; /* Search position is ray -> rorg for volume photons, since we have no intersection point. Normals are ignored -- these are incident directions). */ /* XXX/NOTE: status returned by XXX_FindPhotons() is currently ignored; if no photons are found, an empty queue is returned under the assumption all photons are too distant to contribute significant flux. */ if (isVolumePmap(pmap)) { #ifdef PMAP_OOC OOC_FindPhotons(pmap, ray -> rorg, NULL); #else kdT_FindPhotons(pmap, ray -> rorg, NULL); #endif } else { #ifdef PMAP_OOC OOC_FindPhotons(pmap, ray -> rop, ray -> ron); #else kdT_FindPhotons(pmap, ray -> rop, ray -> ron); #endif } #ifdef PMAP_LOOKUP_INFO fprintf( stderr, "%d/%d %s photons found within radius %.3f " "at (%.2f,%.2f,%.2f) on %s\n", pmap -> squeue.tail, pmap -> squeue.len, pmapName [pmap -> type], sqrt(pmap -> maxDist2), ray -> rop [0], ray -> rop [1], ray -> rop [2], ray -> ro ? ray -> ro -> oname : "" ); #endif if (pmap -> squeue.tail < pmap -> squeue.len * pmap->gatherTolerance) { /* Short lookup; too few photons found */ if (pmap -> squeue.tail > PMAP_SHORT_LOOKUP_THRESH) { /* Ignore short lookups which return fewer than * PMAP_SHORT_LOOKUP_THRESH photons under the assumption there * really are no photons in the vicinity, and increasing the max * search radius therefore won't help */ #ifdef PMAP_LOOKUP_WARN sprintf( errmsg, "%d/%d %s photons found at (%.2f,%.2f,%.2f) on %s", pmap -> squeue.tail, pmap->squeue.len, pmapName [pmap->type], ray -> rop [0], ray -> rop [1], ray -> rop [2], ray -> ro ? ray -> ro -> oname : "" ); error(WARNING, errmsg); #endif /* Bail out after warning if maxDist is fixed */ if (maxDistFix > 0) return; if (pmap -> maxDist0 < pmap -> maxDist2Limit) { /* Increase max search radius if below limit & redo search */ pmap -> maxDist0 *= PMAP_MAXDIST_INC; #ifdef PMAP_LOOKUP_REDO redo = 1; #endif #ifdef PMAP_LOOKUP_WARN sprintf( errmsg, redo ? "restarting photon lookup with max radius %.1e" : "max photon lookup radius adjusted to %.1e", pmap -> maxDist0 ); error(WARNING, errmsg); #endif } #ifdef PMAP_LOOKUP_REDO else { sprintf( errmsg, "max photon lookup radius clamped to %.1e", pmap -> maxDist0 ); error(WARNING, errmsg); } #endif } /* Reset successful lookup counter */ pmap -> numLookups = 0; } else { /* Skip search radius reduction if maxDist is fixed */ if (maxDistFix > 0) return; /* Increment successful lookup counter and reduce max search radius if * wraparound */ pmap -> numLookups = (pmap -> numLookups + 1) % PMAP_MAXDIST_CNT; if (!pmap -> numLookups) pmap -> maxDist0 *= PMAP_MAXDIST_DEC; redo = 0; } } while (redo); } Photon *find1Photon (PhotonMap *pmap, const RAY* ray, Photon *photon) { /* Init (squared) search radius to avg photon dist to centre of gravity */ float maxDist2_0 = pmap -> CoGdist; int found = 0; #ifdef PMAP_LOOKUP_REDO #define REDO 1 #else #define REDO 0 #endif do { pmap -> maxDist2 = maxDist2_0; #ifdef PMAP_OOC found = OOC_Find1Photon(pmap, ray -> rop, ray -> ron, photon); #else found = kdT_Find1Photon(pmap, ray -> rop, ray -> ron, photon); #endif if (found < 0) { /* Expand search radius to retry */ maxDist2_0 *= 2; #ifdef PMAP_LOOKUP_WARN sprintf(errmsg, "failed 1-NN photon lookup" #ifdef PMAP_LOOKUP_REDO ", retrying with search radius %.2f", maxDist2_0 #endif ); error(WARNING, errmsg); #endif } } while (REDO && found < 0); /* Return photon buffer containing valid photon, else NULL */ return found < 0 ? NULL : photon; } void getPhoton (PhotonMap *pmap, PhotonIdx idx, Photon *photon) { #ifdef PMAP_OOC if (OOC_GetPhoton(pmap, idx, photon)) #else if (kdT_GetPhoton(pmap, idx, photon)) #endif error(INTERNAL, "failed photon lookup"); } Photon *getNearestPhoton (const PhotonSearchQueue *squeue, PhotonIdx idx) { #ifdef PMAP_OOC return OOC_GetNearestPhoton(squeue, idx); #else return kdT_GetNearestPhoton(squeue, idx); #endif } PhotonIdx firstPhoton (const PhotonMap *pmap) { #ifdef PMAP_OOC return OOC_FirstPhoton(pmap); #else return kdT_FirstPhoton(pmap); #endif } /* PHOTON MAP CLEANUP ROUTINES ------------------------------------------ */ void deletePhotons (PhotonMap* pmap) { #ifdef PMAP_OOC OOC_Delete(&pmap -> store); #else kdT_Delete(&pmap -> store); #endif free(pmap -> squeue.node); free(pmap -> biasCompHist); pmap -> numPhotons = pmap -> minGather = pmap -> maxGather = pmap -> squeue.len = pmap -> squeue.tail = 0; #ifdef PMAP_PHOTONFLOW if (pmap -> pathLUT) { lu_done(pmap -> pathLUT); free(pmap -> pathLUT); } if (pmap -> pathLUTKeys) { unsigned k; for (k = 0; k < pmap->squeue.len + 1; free(pmap->pathLUTKeys [k++])); free(pmap -> pathLUTKeys); } #endif } diff --git a/pmapparm.h b/pmapparm.h index 2ee7c6f..9409a9e 100644 --- a/pmapparm.h +++ b/pmapparm.h @@ -1,82 +1,86 @@ /* RCSid $Id: pmapparm.h,v 2.9 2018/02/02 19:47:55 rschregle Exp $ */ /* ====================================================================== Parameters for photon map generation and rendering; used by mkpmap and rpict/rvu/rtrace. 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") (c) Tokyo University of Science, supported by the JSPS Grants-in-Aid for Scientific Research (KAKENHI JP19KK0115, "Three-Dimensional Light Flow") ====================================================================== $Id: pmapparm.h,v 2.9 2018/02/02 19:47:55 rschregle Exp $ */ #ifndef PMAPPARAMS_H #define PMAPPARAMS_H #include "pmaptype.h" /* Struct for passing params per photon map from rpict/rtrace/rvu */ typedef struct { char *fileName; /* Photon map file */ unsigned minGather, maxGather; /* Num photons to gather */ unsigned long distribTarget; /* Num photons to store */ } PhotonMapParams; - /* Bounding box for region of interest */ - typedef struct { - float min [3], max [3]; + /* Region of interest */ + typedef struct { + /* siz [1], siz [2] <= 0 --> sphere, else rectangle */ + float pos [3], siz [3]; } PhotonMapROI; + + #define PMAP_ROI_ISSPHERE(roi) ((roi)->siz[1] <= 0 && (roi)->siz[2] <= 0) + #define PMAP_ROI_SETSPHERE(roi) ((roi)->siz[1] = (roi)->siz[2] = -1) extern PhotonMapParams pmapParams [NUM_PMAP_TYPES]; /* Macros for type specific photon map parameters */ #define globalPmapParams (pmapParams [PMAP_TYPE_GLOBAL]) #define preCompPmapParams (pmapParams [PMAP_TYPE_PRECOMP]) #define causticPmapParams (pmapParams [PMAP_TYPE_CAUSTIC]) #define volumePmapParams (pmapParams [PMAP_TYPE_VOLUME]) #define directPmapParams (pmapParams [PMAP_TYPE_DIRECT]) #define contribPmapParams (pmapParams [PMAP_TYPE_CONTRIB]) extern float pdfSamples, preDistrib, finalGather, gatherTolerance, maxDistFix, pmapMaxDist, photonMaxDist; extern unsigned long photonHeapSizeInc, photonMaxBounce; extern unsigned photonRepTime, maxPreDistrib, defaultGather, verbose; extern unsigned pmapNumROI; extern PhotonMapROI *pmapROI; #ifdef PMAP_OOC extern float pmapCachePageSize; extern unsigned long pmapCacheSize; #endif #ifdef PMAP_PHOTONFLOW extern unsigned photonFlow; #endif struct PhotonMap; int setPmapParam (struct PhotonMap **pm, const PhotonMapParams *parm); /* Allocate photon map and set its parameters from parm */ unsigned long parseMultiplier (const char *num); /* Evaluate numeric parameter string with optional multiplier suffix (G = 10^9, M = 10^6, K = 10^3). Returns 0 if parsing fails. */ #endif