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

# 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.7 $Id: pmapdata.c,v 2.6 2015/05/21 13:54:59 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 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     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 rschregle 2.3 #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 greg 2.1 /* 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 rschregle 2.3 #endif
347 greg 2.1 }
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 rschregle 2.3
366     #ifndef PMAP_MAXDIST_ABS
367 greg 2.1 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 rschregle 2.3 #ifdef PMAP_LOOKUP_WARN
375 greg 2.1 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 rschregle 2.3 #endif
382    
383 greg 2.1 if (pmap -> maxDist0 < pmap -> maxDistLimit) {
384     /* Increase max search radius if below limit & redo search */
385     pmap -> maxDist0 *= PMAP_MAXDIST_INC;
386 rschregle 2.3 #ifdef PMAP_LOOKUP_REDO
387 greg 2.1 redo = 1;
388 rschregle 2.3 #endif
389 greg 2.1
390 rschregle 2.3 #ifdef PMAP_LOOKUP_WARN
391 greg 2.1 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 rschregle 2.3 #endif
397 greg 2.1 }
398 rschregle 2.3 #ifdef PMAP_LOOKUP_REDO
399 greg 2.1 else {
400     sprintf(errmsg, "max photon lookup radius clamped to %.1e",
401     pmap -> maxDist0);
402     error(WARNING, errmsg);
403     }
404 rschregle 2.3 #endif
405 greg 2.1 }
406    
407     /* Reset successful lookup counter */
408     pmap -> numLookups = 0;
409 rschregle 2.3 }
410 greg 2.1 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 rschregle 2.3 #endif
420 greg 2.1 } 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 rschregle 2.7 if (d2 < pmap -> maxDist && DOT(norm, p -> norm) > 0.5 * frandom()) {
460 greg 2.1 /* 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     }