ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapdata.c
Revision: 2.6
Committed: Thu May 21 13:54:59 2015 UTC (9 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.5: +2 -2 lines
Log Message:
Added calls to findmaterial() and simplified photon map shadow check

File Contents

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