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, 3 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

# User Rev Content
1 greg 2.9 #ifndef lint
2 rschregle 2.18 static const char RCSid[] = "$Id: pmap.c,v 2.17 2018/12/18 22:14:04 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.18 supported by the German Research Foundation
13     (DFG LU-204/10-2, "Fassadenintegrierte Regelsysteme FARESYS")
14 rschregle 2.4 (c) Lucerne University of Applied Sciences and Arts,
15 rschregle 2.18 supported by the Swiss National Science Foundation
16     (SNSF #147053, "Daylight Redirecting Components")
17 rschregle 2.11 ======================================================================
18 greg 2.1
19 rschregle 2.18 $Id: pmap.c,v 2.17 2018/12/18 22:14:04 rschregle Exp $
20 greg 2.1 */
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 rschregle 2.18 #include "otspecial.h"
32 greg 2.1 #include <time.h>
33 rschregle 2.13 #if NIX
34     #include <sys/stat.h>
35     #include <sys/mman.h>
36     #include <sys/wait.h>
37     #endif
38 greg 2.1
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 greg 2.7 savePhotonMap(pmaps [t], pmaps [t] -> fileName, argc, argv);
47 greg 2.1 }
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 rschregle 2.17 RREAL xi1, cosTheta, phi, du, dv;
58 greg 2.1 const float cext = colorAvg(ray -> cext),
59 rschregle 2.16 albedo = colorAvg(ray -> albedo),
60 rschregle 2.17 gecc = ray -> gecc, gecc2 = sqr(gecc);
61 greg 2.1 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 rschregle 2.16 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 greg 2.1 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 rschregle 2.15 #if 0
88 rschregle 2.11 if (albedo > FTINY && ray -> rlvl > 0)
89 rschregle 2.15 #else
90     /* Store volume photons unconditionally in mist to also account for
91     direct inscattering from sources */
92     if (albedo > FTINY)
93 rschregle 2.17 #endif
94 greg 2.1 /* Add to volume photon map */
95 rschregle 2.11 newPhoton(volumePmap, ray);
96 greg 2.1
97     /* Absorbed? */
98 rschregle 2.11 if (pmapRandom(rouletteState) > albedo)
99     return 0;
100 greg 2.1
101     /* Colour bleeding without attenuation (?) */
102     multcolor(ray -> rcol, ray -> albedo);
103     scalecolor(ray -> rcol, 1 / albedo);
104    
105     /* Scatter photon */
106 rschregle 2.17 xi1 = pmapRandom(scatterState);
107 rschregle 2.16 cosTheta = ray -> gecc <= FTINY
108 rschregle 2.17 ? 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 greg 2.1
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 rschregle 2.17
131 greg 2.1 ray -> rlvl++;
132     ray -> rmax = -log(pmapRandom(mediumState)) / cext;
133     }
134 rschregle 2.16
135     /* Passed through medium until intersecting local object */
136 greg 2.1 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 rschregle 2.16 colorNorm(ray -> rcol);
143    
144 greg 2.1 return 1;
145     }
146    
147    
148    
149     void tracePhoton (RAY *ray)
150     /* Follow photon as it bounces around... */
151     {
152     long mod;
153 rschregle 2.13 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 greg 2.1
163     if (ray -> rlvl > photonMaxBounce) {
164 rschregle 2.5 #ifdef PMAP_RUNAWAY_WARN
165 greg 2.1 error(WARNING, "runaway photon!");
166 rschregle 2.5 #endif
167 greg 2.1 return;
168     }
169 rschregle 2.5
170 greg 2.1 if (colorAvg(ray -> cext) > FTINY && !photonParticipate(ray))
171     return;
172 rschregle 2.13
173 greg 2.1 if (localhit(ray, &thescene)) {
174     mod = ray -> ro -> omod;
175 rschregle 2.13
176 rschregle 2.18 /* XXX: Is port -> omod != mod sufficient here? Probably not... */
177     if (
178     port && ray -> ro != port &&
179     findmaterial(port) != findmaterial(ray -> ro)
180     ) {
181 rschregle 2.13 /* !!! PHOTON PORT REJECTION SAMPLING HACK !!!
182 rschregle 2.18 * 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 rschregle 2.13 * 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 greg 2.1 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 rschregle 2.11 /* 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 greg 2.1
226 rschregle 2.11 repComplete = numPreComp = finalGather * pmap -> numPhotons;
227 greg 2.1
228 rschregle 2.13 if (verbose) {
229     sprintf(errmsg,
230     "\nPrecomputing irradiance for %ld global photons\n",
231 rschregle 2.11 numPreComp);
232 greg 2.1 eputs(errmsg);
233 rschregle 2.13 #if NIX
234 greg 2.1 fflush(stderr);
235 rschregle 2.13 #endif
236 greg 2.1 }
237    
238 rschregle 2.11 /* 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 greg 2.1 }
249 rschregle 2.11
250 greg 2.1 /* Record start time, baby */
251     repStartTime = time(NULL);
252 rschregle 2.11 #ifdef SIGCONT
253     signal(SIGCONT, pmapPreCompReport);
254     #endif
255 greg 2.1 repProgress = 0;
256    
257 rschregle 2.11 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 rschregle 2.13 * avoid duplicates and clustering */
263 rschregle 2.11 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 greg 2.1
279 rschregle 2.11 /* Update progress */
280 greg 2.1 repProgress++;
281    
282     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
283     pmapPreCompReport();
284 rschregle 2.11 #ifdef SIGCONT
285     else signal(SIGCONT, pmapPreCompReport);
286     #endif
287 greg 2.1 }
288    
289 rschregle 2.11 /* 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 greg 2.1
300 rschregle 2.13 if (verbose) {
301     eputs("\nRebuilding precomputed photon map\n");
302     #if NIX
303 greg 2.1 fflush(stderr);
304 rschregle 2.13 #endif
305 greg 2.1 }
306 rschregle 2.11
307     /* Rebuild underlying data structure, destroying heap */
308     buildPhotonMap(pmap, NULL, NULL, 1);
309 greg 2.1 }
310    
311    
312    
313 rschregle 2.11 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 rschregle 2.13 char errmsg2 [128], shmFname [PMAP_TMPFNLEN];
324 rschregle 2.11 unsigned t, srcIdx, proc;
325     double totalFlux = 0;
326     int shmFile, stat, pid;
327     PhotonMap *pm;
328     PhotonCnt *photonCnt;
329 greg 2.1
330 rschregle 2.8 for (t = 0; t < NUM_PMAP_TYPES && !pmaps [t]; t++);
331 rschregle 2.11
332 greg 2.1 if (t >= NUM_PMAP_TYPES)
333 rschregle 2.11 error(USER, "no photon maps defined in distribPhotons");
334 greg 2.1
335     if (!nsources)
336 rschregle 2.11 error(USER, "no light sources in distribPhotons");
337 greg 2.1
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 rschregle 2.11 error(INTERNAL, "can't allocate source partitions in distribPhotons");
346 greg 2.1
347     /* Initialise all defined photon maps */
348     for (t = 0; t < NUM_PMAP_TYPES; t++)
349 rschregle 2.11 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 rschregle 2.13
356     if (!pmaps [t] -> distribTarget)
357     error(INTERNAL, "no photons to distribute in distribPhotons");
358 rschregle 2.11 }
359 greg 2.1
360     initPhotonEmissionFuncs();
361     initPhotonScatterFuncs();
362    
363 rschregle 2.14 /* Get photon ports from modifier list */
364     getPhotonPorts(photonPortList);
365 greg 2.1
366     /* Get photon sensor modifiers */
367     getPhotonSensors(photonSensorList);
368    
369 rschregle 2.13 #if NIX
370 rschregle 2.11 /* Set up shared mem for photon counters (zeroed by ftruncate) */
371 rschregle 2.13 strcpy(shmFname, PMAP_TMPFNAME);
372 rschregle 2.11 shmFile = mkstemp(shmFname);
373    
374 rschregle 2.13 if (shmFile < 0 || ftruncate(shmFile, sizeof(*photonCnt)) < 0)
375     error(SYSTEM, "failed shared mem init in distribPhotons");
376 rschregle 2.11
377     photonCnt = mmap(NULL, sizeof(*photonCnt), PROT_READ | PROT_WRITE,
378     MAP_SHARED, shmFile, 0);
379    
380     if (photonCnt == MAP_FAILED)
381 rschregle 2.13 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 greg 2.1
401     /* ===================================================================
402     * FLUX INTEGRATION - Get total photon flux from light sources
403     * =================================================================== */
404 rschregle 2.11 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
405 greg 2.1 unsigned portCnt = 0;
406     emap.src = source + srcIdx;
407    
408 rschregle 2.11 do { /* Need at least one iteration if no ports! */
409 greg 2.1 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
410     : NULL;
411     photonPartition [emap.src -> so -> otype] (&emap);
412    
413 rschregle 2.13 if (verbose) {
414     sprintf(errmsg, "\tIntegrating flux from source %s ",
415 greg 2.1 source [srcIdx].so -> oname);
416 rschregle 2.13
417 greg 2.1 if (emap.port) {
418     sprintf(errmsg2, "via port %s ",
419     photonPorts [portCnt].so -> oname);
420     strcat(errmsg, errmsg2);
421     }
422 rschregle 2.13
423     sprintf(errmsg2, "(%lu partitions)\n", emap.numPartitions);
424 greg 2.1 strcat(errmsg, errmsg2);
425     eputs(errmsg);
426 rschregle 2.13 #if NIX
427 greg 2.1 fflush(stderr);
428 rschregle 2.13 #endif
429 greg 2.1 }
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 rschregle 2.13
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 greg 2.1
452 rschregle 2.11 /* MAIN LOOP */
453     for (proc = 0; proc < numProc; proc++) {
454 rschregle 2.13 #if NIX
455 rschregle 2.11 if (!(pid = fork())) {
456 rschregle 2.13 /* 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 rschregle 2.11 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 greg 2.1
467 rschregle 2.11 /* Seed RNGs from PID for decorellated photon distribution */
468     pmapSeed(randSeed + proc, partState);
469 rschregle 2.13 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 rschregle 2.15
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 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
487 rschregle 2.11 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 greg 2.1 do {
497 rschregle 2.11 double numEmit;
498 greg 2.1
499 rschregle 2.11 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 rschregle 2.13 sprintf(errmsg, "proc %d: too many prepasses", proc);
506 rschregle 2.11
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 greg 2.1 }
517 rschregle 2.11
518     /* Num to emit is fraction of minimum target count */
519     numEmit = FHUGE;
520 greg 2.1
521 rschregle 2.11 for (t = 0; t < NUM_PMAP_TYPES; t++)
522     if (pmaps [t])
523     numEmit = min(pmaps [t] -> distribTarget, numEmit);
524    
525     numEmit *= preDistrib;
526 greg 2.1 }
527 rschregle 2.11 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 greg 2.1
566 rschregle 2.11 /* 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 rschregle 2.13 /* Update shared completion counter for progreport by parent */
578 rschregle 2.11 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 rschregle 2.13 if (verbose && !proc) {
591     /* Output from subproc 0 only to avoid race condition
592     * on console I/O */
593 rschregle 2.11 if (!passCnt)
594 rschregle 2.13 sprintf(errmsg, "\tPREPASS %d on source %s ",
595 rschregle 2.11 prePassCnt, source [srcIdx].so -> oname);
596     else
597 rschregle 2.13 sprintf(errmsg, "\tMAIN PASS on source %s ",
598 rschregle 2.11 source [srcIdx].so -> oname);
599 rschregle 2.13
600 rschregle 2.11 if (emap.port) {
601     sprintf(errmsg2, "via port %s ",
602     photonPorts [portCnt].so -> oname);
603     strcat(errmsg, errmsg2);
604     }
605 rschregle 2.13
606 rschregle 2.11 sprintf(errmsg2, "(%lu partitions)\n",
607     emap.numPartitions);
608     strcat(errmsg, errmsg2);
609     eputs(errmsg);
610 rschregle 2.13 #if NIX
611 rschregle 2.11 fflush(stderr);
612 rschregle 2.13 #endif
613 rschregle 2.11 }
614 greg 2.1
615 rschregle 2.11 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 rschregle 2.13 * flux are uniform (latter only varying in RGB). */
628 rschregle 2.11 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 rschregle 2.13 #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 rschregle 2.11 tracePhoton(&photonRay);
657     }
658 rschregle 2.13
659 rschregle 2.11 /* 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 rschregle 2.13 #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 rschregle 2.11 }
676 greg 2.1
677 rschregle 2.11 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 greg 2.1 }
689 rschregle 2.11
690 rschregle 2.13 if (t >= NUM_PMAP_TYPES)
691 rschregle 2.11 /* No empty photon maps found; now do pass 2 */
692     passCnt++;
693     } while (passCnt < 2);
694    
695 rschregle 2.13 /* Flush heap buffa for every pmap one final time;
696     * avoids potential data corruption! */
697 rschregle 2.11 for (t = 0; t < NUM_PMAP_TYPES; t++)
698     if (pmaps [t]) {
699     flushPhotonHeap(pmaps [t]);
700 rschregle 2.13 /* Heap file closed automatically on exit
701     fclose(pmaps [t] -> heap); */
702 rschregle 2.11 #ifdef DEBUG_PMAP
703 rschregle 2.13 sprintf(errmsg, "Proc %d: total %ld photons\n", proc,
704 rschregle 2.11 pmaps [t] -> numPhotons);
705     eputs(errmsg);
706     #endif
707     }
708 rschregle 2.13 #if NIX
709     /* Terminate subprocess */
710 rschregle 2.11 exit(0);
711 rschregle 2.13 #endif
712 greg 2.1 }
713 rschregle 2.11 else if (pid < 0)
714     error(SYSTEM, "failed to fork subprocess in distribPhotons");
715     }
716    
717 rschregle 2.13 #if NIX
718 rschregle 2.11 /* PARENT PROCESS CONTINUES HERE */
719     #ifdef SIGCONT
720 rschregle 2.13 /* Enable progress report signal handler */
721 rschregle 2.11 signal(SIGCONT, pmapDistribReport);
722 rschregle 2.13 #endif
723     /* Wait for subprocesses complete while reporting progress */
724 rschregle 2.11 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 rschregle 2.13
737     /* Asynchronous progress report from shared subprocess counters */
738 rschregle 2.11 repEmitted = repProgress = photonCnt -> numEmitted;
739 rschregle 2.13 repComplete = photonCnt -> numComplete;
740 rschregle 2.11
741 rschregle 2.13 repProgress = repComplete = 0;
742 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
743 rschregle 2.11 if ((pm = pmaps [t])) {
744     /* Get global photon count from shmem updated by subprocs */
745 rschregle 2.13 repProgress += pm -> numPhotons = photonCnt -> numPhotons [t];
746     repComplete += pm -> distribTarget;
747 greg 2.1 }
748 rschregle 2.13 repComplete *= numProc;
749 rschregle 2.11
750     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
751     pmapDistribReport();
752     #ifdef SIGCONT
753     else signal(SIGCONT, pmapDistribReport);
754     #endif
755     }
756 rschregle 2.13 #endif /* NIX */
757 greg 2.1
758     /* ===================================================================
759 rschregle 2.11 * POST-DISTRIBUTION - Set photon flux and build data struct for photon
760     * storage, etc.
761 greg 2.1 * =================================================================== */
762 rschregle 2.11 #ifdef SIGCONT
763 rschregle 2.13 /* Reset signal handler */
764 rschregle 2.11 signal(SIGCONT, SIG_DFL);
765     #endif
766 greg 2.1 free(emap.samples);
767    
768 rschregle 2.13 /* Set photon flux */
769 rschregle 2.11 totalFlux /= photonCnt -> numEmitted;
770 rschregle 2.13 #if NIX
771 rschregle 2.11 /* Photon counters no longer needed, unmap shared memory */
772     munmap(photonCnt, sizeof(*photonCnt));
773     close(shmFile);
774 rschregle 2.13 unlink(shmFname);
775 rschregle 2.11 #else
776 rschregle 2.13 free(photonCnt);
777 rschregle 2.11 #endif
778 rschregle 2.13 if (verbose)
779     eputs("\n");
780    
781 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
782 rschregle 2.8 if (pmaps [t]) {
783 rschregle 2.13 if (verbose) {
784     sprintf(errmsg, "Building %s photon map\n", pmapName [t]);
785 greg 2.1 eputs(errmsg);
786 rschregle 2.13 #if NIX
787 greg 2.1 fflush(stderr);
788 rschregle 2.13 #endif
789 greg 2.1 }
790 rschregle 2.11
791     /* Build underlying data structure; heap is destroyed */
792     buildPhotonMap(pmaps [t], &totalFlux, NULL, numProc);
793 greg 2.1 }
794 rschregle 2.13
795 greg 2.1 /* Precompute photon irradiance if necessary */
796 rschregle 2.13 if (preCompPmap) {
797     if (verbose)
798     eputs("\n");
799 greg 2.1 preComputeGlobal(preCompPmap);
800 rschregle 2.13 }
801    
802     if (verbose)
803     eputs("\n");
804 greg 2.1 }