ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmap.c
Revision: 2.17
Committed: Tue Dec 18 22:14:04 2018 UTC (5 years, 5 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R3
Changes since 2.16: +16 -14 lines
Log Message:
Fixed skewed volume photon scattering for gecc < 1 due to buggy sin(phi) in photonParticipate()

File Contents

# User Rev Content
1 greg 2.9 #ifndef lint
2 rschregle 2.17 static const char RCSid[] = "$Id: pmap.c,v 2.16 2018/11/21 19:33:30 rschregle Exp $";
3 greg 2.9 #endif
4 rschregle 2.11
5 rschregle 2.13
6 greg 2.1 /*
7 rschregle 2.11 ======================================================================
8 greg 2.1 Photon map main module
9    
10     Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
11     (c) Fraunhofer Institute for Solar Energy Systems,
12 rschregle 2.4 (c) Lucerne University of Applied Sciences and Arts,
13 rschregle 2.11 supported by the Swiss National Science Foundation (SNSF, #147053)
14     ======================================================================
15 greg 2.1
16 rschregle 2.17 $Id: pmap.c,v 2.16 2018/11/21 19:33:30 rschregle Exp $
17 greg 2.1 */
18    
19    
20     #include "pmap.h"
21     #include "pmapmat.h"
22     #include "pmapsrc.h"
23     #include "pmaprand.h"
24     #include "pmapio.h"
25     #include "pmapbias.h"
26     #include "pmapdiag.h"
27     #include "otypes.h"
28     #include <time.h>
29 rschregle 2.13 #if NIX
30     #include <sys/stat.h>
31     #include <sys/mman.h>
32     #include <sys/wait.h>
33     #endif
34 greg 2.1
35    
36     void savePmaps (const PhotonMap **pmaps, int argc, char **argv)
37     {
38     unsigned t;
39    
40     for (t = 0; t < NUM_PMAP_TYPES; t++) {
41     if (pmaps [t])
42 greg 2.7 savePhotonMap(pmaps [t], pmaps [t] -> fileName, argc, argv);
43 greg 2.1 }
44     }
45    
46    
47    
48     static int photonParticipate (RAY *ray)
49     /* Trace photon through participating medium. Returns 1 if passed through,
50     or 0 if absorbed and $*%&ed. Analogon to rayparticipate(). */
51     {
52     int i;
53 rschregle 2.17 RREAL xi1, cosTheta, phi, du, dv;
54 greg 2.1 const float cext = colorAvg(ray -> cext),
55 rschregle 2.16 albedo = colorAvg(ray -> albedo),
56 rschregle 2.17 gecc = ray -> gecc, gecc2 = sqr(gecc);
57 greg 2.1 FVECT u, v;
58     COLOR cvext;
59    
60     /* Mean free distance until interaction with medium */
61     ray -> rmax = -log(pmapRandom(mediumState)) / cext;
62    
63     while (!localhit(ray, &thescene)) {
64 rschregle 2.16 if (!incube(&thescene, ray -> rop)) {
65     /* Terminate photon if it has leaked from the scene */
66     #ifdef DEBUG_PMAP
67     fprintf(stderr,
68     "Volume photon leaked from scene at [%.3f %.3f %.3f]\n",
69     ray -> rop [0], ray -> rop [1], ray -> rop [2]);
70     #endif
71     return 0;
72     }
73    
74 greg 2.1 setcolor(cvext, exp(-ray -> rmax * ray -> cext [0]),
75     exp(-ray -> rmax * ray -> cext [1]),
76     exp(-ray -> rmax * ray -> cext [2]));
77    
78     /* Modify ray color and normalise */
79     multcolor(ray -> rcol, cvext);
80     colorNorm(ray -> rcol);
81     VCOPY(ray -> rorg, ray -> rop);
82    
83 rschregle 2.15 #if 0
84 rschregle 2.11 if (albedo > FTINY && ray -> rlvl > 0)
85 rschregle 2.15 #else
86     /* Store volume photons unconditionally in mist to also account for
87     direct inscattering from sources */
88     if (albedo > FTINY)
89 rschregle 2.17 #endif
90 greg 2.1 /* Add to volume photon map */
91 rschregle 2.11 newPhoton(volumePmap, ray);
92 greg 2.1
93     /* Absorbed? */
94 rschregle 2.11 if (pmapRandom(rouletteState) > albedo)
95     return 0;
96 greg 2.1
97     /* Colour bleeding without attenuation (?) */
98     multcolor(ray -> rcol, ray -> albedo);
99     scalecolor(ray -> rcol, 1 / albedo);
100    
101     /* Scatter photon */
102 rschregle 2.17 xi1 = pmapRandom(scatterState);
103 rschregle 2.16 cosTheta = ray -> gecc <= FTINY
104 rschregle 2.17 ? 2 * xi1 - 1
105     : 0.5 / gecc *
106     (1 + gecc2 - sqr((1 - gecc2) /
107     (1 + gecc * (2 * xi1 - 1))));
108    
109     phi = 2 * PI * pmapRandom(scatterState);
110     du = dv = sqrt(1 - sqr(cosTheta)); /* sin(theta) */
111     du *= cos(phi);
112     dv *= sin(phi);
113 greg 2.1
114     /* Get axes u & v perpendicular to photon direction */
115     i = 0;
116     do {
117     v [0] = v [1] = v [2] = 0;
118     v [i++] = 1;
119     fcross(u, v, ray -> rdir);
120     } while (normalize(u) < FTINY);
121     fcross(v, ray -> rdir, u);
122    
123     for (i = 0; i < 3; i++)
124     ray -> rdir [i] = du * u [i] + dv * v [i] +
125     cosTheta * ray -> rdir [i];
126 rschregle 2.17
127 greg 2.1 ray -> rlvl++;
128     ray -> rmax = -log(pmapRandom(mediumState)) / cext;
129     }
130 rschregle 2.16
131     /* Passed through medium until intersecting local object */
132 greg 2.1 setcolor(cvext, exp(-ray -> rot * ray -> cext [0]),
133     exp(-ray -> rot * ray -> cext [1]),
134     exp(-ray -> rot * ray -> cext [2]));
135    
136     /* Modify ray color and normalise */
137     multcolor(ray -> rcol, cvext);
138 rschregle 2.16 colorNorm(ray -> rcol);
139    
140 greg 2.1 return 1;
141     }
142    
143    
144    
145     void tracePhoton (RAY *ray)
146     /* Follow photon as it bounces around... */
147     {
148     long mod;
149 rschregle 2.13 OBJREC *mat, *port = NULL;
150    
151     if (!ray -> parent) {
152     /* !!! PHOTON PORT REJECTION SAMPLING HACK: get photon port for
153     * !!! primary ray from ray -> ro, then reset the latter to NULL so
154     * !!! as not to interfere with localhit() */
155     port = ray -> ro;
156     ray -> ro = NULL;
157     }
158 greg 2.1
159     if (ray -> rlvl > photonMaxBounce) {
160 rschregle 2.5 #ifdef PMAP_RUNAWAY_WARN
161 greg 2.1 error(WARNING, "runaway photon!");
162 rschregle 2.5 #endif
163 greg 2.1 return;
164     }
165 rschregle 2.5
166 greg 2.1 if (colorAvg(ray -> cext) > FTINY && !photonParticipate(ray))
167     return;
168 rschregle 2.13
169 greg 2.1 if (localhit(ray, &thescene)) {
170     mod = ray -> ro -> omod;
171 rschregle 2.13
172     if (port && ray -> ro != port) {
173     /* !!! PHOTON PORT REJECTION SAMPLING HACK !!!
174     * Terminate photon if emitted from port without intersecting it;
175     * this can happen when the port's partitions extend beyond its
176     * actual geometry, e.g. with polygons. Since the total flux
177     * relayed by the port is based on the (in this case) larger
178     * partition area, it is overestimated; terminating these photons
179     * constitutes rejection sampling and thereby compensates any bias
180     * incurred by the overestimated flux. */
181     #ifdef PMAP_PORTREJECT_WARN
182     sprintf(errmsg, "photon outside port %s", ray -> ro -> oname);
183     error(WARNING, errmsg);
184     #endif
185     return;
186     }
187    
188 greg 2.1 if ((ray -> clipset && inset(ray -> clipset, mod)) || mod == OVOID) {
189     /* Transfer ray if modifier is VOID or clipped within antimatta */
190     RAY tray;
191     photonRay(ray, &tray, PMAP_XFER, NULL);
192     tracePhoton(&tray);
193     }
194     else {
195     /* Scatter for modifier material */
196     mat = objptr(mod);
197     photonScatter [mat -> otype] (mat, ray);
198     }
199     }
200     }
201    
202    
203    
204     static void preComputeGlobal (PhotonMap *pmap)
205 rschregle 2.11 /* Precompute irradiance from global photons for final gathering for
206     a random subset of finalGather * pmap -> numPhotons photons, and builds
207     the photon map, discarding the original photons. */
208     /* !!! NOTE: PRECOMPUTATION WITH OOC CURRENTLY WITHOUT CACHE !!! */
209     {
210     unsigned long i, numPreComp;
211     unsigned j;
212     PhotonIdx pIdx;
213     Photon photon;
214     RAY ray;
215     PhotonMap nuPmap;
216 greg 2.1
217 rschregle 2.11 repComplete = numPreComp = finalGather * pmap -> numPhotons;
218 greg 2.1
219 rschregle 2.13 if (verbose) {
220     sprintf(errmsg,
221     "\nPrecomputing irradiance for %ld global photons\n",
222 rschregle 2.11 numPreComp);
223 greg 2.1 eputs(errmsg);
224 rschregle 2.13 #if NIX
225 greg 2.1 fflush(stderr);
226 rschregle 2.13 #endif
227 greg 2.1 }
228    
229 rschregle 2.11 /* Copy photon map for precomputed photons */
230     memcpy(&nuPmap, pmap, sizeof(PhotonMap));
231    
232     /* Zero counters, init new heap and extents */
233     nuPmap.numPhotons = 0;
234     initPhotonHeap(&nuPmap);
235    
236     for (j = 0; j < 3; j++) {
237     nuPmap.minPos [j] = FHUGE;
238     nuPmap.maxPos [j] = -FHUGE;
239 greg 2.1 }
240 rschregle 2.11
241 greg 2.1 /* Record start time, baby */
242     repStartTime = time(NULL);
243 rschregle 2.11 #ifdef SIGCONT
244     signal(SIGCONT, pmapPreCompReport);
245     #endif
246 greg 2.1 repProgress = 0;
247    
248 rschregle 2.11 photonRay(NULL, &ray, PRIMARY, NULL);
249     ray.ro = NULL;
250    
251     for (i = 0; i < numPreComp; i++) {
252     /* Get random photon from stratified distribution in source heap to
253 rschregle 2.13 * avoid duplicates and clustering */
254 rschregle 2.11 pIdx = firstPhoton(pmap) +
255     (unsigned long)((i + pmapRandom(pmap -> randState)) /
256     finalGather);
257     getPhoton(pmap, pIdx, &photon);
258    
259     /* Init dummy photon ray with intersection at photon position */
260     VCOPY(ray.rop, photon.pos);
261     for (j = 0; j < 3; j++)
262     ray.ron [j] = photon.norm [j] / 127.0;
263    
264     /* Get density estimate at photon position */
265     photonDensity(pmap, &ray, ray.rcol);
266    
267     /* Append photon to new heap from ray */
268     newPhoton(&nuPmap, &ray);
269 greg 2.1
270 rschregle 2.11 /* Update progress */
271 greg 2.1 repProgress++;
272    
273     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
274     pmapPreCompReport();
275 rschregle 2.11 #ifdef SIGCONT
276     else signal(SIGCONT, pmapPreCompReport);
277     #endif
278 greg 2.1 }
279    
280 rschregle 2.11 /* Flush heap */
281     flushPhotonHeap(&nuPmap);
282    
283     #ifdef SIGCONT
284     signal(SIGCONT, SIG_DFL);
285     #endif
286    
287     /* Trash original pmap, replace with precomputed one */
288     deletePhotons(pmap);
289     memcpy(pmap, &nuPmap, sizeof(PhotonMap));
290 greg 2.1
291 rschregle 2.13 if (verbose) {
292     eputs("\nRebuilding precomputed photon map\n");
293     #if NIX
294 greg 2.1 fflush(stderr);
295 rschregle 2.13 #endif
296 greg 2.1 }
297 rschregle 2.11
298     /* Rebuild underlying data structure, destroying heap */
299     buildPhotonMap(pmap, NULL, NULL, 1);
300 greg 2.1 }
301    
302    
303    
304 rschregle 2.11 typedef struct {
305     unsigned long numPhotons [NUM_PMAP_TYPES],
306     numEmitted, numComplete;
307     } PhotonCnt;
308    
309    
310    
311     void distribPhotons (PhotonMap **pmaps, unsigned numProc)
312     {
313     EmissionMap emap;
314 rschregle 2.13 char errmsg2 [128], shmFname [PMAP_TMPFNLEN];
315 rschregle 2.11 unsigned t, srcIdx, proc;
316     double totalFlux = 0;
317     int shmFile, stat, pid;
318     PhotonMap *pm;
319     PhotonCnt *photonCnt;
320 greg 2.1
321 rschregle 2.8 for (t = 0; t < NUM_PMAP_TYPES && !pmaps [t]; t++);
322 rschregle 2.11
323 greg 2.1 if (t >= NUM_PMAP_TYPES)
324 rschregle 2.11 error(USER, "no photon maps defined in distribPhotons");
325 greg 2.1
326     if (!nsources)
327 rschregle 2.11 error(USER, "no light sources in distribPhotons");
328 greg 2.1
329     /* ===================================================================
330     * INITIALISATION - Set up emission and scattering funcs
331     * =================================================================== */
332     emap.samples = NULL;
333     emap.maxPartitions = MAXSPART;
334     emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
335     if (!emap.partitions)
336 rschregle 2.11 error(INTERNAL, "can't allocate source partitions in distribPhotons");
337 greg 2.1
338     /* Initialise all defined photon maps */
339     for (t = 0; t < NUM_PMAP_TYPES; t++)
340 rschregle 2.11 if (pmaps [t]) {
341     initPhotonMap(pmaps [t], t);
342     /* Open photon heapfile */
343     initPhotonHeap(pmaps [t]);
344     /* Per-subprocess target count */
345     pmaps [t] -> distribTarget /= numProc;
346 rschregle 2.13
347     if (!pmaps [t] -> distribTarget)
348     error(INTERNAL, "no photons to distribute in distribPhotons");
349 rschregle 2.11 }
350 greg 2.1
351     initPhotonEmissionFuncs();
352     initPhotonScatterFuncs();
353    
354 rschregle 2.14 /* Get photon ports from modifier list */
355     getPhotonPorts(photonPortList);
356 greg 2.1
357     /* Get photon sensor modifiers */
358     getPhotonSensors(photonSensorList);
359    
360 rschregle 2.13 #if NIX
361 rschregle 2.11 /* Set up shared mem for photon counters (zeroed by ftruncate) */
362 rschregle 2.13 strcpy(shmFname, PMAP_TMPFNAME);
363 rschregle 2.11 shmFile = mkstemp(shmFname);
364    
365 rschregle 2.13 if (shmFile < 0 || ftruncate(shmFile, sizeof(*photonCnt)) < 0)
366     error(SYSTEM, "failed shared mem init in distribPhotons");
367 rschregle 2.11
368     photonCnt = mmap(NULL, sizeof(*photonCnt), PROT_READ | PROT_WRITE,
369     MAP_SHARED, shmFile, 0);
370    
371     if (photonCnt == MAP_FAILED)
372 rschregle 2.13 error(SYSTEM, "failed mapping shared memory in distribPhotons");
373     #else
374     /* Allocate photon counters statically on Windoze */
375     if (!(photonCnt = malloc(sizeof(PhotonCnt))))
376     error(SYSTEM, "failed trivial malloc in distribPhotons");
377     photonCnt -> numEmitted = photonCnt -> numComplete = 0;
378     #endif /* NIX */
379    
380     if (verbose) {
381     sprintf(errmsg, "\nIntegrating flux from %d sources", nsources);
382    
383     if (photonPorts) {
384     sprintf(errmsg2, " via %d ports", numPhotonPorts);
385     strcat(errmsg, errmsg2);
386     }
387    
388     strcat(errmsg, "\n");
389     eputs(errmsg);
390     }
391 greg 2.1
392     /* ===================================================================
393     * FLUX INTEGRATION - Get total photon flux from light sources
394     * =================================================================== */
395 rschregle 2.11 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
396 greg 2.1 unsigned portCnt = 0;
397     emap.src = source + srcIdx;
398    
399 rschregle 2.11 do { /* Need at least one iteration if no ports! */
400 greg 2.1 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
401     : NULL;
402     photonPartition [emap.src -> so -> otype] (&emap);
403    
404 rschregle 2.13 if (verbose) {
405     sprintf(errmsg, "\tIntegrating flux from source %s ",
406 greg 2.1 source [srcIdx].so -> oname);
407 rschregle 2.13
408 greg 2.1 if (emap.port) {
409     sprintf(errmsg2, "via port %s ",
410     photonPorts [portCnt].so -> oname);
411     strcat(errmsg, errmsg2);
412     }
413 rschregle 2.13
414     sprintf(errmsg2, "(%lu partitions)\n", emap.numPartitions);
415 greg 2.1 strcat(errmsg, errmsg2);
416     eputs(errmsg);
417 rschregle 2.13 #if NIX
418 greg 2.1 fflush(stderr);
419 rschregle 2.13 #endif
420 greg 2.1 }
421    
422     for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
423     emap.partitionCnt++) {
424     initPhotonEmission(&emap, pdfSamples);
425     totalFlux += colorAvg(emap.partFlux);
426     }
427    
428     portCnt++;
429     } while (portCnt < numPhotonPorts);
430     }
431    
432     if (totalFlux < FTINY)
433     error(USER, "zero flux from light sources");
434 rschregle 2.13
435     /* Record start time for progress reports */
436     repStartTime = time(NULL);
437    
438     if (verbose) {
439     sprintf(errmsg, "\nPhoton distribution @ %d procs\n", numProc);
440     eputs(errmsg);
441     }
442 greg 2.1
443 rschregle 2.11 /* MAIN LOOP */
444     for (proc = 0; proc < numProc; proc++) {
445 rschregle 2.13 #if NIX
446 rschregle 2.11 if (!(pid = fork())) {
447 rschregle 2.13 /* SUBPROCESS ENTERS HERE; open and mmapped files inherited */
448     #else
449     if (1) {
450     /* No subprocess under Windoze */
451     #endif
452     /* Local photon counters for this subprocess */
453 rschregle 2.11 unsigned passCnt = 0, prePassCnt = 0;
454     unsigned long lastNumPhotons [NUM_PMAP_TYPES];
455     unsigned long localNumEmitted = 0; /* Num photons emitted by this
456     subprocess alone */
457 greg 2.1
458 rschregle 2.11 /* Seed RNGs from PID for decorellated photon distribution */
459     pmapSeed(randSeed + proc, partState);
460 rschregle 2.13 pmapSeed(randSeed + (proc + 1) % numProc, emitState);
461     pmapSeed(randSeed + (proc + 2) % numProc, cntState);
462     pmapSeed(randSeed + (proc + 3) % numProc, mediumState);
463     pmapSeed(randSeed + (proc + 4) % numProc, scatterState);
464     pmapSeed(randSeed + (proc + 5) % numProc, rouletteState);
465 rschregle 2.15
466     #ifdef DEBUG_PMAP
467     /* Output child process PID after random delay to prevent corrupted
468     * console output due to race condition */
469     usleep(1e6 * pmapRandom(rouletteState));
470     fprintf(stderr, "Proc %d: PID = %d "
471     "(waiting 10 sec to attach debugger...)\n",
472     proc, getpid());
473     /* Allow time for debugger to attach to child process */
474     sleep(10);
475     #endif
476    
477 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
478 rschregle 2.11 lastNumPhotons [t] = 0;
479    
480     /* =============================================================
481     * 2-PASS PHOTON DISTRIBUTION
482     * Pass 1 (pre): emit fraction of target photon count
483     * Pass 2 (main): based on outcome of pass 1, estimate remaining
484     * number of photons to emit to approximate target
485     * count
486     * ============================================================= */
487 greg 2.1 do {
488 rschregle 2.11 double numEmit;
489 greg 2.1
490 rschregle 2.11 if (!passCnt) {
491     /* INIT PASS 1 */
492     /* Skip if no photons contributed after sufficient
493     * iterations; make it clear to user which photon maps are
494     * missing so (s)he can check geometry and materials */
495     if (++prePassCnt > maxPreDistrib) {
496 rschregle 2.13 sprintf(errmsg, "proc %d: too many prepasses", proc);
497 rschregle 2.11
498     for (t = 0; t < NUM_PMAP_TYPES; t++)
499     if (pmaps [t] && !pmaps [t] -> numPhotons) {
500     sprintf(errmsg2, ", no %s photons stored",
501     pmapName [t]);
502     strcat(errmsg, errmsg2);
503     }
504    
505     error(USER, errmsg);
506     break;
507 greg 2.1 }
508 rschregle 2.11
509     /* Num to emit is fraction of minimum target count */
510     numEmit = FHUGE;
511 greg 2.1
512 rschregle 2.11 for (t = 0; t < NUM_PMAP_TYPES; t++)
513     if (pmaps [t])
514     numEmit = min(pmaps [t] -> distribTarget, numEmit);
515    
516     numEmit *= preDistrib;
517 greg 2.1 }
518 rschregle 2.11 else {
519     /* INIT PASS 2 */
520     /* Based on the outcome of the predistribution we can now
521     * estimate how many more photons we have to emit for each
522     * photon map to meet its respective target count. This
523     * value is clamped to 0 in case the target has already been
524     * exceeded in the pass 1. */
525     double maxDistribRatio = 0;
526    
527     /* Set the distribution ratio for each map; this indicates
528     * how many photons of each respective type are stored per
529     * emitted photon, and is used as probability for storing a
530     * photon by newPhoton(). Since this biases the photon
531     * density, newPhoton() promotes the flux of stored photons
532     * to compensate. */
533     for (t = 0; t < NUM_PMAP_TYPES; t++)
534     if ((pm = pmaps [t])) {
535     pm -> distribRatio = (double)pm -> distribTarget /
536     pm -> numPhotons - 1;
537    
538     /* Check if photon map "overflowed", i.e. exceeded its
539     * target count in the prepass; correcting the photon
540     * flux via the distribution ratio is no longer
541     * possible, as no more photons of this type will be
542     * stored, so notify the user rather than deliver
543     * incorrect results. In future we should handle this
544     * more intelligently by using the photonFlux in each
545     * photon map to individually correct the flux after
546     * distribution. */
547     if (pm -> distribRatio <= FTINY) {
548     sprintf(errmsg, "%s photon map overflow in "
549     "prepass, reduce -apD", pmapName [t]);
550     error(INTERNAL, errmsg);
551     }
552    
553     maxDistribRatio = max(pm -> distribRatio,
554     maxDistribRatio);
555     }
556 greg 2.1
557 rschregle 2.11 /* Normalise distribution ratios and calculate number of
558     * photons to emit in main pass */
559     for (t = 0; t < NUM_PMAP_TYPES; t++)
560     if ((pm = pmaps [t]))
561     pm -> distribRatio /= maxDistribRatio;
562    
563     if ((numEmit = localNumEmitted * maxDistribRatio) < FTINY)
564     /* No photons left to distribute in main pass */
565     break;
566     }
567    
568 rschregle 2.13 /* Update shared completion counter for progreport by parent */
569 rschregle 2.11 photonCnt -> numComplete += numEmit;
570    
571     /* PHOTON DISTRIBUTION LOOP */
572     for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
573     unsigned portCnt = 0;
574     emap.src = source + srcIdx;
575    
576     do { /* Need at least one iteration if no ports! */
577     emap.port = emap.src -> sflags & SDISTANT
578     ? photonPorts + portCnt : NULL;
579     photonPartition [emap.src -> so -> otype] (&emap);
580    
581 rschregle 2.13 if (verbose && !proc) {
582     /* Output from subproc 0 only to avoid race condition
583     * on console I/O */
584 rschregle 2.11 if (!passCnt)
585 rschregle 2.13 sprintf(errmsg, "\tPREPASS %d on source %s ",
586 rschregle 2.11 prePassCnt, source [srcIdx].so -> oname);
587     else
588 rschregle 2.13 sprintf(errmsg, "\tMAIN PASS on source %s ",
589 rschregle 2.11 source [srcIdx].so -> oname);
590 rschregle 2.13
591 rschregle 2.11 if (emap.port) {
592     sprintf(errmsg2, "via port %s ",
593     photonPorts [portCnt].so -> oname);
594     strcat(errmsg, errmsg2);
595     }
596 rschregle 2.13
597 rschregle 2.11 sprintf(errmsg2, "(%lu partitions)\n",
598     emap.numPartitions);
599     strcat(errmsg, errmsg2);
600     eputs(errmsg);
601 rschregle 2.13 #if NIX
602 rschregle 2.11 fflush(stderr);
603 rschregle 2.13 #endif
604 rschregle 2.11 }
605 greg 2.1
606 rschregle 2.11 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
607     emap.partitionCnt++) {
608     double partNumEmit;
609     unsigned long partEmitCnt;
610    
611     /* Get photon origin within current source partishunn
612     * and build emission map */
613     photonOrigin [emap.src -> so -> otype] (&emap);
614     initPhotonEmission(&emap, pdfSamples);
615    
616     /* Number of photons to emit from ziss partishunn --
617     * proportional to flux; photon ray weight and scalar
618 rschregle 2.13 * flux are uniform (latter only varying in RGB). */
619 rschregle 2.11 partNumEmit = numEmit * colorAvg(emap.partFlux) /
620     totalFlux;
621     partEmitCnt = (unsigned long)partNumEmit;
622    
623     /* Probabilistically account for fractional photons */
624     if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
625     partEmitCnt++;
626    
627     /* Update local and shared (global) emission counter */
628     photonCnt -> numEmitted += partEmitCnt;
629     localNumEmitted += partEmitCnt;
630    
631     /* Integer counter avoids FP rounding errors during
632     * iteration */
633     while (partEmitCnt--) {
634     RAY photonRay;
635    
636     /* Emit photon based on PDF and trace through scene
637     * until absorbed/leaked */
638     emitPhoton(&emap, &photonRay);
639 rschregle 2.13 #if 1
640     if (emap.port)
641     /* !!! PHOTON PORT REJECTION SAMPLING HACK: set
642     * !!! photon port as fake hit object for
643     * !!! primary ray to check for intersection in
644     * !!! tracePhoton() */
645     photonRay.ro = emap.port -> so;
646     #endif
647 rschregle 2.11 tracePhoton(&photonRay);
648     }
649 rschregle 2.13
650 rschregle 2.11 /* Update shared global photon count for each pmap */
651     for (t = 0; t < NUM_PMAP_TYPES; t++)
652     if (pmaps [t]) {
653     photonCnt -> numPhotons [t] +=
654     pmaps [t] -> numPhotons - lastNumPhotons [t];
655     lastNumPhotons [t] = pmaps [t] -> numPhotons;
656     }
657 rschregle 2.13 #if !NIX
658     /* Synchronous progress report on Windoze */
659     if (!proc && photonRepTime > 0 &&
660     time(NULL) >= repLastTime + photonRepTime) {
661     repEmitted = repProgress = photonCnt -> numEmitted;
662     repComplete = photonCnt -> numComplete;
663     pmapDistribReport();
664     }
665     #endif
666 rschregle 2.11 }
667 greg 2.1
668 rschregle 2.11 portCnt++;
669     } while (portCnt < numPhotonPorts);
670     }
671    
672     for (t = 0; t < NUM_PMAP_TYPES; t++)
673     if (pmaps [t] && !pmaps [t] -> numPhotons) {
674     /* Double preDistrib in case a photon map is empty and
675     * redo pass 1 --> possibility of infinite loop for
676     * pathological scenes (e.g. absorbing materials) */
677     preDistrib *= 2;
678     break;
679 greg 2.1 }
680 rschregle 2.11
681 rschregle 2.13 if (t >= NUM_PMAP_TYPES)
682 rschregle 2.11 /* No empty photon maps found; now do pass 2 */
683     passCnt++;
684     } while (passCnt < 2);
685    
686 rschregle 2.13 /* Flush heap buffa for every pmap one final time;
687     * avoids potential data corruption! */
688 rschregle 2.11 for (t = 0; t < NUM_PMAP_TYPES; t++)
689     if (pmaps [t]) {
690     flushPhotonHeap(pmaps [t]);
691 rschregle 2.13 /* Heap file closed automatically on exit
692     fclose(pmaps [t] -> heap); */
693 rschregle 2.11 #ifdef DEBUG_PMAP
694 rschregle 2.13 sprintf(errmsg, "Proc %d: total %ld photons\n", proc,
695 rschregle 2.11 pmaps [t] -> numPhotons);
696     eputs(errmsg);
697     #endif
698     }
699 rschregle 2.13 #if NIX
700     /* Terminate subprocess */
701 rschregle 2.11 exit(0);
702 rschregle 2.13 #endif
703 greg 2.1 }
704 rschregle 2.11 else if (pid < 0)
705     error(SYSTEM, "failed to fork subprocess in distribPhotons");
706     }
707    
708 rschregle 2.13 #if NIX
709 rschregle 2.11 /* PARENT PROCESS CONTINUES HERE */
710     #ifdef SIGCONT
711 rschregle 2.13 /* Enable progress report signal handler */
712 rschregle 2.11 signal(SIGCONT, pmapDistribReport);
713 rschregle 2.13 #endif
714     /* Wait for subprocesses complete while reporting progress */
715 rschregle 2.11 proc = numProc;
716     while (proc) {
717     while (waitpid(-1, &stat, WNOHANG) > 0) {
718     /* Subprocess exited; check status */
719     if (!WIFEXITED(stat) || WEXITSTATUS(stat))
720     error(USER, "failed photon distribution");
721    
722     --proc;
723     }
724    
725     /* Nod off for a bit and update progress */
726     sleep(1);
727 rschregle 2.13
728     /* Asynchronous progress report from shared subprocess counters */
729 rschregle 2.11 repEmitted = repProgress = photonCnt -> numEmitted;
730 rschregle 2.13 repComplete = photonCnt -> numComplete;
731 rschregle 2.11
732 rschregle 2.13 repProgress = repComplete = 0;
733 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
734 rschregle 2.11 if ((pm = pmaps [t])) {
735     /* Get global photon count from shmem updated by subprocs */
736 rschregle 2.13 repProgress += pm -> numPhotons = photonCnt -> numPhotons [t];
737     repComplete += pm -> distribTarget;
738 greg 2.1 }
739 rschregle 2.13 repComplete *= numProc;
740 rschregle 2.11
741     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
742     pmapDistribReport();
743     #ifdef SIGCONT
744     else signal(SIGCONT, pmapDistribReport);
745     #endif
746     }
747 rschregle 2.13 #endif /* NIX */
748 greg 2.1
749     /* ===================================================================
750 rschregle 2.11 * POST-DISTRIBUTION - Set photon flux and build data struct for photon
751     * storage, etc.
752 greg 2.1 * =================================================================== */
753 rschregle 2.11 #ifdef SIGCONT
754 rschregle 2.13 /* Reset signal handler */
755 rschregle 2.11 signal(SIGCONT, SIG_DFL);
756     #endif
757 greg 2.1 free(emap.samples);
758    
759 rschregle 2.13 /* Set photon flux */
760 rschregle 2.11 totalFlux /= photonCnt -> numEmitted;
761 rschregle 2.13 #if NIX
762 rschregle 2.11 /* Photon counters no longer needed, unmap shared memory */
763     munmap(photonCnt, sizeof(*photonCnt));
764     close(shmFile);
765 rschregle 2.13 unlink(shmFname);
766 rschregle 2.11 #else
767 rschregle 2.13 free(photonCnt);
768 rschregle 2.11 #endif
769 rschregle 2.13 if (verbose)
770     eputs("\n");
771    
772 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
773 rschregle 2.8 if (pmaps [t]) {
774 rschregle 2.13 if (verbose) {
775     sprintf(errmsg, "Building %s photon map\n", pmapName [t]);
776 greg 2.1 eputs(errmsg);
777 rschregle 2.13 #if NIX
778 greg 2.1 fflush(stderr);
779 rschregle 2.13 #endif
780 greg 2.1 }
781 rschregle 2.11
782     /* Build underlying data structure; heap is destroyed */
783     buildPhotonMap(pmaps [t], &totalFlux, NULL, numProc);
784 greg 2.1 }
785 rschregle 2.13
786 greg 2.1 /* Precompute photon irradiance if necessary */
787 rschregle 2.13 if (preCompPmap) {
788     if (verbose)
789     eputs("\n");
790 greg 2.1 preComputeGlobal(preCompPmap);
791 rschregle 2.13 }
792    
793     if (verbose)
794     eputs("\n");
795 greg 2.1 }