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, 5 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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: virtuals.c,v 2.22 2015/05/22 11:20:43 greg Exp $";
3 #endif
4 /*
5 * Routines for simulating virtual light sources
6 * Thus far, we only support planar mirrors.
7 *
8 * External symbols declared in source.h
9 */
10
11 #include "copyright.h"
12
13 #include "ray.h"
14 #include "otypes.h"
15 #include "otspecial.h"
16 #include "source.h"
17 #include "random.h"
18
19 #define MINSAMPLES 16 /* minimum number of pretest samples */
20 #define STESTMAX 32 /* maximum seeks per sample */
21
22 #define FEQ(a,b) ((a)-(b)+FTINY >= 0 && (b)-(a)+FTINY >= 0)
23
24
25 static OBJECT *vobject; /* virtual source objects */
26 static int nvobjects = 0; /* number of virtual source objects */
27
28
29 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 markvirtuals(void) /* find and mark virtual sources */
44 {
45 OBJREC *o;
46 int i;
47 /* check number of direct relays */
48 if (directrelay <= 0)
49 return;
50 /* find virtual source objects */
51 for (i = 0; i < nsceneobjs; i++) {
52 o = objptr(i);
53 if (!issurface(o->otype) || o->omod == OVOID)
54 continue;
55 if (!isvlight(vsmaterial(o)->otype))
56 continue;
57 if (sfun[o->otype].of == NULL ||
58 sfun[o->otype].of->getpleq == NULL) {
59 objerror(o,WARNING,"secondary sources not supported");
60 continue;
61 }
62 if (nvobjects == 0)
63 vobject = (OBJECT *)malloc(sizeof(OBJECT));
64 else
65 vobject = (OBJECT *)realloc((void *)vobject,
66 (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 #ifdef DEBUG
74 fprintf(stderr, "found %d virtual source objects\n", nvobjects);
75 #endif
76 /* append virtual sources */
77 for (i = nsources; i-- > 0; )
78 addvirtuals(i, directrelay);
79 /* done with our object list */
80 free((void *)vobject);
81 nvobjects = 0;
82 }
83
84
85 void
86 addvirtuals( /* add virtuals associated with source */
87 int sn,
88 int nr
89 )
90 {
91 int i;
92 /* check relay limit first */
93 if (nr <= 0)
94 return;
95 if (source[sn].sflags & SSKIP)
96 return;
97 /* check each virtual object for projection */
98 for (i = 0; i < nvobjects; i++)
99 /* vproject() calls us recursively */
100 vproject(objptr(vobject[i]), sn, nr-1);
101 }
102
103
104 void
105 vproject( /* create projected source(s) if they exist */
106 OBJREC *o,
107 int sn,
108 int n
109 )
110 {
111 int i;
112 VSMATERIAL *vsmat;
113 MAT4 proj;
114 int ns;
115
116 if (o == source[sn].so) /* objects cannot project themselves */
117 return;
118 /* get virtual source material */
119 vsmat = sfun[vsmaterial(o)->otype].mf;
120 /* project virtual sources */
121 for (i = 0; i < vsmat->nproj; i++)
122 if ((*vsmat->vproj)(proj, o, &source[sn], i))
123 if ((ns = makevsrc(o, sn, proj)) >= 0) {
124 source[ns].sa.sv.pn = i;
125 #ifdef DEBUG
126 virtverb(ns, stderr);
127 #endif
128 addvirtuals(ns, n);
129 }
130 }
131
132
133 OBJREC *
134 vsmaterial( /* get virtual source material pointer */
135 OBJREC *o
136 )
137 {
138 int i;
139 OBJREC *m;
140
141 i = o->omod;
142 m = findmaterial(objptr(i));
143 if (m == NULL)
144 return(objptr(i));
145 if (m->otype != MAT_ILLUM || m->oargs.nsargs < 1 ||
146 !strcmp(m->oargs.sarg[0], VOIDID) ||
147 (i = lastmod(objndx(m), m->oargs.sarg[0])) == OVOID)
148 return(m); /* direct modifier */
149 return(objptr(i)); /* illum alternate */
150 }
151
152
153 int
154 makevsrc( /* make virtual source if reasonable */
155 OBJREC *op,
156 int sn,
157 MAT4 pm
158 )
159 {
160 FVECT nsloc, nsnorm, ocent, v;
161 double maxrad2, d;
162 int nsflags;
163 SPOT theirspot, ourspot;
164 int i;
165 /* check for no-op */
166 if (isident4(pm))
167 return(0);
168 nsflags = source[sn].sflags | (SVIRTUAL|SSPOT|SFOLLOW);
169 /* get object center and max. radius */
170 maxrad2 = getdisk(ocent, op, sn);
171 if (maxrad2 <= FTINY) /* too small? */
172 return(-1);
173 /* get location and spot */
174 if (source[sn].sflags & SDISTANT) { /* distant source */
175 if (source[sn].sflags & SPROX)
176 return(-1); /* should never get here! */
177 multv3(nsloc, source[sn].sloc, pm);
178 normalize(nsloc);
179 VCOPY(ourspot.aim, ocent);
180 ourspot.siz = PI*maxrad2;
181 ourspot.flen = -1.;
182 if (source[sn].sflags & SSPOT) {
183 multp3(theirspot.aim, source[sn].sl.s->aim, pm);
184 /* adjust for source size */
185 d = sqrt(dist2(ourspot.aim, theirspot.aim));
186 d = sqrt(source[sn].sl.s->siz/PI) + d*source[sn].srad;
187 theirspot.siz = PI*d*d;
188 ourspot.flen = theirspot.flen = source[sn].sl.s->flen;
189 d = ourspot.siz;
190 if (!commonbeam(&ourspot, &theirspot, nsloc))
191 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 }
202 } else { /* local source */
203 multp3(nsloc, source[sn].sloc, pm);
204 for (i = 0; i < 3; i++)
205 ourspot.aim[i] = ocent[i] - nsloc[i];
206 if ((d = normalize(ourspot.aim)) == 0.)
207 return(-1); /* at source!! */
208 if (source[sn].sflags & SPROX && d > source[sn].sl.prox)
209 return(-1); /* too far away */
210 ourspot.flen = 0.;
211 /* adjust for source size */
212 d = (sqrt(maxrad2) + source[sn].srad) / d;
213 if (d < 1.-FTINY)
214 ourspot.siz = 2.*PI*(1. - sqrt(1.-d*d));
215 else
216 nsflags &= ~SSPOT;
217 if (source[sn].sflags & SSPOT) {
218 theirspot = *(source[sn].sl.s);
219 multv3(theirspot.aim, source[sn].sl.s->aim, pm);
220 normalize(theirspot.aim);
221 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 ourspot = theirspot;
229 d = 2.*ourspot.siz;
230 }
231 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 }
241 if (source[sn].sflags & SFLAT) { /* behind source? */
242 multv3(nsnorm, source[sn].snorm, pm);
243 normalize(nsnorm);
244 if (nsflags & SSPOT && !checkspot(&ourspot, nsnorm))
245 return(-1);
246 }
247 }
248 /* 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 if ((i = newsource()) < 0)
254 goto memerr;
255 source[i].sflags = nsflags;
256 VCOPY(source[i].sloc, nsloc);
257 multv3(source[i].ss[SU], source[sn].ss[SU], pm);
258 multv3(source[i].ss[SV], source[sn].ss[SV], pm);
259 if (nsflags & SFLAT)
260 VCOPY(source[i].snorm, nsnorm);
261 else
262 multv3(source[i].ss[SW], source[sn].ss[SW], pm);
263 source[i].srad = source[sn].srad;
264 source[i].ss2 = source[sn].ss2;
265 if (nsflags & SSPOT) {
266 if ((source[i].sl.s = (SPOT *)malloc(sizeof(SPOT))) == NULL)
267 goto memerr;
268 *(source[i].sl.s) = ourspot;
269 }
270 if (nsflags & SPROX)
271 source[i].sl.prox = source[sn].sl.prox;
272 source[i].sa.sv.sn = sn;
273 source[i].so = op;
274 return(i);
275 memerr:
276 error(SYSTEM, "out of memory in makevsrc");
277 return -1; /* pro forma return */
278 }
279
280
281 double
282 getdisk( /* get visible object disk */
283 FVECT oc,
284 OBJREC *op,
285 int sn
286 )
287 {
288 double rad2, roffs, offs, d, rd, rdoto;
289 FVECT rnrm, nrm;
290 /* first, use object getdisk function */
291 rad2 = getmaxdisk(oc, op);
292 if (!(source[sn].sflags & SVIRTUAL))
293 return(rad2); /* all done for normal source */
294 /* check for correct side of relay surface */
295 roffs = getplaneq(rnrm, source[sn].so);
296 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 return(0.); /* no relay is possible */
304 /* we need a closer look */
305 offs = getplaneq(nrm, op);
306 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 int
315 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 int sn /* target source number */
321 )
322 {
323 RAY sr;
324 FVECT onorm;
325 double offsdir[3];
326 SRCINDEX si;
327 double or, d, d1;
328 int stestlim, ssn;
329 int nhit, nok;
330 int i, n;
331 /* return if pretesting disabled */
332 if (vspretest <= 0)
333 return(f);
334 /* get surface normal */
335 getplaneq(onorm, o);
336 /* set number of rays to sample */
337 if (source[sn].sflags & SDISTANT) {
338 /* 32. == heuristic constant */
339 n = 32.*or2/(thescene.cusize*thescene.cusize)*vspretest + .5;
340 } else {
341 VSUB(offsdir, source[sn].sloc, oc);
342 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 }
348 if (n < MINSAMPLES) n = MINSAMPLES;
349 #ifdef DEBUG
350 fprintf(stderr, "pretesting source %d in object %s with %d rays\n",
351 sn, o->oname, n);
352 #endif
353 /* sample */
354 or = sqrt(or2);
355 stestlim = n*STESTMAX;
356 ssn = 0;
357 nhit = nok = 0;
358 initsrcindex(&si);
359 while (n-- > 0) {
360 /* get sample point */
361 do {
362 if (ssn >= stestlim) {
363 #ifdef DEBUG
364 fprintf(stderr, "\ttoo hard to hit\n");
365 #endif
366 return(f); /* too small a target! */
367 }
368 multisamp(offsdir, 3, urand(sn*931+5827+ssn));
369 for (i = 0; i < 3; i++)
370 offsdir[i] = or*(1. - 2.*offsdir[i]);
371 ssn++;
372 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 sr.rmax = 0.0;
378 rayorigin(&sr, PRIMARY, NULL, NULL);
379 } while (!(*ofun[o->otype].funp)(o, &sr));
380 /* check against source */
381 VCOPY(sr.rorg, sr.rop); /* starting from intersection */
382 samplendx++;
383 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 sr.revf = srcvalue;
391 rayvalue(&sr); /* check sample validity */
392 if ((d = bright(sr.rcol)) <= FTINY)
393 continue;
394 nok++; /* got sample; check obstructions */
395 rayclear(&sr);
396 sr.revf = raytrace;
397 rayvalue(&sr);
398 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 nhit++;
406 }
407 if (nhit > 0 && nhit < nok) {
408 #ifdef DEBUG
409 fprintf(stderr, "\tpartially occluded\n");
410 #endif
411 return(f); /* need to shadow test */
412 }
413 }
414 if (nhit == 0) {
415 #ifdef DEBUG
416 fprintf(stderr, "\t0%% hit rate\n");
417 #endif
418 return(f | SSKIP); /* 0% hit rate: totally occluded */
419 }
420 #ifdef DEBUG
421 fprintf(stderr, "\t100%% hit rate\n");
422 #endif
423 return(f & ~SFOLLOW); /* 100% hit rate: no occlusion */
424 }
425
426
427 #ifdef DEBUG
428 void
429 virtverb( /* print verbose description of virtual source */
430 int sn,
431 FILE *fp
432 )
433 {
434 fprintf(fp, "%s virtual source %d in %s %s\n",
435 source[sn].sflags & SDISTANT ? "distant" : "local",
436 sn, ofun[source[sn].so->otype].funame,
437 source[sn].so->oname);
438 fprintf(fp, "\tat (%f,%f,%f)\n",
439 source[sn].sloc[0], source[sn].sloc[1], source[sn].sloc[2]);
440 fprintf(fp, "\tlinked to source %d (%s)\n",
441 source[sn].sa.sv.sn, source[source[sn].sa.sv.sn].so->oname);
442 if (source[sn].sflags & SFOLLOW)
443 fprintf(fp, "\talways followed\n");
444 else
445 fprintf(fp, "\tnever followed\n");
446 if (!(source[sn].sflags & SSPOT))
447 return;
448 fprintf(fp, "\twith spot aim (%f,%f,%f) and size %f\n",
449 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 }
452 #endif