ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapdata.c
Revision: 2.13
Committed: Wed Sep 9 16:08:46 2015 UTC (8 years, 8 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R0
Changes since 2.12: +3 -1 lines
Log Message:
Disabled warnings about itsy bitsy photon search radius

File Contents

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