ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.19
Committed: Mon Aug 10 19:51:20 2020 UTC (3 years, 9 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R3
Changes since 2.18: +26 -20 lines
Log Message:
fix(mkpmap): undefined port in setPhotonPortNormal()

File Contents

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