ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapdata.c
Revision: 2.8
Committed: Tue May 26 13:31:19 2015 UTC (8 years, 11 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.7: +27 -22 lines
Log Message:
-am option to rpict/rvu/rtrace now sets fixed max photon search radius,
-overriding the adaptive max search radius

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