ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapdata.c
Revision: 2.3
Committed: Fri May 8 13:20:23 2015 UTC (9 years ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.2: +22 -13 lines
Log Message:
Double-counting bugfix for glow sources (thanks DGM!), revised copyright

File Contents

# Content
1 /*
2 ==================================================================
3 Photon map data structures and kd-tree handling
4
5 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
6 (c) Fraunhofer Institute for Solar Energy Systems,
7 (c) Lucerne University of Applied Sciences and Arts,
8 supported by the Swiss National Science Foundation (SNSF, #147053)
9 ==================================================================
10
11 $Id: pmapdata.c,v 4.32 2015/05/08 13:11:58 taschreg Exp taschreg $
12 */
13
14
15
16 #include "pmap.h"
17 #include "pmaprand.h"
18 #include "pmapmat.h"
19 #include "otypes.h"
20 #include "source.h"
21 #include "rcontrib.h"
22
23
24
25 PhotonMap *photonMaps [NUM_PMAP_TYPES] = {
26 NULL, NULL, NULL, NULL, NULL, NULL
27 };
28
29
30
31 void initPhotonMap (PhotonMap *pmap, PhotonMapType t)
32 /* Init photon map 'n' stuff... */
33 {
34 if (!pmap)
35 return;
36
37 pmap -> heapSize = pmap -> heapEnd = 0;
38 pmap -> heap = NULL;
39 pmap -> squeue = NULL;
40 pmap -> biasCompHist = NULL;
41 pmap -> maxPos [0] = pmap -> maxPos [1] = pmap -> maxPos [2] = -FHUGE;
42 pmap -> minPos [0] = pmap -> minPos [1] = pmap -> minPos [2] = FHUGE;
43 pmap -> minGathered = pmap -> maxGathered = pmap -> totalGathered = 0;
44 pmap -> gatherTolerance = gatherTolerance;
45 pmap -> minError = pmap -> maxError = pmap -> rmsError = 0;
46 pmap -> numDensity = 0;
47 pmap -> distribRatio = 1;
48 pmap -> type = t;
49
50 /* Init local RNG state */
51 pmap -> randState [0] = 10243;
52 pmap -> randState [1] = 39829;
53 pmap -> randState [2] = 9433;
54 /* pmapSeed(25999, pmap -> randState); */
55 pmapSeed(randSeed, pmap -> randState);
56
57 /* Set up type-specific photon lookup callback */
58 pmap -> lookup = pmapLookup [t];
59
60 pmap -> primary = NULL;
61 pmap -> primarySize = pmap -> primaryEnd = 0;
62 }
63
64
65
66 const PhotonPrimary* addPhotonPrimary (PhotonMap *pmap, const RAY *ray)
67 {
68 PhotonPrimary *prim = NULL;
69
70 if (!pmap || !ray)
71 return NULL;
72
73 /* Check if last primary ray has spawned photons (srcIdx >= 0, see
74 * addPhoton()), in which case we keep it and allocate a new one;
75 * otherwise we overwrite the unused entry */
76 if (pmap -> primary && pmap -> primary [pmap -> primaryEnd].srcIdx >= 0)
77 pmap -> primaryEnd++;
78
79 if (!pmap -> primarySize || pmap -> primaryEnd >= pmap -> primarySize) {
80 /* Allocate/enlarge array */
81 pmap -> primarySize += pmap -> heapSizeInc;
82
83 /* Counter wraparound? */
84 if (pmap -> primarySize < pmap -> heapSizeInc)
85 error(INTERNAL, "photon primary overflow");
86
87 pmap -> primary = (PhotonPrimary *)realloc(pmap -> primary,
88 sizeof(PhotonPrimary) *
89 pmap -> primarySize);
90 if (!pmap -> primary)
91 error(USER, "can't allocate photon primaries");
92 }
93
94 prim = pmap -> primary + pmap -> primaryEnd;
95
96 /* Mark unused with negative source index until path spawns a photon (see
97 * addPhoton()) */
98 prim -> srcIdx = -1;
99
100 /* Reverse incident direction to point to light source */
101 prim -> dir [0] = -ray -> rdir [0];
102 prim -> dir [1] = -ray -> rdir [1];
103 prim -> dir [2] = -ray -> rdir [2];
104
105 VCOPY(prim -> org, ray -> rorg);
106
107 return prim;
108 }
109
110
111
112 const Photon* addPhoton (PhotonMap* pmap, const RAY* ray)
113 {
114 unsigned i;
115 Photon* photon = NULL;
116 COLOR photonFlux;
117
118 /* Account for distribution ratio */
119 if (!pmap || pmapRandom(pmap -> randState) > pmap -> distribRatio)
120 return NULL;
121
122 /* Don't store on sources */
123 if (ray -> robj > -1 && islight(objptr(ray -> ro -> omod) -> otype))
124 return NULL;
125
126 #if 0
127 if (inContribPmap(pmap)) {
128 /* Adding contribution photon */
129 if (ray -> parent && ray -> parent -> rtype & PRIMARY)
130 /* Add primary photon ray if parent is primary; by putting this
131 * here and checking the ray's immediate parent, we only add
132 * primaries that actually contribute photons, and we only add them
133 * once. */
134 addPhotonPrimary(pmap, ray -> parent);
135
136 /* Save index to primary ray (remains unchanged if primary already in
137 * array) */
138 primary = pmap -> primaryEnd;
139 }
140 #endif
141
142 #ifdef PMAP_ROI
143 /* Store photon if within region of interest -- for egg-spurtz only! */
144 if (ray -> rop [0] >= pmapROI [0] && ray -> rop [0] <= pmapROI [1] &&
145 ray -> rop [1] >= pmapROI [2] && ray -> rop [1] <= pmapROI [3] &&
146 ray -> rop [2] >= pmapROI [4] && ray -> rop [2] <= pmapROI [5])
147 #endif
148 {
149 if (pmap -> heapEnd >= pmap -> heapSize) {
150 /* Enlarge heap */
151 pmap -> heapSize += pmap -> heapSizeInc;
152
153 /* Counter wraparound? */
154 if (pmap -> heapSize < pmap -> heapSizeInc)
155 error(INTERNAL, "photon heap overflow");
156
157 pmap -> heap = (Photon *)realloc(pmap -> heap,
158 sizeof(Photon) * pmap -> heapSize);
159 if (!pmap -> heap)
160 error(USER, "can't allocate photon heap");
161 }
162
163 photon = pmap -> heap + pmap -> heapEnd++;
164
165 /* Adjust flux according to distribution ratio and ray weight */
166 copycolor(photonFlux, ray -> rcol);
167 scalecolor(photonFlux,
168 ray -> rweight / (pmap -> distribRatio ? pmap -> distribRatio
169 : 1));
170 setPhotonFlux(photon, photonFlux);
171
172 /* Set photon position and flags */
173 VCOPY(photon -> pos, ray -> rop);
174 photon -> flags = PMAP_CAUSTICRAY(ray) ? PMAP_CAUST_BIT : 0;
175
176 /* Set primary ray index and mark as used for contrib photons */
177 if (isContribPmap(pmap)) {
178 photon -> primary = pmap -> primaryEnd;
179 pmap -> primary [pmap -> primaryEnd].srcIdx = ray -> rsrc;
180 }
181 else photon -> primary = 0;
182
183 /* Update min and max positions & set normal */
184 for (i = 0; i <= 2; i++) {
185 if (photon -> pos [i] < pmap -> minPos [i])
186 pmap -> minPos [i] = photon -> pos [i];
187 if (photon -> pos [i] > pmap -> maxPos [i])
188 pmap -> maxPos [i] = photon -> pos [i];
189 photon -> norm [i] = 127.0 * (isVolumePmap(pmap) ? ray -> rdir [i]
190 : ray -> ron [i]);
191 }
192 }
193
194 return photon;
195 }
196
197
198
199 static void nearestNeighbours (PhotonMap* pmap, const float pos [3],
200 const float norm [3], unsigned long node)
201 /* Recursive part of findPhotons(..).
202 Note that all heap and priority queue index handling is 1-based, but
203 accesses to the arrays are 0-based! */
204 {
205 Photon* p = &pmap -> heap [node - 1];
206 unsigned i, j;
207 /* Signed distance to current photon's splitting plane */
208 float d = pos [photonDiscr(*p)] - p -> pos [photonDiscr(*p)],
209 d2 = d * d;
210 PhotonSQNode* sq = pmap -> squeue;
211 const unsigned sqSize = pmap -> squeueSize;
212 float dv [3];
213
214 /* Search subtree closer to pos first; exclude other subtree if the
215 distance to the splitting plane is greater than maxDist */
216 if (d < 0) {
217 if (node << 1 <= pmap -> heapSize)
218 nearestNeighbours(pmap, pos, norm, node << 1);
219 if (d2 < pmap -> maxDist && node << 1 < pmap -> heapSize)
220 nearestNeighbours(pmap, pos, norm, (node << 1) + 1);
221 }
222 else {
223 if (node << 1 < pmap -> heapSize)
224 nearestNeighbours(pmap, pos, norm, (node << 1) + 1);
225 if (d2 < pmap -> maxDist && node << 1 <= pmap -> heapSize)
226 nearestNeighbours(pmap, pos, norm, node << 1);
227 }
228
229 /* Reject photon if normal faces away (ignored for volume photons) */
230 if (norm && DOT(norm, p -> norm) <= 0)
231 return;
232
233 if (isContribPmap(pmap) && pmap -> srcContrib) {
234 /* Lookup in contribution photon map */
235 OBJREC *srcMod;
236 const int srcIdx = photonSrcIdx(pmap, p);
237
238 if (srcIdx < 0 || srcIdx >= nsources)
239 error(INTERNAL, "invalid light source index in photon map");
240
241 srcMod = objptr(source [srcIdx].so -> omod);
242
243 /* Reject photon if contributions from light source which emitted it
244 * are not sought */
245 if (!lu_find(pmap -> srcContrib, srcMod -> oname) -> data)
246 return;
247
248 /* Reject non-caustic photon if lookup for caustic contribs */
249 if (pmap -> lookupFlags & PMAP_CAUST_BIT & ~p -> flags)
250 return;
251 }
252
253 /* Squared distance to current photon */
254 dv [0] = pos [0] - p -> pos [0];
255 dv [1] = pos [1] - p -> pos [1];
256 dv [2] = pos [2] - p -> pos [2];
257 d2 = DOT(dv, dv);
258
259 /* Accept photon if closer than current max dist & add to priority queue */
260 if (d2 < pmap -> maxDist) {
261 if (pmap -> squeueEnd < sqSize) {
262 /* Priority queue not full; append photon and restore heap */
263 i = ++pmap -> squeueEnd;
264
265 while (i > 1 && sq [(i >> 1) - 1].dist <= d2) {
266 sq [i - 1].photon = sq [(i >> 1) - 1].photon;
267 sq [i - 1].dist = sq [(i >> 1) - 1].dist;
268 i >>= 1;
269 }
270
271 sq [--i].photon = p;
272 sq [i].dist = d2;
273 /* Update maxDist if we've just filled the queue */
274 if (pmap -> squeueEnd >= pmap -> squeueSize)
275 pmap -> maxDist = sq [0].dist;
276 }
277 else {
278 /* Priority queue full; replace maximum, restore heap, and
279 update maxDist */
280 i = 1;
281
282 while (i <= sqSize >> 1) {
283 j = i << 1;
284 if (j < sqSize && sq [j - 1].dist < sq [j].dist)
285 j++;
286 if (d2 >= sq [j - 1].dist)
287 break;
288 sq [i - 1].photon = sq [j - 1].photon;
289 sq [i - 1].dist = sq [j - 1].dist;
290 i = j;
291 }
292
293 sq [--i].photon = p;
294 sq [i].dist = d2;
295 pmap -> maxDist = sq [0].dist;
296 }
297 }
298 }
299
300
301
302 /* Dynamic max photon search radius increase and reduction factors */
303 #define PMAP_MAXDIST_INC 4
304 #define PMAP_MAXDIST_DEC 0.9
305
306 /* Num successful lookups before reducing in max search radius */
307 #define PMAP_MAXDIST_CNT 1000
308
309 /* Threshold below which we assume increasing max radius won't help */
310 #define PMAP_SHORT_LOOKUP_THRESH 1
311
312 void findPhotons (PhotonMap* pmap, const RAY* ray)
313 {
314 float pos [3], norm [3];
315 int redo = 0;
316
317 if (!pmap -> squeue) {
318 /* Lazy init priority queue */
319 pmap -> squeueSize = pmap -> maxGather + 1;
320 pmap -> squeue = (PhotonSQNode*)malloc(pmap -> squeueSize *
321 sizeof(PhotonSQNode));
322 if (!pmap -> squeue)
323 error(USER, "can't allocate photon priority queue");
324
325 pmap -> minGathered = pmap -> maxGather;
326 pmap -> maxGathered = pmap -> minGather;
327 pmap -> totalGathered = 0;
328 pmap -> numLookups = pmap -> numShortLookups = 0;
329 pmap -> shortLookupPct = 0;
330 pmap -> minError = FHUGE;
331 pmap -> maxError = -FHUGE;
332 pmap -> rmsError = 0;
333 #ifdef PMAP_MAXDIST_ABS
334 /* Treat maxDistCoeff as an *absolute* and *fixed* max search radius.
335 Primarily intended for debugging; FOR ZE ECKSPURTZ ONLY! */
336 pmap -> maxDist0 = pmap -> maxDistLimit = maxDistCoeff;
337 #else
338 /* Maximum search radius limit based on avg photon distance to
339 * centre of gravity */
340 pmap -> maxDist0 = pmap -> maxDistLimit =
341 maxDistCoeff * pmap -> squeueSize * pmap -> CoGdist /
342 pmap -> heapSize;
343 #endif
344 }
345
346 do {
347 pmap -> squeueEnd = 0;
348 pmap -> maxDist = pmap -> maxDist0;
349
350 /* Search position is ray -> rorg for volume photons, since we have no
351 intersection point. Normals are ignored -- these are incident
352 directions). */
353 if (isVolumePmap(pmap)) {
354 VCOPY(pos, ray -> rorg);
355 nearestNeighbours(pmap, pos, NULL, 1);
356 }
357 else {
358 VCOPY(pos, ray -> rop);
359 VCOPY(norm, ray -> ron);
360 nearestNeighbours(pmap, pos, norm, 1);
361 }
362
363 #ifndef PMAP_MAXDIST_ABS
364 if (pmap -> squeueEnd < pmap -> squeueSize * pmap -> gatherTolerance) {
365 /* Short lookup; too few photons found */
366 if (pmap -> squeueEnd > PMAP_SHORT_LOOKUP_THRESH) {
367 /* Ignore short lookups which return fewer than
368 * PMAP_SHORT_LOOKUP_THRESH photons under the assumption there
369 * really are no photons in the vicinity, and increasing the max
370 * search radius therefore won't help */
371 #ifdef PMAP_LOOKUP_WARN
372 sprintf(errmsg,
373 "%d/%d %s photons found at (%.2f,%.2f,%.2f) on %s",
374 pmap -> squeueEnd, pmap -> squeueSize,
375 pmapName [pmap -> type], pos [0], pos [1], pos [2],
376 ray -> ro ? ray -> ro -> oname : "<null>");
377 error(WARNING, errmsg);
378 #endif
379
380 if (pmap -> maxDist0 < pmap -> maxDistLimit) {
381 /* Increase max search radius if below limit & redo search */
382 pmap -> maxDist0 *= PMAP_MAXDIST_INC;
383 #ifdef PMAP_LOOKUP_REDO
384 redo = 1;
385 #endif
386
387 #ifdef PMAP_LOOKUP_WARN
388 sprintf(errmsg,
389 redo ? "restarting photon lookup with max radius %.1e"
390 : "max photon lookup radius adjusted to %.1e",
391 pmap -> maxDist0);
392 error(WARNING, errmsg);
393 #endif
394 }
395 #ifdef PMAP_LOOKUP_REDO
396 else {
397 sprintf(errmsg, "max photon lookup radius clamped to %.1e",
398 pmap -> maxDist0);
399 error(WARNING, errmsg);
400 }
401 #endif
402 }
403
404 /* Reset successful lookup counter */
405 pmap -> numLookups = 0;
406 }
407 else {
408 /* Increment successful lookup counter and reduce max search radius if
409 * wraparound */
410 pmap -> numLookups = (pmap -> numLookups + 1) % PMAP_MAXDIST_CNT;
411 if (!pmap -> numLookups)
412 pmap -> maxDist0 *= PMAP_MAXDIST_DEC;
413
414 redo = 0;
415 }
416 #endif
417 } while (redo);
418 }
419
420
421
422 static void nearest1Neighbour (PhotonMap *pmap, const float pos [3],
423 const float norm [3], Photon **photon,
424 unsigned long node)
425 /* Recursive part of find1Photon(..).
426 Note that all heap index handling is 1-based, but accesses to the
427 arrays are 0-based! */
428 {
429 Photon *p = pmap -> heap + node - 1;
430 /* Signed distance to current photon's splitting plane */
431 float d = pos [photonDiscr(*p)] - p -> pos [photonDiscr(*p)],
432 d2 = d * d;
433 float dv [3];
434
435 /* Search subtree closer to pos first; exclude other subtree if the
436 distance to the splitting plane is greater than maxDist */
437 if (d < 0) {
438 if (node << 1 <= pmap -> heapSize)
439 nearest1Neighbour(pmap, pos, norm, photon, node << 1);
440 if (d2 < pmap -> maxDist && node << 1 < pmap -> heapSize)
441 nearest1Neighbour(pmap, pos, norm, photon, (node << 1) + 1);
442 }
443 else {
444 if (node << 1 < pmap -> heapSize)
445 nearest1Neighbour(pmap, pos, norm, photon, (node << 1) + 1);
446 if (d2 < pmap -> maxDist && node << 1 <= pmap -> heapSize)
447 nearest1Neighbour(pmap, pos, norm, photon, node << 1);
448 }
449
450 /* Squared distance to current photon */
451 dv [0] = pos [0] - p -> pos [0];
452 dv [1] = pos [1] - p -> pos [1];
453 dv [2] = pos [2] - p -> pos [2];
454 d2 = DOT(dv, dv);
455
456 if (d2 < pmap -> maxDist && DOT(norm, p -> norm) > 0) {
457 /* Closest photon so far with similar normal */
458 pmap -> maxDist = d2;
459 *photon = p;
460 }
461 }
462
463
464
465 Photon* find1Photon (PhotonMap *pmap, const RAY* ray)
466 {
467 float fpos [3], norm [3];
468 Photon* photon = NULL;
469
470 VCOPY(fpos, ray -> rop);
471 VCOPY(norm, ray -> ron);
472 pmap -> maxDist = thescene.cusize;
473 nearest1Neighbour(pmap, fpos, norm, &photon, 1);
474
475 return photon;
476 }
477
478
479
480 static unsigned long medianPartition (const Photon* heap,
481 unsigned long* heapIdx,
482 unsigned long* heapXdi,
483 unsigned long left,
484 unsigned long right, unsigned dim)
485 /* Returns index to median in heap from indices left to right
486 (inclusive) in dimension dim. The heap is partitioned relative to
487 median using a quicksort algorithm. The heap indices in heapIdx are
488 sorted rather than the heap itself. */
489 {
490 register const float* p;
491 const unsigned long n = right - left + 1;
492 register unsigned long l, r, lg2, n2, m;
493 register unsigned d;
494
495 /* Round down n to nearest power of 2 */
496 for (lg2 = 0, n2 = n; n2 > 1; n2 >>= 1, ++lg2);
497 n2 = 1 << lg2;
498
499 /* Determine median position; this takes into account the fact that
500 only the last level in the heap can be partially empty, and that
501 it fills from left to right */
502 m = left + ((n - n2) > (n2 >> 1) - 1 ? n2 - 1 : n - (n2 >> 1));
503
504 while (right > left) {
505 /* Pivot node */
506 p = heap [heapIdx [right]].pos;
507 l = left;
508 r = right - 1;
509
510 /* l & r converge, swapping elements out of order with respect to
511 pivot node. Identical keys are resolved by cycling through
512 dim. The convergence point is then the pivot's position. */
513 do {
514 while (l <= r) {
515 d = dim;
516
517 while (heap [heapIdx [l]].pos [d] == p [d]) {
518 d = (d + 1) % 3;
519
520 if (d == dim) {
521 /* Ignore dupes? */
522 error(WARNING, "duplicate keys in photon heap");
523 l++;
524 break;
525 }
526 }
527
528 if (heap [heapIdx [l]].pos [d] < p [d])
529 l++;
530 else break;
531 }
532
533 while (r > l) {
534 d = dim;
535
536 while (heap [heapIdx [r]].pos [d] == p [d]) {
537 d = (d + 1) % 3;
538
539 if (d == dim) {
540 /* Ignore dupes? */
541 error(WARNING, "duplicate keys in photon heap");
542 r--;
543 break;
544 }
545 }
546
547 if (heap [heapIdx [r]].pos [d] > p [d])
548 r--;
549 else break;
550 }
551
552 /* Swap indices (not the nodes they point to) */
553 n2 = heapIdx [l];
554 heapIdx [l] = heapIdx [r];
555 heapIdx [r] = n2;
556 /* Update reverse indices */
557 heapXdi [heapIdx [l]] = l;
558 heapXdi [n2] = r;
559 } while (l < r);
560
561 /* Swap indices of convergence and pivot nodes */
562 heapIdx [r] = heapIdx [l];
563 heapIdx [l] = heapIdx [right];
564 heapIdx [right] = n2;
565 /* Update reverse indices */
566 heapXdi [heapIdx [r]] = r;
567 heapXdi [heapIdx [l]] = l;
568 heapXdi [n2] = right;
569 if (l >= m) right = l - 1;
570 if (l <= m) left = l + 1;
571 }
572
573 /* Once left & right have converged at m, we have found the median */
574 return m;
575 }
576
577
578
579 void buildHeap (Photon* heap, unsigned long* heapIdx,
580 unsigned long* heapXdi, const float min [3],
581 const float max [3], unsigned long left,
582 unsigned long right, unsigned long root)
583 /* Recursive part of balancePhotons(..). Builds heap from subarray
584 defined by indices left and right. min and max are the minimum resp.
585 maximum photon positions in the array. root is the index of the
586 current subtree's root, which corresponds to the median's 1-based
587 index in the heap. heapIdx are the balanced heap indices. The heap
588 is accessed indirectly through these. heapXdi are the reverse indices
589 from the heap to heapIdx so that heapXdi [heapIdx [i]] = i. */
590 {
591 float maxLeft [3], minRight [3];
592 Photon rootNode;
593 unsigned d;
594
595 /* Choose median for dimension with largest spread and partition
596 accordingly */
597 const float d0 = max [0] - min [0],
598 d1 = max [1] - min [1],
599 d2 = max [2] - min [2];
600 const unsigned char dim = d0 > d1 ? d0 > d2 ? 0 : 2
601 : d1 > d2 ? 1 : 2;
602 const unsigned long median =
603 left == right ? left
604 : medianPartition(heap, heapIdx, heapXdi, left, right, dim);
605
606 /* Place median at root of current subtree. This consists of swapping
607 the median and the root nodes and updating the heap indices */
608 memcpy(&rootNode, heap + heapIdx [median], sizeof(Photon));
609 memcpy(heap + heapIdx [median], heap + root - 1, sizeof(Photon));
610 setPhotonDiscr(rootNode, dim);
611 memcpy(heap + root - 1, &rootNode, sizeof(Photon));
612 heapIdx [heapXdi [root - 1]] = heapIdx [median];
613 heapXdi [heapIdx [median]] = heapXdi [root - 1];
614 heapIdx [median] = root - 1;
615 heapXdi [root - 1] = median;
616
617 /* Update bounds for left and right subtrees and recurse on them */
618 for (d = 0; d <= 2; d++)
619 if (d == dim)
620 maxLeft [d] = minRight [d] = rootNode.pos [d];
621 else {
622 maxLeft [d] = max [d];
623 minRight [d] = min [d];
624 }
625
626 if (left < median)
627 buildHeap(heap, heapIdx, heapXdi, min, maxLeft,
628 left, median - 1, root << 1);
629
630 if (right > median)
631 buildHeap(heap, heapIdx, heapXdi, minRight, max,
632 median + 1, right, (root << 1) + 1);
633 }
634
635
636
637 void balancePhotons (PhotonMap* pmap, double *photonFlux)
638 {
639 Photon *heap = pmap -> heap;
640 unsigned long i;
641 unsigned long *heapIdx; /* Photon index array */
642 unsigned long *heapXdi; /* Reverse index to heapIdx */
643 unsigned j;
644 COLOR flux;
645 /* Need doubles here to reduce errors from increment */
646 double avgFlux [3] = {0, 0, 0}, CoG [3] = {0, 0, 0}, CoGdist = 0;
647 FVECT d;
648
649 if (pmap -> heapEnd) {
650 pmap -> heapSize = pmap -> heapEnd;
651 heapIdx = (unsigned long*)malloc(pmap -> heapSize *
652 sizeof(unsigned long));
653 heapXdi = (unsigned long*)malloc(pmap -> heapSize *
654 sizeof(unsigned long));
655 if (!heapIdx || !heapXdi)
656 error(USER, "can't allocate heap index");
657
658 for (i = 0; i < pmap -> heapSize; i++) {
659 /* Initialize index arrays */
660 heapXdi [i] = heapIdx [i] = i;
661 getPhotonFlux(heap + i, flux);
662
663 /* Scale photon's flux (hitherto normalised to 1 over RGB); in case
664 * of a contrib photon map, this is done per light source, and
665 * photonFlux is assumed to be an array */
666 if (photonFlux) {
667 scalecolor(flux, photonFlux [isContribPmap(pmap) ?
668 photonSrcIdx(pmap, heap + i) : 0]);
669 setPhotonFlux(heap + i, flux);
670 }
671
672 /* Need a double here */
673 addcolor(avgFlux, flux);
674
675 /* Add photon position to centre of gravity */
676 for (j = 0; j < 3; j++)
677 CoG [j] += heap [i].pos [j];
678 }
679
680 /* Average photon positions to get centre of gravity */
681 for (j = 0; j < 3; j++)
682 pmap -> CoG [j] = CoG [j] /= pmap -> heapSize;
683
684 /* Compute average photon distance to CoG */
685 for (i = 0; i < pmap -> heapSize; i++) {
686 VSUB(d, heap [i].pos, CoG);
687 CoGdist += DOT(d, d);
688 }
689
690 pmap -> CoGdist = CoGdist /= pmap -> heapSize;
691
692 /* Average photon flux based on RGBE representation */
693 scalecolor(avgFlux, 1.0 / pmap -> heapSize);
694 copycolor(pmap -> photonFlux, avgFlux);
695
696 /* Build kd-tree */
697 buildHeap(pmap -> heap, heapIdx, heapXdi, pmap -> minPos,
698 pmap -> maxPos, 0, pmap -> heapSize - 1, 1);
699
700 free(heapIdx);
701 free(heapXdi);
702 }
703 }
704
705
706
707 void deletePhotons (PhotonMap* pmap)
708 {
709 free(pmap -> heap);
710 free(pmap -> squeue);
711 free(pmap -> biasCompHist);
712
713 pmap -> heapSize = 0;
714 pmap -> minGather = pmap -> maxGather =
715 pmap -> squeueSize = pmap -> squeueEnd = 0;
716 }