ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapdata.c
Revision: 2.7
Committed: Tue May 26 11:26:27 2015 UTC (8 years, 11 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.6: +4 -3 lines
Log Message:
Added stochastic photon coplanarity test in
NearestNeighbours()/Nearest1Neighbour()

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.6 2015/05/21 13:54:59 greg 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 void findPhotons (PhotonMap* pmap, const RAY* ray)
316 {
317 float pos [3], norm [3];
318 int redo = 0;
319
320 if (!pmap -> squeue) {
321 /* Lazy init priority queue */
322 pmap -> squeueSize = pmap -> maxGather + 1;
323 pmap -> squeue = (PhotonSQNode*)malloc(pmap -> squeueSize *
324 sizeof(PhotonSQNode));
325 if (!pmap -> squeue)
326 error(USER, "can't allocate photon priority queue");
327
328 pmap -> minGathered = pmap -> maxGather;
329 pmap -> maxGathered = pmap -> minGather;
330 pmap -> totalGathered = 0;
331 pmap -> numLookups = pmap -> numShortLookups = 0;
332 pmap -> shortLookupPct = 0;
333 pmap -> minError = FHUGE;
334 pmap -> maxError = -FHUGE;
335 pmap -> rmsError = 0;
336 #ifdef PMAP_MAXDIST_ABS
337 /* Treat maxDistCoeff as an *absolute* and *fixed* max search radius.
338 Primarily intended for debugging; FOR ZE ECKSPURTZ ONLY! */
339 pmap -> maxDist0 = pmap -> maxDistLimit = maxDistCoeff;
340 #else
341 /* Maximum search radius limit based on avg photon distance to
342 * centre of gravity */
343 pmap -> maxDist0 = pmap -> maxDistLimit =
344 maxDistCoeff * pmap -> squeueSize * pmap -> CoGdist /
345 pmap -> heapSize;
346 #endif
347 }
348
349 do {
350 pmap -> squeueEnd = 0;
351 pmap -> maxDist = pmap -> maxDist0;
352
353 /* Search position is ray -> rorg for volume photons, since we have no
354 intersection point. Normals are ignored -- these are incident
355 directions). */
356 if (isVolumePmap(pmap)) {
357 VCOPY(pos, ray -> rorg);
358 nearestNeighbours(pmap, pos, NULL, 1);
359 }
360 else {
361 VCOPY(pos, ray -> rop);
362 VCOPY(norm, ray -> ron);
363 nearestNeighbours(pmap, pos, norm, 1);
364 }
365
366 #ifndef PMAP_MAXDIST_ABS
367 if (pmap -> squeueEnd < pmap -> squeueSize * pmap -> gatherTolerance) {
368 /* Short lookup; too few photons found */
369 if (pmap -> squeueEnd > PMAP_SHORT_LOOKUP_THRESH) {
370 /* Ignore short lookups which return fewer than
371 * PMAP_SHORT_LOOKUP_THRESH photons under the assumption there
372 * really are no photons in the vicinity, and increasing the max
373 * search radius therefore won't help */
374 #ifdef PMAP_LOOKUP_WARN
375 sprintf(errmsg,
376 "%d/%d %s photons found at (%.2f,%.2f,%.2f) on %s",
377 pmap -> squeueEnd, pmap -> squeueSize,
378 pmapName [pmap -> type], pos [0], pos [1], pos [2],
379 ray -> ro ? ray -> ro -> oname : "<null>");
380 error(WARNING, errmsg);
381 #endif
382
383 if (pmap -> maxDist0 < pmap -> maxDistLimit) {
384 /* Increase max search radius if below limit & redo search */
385 pmap -> maxDist0 *= PMAP_MAXDIST_INC;
386 #ifdef PMAP_LOOKUP_REDO
387 redo = 1;
388 #endif
389
390 #ifdef PMAP_LOOKUP_WARN
391 sprintf(errmsg,
392 redo ? "restarting photon lookup with max radius %.1e"
393 : "max photon lookup radius adjusted to %.1e",
394 pmap -> maxDist0);
395 error(WARNING, errmsg);
396 #endif
397 }
398 #ifdef PMAP_LOOKUP_REDO
399 else {
400 sprintf(errmsg, "max photon lookup radius clamped to %.1e",
401 pmap -> maxDist0);
402 error(WARNING, errmsg);
403 }
404 #endif
405 }
406
407 /* Reset successful lookup counter */
408 pmap -> numLookups = 0;
409 }
410 else {
411 /* Increment successful lookup counter and reduce max search radius if
412 * wraparound */
413 pmap -> numLookups = (pmap -> numLookups + 1) % PMAP_MAXDIST_CNT;
414 if (!pmap -> numLookups)
415 pmap -> maxDist0 *= PMAP_MAXDIST_DEC;
416
417 redo = 0;
418 }
419 #endif
420 } while (redo);
421 }
422
423
424
425 static void nearest1Neighbour (PhotonMap *pmap, const float pos [3],
426 const float norm [3], Photon **photon,
427 unsigned long node)
428 /* Recursive part of find1Photon(..).
429 Note that all heap index handling is 1-based, but accesses to the
430 arrays are 0-based! */
431 {
432 Photon *p = pmap -> heap + node - 1;
433 /* Signed distance to current photon's splitting plane */
434 float d = pos [photonDiscr(*p)] - p -> pos [photonDiscr(*p)],
435 d2 = d * d;
436 float dv [3];
437
438 /* Search subtree closer to pos first; exclude other subtree if the
439 distance to the splitting plane is greater than maxDist */
440 if (d < 0) {
441 if (node << 1 <= pmap -> heapSize)
442 nearest1Neighbour(pmap, pos, norm, photon, node << 1);
443 if (d2 < pmap -> maxDist && node << 1 < pmap -> heapSize)
444 nearest1Neighbour(pmap, pos, norm, photon, (node << 1) + 1);
445 }
446 else {
447 if (node << 1 < pmap -> heapSize)
448 nearest1Neighbour(pmap, pos, norm, photon, (node << 1) + 1);
449 if (d2 < pmap -> maxDist && node << 1 <= pmap -> heapSize)
450 nearest1Neighbour(pmap, pos, norm, photon, node << 1);
451 }
452
453 /* Squared distance to current photon */
454 dv [0] = pos [0] - p -> pos [0];
455 dv [1] = pos [1] - p -> pos [1];
456 dv [2] = pos [2] - p -> pos [2];
457 d2 = DOT(dv, dv);
458
459 if (d2 < pmap -> maxDist && DOT(norm, p -> norm) > 0.5 * frandom()) {
460 /* Closest photon so far with similar normal */
461 pmap -> maxDist = d2;
462 *photon = p;
463 }
464 }
465
466
467
468 Photon* find1Photon (PhotonMap *pmap, const RAY* ray)
469 {
470 float fpos [3], norm [3];
471 Photon* photon = NULL;
472
473 VCOPY(fpos, ray -> rop);
474 VCOPY(norm, ray -> ron);
475 pmap -> maxDist = thescene.cusize;
476 nearest1Neighbour(pmap, fpos, norm, &photon, 1);
477
478 return photon;
479 }
480
481
482
483 static unsigned long medianPartition (const Photon* heap,
484 unsigned long* heapIdx,
485 unsigned long* heapXdi,
486 unsigned long left,
487 unsigned long right, unsigned dim)
488 /* Returns index to median in heap from indices left to right
489 (inclusive) in dimension dim. The heap is partitioned relative to
490 median using a quicksort algorithm. The heap indices in heapIdx are
491 sorted rather than the heap itself. */
492 {
493 register const float* p;
494 const unsigned long n = right - left + 1;
495 register unsigned long l, r, lg2, n2, m;
496 register unsigned d;
497
498 /* Round down n to nearest power of 2 */
499 for (lg2 = 0, n2 = n; n2 > 1; n2 >>= 1, ++lg2);
500 n2 = 1 << lg2;
501
502 /* Determine median position; this takes into account the fact that
503 only the last level in the heap can be partially empty, and that
504 it fills from left to right */
505 m = left + ((n - n2) > (n2 >> 1) - 1 ? n2 - 1 : n - (n2 >> 1));
506
507 while (right > left) {
508 /* Pivot node */
509 p = heap [heapIdx [right]].pos;
510 l = left;
511 r = right - 1;
512
513 /* l & r converge, swapping elements out of order with respect to
514 pivot node. Identical keys are resolved by cycling through
515 dim. The convergence point is then the pivot's position. */
516 do {
517 while (l <= r) {
518 d = dim;
519
520 while (heap [heapIdx [l]].pos [d] == p [d]) {
521 d = (d + 1) % 3;
522
523 if (d == dim) {
524 /* Ignore dupes? */
525 error(WARNING, "duplicate keys in photon heap");
526 l++;
527 break;
528 }
529 }
530
531 if (heap [heapIdx [l]].pos [d] < p [d])
532 l++;
533 else break;
534 }
535
536 while (r > l) {
537 d = dim;
538
539 while (heap [heapIdx [r]].pos [d] == p [d]) {
540 d = (d + 1) % 3;
541
542 if (d == dim) {
543 /* Ignore dupes? */
544 error(WARNING, "duplicate keys in photon heap");
545 r--;
546 break;
547 }
548 }
549
550 if (heap [heapIdx [r]].pos [d] > p [d])
551 r--;
552 else break;
553 }
554
555 /* Swap indices (not the nodes they point to) */
556 n2 = heapIdx [l];
557 heapIdx [l] = heapIdx [r];
558 heapIdx [r] = n2;
559 /* Update reverse indices */
560 heapXdi [heapIdx [l]] = l;
561 heapXdi [n2] = r;
562 } while (l < r);
563
564 /* Swap indices of convergence and pivot nodes */
565 heapIdx [r] = heapIdx [l];
566 heapIdx [l] = heapIdx [right];
567 heapIdx [right] = n2;
568 /* Update reverse indices */
569 heapXdi [heapIdx [r]] = r;
570 heapXdi [heapIdx [l]] = l;
571 heapXdi [n2] = right;
572 if (l >= m) right = l - 1;
573 if (l <= m) left = l + 1;
574 }
575
576 /* Once left & right have converged at m, we have found the median */
577 return m;
578 }
579
580
581
582 void buildHeap (Photon* heap, unsigned long* heapIdx,
583 unsigned long* heapXdi, const float min [3],
584 const float max [3], unsigned long left,
585 unsigned long right, unsigned long root)
586 /* Recursive part of balancePhotons(..). Builds heap from subarray
587 defined by indices left and right. min and max are the minimum resp.
588 maximum photon positions in the array. root is the index of the
589 current subtree's root, which corresponds to the median's 1-based
590 index in the heap. heapIdx are the balanced heap indices. The heap
591 is accessed indirectly through these. heapXdi are the reverse indices
592 from the heap to heapIdx so that heapXdi [heapIdx [i]] = i. */
593 {
594 float maxLeft [3], minRight [3];
595 Photon rootNode;
596 unsigned d;
597
598 /* Choose median for dimension with largest spread and partition
599 accordingly */
600 const float d0 = max [0] - min [0],
601 d1 = max [1] - min [1],
602 d2 = max [2] - min [2];
603 const unsigned char dim = d0 > d1 ? d0 > d2 ? 0 : 2
604 : d1 > d2 ? 1 : 2;
605 const unsigned long median =
606 left == right ? left
607 : medianPartition(heap, heapIdx, heapXdi, left, right, dim);
608
609 /* Place median at root of current subtree. This consists of swapping
610 the median and the root nodes and updating the heap indices */
611 memcpy(&rootNode, heap + heapIdx [median], sizeof(Photon));
612 memcpy(heap + heapIdx [median], heap + root - 1, sizeof(Photon));
613 setPhotonDiscr(rootNode, dim);
614 memcpy(heap + root - 1, &rootNode, sizeof(Photon));
615 heapIdx [heapXdi [root - 1]] = heapIdx [median];
616 heapXdi [heapIdx [median]] = heapXdi [root - 1];
617 heapIdx [median] = root - 1;
618 heapXdi [root - 1] = median;
619
620 /* Update bounds for left and right subtrees and recurse on them */
621 for (d = 0; d <= 2; d++)
622 if (d == dim)
623 maxLeft [d] = minRight [d] = rootNode.pos [d];
624 else {
625 maxLeft [d] = max [d];
626 minRight [d] = min [d];
627 }
628
629 if (left < median)
630 buildHeap(heap, heapIdx, heapXdi, min, maxLeft,
631 left, median - 1, root << 1);
632
633 if (right > median)
634 buildHeap(heap, heapIdx, heapXdi, minRight, max,
635 median + 1, right, (root << 1) + 1);
636 }
637
638
639
640 void balancePhotons (PhotonMap* pmap, double *photonFlux)
641 {
642 Photon *heap = pmap -> heap;
643 unsigned long i;
644 unsigned long *heapIdx; /* Photon index array */
645 unsigned long *heapXdi; /* Reverse index to heapIdx */
646 unsigned j;
647 COLOR flux;
648 /* Need doubles here to reduce errors from increment */
649 double avgFlux [3] = {0, 0, 0}, CoG [3] = {0, 0, 0}, CoGdist = 0;
650 FVECT d;
651
652 if (pmap -> heapEnd) {
653 pmap -> heapSize = pmap -> heapEnd;
654 heapIdx = (unsigned long*)malloc(pmap -> heapSize *
655 sizeof(unsigned long));
656 heapXdi = (unsigned long*)malloc(pmap -> heapSize *
657 sizeof(unsigned long));
658 if (!heapIdx || !heapXdi)
659 error(USER, "can't allocate heap index");
660
661 for (i = 0; i < pmap -> heapSize; i++) {
662 /* Initialize index arrays */
663 heapXdi [i] = heapIdx [i] = i;
664 getPhotonFlux(heap + i, flux);
665
666 /* Scale photon's flux (hitherto normalised to 1 over RGB); in case
667 * of a contrib photon map, this is done per light source, and
668 * photonFlux is assumed to be an array */
669 if (photonFlux) {
670 scalecolor(flux, photonFlux [isContribPmap(pmap) ?
671 photonSrcIdx(pmap, heap + i) : 0]);
672 setPhotonFlux(heap + i, flux);
673 }
674
675 /* Need a double here */
676 addcolor(avgFlux, flux);
677
678 /* Add photon position to centre of gravity */
679 for (j = 0; j < 3; j++)
680 CoG [j] += heap [i].pos [j];
681 }
682
683 /* Average photon positions to get centre of gravity */
684 for (j = 0; j < 3; j++)
685 pmap -> CoG [j] = CoG [j] /= pmap -> heapSize;
686
687 /* Compute average photon distance to CoG */
688 for (i = 0; i < pmap -> heapSize; i++) {
689 VSUB(d, heap [i].pos, CoG);
690 CoGdist += DOT(d, d);
691 }
692
693 pmap -> CoGdist = CoGdist /= pmap -> heapSize;
694
695 /* Average photon flux based on RGBE representation */
696 scalecolor(avgFlux, 1.0 / pmap -> heapSize);
697 copycolor(pmap -> photonFlux, avgFlux);
698
699 /* Build kd-tree */
700 buildHeap(pmap -> heap, heapIdx, heapXdi, pmap -> minPos,
701 pmap -> maxPos, 0, pmap -> heapSize - 1, 1);
702
703 free(heapIdx);
704 free(heapXdi);
705 }
706 }
707
708
709
710 void deletePhotons (PhotonMap* pmap)
711 {
712 free(pmap -> heap);
713 free(pmap -> squeue);
714 free(pmap -> biasCompHist);
715
716 pmap -> heapSize = 0;
717 pmap -> minGather = pmap -> maxGather =
718 pmap -> squeueSize = pmap -> squeueEnd = 0;
719 }