ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapcontrib.c
Revision: 2.12
Committed: Tue May 17 17:39:47 2016 UTC (8 years, 11 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.11: +587 -296 lines
Log Message:
Initial import of ooC photon map

File Contents

# User Rev Content
1 greg 2.9 #ifndef lint
2 rschregle 2.12 static const char RCSid[] = "$Id: pmapcontrib.c,v 4.30.1.12 2016/05/11 12:40:00 taschreg Exp taschreg $";
3 greg 2.9 #endif
4 rschregle 2.12
5 greg 2.1 /*
6 rschregle 2.12 ======================================================================
7 greg 2.1 Photon map support for light source contributions
8    
9     Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
10 rschregle 2.4 (c) Lucerne University of Applied Sciences and Arts,
11 rschregle 2.12 supported by the Swiss National Science Foundation (SNSF, #147053)
12     ======================================================================
13 greg 2.1
14 rschregle 2.12 $Id: pmapcontrib.c,v 4.30.1.12 2016/05/11 12:40:00 taschreg Exp taschreg $
15 greg 2.1 */
16    
17    
18     #include "pmapcontrib.h"
19     #include "pmapmat.h"
20     #include "pmapsrc.h"
21     #include "pmaprand.h"
22     #include "pmapio.h"
23     #include "pmapdiag.h"
24     #include "rcontrib.h"
25     #include "otypes.h"
26 rschregle 2.12 #include <sys/mman.h>
27     #include <sys/wait.h>
28 greg 2.1
29    
30    
31     static void setPmapContribParams (PhotonMap *pmap, LUTAB *srcContrib)
32     /* Set parameters for light source contributions */
33     {
34     /* Set light source modifier list and appropriate callback to extract
35     * their contributions from the photon map */
36     if (pmap) {
37     pmap -> srcContrib = srcContrib;
38     pmap -> lookup = photonContrib;
39     /* Ensure we get all requested photon contribs during lookups */
40     pmap -> gatherTolerance = 1.0;
41     }
42     }
43    
44    
45    
46     static void checkPmapContribs (const PhotonMap *pmap, LUTAB *srcContrib)
47     /* Check modifiers for light source contributions */
48     {
49 rschregle 2.12 const PhotonPrimary *primary = pmap -> primaries;
50     PhotonPrimaryIdx i, found = 0;
51 greg 2.1 OBJREC *srcMod;
52    
53     /* Make sure at least one of the modifiers is actually in the pmap,
54     * otherwise findPhotons() winds up in an infinite loop! */
55 rschregle 2.12 for (i = pmap -> numPrimary; i; --i, ++primary) {
56 greg 2.1 if (primary -> srcIdx < 0 || primary -> srcIdx >= nsources)
57     error(INTERNAL, "invalid light source index in photon map");
58    
59 greg 2.8 srcMod = findmaterial(source [primary -> srcIdx].so);
60 greg 2.1 if ((MODCONT*)lu_find(srcContrib, srcMod -> oname) -> data)
61     ++found;
62     }
63    
64     if (!found)
65     error(USER, "modifiers not in photon map");
66     }
67    
68    
69    
70     void initPmapContrib (LUTAB *srcContrib, unsigned numSrcContrib)
71     {
72     unsigned t;
73    
74     for (t = 0; t < NUM_PMAP_TYPES; t++)
75     if (photonMaps [t] && t != PMAP_TYPE_CONTRIB) {
76     sprintf(errmsg, "%s photon map does not support contributions",
77     pmapName [t]);
78     error(USER, errmsg);
79     }
80    
81     /* Get params */
82     setPmapContribParams(contribPmap, srcContrib);
83    
84     if (contribPhotonMapping) {
85     if (contribPmap -> maxGather < numSrcContrib) {
86     /* Adjust density estimate bandwidth if lower than modifier
87     * count, otherwise contributions are missing */
88     error(WARNING, "contrib density estimate bandwidth too low, "
89     "adjusting to modifier count");
90     contribPmap -> maxGather = numSrcContrib;
91     }
92    
93     /* Sanity check */
94     checkPmapContribs(contribPmap, srcContrib);
95     }
96     }
97    
98    
99    
100 rschregle 2.12 static PhotonPrimaryIdx newPhotonPrimary (PhotonMap *pmap,
101     const RAY *primRay,
102     FILE *primHeap)
103     /* Add primary ray for emitted photon and save light source index, origin on
104     * source, and emitted direction; used by contrib photons. The current
105     * primary is stored in pmap -> lastPrimary. If the previous primary
106     * contributed photons (has srcIdx >= 0), it's appended to primHeap. If
107     * primRay == NULL, the current primary is still flushed, but no new primary
108     * is set. Returns updated primary counter pmap -> numPrimary. */
109 greg 2.1 {
110 rschregle 2.12 if (!pmap || !primHeap)
111     return 0;
112 greg 2.1
113 rschregle 2.12 /* Check if last primary ray has spawned photons (srcIdx >= 0, see
114     * newPhoton()), in which case we write it to the primary heap file
115     * before overwriting it */
116     if (pmap -> lastPrimary.srcIdx >= 0) {
117     if (!fwrite(&pmap -> lastPrimary, sizeof(PhotonPrimary), 1, primHeap))
118     error(SYSTEM, "failed writing photon primary in newPhotonPrimary");
119    
120     pmap -> numPrimary++;
121     if (pmap -> numPrimary > PMAP_MAXPRIMARY)
122     error(INTERNAL, "photon primary overflow in newPhotonPrimary");
123     }
124    
125     /* Mark unused with negative source index until path spawns a photon (see
126     * newPhoton()) */
127     pmap -> lastPrimary.srcIdx = -1;
128    
129     if (primRay) {
130     FVECT dvec;
131    
132     /* Reverse incident direction to point to light source */
133     dvec [0] = -primRay -> rdir [0];
134     dvec [1] = -primRay -> rdir [1];
135     dvec [2] = -primRay -> rdir [2];
136     pmap -> lastPrimary.dir = encodedir(dvec);
137     #ifdef PMAP_PRIMARYPOS
138     VCOPY(pmap -> lastPrimary.pos, primRay -> rop);
139     #endif
140     }
141    
142     return pmap -> numPrimary;
143     }
144    
145 greg 2.1
146    
147 rschregle 2.12 #ifdef DEBUG_PMAP_CONTRIB
148     static int checkPrimaryHeap (FILE *file)
149     /* Check heap for ordered primaries */
150     {
151     Photon p, lastp;
152     int i, dup;
153    
154     rewind(file);
155     memset(&lastp, 0, sizeof(lastp));
156 greg 2.1
157 rschregle 2.12 while (fread(&p, sizeof(p), 1, file)) {
158     dup = 1;
159    
160     for (i = 0; i <= 2; i++) {
161     if (p.pos [i] < thescene.cuorg [i] ||
162     p.pos [i] > thescene.cuorg [i] + thescene.cusize) {
163    
164     sprintf(errmsg, "corrupt photon in heap at [%f, %f, %f]\n",
165     p.pos [0], p.pos [1], p.pos [2]);
166     error(WARNING, errmsg);
167     }
168    
169     dup &= p.pos [i] == lastp.pos [i];
170     }
171    
172     if (dup) {
173     sprintf(errmsg,
174     "consecutive duplicate photon in heap at [%f, %f, %f]\n",
175     p.pos [0], p.pos [1], p.pos [2]);
176 greg 2.1 error(WARNING, errmsg);
177 rschregle 2.12 }
178 greg 2.1 }
179    
180 rschregle 2.12 return 0;
181     }
182     #endif
183    
184    
185    
186     static PhotonPrimaryIdx buildPrimaries (PhotonMap *pmap, FILE **primaryHeap,
187     PhotonPrimaryIdx *primaryOfs,
188     unsigned numHeaps)
189     /* Consolidate per-subprocess photon primary heaps into the primary array
190     * pmap -> primaries. Returns offset for primary index linearisation in
191     * numPrimary. The heap files in primaryHeap are closed on return. */
192     {
193     PhotonPrimaryIdx heapLen;
194     unsigned heap;
195 greg 2.1
196 rschregle 2.12 if (!pmap || !primaryHeap || !primaryOfs || !numHeaps)
197     return 0;
198 greg 2.1
199 rschregle 2.12 pmap -> numPrimary = 0;
200    
201     for (heap = 0; heap < numHeaps; heap++) {
202     primaryOfs [heap] = pmap -> numPrimary;
203    
204     if (fseek(primaryHeap [heap], 0, SEEK_END))
205     error(SYSTEM, "failed photon primary seek in buildPrimaries");
206     pmap -> numPrimary += heapLen = ftell(primaryHeap [heap]) /
207     sizeof(PhotonPrimary);
208    
209     pmap -> primaries = realloc(pmap -> primaries,
210     pmap -> numPrimary *
211     sizeof(PhotonPrimary));
212     if (!pmap -> primaries)
213     error(SYSTEM, "failed photon primary alloc in buildPrimaries");
214    
215     rewind(primaryHeap [heap]);
216     if (fread(pmap -> primaries + primaryOfs [heap], sizeof(PhotonPrimary),
217     heapLen, primaryHeap [heap]) != heapLen)
218     error(SYSTEM, "failed reading photon primaries in buildPrimaries");
219 greg 2.1
220 rschregle 2.12 fclose(primaryHeap [heap]);
221     }
222    
223     return pmap -> numPrimary;
224     }
225 greg 2.6
226    
227 greg 2.7
228 rschregle 2.12 /* Defs for photon emission counter array passed by sub-processes to parent
229     * via shared memory */
230     typedef unsigned long PhotonContribCnt;
231 greg 2.7
232 rschregle 2.12 /* Indices for photon emission counter array: num photons stored and num
233     * emitted per source */
234     #define PHOTONCNT_NUMPHOT 0
235     #define PHOTONCNT_NUMEMIT(n) (1 + n)
236 greg 2.1
237    
238    
239 rschregle 2.12 void distribPhotonContrib (PhotonMap* pm, unsigned numProc)
240 greg 2.1 {
241 rschregle 2.12 EmissionMap emap;
242     char errmsg2 [128], shmFname [255];
243     unsigned srcIdx, proc;
244     int shmFile, stat, pid;
245     double *srcFlux, /* Emitted flux per light source */
246     srcDistribTarget; /* Target photon count per source */
247     PhotonContribCnt *photonCnt; /* Photon emission counter array */
248     const unsigned photonCntSize = sizeof(PhotonContribCnt) *
249     PHOTONCNT_NUMEMIT(nsources);
250     FILE *primaryHeap [numProc];
251     PhotonPrimaryIdx primaryOfs [numProc];
252    
253 greg 2.1 if (!pm)
254 rschregle 2.12 error(USER, "no photon map defined in distribPhotonContrib");
255 greg 2.1
256     if (!nsources)
257 rschregle 2.12 error(USER, "no light sources in distribPhotonContrib");
258    
259     if (nsources > MAXMODLIST)
260     error(USER, "too many light sources in distribPhotonContrib");
261    
262 greg 2.1 /* Allocate photon flux per light source; this differs for every
263     * source as all sources contribute the same number of distributed
264     * photons (srcDistribTarget), hence the number of photons emitted per
265     * source does not correlate with its emitted flux. The resulting flux
266     * per photon is therefore adjusted individually for each source. */
267     if (!(srcFlux = calloc(nsources, sizeof(double))))
268 rschregle 2.12 error(SYSTEM, "can't allocate source flux in distribPhotonContrib");
269 greg 2.1
270 rschregle 2.12 /* ===================================================================
271     * INITIALISATION - Set up emission and scattering funcs
272     * =================================================================== */
273 greg 2.1 emap.samples = NULL;
274     emap.src = NULL;
275     emap.maxPartitions = MAXSPART;
276     emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
277     if (!emap.partitions)
278 rschregle 2.12 error(USER, "can't allocate source partitions in distribPhotonContrib");
279 greg 2.1
280 rschregle 2.12 /* Initialise contrib photon map */
281 greg 2.1 initPhotonMap(pm, PMAP_TYPE_CONTRIB);
282 rschregle 2.12 initPhotonHeap(pm);
283 greg 2.1 initPhotonEmissionFuncs();
284     initPhotonScatterFuncs();
285    
286 rschregle 2.12 /* Per-subprocess / per-source target counts */
287     pm -> distribTarget /= numProc;
288     srcDistribTarget = nsources ? (double)pm -> distribTarget / nsources : 0;
289    
290 greg 2.1 /* Get photon ports if specified */
291     if (ambincl == 1)
292     getPhotonPorts();
293    
294     /* Get photon sensor modifiers */
295     getPhotonSensors(photonSensorList);
296    
297 rschregle 2.12 /* Set up shared mem for photon counters (zeroed by ftruncate) */
298     #if 0
299     snprintf(shmFname, 255, PMAP_SHMFNAME, getpid());
300     shmFile = shm_open(shmFname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
301     #else
302     strcpy(shmFname, PMAP_SHMFNAME);
303     shmFile = mkstemp(shmFname);
304     #endif
305    
306     if (shmFile < 0 || ftruncate(shmFile, photonCntSize) < 0)
307     error(SYSTEM, "failed shared mem init in distribPhotonContrib");
308 greg 2.1
309 rschregle 2.12 photonCnt = mmap(NULL, photonCntSize, PROT_READ | PROT_WRITE,
310     MAP_SHARED, shmFile, 0);
311    
312     if (photonCnt == MAP_FAILED)
313     error(SYSTEM, "failed shared mem mapping in distribPhotonContrib");
314    
315     /* =============================================================
316     * FLUX INTEGRATION - Get total flux emitted from light source
317     * ============================================================= */
318 greg 2.1 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
319 rschregle 2.12 unsigned portCnt = 0;
320    
321     srcFlux [srcIdx] = 0;
322 greg 2.1 emap.src = source + srcIdx;
323    
324     if (photonRepTime)
325     eputs("\n");
326 rschregle 2.12
327     do { /* Need at least one iteration if no ports! */
328     emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
329     : NULL;
330 greg 2.1 photonPartition [emap.src -> so -> otype] (&emap);
331    
332     if (photonRepTime) {
333     sprintf(errmsg, "Integrating flux from source %s (mod %s) ",
334     source [srcIdx].so -> oname,
335     objptr(source [srcIdx].so -> omod) -> oname);
336    
337     if (emap.port) {
338     sprintf(errmsg2, "via port %s ",
339     photonPorts [portCnt].so -> oname);
340     strcat(errmsg, errmsg2);
341     }
342    
343 rschregle 2.12 sprintf(errmsg2, "(%lu partitions)...\n", emap.numPartitions);
344 greg 2.1 strcat(errmsg, errmsg2);
345     eputs(errmsg);
346     fflush(stderr);
347     }
348    
349 rschregle 2.12 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
350 greg 2.1 emap.partitionCnt++) {
351     initPhotonEmission(&emap, pdfSamples);
352     srcFlux [srcIdx] += colorAvg(emap.partFlux);
353     }
354    
355     portCnt++;
356 rschregle 2.12 } while (portCnt < numPhotonPorts);
357    
358 greg 2.1 if (srcFlux [srcIdx] < FTINY) {
359     sprintf(errmsg, "source %s has zero emission",
360     source [srcIdx].so -> oname);
361     error(WARNING, errmsg);
362     }
363 rschregle 2.12 }
364    
365     if (photonRepTime)
366     eputs("\n");
367    
368     /* Init per-subprocess primary heap files */
369     for (proc = 0; proc < numProc; proc++)
370     if (!(primaryHeap [proc] = tmpfile()))
371     error(SYSTEM, "failed opening primary heap file in "
372     "distribPhotonContrib");
373    
374     /* MAIN LOOP */
375     for (proc = 0; proc < numProc; proc++) {
376     if (!(pid = fork())) {
377     /* SUBPROCESS ENTERS HERE;
378     * all opened and memory mapped files are inherited */
379    
380     /* Local photon counters for this subprocess */
381     unsigned long lastNumPhotons = 0, localNumEmitted = 0;
382     double photonFluxSum = 0; /* Running photon flux sum */
383    
384     /* Seed RNGs from PID for decorellated photon distribution */
385     pmapSeed(randSeed + proc, partState);
386     pmapSeed(randSeed + proc, emitState);
387     pmapSeed(randSeed + proc, cntState);
388     pmapSeed(randSeed + proc, mediumState);
389     pmapSeed(randSeed + proc, scatterState);
390     pmapSeed(randSeed + proc, rouletteState);
391    
392     /* =============================================================
393 greg 2.1 * 2-PASS PHOTON DISTRIBUTION
394     * Pass 1 (pre): emit fraction of target photon count
395 rschregle 2.12 * Pass 2 (main): based on outcome of pass 1, estimate remaining
396     * number of photons to emit to approximate target
397     * count
398     * ============================================================= */
399     for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
400     unsigned portCnt, passCnt = 0, prePassCnt = 0;
401     float srcPreDistrib = preDistrib;
402     double srcNumEmit = 0; /* # to emit from source */
403     unsigned long srcNumDistrib = pm -> numPhotons; /* # stored */
404    
405     if (srcFlux [srcIdx] < FTINY)
406     continue;
407    
408     while (passCnt < 2) {
409     if (!passCnt) {
410     /* INIT PASS 1 */
411     if (++prePassCnt > maxPreDistrib) {
412     /* Warn if no photons contributed after sufficient
413     * iterations */
414     sprintf(errmsg, "proc %d, source %s: "
415     "too many prepasses, skipped",
416     proc, source [srcIdx].so -> oname);
417     error(WARNING, errmsg);
418     break;
419     }
420    
421     /* Num to emit is fraction of target count */
422     srcNumEmit = srcPreDistrib * srcDistribTarget;
423 greg 2.1 }
424 rschregle 2.12 else {
425     /* INIT PASS 2 */
426     double srcPhotonFlux, avgPhotonFlux;
427    
428     /* Based on the outcome of the predistribution we can now
429     * figure out how many more photons we have to emit from
430     * the current source to meet the target count,
431     * srcDistribTarget. This value is clamped to 0 in case
432     * the target has already been exceeded in pass 1.
433     * srcNumEmit and srcNumDistrib is the number of photons
434     * emitted and distributed (stored) from the current
435     * source in pass 1, respectively. */
436     srcNumDistrib = pm -> numPhotons - srcNumDistrib;
437     srcNumEmit *= srcNumDistrib
438     ? max(srcDistribTarget/srcNumDistrib, 1) - 1
439     : 0;
440    
441     if (!srcNumEmit)
442     /* No photons left to distribute in main pass */
443     break;
444 greg 2.1
445 rschregle 2.12 srcPhotonFlux = srcFlux [srcIdx] / srcNumEmit;
446     avgPhotonFlux = photonFluxSum / (srcIdx + 1);
447    
448     if (avgPhotonFlux > 0 &&
449     srcPhotonFlux / avgPhotonFlux < FTINY) {
450     /* Skip source if its photon flux is grossly below the
451     * running average, indicating negligible contribs at
452     * the expense of excessive distribution time */
453     sprintf(errmsg, "proc %d, source %s: "
454     "itsy bitsy photon flux, skipped",
455     proc, source [srcIdx].so -> oname);
456     error(WARNING, errmsg);
457     srcNumEmit = 0;
458 greg 2.1 }
459 rschregle 2.12
460     /* Update sum of photon flux per light source */
461     photonFluxSum += srcPhotonFlux;
462 greg 2.1 }
463    
464 rschregle 2.12 portCnt = 0;
465     do { /* Need at least one iteration if no ports! */
466     emap.src = source + srcIdx;
467     emap.port = emap.src -> sflags & SDISTANT
468     ? photonPorts + portCnt : NULL;
469     photonPartition [emap.src -> so -> otype] (&emap);
470 greg 2.1
471 rschregle 2.12 if (photonRepTime && !proc) {
472     if (!passCnt)
473     sprintf(errmsg, "PREPASS %d on source %s (mod %s) ",
474     prePassCnt, source [srcIdx].so -> oname,
475     objptr(source[srcIdx].so->omod) -> oname);
476     else
477     sprintf(errmsg, "MAIN PASS on source %s (mod %s) ",
478     source [srcIdx].so -> oname,
479     objptr(source[srcIdx].so->omod) -> oname);
480    
481     if (emap.port) {
482     sprintf(errmsg2, "via port %s ",
483     photonPorts [portCnt].so -> oname);
484     strcat(errmsg, errmsg2);
485     }
486    
487     sprintf(errmsg2, "(%lu partitions)\n",
488     emap.numPartitions);
489     strcat(errmsg, errmsg2);
490     eputs(errmsg);
491     fflush(stderr);
492     }
493 greg 2.1
494 rschregle 2.12 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
495     emap.partitionCnt++) {
496     double partNumEmit;
497     unsigned long partEmitCnt;
498 greg 2.1
499 rschregle 2.12 /* Get photon origin within current source partishunn
500     * and build emission map */
501     photonOrigin [emap.src -> so -> otype] (&emap);
502     initPhotonEmission(&emap, pdfSamples);
503    
504     /* Number of photons to emit from ziss partishunn;
505     * scale according to its normalised contribushunn to
506     * the emitted source flux */
507     partNumEmit = srcNumEmit * colorAvg(emap.partFlux) /
508     srcFlux [srcIdx];
509     partEmitCnt = (unsigned long)partNumEmit;
510    
511     /* Probabilistically account for fractional photons */
512     if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
513     partEmitCnt++;
514    
515     /* Update local and shared global emission counter */
516     localNumEmitted += partEmitCnt;
517     photonCnt [PHOTONCNT_NUMEMIT(srcIdx)] += partEmitCnt;
518    
519     /* Integer counter avoids FP rounding errors */
520     while (partEmitCnt--) {
521     RAY photonRay;
522 greg 2.1
523 rschregle 2.12 /* Emit photon according to PDF (if any), allocate
524     * associated primary ray, and trace through scene
525     * until absorbed/leaked; emitPhoton() sets the
526     * emitting light source index in photonRay */
527     emitPhoton(&emap, &photonRay);
528     newPhotonPrimary(pm, &photonRay, primaryHeap[proc]);
529     /* Set subprocess index in photonRay for post-
530     * distrib primary index linearisation; this is
531     * propagated with the primary index in photonRay
532     * and set for photon hits by newPhoton() */
533     PMAP_SETRAYPROC(&photonRay, proc);
534     tracePhoton(&photonRay);
535     }
536 greg 2.1
537 rschregle 2.12 /* Update shared global photon count */
538     photonCnt [PHOTONCNT_NUMPHOT] += pm -> numPhotons -
539     lastNumPhotons;
540     lastNumPhotons = pm -> numPhotons;
541 greg 2.1 }
542 rschregle 2.12
543     portCnt++;
544     } while (portCnt < numPhotonPorts);
545    
546     if (pm -> numPhotons == srcNumDistrib)
547     /* Double predistrib factor in case no photons were stored
548     * for this source and redo pass 1 */
549     srcPreDistrib *= 2;
550     else {
551     /* Now do pass 2 */
552     passCnt++;
553     /* if (photonRepTime)
554     eputs("\n"); */
555 greg 2.1 }
556     }
557 rschregle 2.12 }
558    
559     /* Flush heap buffa one final time to prevent data corruption */
560     flushPhotonHeap(pm);
561     fclose(pm -> heap);
562 greg 2.1
563 rschregle 2.12 /* Flush final photon primary to primary heap file */
564     newPhotonPrimary(pm, NULL, primaryHeap [proc]);
565     fclose(primaryHeap [proc]);
566    
567     #ifdef DEBUG_PMAP
568     sprintf(errmsg, "Proc %d exited with total %ld photons\n", proc,
569     pm -> numPhotons);
570     eputs(errmsg);
571     #endif
572    
573     exit(0);
574 greg 2.1 }
575 rschregle 2.12 else if (pid < 0)
576     error(SYSTEM, "failed to fork subprocess in distribPhotonContrib");
577     }
578    
579     /* PARENT PROCESS CONTINUES HERE */
580     /* Record start time and enable progress report signal handler */
581     repStartTime = time(NULL);
582     #ifdef SIGCONT
583     signal(SIGCONT, pmapDistribReport);
584     #endif
585     /*
586     if (photonRepTime)
587     eputs("\n"); */
588    
589     /* Wait for subprocesses to complete while reporting progress */
590     proc = numProc;
591     while (proc) {
592     while (waitpid(-1, &stat, WNOHANG) > 0) {
593     /* Subprocess exited; check status */
594     if (!WIFEXITED(stat) || WEXITSTATUS(stat))
595     error(USER, "failed photon distribution");
596    
597     --proc;
598     }
599    
600     /* Nod off for a bit and update progress */
601     sleep(1);
602    
603     /* Update progress report from shared subprocess counters */
604     repComplete = pm -> distribTarget * numProc;
605     repProgress = photonCnt [PHOTONCNT_NUMPHOT];
606     for (repEmitted = 0, srcIdx = 0; srcIdx < nsources; srcIdx++)
607     repEmitted += photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];
608    
609     /* Get global photon count from shmem updated by subprocs */
610     pm -> numPhotons = photonCnt [PHOTONCNT_NUMPHOT];
611    
612     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
613     pmapDistribReport();
614     #ifdef SIGCONT
615     else signal(SIGCONT, pmapDistribReport);
616     #endif
617 greg 2.1 }
618    
619     /* ================================================================
620     * POST-DISTRIBUTION - Set photon flux and build kd-tree, etc.
621     * ================================================================ */
622 rschregle 2.12 #ifdef SIGCONT
623     signal(SIGCONT, SIG_DFL);
624     #endif
625 greg 2.1 free(emap.samples);
626    
627 rschregle 2.12 if (!pm -> numPhotons)
628 greg 2.1 error(USER, "empty photon map");
629    
630 rschregle 2.12 /* Load per-subprocess primary rays into pm -> primary array */
631     pm -> numPrimary = buildPrimaries(pm, primaryHeap, primaryOfs, numProc);
632     if (!pm -> numPrimary)
633 greg 2.1 error(INTERNAL, "no primary rays in contribution photon map");
634 rschregle 2.12
635     /* Set photon flux per source */
636     for (srcIdx = 0; srcIdx < nsources; srcIdx++)
637     srcFlux [srcIdx] /= photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];
638    
639     /* Photon counters no longer needed, unmap shared memory */
640     munmap(photonCnt, sizeof(*photonCnt));
641     close(shmFile);
642     #if 0
643     shm_unlink(shmFname);
644     #else
645     unlink(shmFname);
646     #endif
647 greg 2.1
648     if (photonRepTime) {
649 rschregle 2.12 eputs("\nBuilding contrib photon map...\n");
650 greg 2.1 fflush(stderr);
651     }
652 rschregle 2.12
653     /* Build underlying data structure; heap is destroyed */
654     buildPhotonMap(pm, srcFlux, primaryOfs, numProc);
655     }
656    
657    
658    
659     void photonContrib (PhotonMap *pmap, RAY *ray, COLOR irrad)
660     /* Sum up light source contributions from photons in pmap->srcContrib */
661     {
662     unsigned i;
663     PhotonSearchQueueNode *sqn;
664     float r, invArea;
665     RREAL rayCoeff [3];
666     Photon *photon;
667     static char warn = 1;
668    
669     setcolor(irrad, 0, 0, 0);
670    
671     if (!pmap -> maxGather)
672     return;
673    
674     /* Ignore sources */
675     if (ray -> ro && islight(objptr(ray -> ro -> omod) -> otype))
676     return;
677    
678     /* Get cumulative path coefficient up to photon lookup point */
679     raycontrib(rayCoeff, ray, PRIMARY);
680    
681     /* Lookup photons */
682     pmap -> squeue.tail = 0;
683     findPhotons(pmap, ray);
684    
685     /* Need at least 2 photons */
686     if (pmap -> squeue.tail < 2) {
687     #ifdef PMAP_NONEFOUND
688     sprintf(errmsg, "no photons found on %s at (%.3f, %.3f, %.3f)",
689     ray -> ro ? ray -> ro -> oname : "<null>",
690     ray -> rop [0], ray -> rop [1], ray -> rop [2]);
691     error(WARNING, errmsg);
692     #endif
693    
694     return;
695     }
696    
697     /* Average (squared) radius between furthest two photons to improve
698     * accuracy and get inverse search area 1 / (PI * r^2), with extra
699     * normalisation factor 1 / PI for ambient calculation */
700     sqn = pmap -> squeue.node + 1;
701     r = max(sqn -> dist2, (sqn + 1) -> dist2);
702     r = 0.25 * (pmap -> maxDist2 + r + 2 * sqrt(pmap -> maxDist2 * r));
703     invArea = 1 / (PI * PI * r);
704    
705     /* Skip the extra photon */
706     for (i = 1 ; i < pmap -> squeue.tail; i++, sqn++) {
707     COLOR flux;
708 greg 2.1
709 rschregle 2.12 /* Get photon's contribution to density estimate */
710     photon = getNearestPhoton(&pmap -> squeue, sqn -> idx);
711     getPhotonFlux(photon, flux);
712     scalecolor(flux, invArea);
713     #ifdef PMAP_EPANECHNIKOV
714     /* Apply Epanechnikov kernel to photon flux based on photon distance */
715     scalecolor(flux, 2 * (1 - sqn -> dist2 / r));
716     #endif
717     addcolor(irrad, flux);
718    
719     if (pmap -> srcContrib) {
720     const PhotonPrimary *primary = pmap -> primaries +
721     photon -> primary;
722     const SRCREC *sp = &source [primary -> srcIdx];
723     OBJREC *srcMod = findmaterial(sp -> so);
724     MODCONT *srcContrib = (MODCONT*)lu_find(pmap -> srcContrib,
725     srcMod -> oname) -> data;
726     double srcBinReal;
727     int srcBin;
728     RAY srcRay;
729    
730     if (!srcContrib)
731     continue;
732    
733     /* Photon's emitting light source has modifier whose contributions
734     * are sought */
735     if (srcContrib -> binv -> type != NUM) {
736     /* Use intersection function to set shadow ray parameters if
737     * it's not simply a constant */
738     rayorigin(&srcRay, SHADOW, NULL, NULL);
739     srcRay.rsrc = primary -> srcIdx;
740     #ifdef PMAP_PRIMARYPOS
741     VCOPY(srcRay.rorg, primary -> pos);
742     #else
743     /* No primary hitpoints; set dummy ray origin and warn once */
744     srcRay.rorg [0] = srcRay.rorg [1] = srcRay.rorg [2] = 0;
745     if (warn) {
746     error(WARNING, "no photon primary hitpoints for bin evaluation;"
747     " using dummy (0,0,0) !");
748     warn = 0;
749     }
750     #endif
751     decodedir(srcRay.rdir, primary -> dir);
752    
753     if (!(sp->sflags & SDISTANT
754     ? sourcehit(&srcRay)
755     : (*ofun[sp -> so -> otype].funp)(sp -> so, &srcRay)))
756     continue; /* XXX shouldn't happen! */
757    
758     worldfunc(RCCONTEXT, &srcRay);
759     set_eparams((char *)srcContrib -> params);
760     }
761    
762     if ((srcBinReal = evalue(srcContrib -> binv)) < -.5)
763     continue; /* silently ignore negative bins */
764    
765     if ((srcBin = srcBinReal + .5) >= srcContrib -> nbins) {
766     error(WARNING, "bad bin number (ignored)");
767     continue;
768     }
769    
770     if (!contrib) {
771     /* Ray coefficient mode; normalise by light source radiance
772     * after applying distrib pattern */
773     int j;
774    
775     raytexture(ray, srcMod -> omod);
776     setcolor(ray -> rcol, srcMod -> oargs.farg [0],
777     srcMod -> oargs.farg [1], srcMod -> oargs.farg [2]);
778     multcolor(ray -> rcol, ray -> pcol);
779     for (j = 0; j < 3; j++)
780     flux [j] = ray -> rcol [j] ? flux [j] / ray -> rcol [j] : 0;
781     }
782    
783     multcolor(flux, rayCoeff);
784     addcolor(srcContrib -> cbin [srcBin], flux);
785     }
786     }
787    
788     return;
789 greg 2.1 }