ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.18
Committed: Fri Aug 7 01:21:13 2020 UTC (3 years, 10 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.17: +271 -180 lines
Log Message:
feat(mkpmap): Extended -apo option to reorient photon ports.

File Contents

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