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

File Contents

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