ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmap.c
Revision: 2.18
Committed: Fri Feb 19 02:10:35 2021 UTC (3 years, 2 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, HEAD
Changes since 2.17: +15 -6 lines
Log Message:
Modified photon port rejection sampling in tracePhoton() to better handle
faceted photon ports by testing for identical material

File Contents

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