ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.15
Committed: Tue Mar 20 19:55:33 2018 UTC (7 years, 1 month ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.14: +28 -25 lines
Log Message:
Added -ae/-ai ambient exclude options to mkpmap, cleaned up opt parsing.

File Contents

# User Rev Content
1 greg 2.7 #ifndef lint
2 rschregle 2.15 static const char RCSid[] = "$Id: pmapsrc.c,v 2.14 2018/02/02 19:47:55 rschregle Exp $";
3 greg 2.7 #endif
4 greg 2.1 /*
5     ==================================================================
6     Photon map support routines for emission from light sources
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 greg 2.8
14 greg 2.1 */
15    
16    
17    
18     #include "pmapsrc.h"
19     #include "pmap.h"
20     #include "pmaprand.h"
21     #include "otypes.h"
22    
23 rschregle 2.15 /* List of photon port modifier names */
24     char *photonPortList [MAXSET + 1] = {NULL};
25     /* Photon port objects (with modifiers in photonPortMods) */
26     SRCREC *photonPorts = NULL;
27 greg 2.1 unsigned numPhotonPorts = 0;
28    
29     void (*photonPartition [NUMOTYPE]) (EmissionMap*);
30     void (*photonOrigin [NUMOTYPE]) (EmissionMap*);
31    
32    
33    
34     static int flatPhotonPartition2 (EmissionMap* emap, unsigned long mp,
35     FVECT cent, FVECT u, FVECT v,
36     double du2, double dv2)
37     /* Recursive part of flatPhotonPartition(..) */
38     {
39     FVECT newct, newax;
40     unsigned long npl, npu;
41    
42     if (mp > emap -> maxPartitions) {
43     /* Enlarge partition array */
44     emap -> maxPartitions <<= 1;
45     emap -> partitions = (unsigned char*)realloc(emap -> partitions,
46     emap -> maxPartitions >> 1);
47    
48     if (!emap -> partitions)
49     error(USER, "can't allocate source partitions");
50    
51 greg 2.2 memset(emap -> partitions + (emap -> maxPartitions >> 2), 0,
52     emap -> maxPartitions >> 2);
53 greg 2.1 }
54    
55     if (du2 * dv2 <= 1) { /* hit limit? */
56     setpart(emap -> partitions, emap -> partitionCnt, S0);
57     emap -> partitionCnt++;
58     return 1;
59     }
60    
61     if (du2 > dv2) { /* subdivide in U */
62     setpart(emap -> partitions, emap -> partitionCnt, SU);
63     emap -> partitionCnt++;
64     newax [0] = 0.5 * u [0];
65     newax [1] = 0.5 * u [1];
66     newax [2] = 0.5 * u [2];
67     u = newax;
68     du2 *= 0.25;
69     }
70    
71     else { /* subdivide in V */
72     setpart(emap -> partitions, emap -> partitionCnt, SV);
73     emap -> partitionCnt++;
74     newax [0] = 0.5 * v [0];
75     newax [1] = 0.5 * v [1];
76     newax [2] = 0.5 * v [2];
77     v = newax;
78     dv2 *= 0.25;
79     }
80    
81     /* lower half */
82     newct [0] = cent [0] - newax [0];
83     newct [1] = cent [1] - newax [1];
84     newct [2] = cent [2] - newax [2];
85     npl = flatPhotonPartition2(emap, mp << 1, newct, u, v, du2, dv2);
86    
87     /* upper half */
88     newct [0] = cent [0] + newax [0];
89     newct [1] = cent [1] + newax [1];
90     newct [2] = cent [2] + newax [2];
91     npu = flatPhotonPartition2(emap, mp << 1, newct, u, v, du2, dv2);
92    
93     /* return total */
94     return npl + npu;
95     }
96    
97    
98    
99     static void flatPhotonPartition (EmissionMap* emap)
100     /* Partition flat source for photon emission */
101     {
102     RREAL *vp;
103     double du2, dv2;
104    
105 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
106 greg 2.1 emap -> partArea = srcsizerat * thescene.cusize;
107     emap -> partArea *= emap -> partArea;
108     vp = emap -> src -> ss [SU];
109     du2 = DOT(vp, vp) / emap -> partArea;
110     vp = emap -> src -> ss [SV];
111     dv2 = DOT(vp, vp) / emap -> partArea;
112     emap -> partitionCnt = 0;
113     emap -> numPartitions = flatPhotonPartition2(emap, 1, emap -> src -> sloc,
114     emap -> src -> ss [SU],
115     emap -> src -> ss [SV],
116     du2, dv2);
117     emap -> partitionCnt = 0;
118     emap -> partArea = emap -> src -> ss2 / emap -> numPartitions;
119     }
120    
121    
122    
123     static void sourcePhotonPartition (EmissionMap* emap)
124     /* Partition scene cube faces or photon port for photon emission from
125     distant source */
126     {
127     if (emap -> port) {
128     /* Partition photon port */
129     SRCREC *src = emap -> src;
130     emap -> src = emap -> port;
131     photonPartition [emap -> src -> so -> otype] (emap);
132     emap -> src = src;
133     }
134    
135     else {
136     /* No photon ports defined, so partition scene cube faces */
137 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
138 greg 2.1 setpart(emap -> partitions, 0, S0);
139     emap -> partitionCnt = 0;
140     emap -> numPartitions = 1 / srcsizerat;
141     emap -> numPartitions *= emap -> numPartitions;
142     emap -> partArea = sqr(thescene.cusize) / emap -> numPartitions;
143     emap -> numPartitions *= 6;
144     }
145     }
146    
147    
148    
149     static void spherePhotonPartition (EmissionMap* emap)
150     /* Partition spherical source into equal solid angles using uniform
151     mapping for photon emission */
152     {
153     unsigned numTheta, numPhi;
154    
155 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
156 greg 2.1 setpart(emap -> partitions, 0, S0);
157     emap -> partArea = 4 * PI * sqr(emap -> src -> srad);
158     emap -> numPartitions = emap -> partArea /
159     sqr(srcsizerat * thescene.cusize);
160    
161     numTheta = max(sqrt(2 * emap -> numPartitions / PI) + 0.5, 1);
162     numPhi = 0.5 * PI * numTheta + 0.5;
163    
164     emap -> numPartitions = (unsigned long)numTheta * numPhi;
165     emap -> partitionCnt = 0;
166     emap -> partArea /= emap -> numPartitions;
167     }
168    
169    
170    
171     static int cylPhotonPartition2 (EmissionMap* emap, unsigned long mp,
172     FVECT cent, FVECT axis, double d2)
173     /* Recursive part of cyPhotonPartition(..) */
174     {
175     FVECT newct, newax;
176     unsigned long npl, npu;
177    
178     if (mp > emap -> maxPartitions) {
179     /* Enlarge partition array */
180     emap -> maxPartitions <<= 1;
181     emap -> partitions = (unsigned char*)realloc(emap -> partitions,
182     emap -> maxPartitions >> 1);
183     if (!emap -> partitions)
184     error(USER, "can't allocate source partitions");
185 greg 2.2
186     memset(emap -> partitions + (emap -> maxPartitions >> 2), 0,
187 greg 2.1 emap -> maxPartitions >> 2);
188     }
189    
190     if (d2 <= 1) {
191     /* hit limit? */
192     setpart(emap -> partitions, emap -> partitionCnt, S0);
193     emap -> partitionCnt++;
194     return 1;
195     }
196    
197     /* subdivide */
198     setpart(emap -> partitions, emap -> partitionCnt, SU);
199     emap -> partitionCnt++;
200     newax [0] = 0.5 * axis [0];
201     newax [1] = 0.5 * axis [1];
202     newax [2] = 0.5 * axis [2];
203     d2 *= 0.25;
204    
205     /* lower half */
206     newct [0] = cent [0] - newax [0];
207     newct [1] = cent [1] - newax [1];
208     newct [2] = cent [2] - newax [2];
209     npl = cylPhotonPartition2(emap, mp << 1, newct, newax, d2);
210    
211     /* upper half */
212     newct [0] = cent [0] + newax [0];
213     newct [1] = cent [1] + newax [1];
214     newct [2] = cent [2] + newax [2];
215     npu = cylPhotonPartition2(emap, mp << 1, newct, newax, d2);
216    
217     /* return total */
218     return npl + npu;
219     }
220    
221    
222    
223     static void cylPhotonPartition (EmissionMap* emap)
224     /* Partition cylindrical source for photon emission */
225     {
226     double d2;
227    
228 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
229 greg 2.1 d2 = srcsizerat * thescene.cusize;
230     d2 = PI * emap -> src -> ss2 / (2 * emap -> src -> srad * sqr(d2));
231     d2 *= d2 * DOT(emap -> src -> ss [SU], emap -> src -> ss [SU]);
232    
233     emap -> partitionCnt = 0;
234     emap -> numPartitions = cylPhotonPartition2(emap, 1, emap -> src -> sloc,
235     emap -> src -> ss [SU], d2);
236     emap -> partitionCnt = 0;
237     emap -> partArea = PI * emap -> src -> ss2 / emap -> numPartitions;
238     }
239    
240    
241    
242     static void flatPhotonOrigin (EmissionMap* emap)
243     /* Init emission map with photon origin and associated surface axes on
244     flat light source surface. Also sets source aperture and sampling
245     hemisphere axes at origin */
246     {
247     int i, cent[3], size[3], parr[2];
248     FVECT vpos;
249    
250     cent [0] = cent [1] = cent [2] = 0;
251     size [0] = size [1] = size [2] = emap -> maxPartitions;
252     parr [0] = 0;
253     parr [1] = emap -> partitionCnt;
254    
255     if (!skipparts(cent, size, parr, emap -> partitions))
256     error(CONSISTENCY, "bad source partition in flatPhotonOrigin");
257    
258     vpos [0] = (1 - 2 * pmapRandom(partState)) * size [0] /
259     emap -> maxPartitions;
260     vpos [1] = (1 - 2 * pmapRandom(partState)) * size [1] /
261     emap -> maxPartitions;
262     vpos [2] = 0;
263    
264     for (i = 0; i < 3; i++)
265     vpos [i] += (double)cent [i] / emap -> maxPartitions;
266    
267     /* Get origin */
268     for (i = 0; i < 3; i++)
269     emap -> photonOrg [i] = emap -> src -> sloc [i] +
270     vpos [SU] * emap -> src -> ss [SU][i] +
271     vpos [SV] * emap -> src -> ss [SV][i] +
272     vpos [SW] * emap -> src -> ss [SW][i];
273    
274     /* Get surface axes */
275     VCOPY(emap -> us, emap -> src -> ss [SU]);
276     normalize(emap -> us);
277     VCOPY(emap -> ws, emap -> src -> ss [SW]);
278    
279     if (emap -> port)
280     /* Acts as a photon port; reverse normal as it points INSIDE per
281     * mkillum convention */
282     for (i = 0; i < 3; i++)
283     emap -> ws [i] = -emap -> ws [i];
284    
285     fcross(emap -> vs, emap -> ws, emap -> us);
286    
287     /* Get hemisphere axes & aperture */
288     if (emap -> src -> sflags & SSPOT) {
289     VCOPY(emap -> wh, emap -> src -> sl.s -> aim);
290     i = 0;
291    
292     do {
293     emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
294     emap -> vh [i++] = 1;
295     fcross(emap -> uh, emap -> vh, emap -> wh);
296     } while (normalize(emap -> uh) < FTINY);
297    
298     fcross(emap -> vh, emap -> wh, emap -> uh);
299     emap -> cosThetaMax = 1 - emap -> src -> sl.s -> siz / (2 * PI);
300     }
301    
302     else {
303     VCOPY(emap -> uh, emap -> us);
304     VCOPY(emap -> vh, emap -> vs);
305     VCOPY(emap -> wh, emap -> ws);
306     emap -> cosThetaMax = 0;
307     }
308     }
309    
310    
311    
312     static void spherePhotonOrigin (EmissionMap* emap)
313     /* Init emission map with photon origin and associated surface axes on
314     spherical light source. Also sets source aperture and sampling
315     hemisphere axes at origin */
316     {
317     int i = 0;
318     unsigned numTheta, numPhi, t, p;
319     RREAL cosTheta, sinTheta, phi;
320    
321     /* Get current partition */
322     numTheta = max(sqrt(2 * emap -> numPartitions / PI) + 0.5, 1);
323     numPhi = 0.5 * PI * numTheta + 0.5;
324    
325     t = emap -> partitionCnt / numPhi;
326     p = emap -> partitionCnt - t * numPhi;
327    
328     emap -> ws [2] = cosTheta = 1 - 2 * (t + pmapRandom(partState)) / numTheta;
329     sinTheta = sqrt(1 - sqr(cosTheta));
330     phi = 2 * PI * (p + pmapRandom(partState)) / numPhi;
331     emap -> ws [0] = cos(phi) * sinTheta;
332     emap -> ws [1] = sin(phi) * sinTheta;
333    
334     if (emap -> port)
335     /* Acts as a photon port; reverse normal as it points INSIDE per
336     * mkillum convention */
337     for (i = 0; i < 3; i++)
338     emap -> ws [i] = -emap -> ws [i];
339    
340     /* Get surface axes us & vs perpendicular to ws */
341     do {
342     emap -> vs [0] = emap -> vs [1] = emap -> vs [2] = 0;
343     emap -> vs [i++] = 1;
344     fcross(emap -> us, emap -> vs, emap -> ws);
345     } while (normalize(emap -> us) < FTINY);
346    
347     fcross(emap -> vs, emap -> ws, emap -> us);
348    
349     /* Get origin */
350     for (i = 0; i < 3; i++)
351     emap -> photonOrg [i] = emap -> src -> sloc [i] +
352     emap -> src -> srad * emap -> ws [i];
353    
354     /* Get hemisphere axes & aperture */
355     if (emap -> src -> sflags & SSPOT) {
356     VCOPY(emap -> wh, emap -> src -> sl.s -> aim);
357     i = 0;
358    
359     do {
360     emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
361     emap -> vh [i++] = 1;
362     fcross(emap -> uh, emap -> vh, emap -> wh);
363     } while (normalize(emap -> uh) < FTINY);
364    
365     fcross(emap -> vh, emap -> wh, emap -> uh);
366     emap -> cosThetaMax = 1 - emap -> src -> sl.s -> siz / (2 * PI);
367     }
368    
369     else {
370     VCOPY(emap -> uh, emap -> us);
371     VCOPY(emap -> vh, emap -> vs);
372     VCOPY(emap -> wh, emap -> ws);
373     emap -> cosThetaMax = 0;
374     }
375     }
376    
377    
378    
379     static void sourcePhotonOrigin (EmissionMap* emap)
380     /* Init emission map with photon origin and associated surface axes
381     on scene cube face for distant light source. Also sets source
382     aperture (solid angle) and sampling hemisphere axes at origin */
383     {
384     unsigned long i, partsPerDim, partsPerFace;
385     unsigned face;
386     RREAL du, dv;
387    
388     if (emap -> port) {
389     /* Get origin on photon port */
390     SRCREC *src = emap -> src;
391     emap -> src = emap -> port;
392     photonOrigin [emap -> src -> so -> otype] (emap);
393     emap -> src = src;
394     }
395    
396     else {
397     /* No ports defined, so get origin on scene cube face and SUFFA! */
398     /* Get current face from partition number */
399     partsPerDim = 1 / srcsizerat;
400     partsPerFace = sqr(partsPerDim);
401     face = emap -> partitionCnt / partsPerFace;
402    
403     if (!(emap -> partitionCnt % partsPerFace)) {
404     /* Skipped to a new face; get its normal */
405     emap -> ws [0] = emap -> ws [1] = emap -> ws [2] = 0;
406     emap -> ws [face >> 1] = face & 1 ? 1 : -1;
407    
408     /* Get surface axes us & vs perpendicular to ws */
409     face >>= 1;
410     emap -> vs [0] = emap -> vs [1] = emap -> vs [2] = 0;
411     emap -> vs [(face + (emap -> ws [face] > 0 ? 2 : 1)) % 3] = 1;
412     fcross(emap -> us, emap -> vs, emap -> ws);
413     }
414    
415     /* Get jittered offsets within face from partition number
416     (in range [-0.5, 0.5]) */
417     i = emap -> partitionCnt % partsPerFace;
418     du = (i / partsPerDim + pmapRandom(partState)) / partsPerDim - 0.5;
419     dv = (i % partsPerDim + pmapRandom(partState)) / partsPerDim - 0.5;
420    
421     /* Jittered destination point within partition */
422     for (i = 0; i < 3; i++)
423     emap -> photonOrg [i] = thescene.cuorg [i] +
424     thescene.cusize * (0.5 + du * emap -> us [i] +
425     dv * emap -> vs [i] +
426     0.5 * emap -> ws [i]);
427     }
428    
429     /* Get hemisphere axes & aperture */
430     VCOPY(emap -> wh, emap -> src -> sloc);
431     i = 0;
432    
433     do {
434     emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
435     emap -> vh [i++] = 1;
436     fcross(emap -> uh, emap -> vh, emap -> wh);
437     } while (normalize(emap -> uh) < FTINY);
438    
439     fcross(emap -> vh, emap -> wh, emap -> uh);
440    
441     /* Get aperture */
442     emap -> cosThetaMax = 1 - emap -> src -> ss2 / (2 * PI);
443     emap -> cosThetaMax = min(1, max(-1, emap -> cosThetaMax));
444     }
445    
446    
447    
448     static void cylPhotonOrigin (EmissionMap* emap)
449     /* Init emission map with photon origin and associated surface axes
450     on cylindrical light source surface. Also sets source aperture
451     and sampling hemisphere axes at origin */
452     {
453     int i, cent[3], size[3], parr[2];
454     FVECT v;
455    
456     cent [0] = cent [1] = cent [2] = 0;
457     size [0] = size [1] = size [2] = emap -> maxPartitions;
458     parr [0] = 0;
459     parr [1] = emap -> partitionCnt;
460    
461     if (!skipparts(cent, size, parr, emap -> partitions))
462     error(CONSISTENCY, "bad source partition in cylPhotonOrigin");
463    
464     v [SU] = 0;
465     v [SV] = (1 - 2 * pmapRandom(partState)) * (double)size [1] /
466     emap -> maxPartitions;
467     v [SW] = (1 - 2 * pmapRandom(partState)) * (double)size [2] /
468     emap -> maxPartitions;
469     normalize(v);
470     v [SU] = (1 - 2 * pmapRandom(partState)) * (double)size [1] /
471     emap -> maxPartitions;
472    
473     for (i = 0; i < 3; i++)
474     v [i] += (double)cent [i] / emap -> maxPartitions;
475    
476     /* Get surface axes */
477     for (i = 0; i < 3; i++)
478     emap -> photonOrg [i] = emap -> ws [i] =
479     (v [SV] * emap -> src -> ss [SV][i] +
480     v [SW] * emap -> src -> ss [SW][i]) / 0.8559;
481    
482     if (emap -> port)
483     /* Acts as a photon port; reverse normal as it points INSIDE per
484     * mkillum convention */
485     for (i = 0; i < 3; i++)
486     emap -> ws [i] = -emap -> ws [i];
487    
488     normalize(emap -> ws);
489     VCOPY(emap -> us, emap -> src -> ss [SU]);
490     normalize(emap -> us);
491     fcross(emap -> vs, emap -> ws, emap -> us);
492    
493     /* Get origin */
494     for (i = 0; i < 3; i++)
495     emap -> photonOrg [i] += v [SU] * emap -> src -> ss [SU][i] +
496     emap -> src -> sloc [i];
497    
498     /* Get hemisphere axes & aperture */
499     if (emap -> src -> sflags & SSPOT) {
500     VCOPY(emap -> wh, emap -> src -> sl.s -> aim);
501     i = 0;
502    
503     do {
504     emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
505     emap -> vh [i++] = 1;
506     fcross(emap -> uh, emap -> vh, emap -> wh);
507     } while (normalize(emap -> uh) < FTINY);
508    
509     fcross(emap -> vh, emap -> wh, emap -> uh);
510     emap -> cosThetaMax = 1 - emap -> src -> sl.s -> siz / (2 * PI);
511     }
512    
513     else {
514     VCOPY(emap -> uh, emap -> us);
515     VCOPY(emap -> vh, emap -> vs);
516     VCOPY(emap -> wh, emap -> ws);
517     emap -> cosThetaMax = 0;
518     }
519     }
520    
521    
522    
523 rschregle 2.15 void getPhotonPorts (char **portList)
524     /* Find geometry declared as photon ports from modifiers in portList */
525 greg 2.1 {
526     OBJECT i;
527 rschregle 2.11 OBJREC *obj, *mat;
528 rschregle 2.15 char **lp;
529 greg 2.1
530     /* Check for missing port modifiers */
531 rschregle 2.15 if (!portList [0])
532 rschregle 2.11 error(USER, "no photon ports");
533 greg 2.1
534     for (i = 0; i < nobjects; i++) {
535     obj = objptr(i);
536 rschregle 2.11 mat = findmaterial(obj);
537 greg 2.1
538 rschregle 2.10 /* Check if object is a surface and NOT a light source (duh) and
539     * resolve its material via any aliases, then check for inclusion in
540 rschregle 2.15 * modifier list */
541     if (issurface(obj -> otype) && mat && !islight(mat -> otype)) {
542     for (lp = portList; *lp && strcmp(mat -> oname, *lp); lp++);
543    
544     if (*lp) {
545     /* Add photon port */
546     photonPorts = (SRCREC*)realloc(photonPorts,
547     (numPhotonPorts + 1) *
548     sizeof(SRCREC));
549     if (!photonPorts)
550     error(USER, "can't allocate photon ports");
551 greg 2.1
552 rschregle 2.15 photonPorts [numPhotonPorts].so = obj;
553     photonPorts [numPhotonPorts].sflags = 0;
554 greg 2.1
555 rschregle 2.15 if (!sfun [obj -> otype].of || !sfun[obj -> otype].of -> setsrc)
556     objerror(obj, USER, "illegal photon port");
557    
558     setsource(photonPorts + numPhotonPorts, obj);
559     numPhotonPorts++;
560     }
561 greg 2.1 }
562     }
563 rschregle 2.11
564     if (!numPhotonPorts)
565     error(USER, "no valid photon ports found");
566 greg 2.1 }
567    
568    
569    
570     static void defaultEmissionFunc (EmissionMap* emap)
571     /* Default behaviour when no emission funcs defined for this source type */
572     {
573     objerror(emap -> src -> so, INTERNAL,
574     "undefined photon emission function");
575     }
576    
577    
578    
579     void initPhotonEmissionFuncs ()
580     /* Init photonPartition[] and photonOrigin[] dispatch tables */
581     {
582     int i;
583    
584     for (i = 0; i < NUMOTYPE; i++)
585     photonPartition [i] = photonOrigin [i] = defaultEmissionFunc;
586    
587     photonPartition [OBJ_FACE] = photonPartition [OBJ_RING] = flatPhotonPartition;
588     photonPartition [OBJ_SOURCE] = sourcePhotonPartition;
589     photonPartition [OBJ_SPHERE] = spherePhotonPartition;
590     photonPartition [OBJ_CYLINDER] = cylPhotonPartition;
591     photonOrigin [OBJ_FACE] = photonOrigin [OBJ_RING] = flatPhotonOrigin;
592     photonOrigin [OBJ_SOURCE] = sourcePhotonOrigin;
593     photonOrigin [OBJ_SPHERE] = spherePhotonOrigin;
594     photonOrigin [OBJ_CYLINDER] = cylPhotonOrigin;
595     }
596    
597    
598    
599     void initPhotonEmission (EmissionMap *emap, float numPdfSamples)
600     /* Initialize photon emission from partitioned light source emap -> src;
601     * this involves integrating the flux emitted from the current photon
602     * origin emap -> photonOrg and setting up a PDF to sample the emission
603     * distribution with numPdfSamples samples */
604     {
605     unsigned i, t, p;
606     double phi, cosTheta, sinTheta, du, dv, dOmega, thetaScale;
607     EmissionSample* sample;
608 greg 2.4 const OBJREC* mod = findmaterial(emap -> src -> so);
609 greg 2.1 static RAY r;
610     #if 0
611     static double lastCosNorm = FHUGE;
612     static SRCREC *lastSrc = NULL, *lastPort = NULL;
613     #endif
614    
615 greg 2.4 setcolor(emap -> partFlux, 0, 0, 0);
616    
617 greg 2.1 photonOrigin [emap -> src -> so -> otype] (emap);
618     cosTheta = DOT(emap -> ws, emap -> wh);
619    
620     #if 0
621     if (emap -> src == lastSrc && emap -> port == lastPort &&
622     (emap -> src -> sflags & SDISTANT || mod -> omod == OVOID) &&
623     cosTheta == lastCosNorm)
624     /* Same source, port, and aperture-normal angle, and source is
625     either distant (and thus translationally invariant) or has
626     no modifier --> flux unchanged */
627     /* BUG: this optimisation ignores partial occlusion of ports and
628     can lead to erroneous "zero emission" bailouts.
629     It can also lead to bias with modifiers exhibiting high variance!
630     Disabled for now -- RS 12/13 */
631     return;
632    
633     lastSrc = emap -> src;
634     lastPort = emap -> port;
635     lastCosNorm = cosTheta;
636     #endif
637    
638     /* Need to recompute flux & PDF */
639     emap -> cdf = 0;
640     emap -> numSamples = 0;
641    
642     if (cosTheta <= 0 &&
643     sqrt(1 - sqr(cosTheta)) <= emap -> cosThetaMax + FTINY)
644     /* Aperture below surface; no emission from current origin */
645     return;
646    
647     if (mod -> omod == OVOID && !emap -> port &&
648     (cosTheta >= 1 - FTINY || (emap -> src -> sflags & SDISTANT &&
649     acos(cosTheta) + acos(emap -> cosThetaMax) <= 0.5 * PI))) {
650     /* Source is unmodified and has no port (which requires testing for
651     occlusion), and is either local with normal aligned aperture or
652     distant with aperture above surface; analytical flux & PDF */
653     setcolor(emap -> partFlux, mod -> oargs.farg [0],
654     mod -> oargs.farg [1], mod -> oargs.farg [2]);
655    
656     /* Multiply radiance by Omega * dA to get flux */
657     scalecolor(emap -> partFlux,
658     PI * cosTheta * (1 - sqr(max(emap -> cosThetaMax, 0))) *
659     emap -> partArea);
660     }
661    
662     else {
663     /* Source is either modified, has a port, is local with off-normal
664     aperture, or distant with aperture partly below surface; get flux
665     via numerical integration */
666     thetaScale = (1 - emap -> cosThetaMax);
667    
668     /* Figga out numba of aperture samples for integration;
669     numTheta / numPhi ratio is 1 / PI */
670     du = sqrt(pdfSamples * 2 * thetaScale);
671     emap -> numTheta = max(du + 0.5, 1);
672     emap -> numPhi = max(PI * du + 0.5, 1);
673    
674     dOmega = 2 * PI * thetaScale / (emap -> numTheta * emap -> numPhi);
675     thetaScale /= emap -> numTheta;
676    
677     /* Allocate PDF, baby */
678     sample = emap -> samples = (EmissionSample*)
679     realloc(emap -> samples,
680     sizeof(EmissionSample) *
681     emap -> numTheta * emap -> numPhi);
682     if (!emap -> samples)
683     error(USER, "can't allocate emission PDF");
684    
685     VCOPY(r.rorg, emap -> photonOrg);
686     VCOPY(r.rop, emap -> photonOrg);
687 greg 2.5 r.rmax = 0;
688 greg 2.1
689     for (t = 0; t < emap -> numTheta; t++) {
690     for (p = 0; p < emap -> numPhi; p++) {
691     /* This uniform mapping handles 0 <= thetaMax <= PI */
692     cosTheta = 1 - (t + pmapRandom(emitState)) * thetaScale;
693     sinTheta = sqrt(1 - sqr(cosTheta));
694     phi = 2 * PI * (p + pmapRandom(emitState)) / emap -> numPhi;
695     du = cos(phi) * sinTheta;
696     dv = sin(phi) * sinTheta;
697     rayorigin(&r, PRIMARY, NULL, NULL);
698    
699     for (i = 0; i < 3; i++)
700     r.rdir [i] = du * emap -> uh [i] + dv * emap -> vh [i] +
701     cosTheta * emap -> wh [i];
702    
703     /* Sample behind surface? */
704     VCOPY(r.ron, emap -> ws);
705     if ((r.rod = DOT(r.rdir, r.ron)) <= 0)
706     continue;
707    
708     /* Get radiance emitted in this direction; to get flux we
709     multiply by cos(theta_surface), dOmega, and dA. Ray
710     is directed towards light source for raytexture(). */
711     if (!(emap -> src -> sflags & SDISTANT))
712     for (i = 0; i < 3; i++)
713     r.rdir [i] = -r.rdir [i];
714    
715     /* Port occluded in this direction? */
716     if (emap -> port && localhit(&r, &thescene))
717     continue;
718    
719     raytexture(&r, mod -> omod);
720     setcolor(r.rcol, mod -> oargs.farg [0], mod -> oargs.farg [1],
721     mod -> oargs.farg [2]);
722     multcolor(r.rcol, r.pcol);
723    
724     /* Multiply by cos(theta_surface) */
725     scalecolor(r.rcol, r.rod);
726    
727     /* Add PDF sample if nonzero; importance info for photon emission
728     * could go here... */
729     if (colorAvg(r.rcol)) {
730     copycolor(sample -> pdf, r.rcol);
731     sample -> cdf = emap -> cdf += colorAvg(sample -> pdf);
732     sample -> theta = t;
733     sample++ -> phi = p;
734     emap -> numSamples++;
735     addcolor(emap -> partFlux, r.rcol);
736     }
737     }
738     }
739    
740     /* Multiply by dOmega * dA */
741     scalecolor(emap -> partFlux, dOmega * emap -> partArea);
742     }
743     }
744    
745    
746    
747     #define vomitPhoton emitPhoton
748     #define bluarrrghPhoton vomitPhoton
749    
750     void emitPhoton (const EmissionMap* emap, RAY* ray)
751     /* Emit photon from current partition emap -> partitionCnt based on
752     emission distribution. Returns new photon ray. */
753     {
754     unsigned long i, lo, hi;
755     const EmissionSample* sample = emap -> samples;
756     RREAL du, dv, cosTheta, cosThetaSqr, sinTheta, phi;
757 greg 2.4 const OBJREC* mod = findmaterial(emap -> src -> so);
758 greg 2.1
759     /* Choose a new origin within current partition for every
760     emitted photon to break up clustering artifacts */
761     photonOrigin [emap -> src -> so -> otype] ((EmissionMap*)emap);
762 greg 2.5 /* If we have a local glow source with a maximum radius, then
763 rschregle 2.14 restrict our photon to the specified distance, otherwise we set
764     the limit imposed by photonMaxDist (or no limit if 0) */
765 greg 2.6 if (mod -> otype == MAT_GLOW && !(emap -> src -> sflags & SDISTANT)
766 greg 2.5 && mod -> oargs.farg[3] > FTINY)
767     ray -> rmax = mod -> oargs.farg[3];
768     else
769 rschregle 2.14 ray -> rmax = photonMaxDist;
770 greg 2.1 rayorigin(ray, PRIMARY, NULL, NULL);
771    
772     if (!emap -> numSamples) {
773     /* Source is unmodified and has no port, and either local with
774     normal aligned aperture, or distant with aperture above surface;
775     use cosine weighted distribution */
776     cosThetaSqr = 1 - pmapRandom(emitState) *
777     (1 - sqr(max(emap -> cosThetaMax, 0)));
778     cosTheta = sqrt(cosThetaSqr);
779     sinTheta = sqrt(1 - cosThetaSqr);
780     phi = 2 * PI * pmapRandom(emitState);
781     setcolor(ray -> rcol, mod -> oargs.farg [0], mod -> oargs.farg [1],
782     mod -> oargs.farg [2]);
783     }
784    
785     else {
786     /* Source is either modified, has a port, is local with off-normal
787     aperture, or distant with aperture partly below surface; choose
788     direction from constructed cumulative distribution function with
789     Monte Carlo inversion using binary search. */
790     du = pmapRandom(emitState) * emap -> cdf;
791     lo = 1;
792     hi = emap -> numSamples;
793    
794     while (hi > lo) {
795     i = (lo + hi) >> 1;
796     sample = emap -> samples + i - 1;
797    
798     if (sample -> cdf >= du)
799     hi = i;
800     if (sample -> cdf < du)
801     lo = i + 1;
802     }
803    
804     /* This is a uniform mapping, mon */
805     cosTheta = 1 - (sample -> theta + pmapRandom(emitState)) *
806     (1 - emap -> cosThetaMax) / emap -> numTheta;
807     sinTheta = sqrt(1 - sqr(cosTheta));
808     phi = 2 * PI * (sample -> phi + pmapRandom(emitState)) / emap -> numPhi;
809     copycolor(ray -> rcol, sample -> pdf);
810     }
811    
812     /* Normalize photon flux so that average over RGB is 1 */
813     colorNorm(ray -> rcol);
814    
815     VCOPY(ray -> rorg, emap -> photonOrg);
816     du = cos(phi) * sinTheta;
817     dv = sin(phi) * sinTheta;
818    
819     for (i = 0; i < 3; i++)
820     ray -> rdir [i] = du * emap -> uh [i] + dv * emap -> vh [i] +
821     cosTheta * emap -> wh [i];
822    
823     if (emap -> src -> sflags & SDISTANT)
824     /* Distant source; reverse ray direction to point into the scene. */
825     for (i = 0; i < 3; i++)
826     ray -> rdir [i] = -ray -> rdir [i];
827    
828     if (emap -> port)
829     /* Photon emitted from port; move origin just behind port so it
830     will be scattered */
831     for (i = 0; i < 3; i++)
832     ray -> rorg [i] -= 2 * FTINY * ray -> rdir [i];
833    
834     /* Assign emitting light source index */
835     ray -> rsrc = emap -> src - source;
836     }
837    
838    
839    
840     void multDirectPmap (RAY *r)
841     /* Factor irradiance from direct photons into r -> rcol; interface to
842     * direct() */
843     {
844     COLOR photonIrrad;
845    
846     /* Lookup direct photon irradiance */
847     (directPmap -> lookup)(directPmap, r, photonIrrad);
848    
849     /* Multiply with coefficient in ray */
850     multcolor(r -> rcol, photonIrrad);
851    
852     return;
853     }
854    
855    
856    
857     void inscatterVolumePmap (RAY *r, COLOR inscatter)
858     /* Add inscattering from volume photon map; interface to srcscatter() */
859     {
860     /* Add ambient in-scattering via lookup callback */
861     (volumePmap -> lookup)(volumePmap, r, inscatter);
862     }