ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/virtuals.c
Revision: 2.23
Committed: Thu Nov 8 00:54:07 2018 UTC (5 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R3
Changes since 2.22: +2 -4 lines
Log Message:
Moved findmaterial() from source.c to initotypes.c

File Contents

# User Rev Content
1 greg 1.1 #ifndef lint
2 greg 2.23 static const char RCSid[] = "$Id: virtuals.c,v 2.22 2015/05/22 11:20:43 greg Exp $";
3 greg 1.1 #endif
4     /*
5     * Routines for simulating virtual light sources
6     * Thus far, we only support planar mirrors.
7 greg 2.7 *
8     * External symbols declared in source.h
9     */
10    
11 greg 2.8 #include "copyright.h"
12 greg 1.1
13     #include "ray.h"
14 greg 1.3 #include "otypes.h"
15 greg 2.23 #include "otspecial.h"
16 greg 1.1 #include "source.h"
17 greg 1.7 #include "random.h"
18 greg 1.1
19 greg 1.22 #define MINSAMPLES 16 /* minimum number of pretest samples */
20     #define STESTMAX 32 /* maximum seeks per sample */
21 greg 1.1
22 greg 2.20 #define FEQ(a,b) ((a)-(b)+FTINY >= 0 && (b)-(a)+FTINY >= 0)
23    
24 greg 1.13
25 greg 1.1 static OBJECT *vobject; /* virtual source objects */
26     static int nvobjects = 0; /* number of virtual source objects */
27    
28    
29 greg 2.20 static int
30     isident4(MAT4 m)
31     {
32     int i, j;
33    
34     for (i = 4; i--; )
35     for (j = 4; j--; )
36     if (!FEQ(m[i][j], i==j))
37     return(0);
38     return(1);
39     }
40    
41    
42     void
43 schorsch 2.14 markvirtuals(void) /* find and mark virtual sources */
44 greg 1.1 {
45 greg 2.20 OBJREC *o;
46     int i;
47 greg 1.1 /* check number of direct relays */
48     if (directrelay <= 0)
49     return;
50     /* find virtual source objects */
51 greg 2.10 for (i = 0; i < nsceneobjs; i++) {
52 greg 1.1 o = objptr(i);
53 greg 1.3 if (!issurface(o->otype) || o->omod == OVOID)
54 greg 1.1 continue;
55 greg 1.31 if (!isvlight(vsmaterial(o)->otype))
56 greg 1.1 continue;
57 greg 1.3 if (sfun[o->otype].of == NULL ||
58 greg 1.21 sfun[o->otype].of->getpleq == NULL) {
59     objerror(o,WARNING,"secondary sources not supported");
60     continue;
61     }
62 greg 1.1 if (nvobjects == 0)
63     vobject = (OBJECT *)malloc(sizeof(OBJECT));
64     else
65 greg 2.9 vobject = (OBJECT *)realloc((void *)vobject,
66 greg 1.1 (unsigned)(nvobjects+1)*sizeof(OBJECT));
67     if (vobject == NULL)
68     error(SYSTEM, "out of memory in addvirtuals");
69     vobject[nvobjects++] = i;
70     }
71     if (nvobjects == 0)
72     return;
73 greg 1.4 #ifdef DEBUG
74     fprintf(stderr, "found %d virtual source objects\n", nvobjects);
75     #endif
76 greg 1.1 /* append virtual sources */
77     for (i = nsources; i-- > 0; )
78 greg 1.7 addvirtuals(i, directrelay);
79 greg 1.1 /* done with our object list */
80 greg 2.7 free((void *)vobject);
81 greg 1.1 nvobjects = 0;
82     }
83    
84    
85 greg 2.20 void
86 schorsch 2.14 addvirtuals( /* add virtuals associated with source */
87     int sn,
88     int nr
89     )
90 greg 1.1 {
91 greg 2.20 int i;
92 greg 1.1 /* check relay limit first */
93     if (nr <= 0)
94     return;
95 greg 1.7 if (source[sn].sflags & SSKIP)
96     return;
97 greg 1.1 /* check each virtual object for projection */
98     for (i = 0; i < nvobjects; i++)
99 greg 1.3 /* vproject() calls us recursively */
100 greg 1.4 vproject(objptr(vobject[i]), sn, nr-1);
101 greg 1.1 }
102    
103    
104 greg 2.20 void
105 schorsch 2.14 vproject( /* create projected source(s) if they exist */
106     OBJREC *o,
107     int sn,
108     int n
109     )
110 greg 1.3 {
111 greg 2.20 int i;
112     VSMATERIAL *vsmat;
113 greg 1.3 MAT4 proj;
114 greg 1.4 int ns;
115    
116     if (o == source[sn].so) /* objects cannot project themselves */
117     return;
118 greg 1.3 /* get virtual source material */
119 greg 1.31 vsmat = sfun[vsmaterial(o)->otype].mf;
120 greg 1.3 /* project virtual sources */
121     for (i = 0; i < vsmat->nproj; i++)
122 greg 1.4 if ((*vsmat->vproj)(proj, o, &source[sn], i))
123     if ((ns = makevsrc(o, sn, proj)) >= 0) {
124 greg 1.17 source[ns].sa.sv.pn = i;
125 greg 1.4 #ifdef DEBUG
126 greg 1.6 virtverb(ns, stderr);
127 greg 1.4 #endif
128 greg 1.3 addvirtuals(ns, n);
129 greg 1.4 }
130 greg 1.31 }
131    
132    
133 greg 2.20 OBJREC *
134 schorsch 2.14 vsmaterial( /* get virtual source material pointer */
135     OBJREC *o
136     )
137 greg 1.31 {
138 greg 2.20 int i;
139     OBJREC *m;
140 greg 1.31
141     i = o->omod;
142 greg 2.12 m = findmaterial(objptr(i));
143 greg 2.13 if (m == NULL)
144     return(objptr(i));
145 greg 1.31 if (m->otype != MAT_ILLUM || m->oargs.nsargs < 1 ||
146     !strcmp(m->oargs.sarg[0], VOIDID) ||
147 gwlarson 2.6 (i = lastmod(objndx(m), m->oargs.sarg[0])) == OVOID)
148 greg 1.31 return(m); /* direct modifier */
149     return(objptr(i)); /* illum alternate */
150 greg 1.3 }
151    
152    
153 greg 2.20 int
154 schorsch 2.14 makevsrc( /* make virtual source if reasonable */
155     OBJREC *op,
156 greg 2.20 int sn,
157 schorsch 2.14 MAT4 pm
158     )
159 greg 1.1 {
160 greg 1.9 FVECT nsloc, nsnorm, ocent, v;
161     double maxrad2, d;
162 greg 1.3 int nsflags;
163 greg 1.1 SPOT theirspot, ourspot;
164 greg 2.20 int i;
165     /* check for no-op */
166     if (isident4(pm))
167     return(0);
168 greg 1.6 nsflags = source[sn].sflags | (SVIRTUAL|SSPOT|SFOLLOW);
169 greg 1.1 /* get object center and max. radius */
170 greg 1.6 maxrad2 = getdisk(ocent, op, sn);
171     if (maxrad2 <= FTINY) /* too small? */
172     return(-1);
173 greg 1.1 /* get location and spot */
174 greg 1.4 if (source[sn].sflags & SDISTANT) { /* distant source */
175     if (source[sn].sflags & SPROX)
176 greg 1.5 return(-1); /* should never get here! */
177 greg 1.4 multv3(nsloc, source[sn].sloc, pm);
178 greg 1.17 normalize(nsloc);
179 greg 1.6 VCOPY(ourspot.aim, ocent);
180     ourspot.siz = PI*maxrad2;
181 greg 2.5 ourspot.flen = -1.;
182 greg 1.4 if (source[sn].sflags & SSPOT) {
183     multp3(theirspot.aim, source[sn].sl.s->aim, pm);
184 greg 1.29 /* adjust for source size */
185 greg 1.19 d = sqrt(dist2(ourspot.aim, theirspot.aim));
186 greg 1.28 d = sqrt(source[sn].sl.s->siz/PI) + d*source[sn].srad;
187 greg 1.19 theirspot.siz = PI*d*d;
188     ourspot.flen = theirspot.flen = source[sn].sl.s->flen;
189 greg 1.9 d = ourspot.siz;
190 greg 1.6 if (!commonbeam(&ourspot, &theirspot, nsloc))
191 greg 1.9 return(-1); /* no overlap */
192     if (ourspot.siz < d-FTINY) { /* it shrunk */
193     d = beamdisk(v, op, &ourspot, nsloc);
194     if (d <= FTINY)
195     return(-1);
196     if (d < maxrad2) {
197     maxrad2 = d;
198     VCOPY(ocent, v);
199     }
200     }
201 greg 1.1 }
202     } else { /* local source */
203 greg 1.4 multp3(nsloc, source[sn].sloc, pm);
204 greg 1.6 for (i = 0; i < 3; i++)
205     ourspot.aim[i] = ocent[i] - nsloc[i];
206 greg 1.9 if ((d = normalize(ourspot.aim)) == 0.)
207 greg 1.6 return(-1); /* at source!! */
208 greg 1.9 if (source[sn].sflags & SPROX && d > source[sn].sl.prox)
209 greg 1.6 return(-1); /* too far away */
210     ourspot.flen = 0.;
211 greg 1.29 /* adjust for source size */
212 greg 1.28 d = (sqrt(maxrad2) + source[sn].srad) / d;
213 greg 1.19 if (d < 1.-FTINY)
214     ourspot.siz = 2.*PI*(1. - sqrt(1.-d*d));
215 greg 1.14 else
216     nsflags &= ~SSPOT;
217 greg 1.4 if (source[sn].sflags & SSPOT) {
218 schorsch 2.11 theirspot = *(source[sn].sl.s);
219 greg 1.4 multv3(theirspot.aim, source[sn].sl.s->aim, pm);
220 greg 1.17 normalize(theirspot.aim);
221 greg 1.14 if (nsflags & SSPOT) {
222     ourspot.flen = theirspot.flen;
223     d = ourspot.siz;
224     if (!commonspot(&ourspot, &theirspot, nsloc))
225     return(-1); /* no overlap */
226     } else {
227     nsflags |= SSPOT;
228 schorsch 2.11 ourspot = theirspot;
229 greg 1.14 d = 2.*ourspot.siz;
230     }
231 greg 1.9 if (ourspot.siz < d-FTINY) { /* it shrunk */
232     d = spotdisk(v, op, &ourspot, nsloc);
233     if (d <= FTINY)
234     return(-1);
235     if (d < maxrad2) {
236     maxrad2 = d;
237     VCOPY(ocent, v);
238     }
239     }
240 greg 1.1 }
241 greg 1.4 if (source[sn].sflags & SFLAT) { /* behind source? */
242     multv3(nsnorm, source[sn].snorm, pm);
243 greg 1.17 normalize(nsnorm);
244 greg 1.20 if (nsflags & SSPOT && !checkspot(&ourspot, nsnorm))
245 greg 1.5 return(-1);
246 greg 1.1 }
247     }
248 greg 1.7 /* pretest visibility */
249     nsflags = vstestvis(nsflags, op, ocent, maxrad2, sn);
250     if (nsflags & SSKIP)
251     return(-1); /* obstructed */
252     /* it all checks out, so make it */
253 greg 1.6 if ((i = newsource()) < 0)
254 greg 1.1 goto memerr;
255 greg 1.6 source[i].sflags = nsflags;
256     VCOPY(source[i].sloc, nsloc);
257 greg 1.28 multv3(source[i].ss[SU], source[sn].ss[SU], pm);
258     multv3(source[i].ss[SV], source[sn].ss[SV], pm);
259 greg 1.3 if (nsflags & SFLAT)
260 greg 1.6 VCOPY(source[i].snorm, nsnorm);
261 greg 1.28 else
262     multv3(source[i].ss[SW], source[sn].ss[SW], pm);
263 greg 1.29 source[i].srad = source[sn].srad;
264 greg 1.28 source[i].ss2 = source[sn].ss2;
265 greg 1.14 if (nsflags & SSPOT) {
266     if ((source[i].sl.s = (SPOT *)malloc(sizeof(SPOT))) == NULL)
267     goto memerr;
268 schorsch 2.11 *(source[i].sl.s) = ourspot;
269 greg 1.14 }
270 greg 1.3 if (nsflags & SPROX)
271 greg 1.6 source[i].sl.prox = source[sn].sl.prox;
272 greg 1.17 source[i].sa.sv.sn = sn;
273 greg 1.6 source[i].so = op;
274     return(i);
275 greg 1.1 memerr:
276     error(SYSTEM, "out of memory in makevsrc");
277 schorsch 2.14 return -1; /* pro forma return */
278 greg 1.1 }
279    
280    
281 greg 2.20 double
282 schorsch 2.14 getdisk( /* get visible object disk */
283     FVECT oc,
284     OBJREC *op,
285 greg 2.20 int sn
286 schorsch 2.14 )
287 greg 1.6 {
288     double rad2, roffs, offs, d, rd, rdoto;
289     FVECT rnrm, nrm;
290     /* first, use object getdisk function */
291 greg 1.9 rad2 = getmaxdisk(oc, op);
292 greg 1.6 if (!(source[sn].sflags & SVIRTUAL))
293     return(rad2); /* all done for normal source */
294     /* check for correct side of relay surface */
295 greg 1.9 roffs = getplaneq(rnrm, source[sn].so);
296 greg 1.6 rd = DOT(rnrm, source[sn].sloc); /* source projection */
297     if (!(source[sn].sflags & SDISTANT))
298     rd -= roffs;
299     d = DOT(rnrm, oc) - roffs; /* disk distance to relay plane */
300     if ((d > 0.) ^ (rd > 0.))
301     return(rad2); /* OK if opposite sides */
302     if (d*d >= rad2)
303 greg 1.9 return(0.); /* no relay is possible */
304 greg 1.6 /* we need a closer look */
305 greg 1.9 offs = getplaneq(nrm, op);
306 greg 1.6 rdoto = DOT(rnrm, nrm);
307     if (d*d >= rad2*(1.-rdoto*rdoto))
308     return(0.); /* disk entirely on projection side */
309     /* should shrink disk but I'm lazy */
310     return(rad2);
311     }
312    
313    
314 greg 2.20 int
315 schorsch 2.14 vstestvis( /* pretest source visibility */
316     int f, /* virtual source flags */
317     OBJREC *o, /* relay object */
318     FVECT oc, /* relay object center */
319     double or2, /* relay object radius squared */
320 greg 2.20 int sn /* target source number */
321 schorsch 2.14 )
322 greg 1.1 {
323 greg 1.7 RAY sr;
324     FVECT onorm;
325 greg 2.21 double offsdir[3];
326 greg 1.28 SRCINDEX si;
327 greg 2.17 double or, d, d1;
328 greg 1.16 int stestlim, ssn;
329 greg 1.11 int nhit, nok;
330 greg 2.20 int i, n;
331 greg 1.7 /* return if pretesting disabled */
332     if (vspretest <= 0)
333     return(f);
334     /* get surface normal */
335 greg 1.9 getplaneq(onorm, o);
336 greg 1.7 /* set number of rays to sample */
337 greg 1.8 if (source[sn].sflags & SDISTANT) {
338 greg 1.26 /* 32. == heuristic constant */
339     n = 32.*or2/(thescene.cusize*thescene.cusize)*vspretest + .5;
340 greg 1.8 } else {
341 greg 2.21 VSUB(offsdir, source[sn].sloc, oc);
342 greg 1.20 d = DOT(offsdir,offsdir);
343     if (d <= FTINY)
344     n = 2.*PI * vspretest + .5;
345     else
346     n = 2.*PI * (1.-sqrt(1./(1.+or2/d)))*vspretest + .5;
347 greg 1.8 }
348 greg 1.13 if (n < MINSAMPLES) n = MINSAMPLES;
349 greg 1.9 #ifdef DEBUG
350     fprintf(stderr, "pretesting source %d in object %s with %d rays\n",
351     sn, o->oname, n);
352     #endif
353 greg 1.7 /* sample */
354 greg 1.8 or = sqrt(or2);
355 greg 1.16 stestlim = n*STESTMAX;
356     ssn = 0;
357 greg 1.11 nhit = nok = 0;
358 greg 2.4 initsrcindex(&si);
359 greg 1.7 while (n-- > 0) {
360 greg 1.8 /* get sample point */
361     do {
362 greg 1.16 if (ssn >= stestlim) {
363 greg 1.9 #ifdef DEBUG
364     fprintf(stderr, "\ttoo hard to hit\n");
365     #endif
366 greg 1.8 return(f); /* too small a target! */
367 greg 1.9 }
368 greg 1.25 multisamp(offsdir, 3, urand(sn*931+5827+ssn));
369 greg 1.8 for (i = 0; i < 3; i++)
370 greg 1.23 offsdir[i] = or*(1. - 2.*offsdir[i]);
371 greg 1.16 ssn++;
372 greg 2.4 d = 1. - DOT(offsdir, onorm);
373     for (i = 0; i < 3; i++) {
374     sr.rorg[i] = oc[i] + offsdir[i] + d*onorm[i];
375     sr.rdir[i] = -onorm[i];
376     }
377 greg 2.22 sr.rmax = 0.0;
378 greg 2.16 rayorigin(&sr, PRIMARY, NULL, NULL);
379 greg 1.8 } while (!(*ofun[o->otype].funp)(o, &sr));
380     /* check against source */
381 greg 2.4 VCOPY(sr.rorg, sr.rop); /* starting from intersection */
382 greg 1.7 samplendx++;
383 greg 2.4 if (si.sp >= si.np-1 ||
384     !srcray(&sr, NULL, &si) || sr.rsrc != sn) {
385     si.sn = sn-1; /* reset index to our source */
386     si.np = 0;
387     if (!srcray(&sr, NULL, &si) || sr.rsrc != sn)
388     continue; /* can't get there from here */
389     }
390 greg 2.17 sr.revf = srcvalue;
391     rayvalue(&sr); /* check sample validity */
392     if ((d = bright(sr.rcol)) <= FTINY)
393 greg 1.7 continue;
394 greg 2.4 nok++; /* got sample; check obstructions */
395 greg 1.18 rayclear(&sr);
396 greg 2.17 sr.revf = raytrace;
397 greg 1.27 rayvalue(&sr);
398 greg 2.17 if ((d1 = bright(sr.rcol)) > FTINY) {
399     if (d - d1 > FTINY) {
400     #ifdef DEBUG
401     fprintf(stderr, "\tpartially shadowed\n");
402     #endif
403     return(f); /* intervening transmitter */
404     }
405 greg 1.11 nhit++;
406 greg 2.17 }
407 greg 1.11 if (nhit > 0 && nhit < nok) {
408 greg 1.9 #ifdef DEBUG
409 greg 1.11 fprintf(stderr, "\tpartially occluded\n");
410 greg 1.9 #endif
411 greg 1.11 return(f); /* need to shadow test */
412     }
413 greg 1.1 }
414 greg 1.9 if (nhit == 0) {
415     #ifdef DEBUG
416     fprintf(stderr, "\t0%% hit rate\n");
417     #endif
418 greg 1.7 return(f | SSKIP); /* 0% hit rate: totally occluded */
419 greg 1.9 }
420     #ifdef DEBUG
421     fprintf(stderr, "\t100%% hit rate\n");
422     #endif
423     return(f & ~SFOLLOW); /* 100% hit rate: no occlusion */
424 greg 1.1 }
425 greg 1.7
426 greg 1.4
427     #ifdef DEBUG
428 greg 2.20 void
429 schorsch 2.14 virtverb( /* print verbose description of virtual source */
430 greg 2.20 int sn,
431 schorsch 2.14 FILE *fp
432     )
433 greg 1.4 {
434     fprintf(fp, "%s virtual source %d in %s %s\n",
435 greg 1.6 source[sn].sflags & SDISTANT ? "distant" : "local",
436     sn, ofun[source[sn].so->otype].funame,
437     source[sn].so->oname);
438 greg 1.4 fprintf(fp, "\tat (%f,%f,%f)\n",
439 greg 1.6 source[sn].sloc[0], source[sn].sloc[1], source[sn].sloc[2]);
440 greg 1.4 fprintf(fp, "\tlinked to source %d (%s)\n",
441 greg 1.17 source[sn].sa.sv.sn, source[source[sn].sa.sv.sn].so->oname);
442 greg 1.6 if (source[sn].sflags & SFOLLOW)
443 greg 1.4 fprintf(fp, "\talways followed\n");
444     else
445     fprintf(fp, "\tnever followed\n");
446 greg 1.6 if (!(source[sn].sflags & SSPOT))
447 greg 1.4 return;
448     fprintf(fp, "\twith spot aim (%f,%f,%f) and size %f\n",
449 greg 1.6 source[sn].sl.s->aim[0], source[sn].sl.s->aim[1],
450     source[sn].sl.s->aim[2], source[sn].sl.s->siz);
451 greg 1.4 }
452     #endif