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 (9 years 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

# 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 rschregle 2.8 $Id: pmapdata.c,v 2.7 2015/05/26 11:26:27 rschregle 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 rschregle 2.7 #include "random.h"
23 greg 2.1
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 greg 2.5 FVECT dvec;
71 greg 2.1
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 greg 2.5 dvec [0] = -ray -> rdir [0];
104     dvec [1] = -ray -> rdir [1];
105     dvec [2] = -ray -> rdir [2];
106     prim -> dir = encodedir(dvec);
107 greg 2.1
108 greg 2.4 VCOPY(prim -> pos, ray -> rop);
109 greg 2.1
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 rschregle 2.7 if (norm && DOT(norm, p -> norm) <= 0.5 * frandom())
234 greg 2.1 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 greg 2.6 srcMod = findmaterial(source [srcIdx].so);
245 greg 2.1
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 rschregle 2.8 /* Coefficient for adaptive maximum search radius */
316     #define PMAP_MAXDIST_COEFF 100
317    
318    
319 greg 2.1 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 rschregle 2.8 /* Maximum search radius limit is based on avg photon distance to
341     * centre of gravity, unless fixed by user (maxDistFix > 0) */
342 greg 2.1 pmap -> maxDist0 = pmap -> maxDistLimit =
343 rschregle 2.8 maxDistFix > 0 ? maxDistFix
344     : PMAP_MAXDIST_COEFF * pmap -> squeueSize *
345     pmap -> CoGdist / pmap -> heapSize;
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 greg 2.1 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 rschregle 2.8 #ifdef PMAP_LOOKUP_WARN
373 greg 2.1 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 rschregle 2.8 #endif
380 rschregle 2.3
381 rschregle 2.8 /* Bail out after warning if maxDist is fixed */
382     if (maxDistFix > 0)
383     return;
384    
385 greg 2.1 if (pmap -> maxDist0 < pmap -> maxDistLimit) {
386     /* Increase max search radius if below limit & redo search */
387     pmap -> maxDist0 *= PMAP_MAXDIST_INC;
388 rschregle 2.8 #ifdef PMAP_LOOKUP_REDO
389 greg 2.1 redo = 1;
390 rschregle 2.8 #endif
391     #ifdef PMAP_LOOKUP_WARN
392 greg 2.1 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 rschregle 2.8 #endif
398 greg 2.1 }
399 rschregle 2.8 #ifdef PMAP_LOOKUP_REDO
400 greg 2.1 else {
401     sprintf(errmsg, "max photon lookup radius clamped to %.1e",
402     pmap -> maxDist0);
403     error(WARNING, errmsg);
404     }
405 rschregle 2.8 #endif
406 greg 2.1 }
407    
408     /* Reset successful lookup counter */
409     pmap -> numLookups = 0;
410 rschregle 2.3 }
411 greg 2.1 else {
412 rschregle 2.8 /* Bail out after warning if maxDist is fixed */
413     if (maxDistFix > 0)
414     return;
415    
416 greg 2.1 /* 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 rschregle 2.8
425 greg 2.1 } 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 rschregle 2.7 if (d2 < pmap -> maxDist && DOT(norm, p -> norm) > 0.5 * frandom()) {
465 greg 2.1 /* 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     }