ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapdata.c
Revision: 2.18
Committed: Mon Aug 14 21:12:10 2017 UTC (6 years, 8 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R1
Changes since 2.17: +97 -74 lines
Log Message:
Updated photon map code for Windows; no multproc or ooC for now

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmapdata.c,v 2.17 2016/09/29 20:51:07 greg Exp $";
3 #endif
4
5 /*
6 =========================================================================
7 Photon map types and interface to nearest neighbour lookups in underlying
8 point cloud data structure.
9
10 The default data structure is an in-core kd-tree (see pmapkdt.{h,c}).
11 This can be overriden with the PMAP_OOC compiletime switch, which enables
12 an out-of-core octree (see oococt.{h,c}).
13
14 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
15 (c) Fraunhofer Institute for Solar Energy Systems,
16 (c) Lucerne University of Applied Sciences and Arts,
17 supported by the Swiss National Science Foundation (SNSF, #147053)
18 ==========================================================================
19
20 $Id: pmapdata.c,v 2.17 2016/09/29 20:51:07 greg Exp $
21 */
22
23
24
25 #include "pmapdata.h"
26 #include "pmaprand.h"
27 #include "pmapmat.h"
28 #include "otypes.h"
29 #include "source.h"
30 #include "rcontrib.h"
31 #include "random.h"
32
33
34
35 PhotonMap *photonMaps [NUM_PMAP_TYPES] = {
36 NULL, NULL, NULL, NULL, NULL, NULL
37 };
38
39
40
41 /* Include routines to handle underlying point cloud data structure */
42 #ifdef PMAP_OOC
43 #include "pmapooc.c"
44 #else
45 #include "pmapkdt.c"
46 #endif
47
48
49
50 void initPhotonMap (PhotonMap *pmap, PhotonMapType t)
51 /* Init photon map 'n' stuff... */
52 {
53 if (!pmap)
54 return;
55
56 pmap -> numPhotons = 0;
57 pmap -> biasCompHist = NULL;
58 pmap -> maxPos [0] = pmap -> maxPos [1] = pmap -> maxPos [2] = -FHUGE;
59 pmap -> minPos [0] = pmap -> minPos [1] = pmap -> minPos [2] = FHUGE;
60 pmap -> minGathered = pmap -> maxGathered = pmap -> totalGathered = 0;
61 pmap -> gatherTolerance = gatherTolerance;
62 pmap -> minError = pmap -> maxError = pmap -> rmsError = 0;
63 pmap -> numDensity = 0;
64 pmap -> distribRatio = 1;
65 pmap -> type = t;
66 pmap -> squeue.node = NULL;
67 pmap -> squeue.len = 0;
68
69 /* Init local RNG state */
70 pmap -> randState [0] = 10243;
71 pmap -> randState [1] = 39829;
72 pmap -> randState [2] = 9433;
73 pmapSeed(randSeed, pmap -> randState);
74
75 /* Set up type-specific photon lookup callback */
76 pmap -> lookup = pmapLookup [t];
77
78 /* Mark primary photon ray as unused */
79 pmap -> lastPrimary.srcIdx = -1;
80 pmap -> numPrimary = 0;
81 pmap -> primaries = NULL;
82
83 /* Init storage */
84 pmap -> heap = NULL;
85 pmap -> heapBuf = NULL;
86 pmap -> heapBufLen = 0;
87 #ifdef PMAP_OOC
88 OOC_Null(&pmap -> store);
89 #else
90 kdT_Null(&pmap -> store);
91 #endif
92 }
93
94
95
96 void initPhotonHeap (PhotonMap *pmap)
97 {
98 int fdFlags;
99
100 if (!pmap)
101 error(INTERNAL, "undefined photon map in initPhotonHeap");
102
103 if (!pmap -> heap) {
104 /* Open heap file */
105 mktemp(strcpy(pmap -> heapFname, PMAP_TMPFNAME));
106 if (!(pmap -> heap = fopen(pmap -> heapFname, "w+b")))
107 error(SYSTEM, "failed opening heap file in initPhotonHeap");
108
109 #ifdef F_SETFL /* XXX is there an alternate needed for Windows? */
110 fdFlags = fcntl(fileno(pmap -> heap), F_GETFL);
111 fcntl(fileno(pmap -> heap), F_SETFL, fdFlags | O_APPEND);
112 #endif/* ftruncate(fileno(pmap -> heap), 0); */
113 }
114 }
115
116
117
118 void flushPhotonHeap (PhotonMap *pmap)
119 {
120 int fd;
121 const unsigned long len = pmap -> heapBufLen * sizeof(Photon);
122
123 if (!pmap)
124 error(INTERNAL, "undefined photon map in flushPhotonHeap");
125
126 if (!pmap -> heap || !pmap -> heapBuf) {
127 /* Silently ignore undefined heap
128 error(INTERNAL, "undefined heap in flushPhotonHeap"); */
129 return;
130 }
131
132 /* Atomically seek and write block to heap */
133 /* !!! Unbuffered I/O via pwrite() avoids potential race conditions
134 * !!! and buffer corruption which can occur with lseek()/fseek()
135 * !!! followed by write()/fwrite(). */
136 fd = fileno(pmap -> heap);
137
138 #ifdef DEBUG_PMAP
139 sprintf(errmsg, "Proc %d: flushing %ld photons from pos %ld\n", getpid(),
140 pmap -> heapBufLen, lseek(fd, 0, SEEK_END) / sizeof(Photon));
141 eputs(errmsg);
142 #endif
143
144 /*if (pwrite(fd, pmap -> heapBuf, len, lseek(fd, 0, SEEK_END)) != len) */
145 if (write(fd, pmap -> heapBuf, len) != len)
146 error(SYSTEM, "failed append to heap file in flushPhotonHeap");
147
148 #if NIX
149 if (fsync(fd))
150 error(SYSTEM, "failed fsync in flushPhotonHeap");
151 #endif
152
153 pmap -> heapBufLen = 0;
154 }
155
156
157
158 #ifdef DEBUG_PMAP
159 static int checkPhotonHeap (FILE *file)
160 /* Check heap for nonsensical or duplicate photons */
161 {
162 Photon p, lastp;
163 int i, dup;
164
165 rewind(file);
166 memset(&lastp, 0, sizeof(lastp));
167
168 while (fread(&p, sizeof(p), 1, file)) {
169 dup = 1;
170
171 for (i = 0; i <= 2; i++) {
172 if (p.pos [i] < thescene.cuorg [i] ||
173 p.pos [i] > thescene.cuorg [i] + thescene.cusize) {
174
175 sprintf(errmsg, "corrupt photon in heap at [%f, %f, %f]\n",
176 p.pos [0], p.pos [1], p.pos [2]);
177 error(WARNING, errmsg);
178 }
179
180 dup &= p.pos [i] == lastp.pos [i];
181 }
182
183 if (dup) {
184 sprintf(errmsg,
185 "consecutive duplicate photon in heap at [%f, %f, %f]\n",
186 p.pos [0], p.pos [1], p.pos [2]);
187 error(WARNING, errmsg);
188 }
189 }
190
191 return 0;
192 }
193 #endif
194
195
196
197 int newPhoton (PhotonMap* pmap, const RAY* ray)
198 {
199 unsigned i, inROI = 0;
200 Photon photon;
201 COLOR photonFlux;
202
203 /* Account for distribution ratio */
204 if (!pmap || pmapRandom(pmap -> randState) > pmap -> distribRatio)
205 return -1;
206
207 /* Don't store on sources */
208 if (ray -> robj > -1 && islight(objptr(ray -> ro -> omod) -> otype))
209 return -1;
210
211 /* Store photon if within a region of interest (for ze Ecksperts!) */
212 if (!pmapNumROI || !pmapROI)
213 inROI = 1;
214 else {
215 for (i = 0; !inROI && i < pmapNumROI; i++)
216 inROI = (ray -> rop [0] >= pmapROI [i].min [0] &&
217 ray -> rop [0] <= pmapROI [i].max [0] &&
218 ray -> rop [1] >= pmapROI [i].min [1] &&
219 ray -> rop [1] <= pmapROI [i].max [1] &&
220 ray -> rop [2] >= pmapROI [i].min [2] &&
221 ray -> rop [2] <= pmapROI [i].max [2]);
222 }
223
224 if (inROI) {
225 /* Adjust flux according to distribution ratio and ray weight */
226 copycolor(photonFlux, ray -> rcol);
227 scalecolor(photonFlux,
228 ray -> rweight / (pmap -> distribRatio ? pmap -> distribRatio
229 : 1));
230 setPhotonFlux(&photon, photonFlux);
231
232 /* Set photon position and flags */
233 VCOPY(photon.pos, ray -> rop);
234 photon.flags = 0;
235 photon.caustic = PMAP_CAUSTICRAY(ray);
236
237 /* Set contrib photon's primary ray and subprocess index (the latter
238 * to linearise the primary ray indices after photon distribution is
239 * complete). Also set primary ray's source index, thereby marking it
240 * as used. */
241 if (isContribPmap(pmap)) {
242 photon.primary = pmap -> numPrimary;
243 photon.proc = PMAP_GETRAYPROC(ray);
244 pmap -> lastPrimary.srcIdx = ray -> rsrc;
245 }
246 else photon.primary = 0;
247
248 /* Set normal */
249 for (i = 0; i <= 2; i++)
250 photon.norm [i] = 127.0 * (isVolumePmap(pmap) ? ray -> rdir [i]
251 : ray -> ron [i]);
252
253 if (!pmap -> heapBuf) {
254 /* Lazily allocate heap buffa */
255 #if NIX
256 /* Randomise buffa size to temporally decorellate flushes in
257 * multiprocessing mode */
258 srandom(randSeed + getpid());
259 pmap -> heapBufSize = PMAP_HEAPBUFSIZE * (0.5 + frandom());
260 #else
261 /* Randomisation disabled for single processes on WIN; also useful
262 * for reproducability during debugging */
263 pmap -> heapBufSize = PMAP_HEAPBUFSIZE;
264 #endif
265 if (!(pmap -> heapBuf = calloc(pmap -> heapBufSize, sizeof(Photon))))
266 error(SYSTEM, "failed heap buffer allocation in newPhoton");
267 pmap -> heapBufLen = 0;
268 }
269
270 /* Photon initialised; now append to heap buffa */
271 memcpy(pmap -> heapBuf + pmap -> heapBufLen, &photon, sizeof(Photon));
272
273 if (++pmap -> heapBufLen >= pmap -> heapBufSize)
274 /* Heap buffa full, flush to heap file */
275 flushPhotonHeap(pmap);
276
277 pmap -> numPhotons++;
278 }
279
280 return 0;
281 }
282
283
284
285 void buildPhotonMap (PhotonMap *pmap, double *photonFlux,
286 PhotonPrimaryIdx *primaryOfs, unsigned nproc)
287 {
288 unsigned long n, nCheck = 0;
289 unsigned i;
290 Photon *p;
291 COLOR flux;
292 char nuHeapFname [sizeof(PMAP_TMPFNAME)];
293 FILE *nuHeap;
294 /* Need double here to reduce summation errors */
295 double avgFlux [3] = {0, 0, 0}, CoG [3] = {0, 0, 0}, CoGdist = 0;
296 FVECT d;
297
298 if (!pmap)
299 error(INTERNAL, "undefined photon map in buildPhotonMap");
300
301 /* Get number of photons from heapfile size */
302 if (fseek(pmap -> heap, 0, SEEK_END) < 0)
303 error(SYSTEM, "failed seek to end of photon heap in buildPhotonMap");
304 pmap -> numPhotons = ftell(pmap -> heap) / sizeof(Photon);
305
306 if (!pmap -> numPhotons)
307 error(INTERNAL, "empty photon map in buildPhotonMap");
308
309 if (!pmap -> heap)
310 error(INTERNAL, "no heap in buildPhotonMap");
311
312 #ifdef DEBUG_PMAP
313 eputs("Checking photon heap consistency...\n");
314 checkPhotonHeap(pmap -> heap);
315
316 sprintf(errmsg, "Heap contains %ld photons\n", pmap -> numPhotons);
317 eputs(errmsg);
318 #endif
319
320 /* Allocate heap buffa */
321 if (!pmap -> heapBuf) {
322 pmap -> heapBufSize = PMAP_HEAPBUFSIZE;
323 pmap -> heapBuf = calloc(pmap -> heapBufSize, sizeof(Photon));
324 if (!pmap -> heapBuf)
325 error(SYSTEM, "failed to allocate postprocessed photon heap in"
326 "buildPhotonMap");
327 }
328
329 /* We REALLY don't need yet another @%&*! heap just to hold the scaled
330 * photons, but can't think of a quicker fix... */
331 mktemp(strcpy(nuHeapFname, PMAP_TMPFNAME));
332 if (!(nuHeap = fopen(nuHeapFname, "w+b")))
333 error(SYSTEM, "failed to open postprocessed photon heap in "
334 "buildPhotonMap");
335
336 rewind(pmap -> heap);
337
338 #ifdef DEBUG_PMAP
339 eputs("Postprocessing photons...\n");
340 #endif
341
342 while (!feof(pmap -> heap)) {
343 #ifdef DEBUG_PMAP
344 printf("Reading %lu at %lu... ", pmap -> heapBufSize, ftell(pmap->heap));
345 #endif
346 pmap -> heapBufLen = fread(pmap -> heapBuf, sizeof(Photon),
347 pmap -> heapBufSize, pmap -> heap);
348 #ifdef DEBUG_PMAP
349 printf("Got %lu\n", pmap -> heapBufLen);
350 #endif
351
352 if (ferror(pmap -> heap))
353 error(SYSTEM, "failed to read photon heap in buildPhotonMap");
354
355 for (n = pmap -> heapBufLen, p = pmap -> heapBuf; n; n--, p++) {
356 /* Update min and max pos and set photon flux */
357 for (i = 0; i <= 2; i++) {
358 if (p -> pos [i] < pmap -> minPos [i])
359 pmap -> minPos [i] = p -> pos [i];
360 else if (p -> pos [i] > pmap -> maxPos [i])
361 pmap -> maxPos [i] = p -> pos [i];
362
363 /* Update centre of gravity with photon position */
364 CoG [i] += p -> pos [i];
365 }
366
367 if (primaryOfs)
368 /* Linearise photon primary index from subprocess index using the
369 * per-subprocess offsets in primaryOfs */
370 p -> primary += primaryOfs [p -> proc];
371
372 /* Scale photon's flux (hitherto normalised to 1 over RGB); in
373 * case of a contrib photon map, this is done per light source,
374 * and photonFlux is assumed to be an array */
375 getPhotonFlux(p, flux);
376
377 if (photonFlux) {
378 scalecolor(flux, photonFlux [isContribPmap(pmap) ?
379 photonSrcIdx(pmap, p) : 0]);
380 setPhotonFlux(p, flux);
381 }
382
383 /* Update average photon flux; need a double here */
384 addcolor(avgFlux, flux);
385 }
386
387 /* Write modified photons to new heap */
388 fwrite(pmap -> heapBuf, sizeof(Photon), pmap -> heapBufLen, nuHeap);
389
390 if (ferror(nuHeap))
391 error(SYSTEM, "failed postprocessing photon flux in "
392 "buildPhotonMap");
393
394 nCheck += pmap -> heapBufLen;
395 }
396
397 #ifdef DEBUG_PMAP
398 if (nCheck < pmap -> numPhotons)
399 error(INTERNAL, "truncated photon heap in buildPhotonMap");
400 #endif
401
402 /* Finalise average photon flux */
403 scalecolor(avgFlux, 1.0 / pmap -> numPhotons);
404 copycolor(pmap -> photonFlux, avgFlux);
405
406 /* Average photon positions to get centre of gravity */
407 for (i = 0; i < 3; i++)
408 pmap -> CoG [i] = CoG [i] /= pmap -> numPhotons;
409
410 rewind(pmap -> heap);
411
412 /* Compute average photon distance to centre of gravity */
413 while (!feof(pmap -> heap)) {
414 pmap -> heapBufLen = fread(pmap -> heapBuf, sizeof(Photon),
415 pmap -> heapBufSize, pmap -> heap);
416
417 for (n = pmap -> heapBufLen, p = pmap -> heapBuf; n; n--, p++) {
418 VSUB(d, p -> pos, CoG);
419 CoGdist += DOT(d, d);
420 }
421 }
422
423 pmap -> CoGdist = CoGdist /= pmap -> numPhotons;
424
425 /* Swap heaps, discarding unscaled photons */
426 fclose(pmap -> heap);
427 unlink(pmap -> heapFname);
428 pmap -> heap = nuHeap;
429 strcpy(pmap -> heapFname, nuHeapFname);
430
431 #ifdef PMAP_OOC
432 OOC_BuildPhotonMap(pmap, nproc);
433 #else
434 kdT_BuildPhotonMap(pmap);
435 #endif
436
437 /* Trash heap and its buffa */
438 free(pmap -> heapBuf);
439 fclose(pmap -> heap);
440 unlink(pmap -> heapFname);
441 pmap -> heap = NULL;
442 pmap -> heapBuf = NULL;
443 }
444
445
446
447 /* Dynamic max photon search radius increase and reduction factors */
448 #define PMAP_MAXDIST_INC 4
449 #define PMAP_MAXDIST_DEC 0.9
450
451 /* Num successful lookups before reducing in max search radius */
452 #define PMAP_MAXDIST_CNT 1000
453
454 /* Threshold below which we assume increasing max radius won't help */
455 #define PMAP_SHORT_LOOKUP_THRESH 1
456
457 /* Coefficient for adaptive maximum search radius */
458 #define PMAP_MAXDIST_COEFF 100
459
460 void findPhotons (PhotonMap* pmap, const RAY* ray)
461 {
462 int redo = 0;
463
464 if (!pmap -> squeue.len) {
465 /* Lazy init priority queue */
466 #ifdef PMAP_OOC
467 OOC_InitFindPhotons(pmap);
468 #else
469 kdT_InitFindPhotons(pmap);
470 #endif
471 pmap -> minGathered = pmap -> maxGather;
472 pmap -> maxGathered = pmap -> minGather;
473 pmap -> totalGathered = 0;
474 pmap -> numLookups = pmap -> numShortLookups = 0;
475 pmap -> shortLookupPct = 0;
476 pmap -> minError = FHUGE;
477 pmap -> maxError = -FHUGE;
478 pmap -> rmsError = 0;
479 /* SQUARED max search radius limit is based on avg photon distance to
480 * centre of gravity, unless fixed by user (maxDistFix > 0) */
481 pmap -> maxDist0 = pmap -> maxDist2Limit =
482 maxDistFix > 0 ? maxDistFix * maxDistFix
483 : PMAP_MAXDIST_COEFF * pmap -> squeue.len *
484 pmap -> CoGdist / pmap -> numPhotons;
485 }
486
487 do {
488 pmap -> squeue.tail = 0;
489 pmap -> maxDist2 = pmap -> maxDist0;
490
491 /* Search position is ray -> rorg for volume photons, since we have no
492 intersection point. Normals are ignored -- these are incident
493 directions). */
494 if (isVolumePmap(pmap)) {
495 #ifdef PMAP_OOC
496 OOC_FindPhotons(pmap, ray -> rorg, NULL);
497 #else
498 kdT_FindPhotons(pmap, ray -> rorg, NULL);
499 #endif
500 }
501 else {
502 #ifdef PMAP_OOC
503 OOC_FindPhotons(pmap, ray -> rop, ray -> ron);
504 #else
505 kdT_FindPhotons(pmap, ray -> rop, ray -> ron);
506 #endif
507 }
508
509 #ifdef PMAP_LOOKUP_INFO
510 fprintf(stderr, "%d/%d %s photons found within radius %.3f "
511 "at (%.2f,%.2f,%.2f) on %s\n", pmap -> squeue.tail,
512 pmap -> squeue.len, pmapName [pmap -> type], sqrt(pmap -> maxDist2),
513 ray -> rop [0], ray -> rop [1], ray -> rop [2],
514 ray -> ro ? ray -> ro -> oname : "<null>");
515 #endif
516
517 if (pmap -> squeue.tail < pmap -> squeue.len * pmap -> gatherTolerance) {
518 /* Short lookup; too few photons found */
519 if (pmap -> squeue.tail > PMAP_SHORT_LOOKUP_THRESH) {
520 /* Ignore short lookups which return fewer than
521 * PMAP_SHORT_LOOKUP_THRESH photons under the assumption there
522 * really are no photons in the vicinity, and increasing the max
523 * search radius therefore won't help */
524 #ifdef PMAP_LOOKUP_WARN
525 sprintf(errmsg,
526 "%d/%d %s photons found at (%.2f,%.2f,%.2f) on %s",
527 pmap -> squeue.tail, pmap -> squeue.len,
528 pmapName [pmap -> type],
529 ray -> rop [0], ray -> rop [1], ray -> rop [2],
530 ray -> ro ? ray -> ro -> oname : "<null>");
531 error(WARNING, errmsg);
532 #endif
533
534 /* Bail out after warning if maxDist is fixed */
535 if (maxDistFix > 0)
536 return;
537
538 if (pmap -> maxDist0 < pmap -> maxDist2Limit) {
539 /* Increase max search radius if below limit & redo search */
540 pmap -> maxDist0 *= PMAP_MAXDIST_INC;
541 #ifdef PMAP_LOOKUP_REDO
542 redo = 1;
543 #endif
544 #ifdef PMAP_LOOKUP_WARN
545 sprintf(errmsg,
546 redo ? "restarting photon lookup with max radius %.1e"
547 : "max photon lookup radius adjusted to %.1e",
548 pmap -> maxDist0);
549 error(WARNING, errmsg);
550 #endif
551 }
552 #ifdef PMAP_LOOKUP_REDO
553 else {
554 sprintf(errmsg, "max photon lookup radius clamped to %.1e",
555 pmap -> maxDist0);
556 error(WARNING, errmsg);
557 }
558 #endif
559 }
560
561 /* Reset successful lookup counter */
562 pmap -> numLookups = 0;
563 }
564 else {
565 /* Bail out after warning if maxDist is fixed */
566 if (maxDistFix > 0)
567 return;
568
569 /* Increment successful lookup counter and reduce max search radius if
570 * wraparound */
571 pmap -> numLookups = (pmap -> numLookups + 1) % PMAP_MAXDIST_CNT;
572 if (!pmap -> numLookups)
573 pmap -> maxDist0 *= PMAP_MAXDIST_DEC;
574
575 redo = 0;
576 }
577
578 } while (redo);
579 }
580
581
582
583 void find1Photon (PhotonMap *pmap, const RAY* ray, Photon *photon)
584 {
585 pmap -> maxDist2 = thescene.cusize; /* ? */
586
587 #ifdef PMAP_OOC
588 OOC_Find1Photon(pmap, ray -> rop, ray -> ron, photon);
589 #else
590 kdT_Find1Photon(pmap, ray -> rop, ray -> ron, photon);
591 #endif
592 }
593
594
595
596 void getPhoton (PhotonMap *pmap, PhotonIdx idx, Photon *photon)
597 {
598 #ifdef PMAP_OOC
599 if (OOC_GetPhoton(pmap, idx, photon))
600 #else
601 if (kdT_GetPhoton(pmap, idx, photon))
602 #endif
603 error(INTERNAL, "failed photon lookup");
604 }
605
606
607
608 Photon *getNearestPhoton (const PhotonSearchQueue *squeue, PhotonIdx idx)
609 {
610 #ifdef PMAP_OOC
611 return OOC_GetNearestPhoton(squeue, idx);
612 #else
613 return kdT_GetNearestPhoton(squeue, idx);
614 #endif
615 }
616
617
618
619 PhotonIdx firstPhoton (const PhotonMap *pmap)
620 {
621 #ifdef PMAP_OOC
622 return OOC_FirstPhoton(pmap);
623 #else
624 return kdT_FirstPhoton(pmap);
625 #endif
626 }
627
628
629
630 void deletePhotons (PhotonMap* pmap)
631 {
632 #ifdef PMAP_OOC
633 OOC_Delete(&pmap -> store);
634 #else
635 kdT_Delete(&pmap -> store);
636 #endif
637
638 free(pmap -> squeue.node);
639 free(pmap -> biasCompHist);
640
641 pmap -> numPhotons = pmap -> minGather = pmap -> maxGather =
642 pmap -> squeue.len = pmap -> squeue.tail = 0;
643 }