ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapdata.c
Revision: 2.12
Committed: Tue Sep 1 16:27:52 2015 UTC (9 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.11: +1 -2 lines
Log Message:
Removed redundant $Id: in file

File Contents

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