ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/source.c
Revision: 2.37
Committed: Fri Sep 12 22:35:54 2003 UTC (20 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.36: +11 -5 lines
Log Message:
Fixed potential bug associated with last change allowing pretextured sources

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: source.c,v 2.36 2003/08/26 16:09:44 greg Exp $";
3 #endif
4 /*
5 * source.c - routines dealing with illumination sources.
6 *
7 * External symbols declared in source.h
8 */
9
10 #include "copyright.h"
11
12 #include "ray.h"
13
14 #include "otypes.h"
15
16 #include "source.h"
17
18 #include "random.h"
19
20 extern double ssampdist; /* scatter sampling distance */
21
22 #ifndef MAXSSAMP
23 #define MAXSSAMP 16 /* maximum samples per ray */
24 #endif
25
26 /*
27 * Structures used by direct()
28 */
29
30 typedef struct {
31 int sno; /* source number */
32 FVECT dir; /* source direction */
33 COLOR coef; /* material coefficient */
34 COLOR val; /* contribution */
35 } CONTRIB; /* direct contribution */
36
37 typedef struct {
38 int sndx; /* source index (to CONTRIB array) */
39 float brt; /* brightness (for comparison) */
40 } CNTPTR; /* contribution pointer */
41
42 static CONTRIB *srccnt; /* source contributions in direct() */
43 static CNTPTR *cntord; /* source ordering in direct() */
44 static int maxcntr = 0; /* size of contribution arrays */
45
46
47 void
48 marksources() /* find and mark source objects */
49 {
50 int foundsource = 0;
51 int i;
52 register OBJREC *o, *m;
53 register int ns;
54 /* initialize dispatch table */
55 initstypes();
56 /* find direct sources */
57 for (i = 0; i < nsceneobjs; i++) {
58
59 o = objptr(i);
60
61 if (!issurface(o->otype) || o->omod == OVOID)
62 continue;
63 /* find material */
64 m = objptr(o->omod);
65 while (!ismaterial(m->otype))
66 if (ismixture(m->otype) || m->omod == OVOID) {
67 m = NULL;
68 break;
69 } else
70 m = objptr(m->omod);
71 if (m == NULL || !islight(m->otype))
72 continue; /* not source modifier */
73
74 if (m->oargs.nfargs != (m->otype == MAT_GLOW ? 4 :
75 m->otype == MAT_SPOT ? 7 : 3))
76 objerror(m, USER, "bad # arguments");
77
78 if (m->otype == MAT_GLOW &&
79 o->otype != OBJ_SOURCE &&
80 m->oargs.farg[3] <= FTINY)
81 continue; /* don't bother */
82 if (m->oargs.farg[0] <= FTINY && m->oargs.farg[1] <= FTINY &&
83 m->oargs.farg[2] <= FTINY)
84 continue; /* don't bother */
85
86 if (sfun[o->otype].of == NULL ||
87 sfun[o->otype].of->setsrc == NULL)
88 objerror(o, USER, "illegal material");
89
90 if ((ns = newsource()) < 0)
91 goto memerr;
92
93 setsource(&source[ns], o);
94
95 if (m->otype == MAT_GLOW) {
96 source[ns].sflags |= SPROX;
97 source[ns].sl.prox = m->oargs.farg[3];
98 if (source[ns].sflags & SDISTANT)
99 source[ns].sflags |= SSKIP;
100 } else if (m->otype == MAT_SPOT) {
101 source[ns].sflags |= SSPOT;
102 if ((source[ns].sl.s = makespot(m)) == NULL)
103 goto memerr;
104 if (source[ns].sflags & SFLAT &&
105 !checkspot(source[ns].sl.s,source[ns].snorm)) {
106 objerror(o, WARNING,
107 "invalid spotlight direction");
108 source[ns].sflags |= SSKIP;
109 }
110 }
111 if (!(source[ns].sflags & SSKIP))
112 foundsource++;
113 }
114 if (!foundsource) {
115 error(WARNING, "no light sources found");
116 return;
117 }
118 markvirtuals(); /* find and add virtual sources */
119 /* allocate our contribution arrays */
120 maxcntr = nsources + MAXSPART; /* start with this many */
121 srccnt = (CONTRIB *)malloc(maxcntr*sizeof(CONTRIB));
122 cntord = (CNTPTR *)malloc(maxcntr*sizeof(CNTPTR));
123 if ((srccnt == NULL) | (cntord == NULL))
124 goto memerr;
125 return;
126 memerr:
127 error(SYSTEM, "out of memory in marksources");
128 }
129
130
131 void
132 freesources() /* free all source structures */
133 {
134 if (nsources > 0) {
135 free((void *)source);
136 source = NULL;
137 nsources = 0;
138 }
139 if (maxcntr <= 0)
140 return;
141 free((void *)srccnt);
142 srccnt = NULL;
143 free((void *)cntord);
144 cntord = NULL;
145 maxcntr = 0;
146 }
147
148
149 int
150 srcray(sr, r, si) /* send a ray to a source, return domega */
151 register RAY *sr; /* returned source ray */
152 RAY *r; /* ray which hit object */
153 SRCINDEX *si; /* source sample index */
154 {
155 double d; /* distance to source */
156 register SRCREC *srcp;
157
158 rayorigin(sr, r, SHADOW, 1.0); /* ignore limits */
159
160 while ((d = nextssamp(sr, si)) != 0.0) {
161 sr->rsrc = si->sn; /* remember source */
162 srcp = source + si->sn;
163 if (srcp->sflags & SDISTANT) {
164 if (srcp->sflags & SSPOT && spotout(sr, srcp->sl.s))
165 continue;
166 return(1); /* sample OK */
167 }
168 /* local source */
169 /* check proximity */
170 if (srcp->sflags & SPROX && d > srcp->sl.prox)
171 continue;
172 /* check angle */
173 if (srcp->sflags & SSPOT) {
174 if (spotout(sr, srcp->sl.s))
175 continue;
176 /* adjust solid angle */
177 si->dom *= d*d;
178 d += srcp->sl.s->flen;
179 si->dom /= d*d;
180 }
181 return(1); /* sample OK */
182 }
183 return(0); /* no more samples */
184 }
185
186
187 void
188 srcvalue(r) /* punch ray to source and compute value */
189 register RAY *r;
190 {
191 register SRCREC *sp;
192
193 sp = &source[r->rsrc];
194 if (sp->sflags & SVIRTUAL) { /* virtual source */
195 /* check intersection */
196 if (!(*ofun[sp->so->otype].funp)(sp->so, r))
197 return;
198 if (!rayshade(r, r->ro->omod)) /* compute contribution */
199 goto nomat;
200 rayparticipate(r);
201 return;
202 }
203 /* compute intersection */
204 if (sp->sflags & SDISTANT ? sourcehit(r) :
205 (*ofun[sp->so->otype].funp)(sp->so, r)) {
206 if (sp->sa.success >= 0)
207 sp->sa.success++;
208 if (!rayshade(r, r->ro->omod)) /* compute contribution */
209 goto nomat;
210 rayparticipate(r);
211 return;
212 }
213 /* we missed our mark! */
214 if (sp->sa.success < 0)
215 return; /* bitched already */
216 sp->sa.success -= AIMREQT;
217 if (sp->sa.success >= 0)
218 return; /* leniency */
219 sprintf(errmsg, "aiming failure for light source \"%s\"",
220 sp->so->oname);
221 error(WARNING, errmsg); /* issue warning */
222 return;
223 nomat:
224 objerror(r->ro, USER, "material not found");
225 }
226
227
228 int
229 sourcehit(r) /* check to see if ray hit distant source */
230 register RAY *r;
231 {
232 int first, last;
233 register int i;
234
235 if (r->rsrc >= 0) { /* check only one if aimed */
236 first = last = r->rsrc;
237 } else { /* otherwise check all */
238 first = 0; last = nsources-1;
239 }
240 for (i = first; i <= last; i++)
241 if ((source[i].sflags & (SDISTANT|SVIRTUAL)) == SDISTANT)
242 /*
243 * Check to see if ray is within
244 * solid angle of source.
245 */
246 if (2.0*PI * (1.0 - DOT(source[i].sloc,r->rdir))
247 <= source[i].ss2) {
248 r->ro = source[i].so;
249 if (!(source[i].sflags & SSKIP))
250 break;
251 }
252
253 if (r->ro != NULL) {
254 r->robj = objndx(r->ro);
255 for (i = 0; i < 3; i++)
256 r->ron[i] = -r->rdir[i];
257 r->rod = 1.0;
258 r->pert[0] = r->pert[1] = r->pert[2] = 0.0;
259 r->uv[0] = r->uv[1] = 0.0;
260 r->rox = NULL;
261 return(1);
262 }
263 return(0);
264 }
265
266
267 static int
268 cntcmp(sc1, sc2) /* contribution compare (descending) */
269 register CNTPTR *sc1, *sc2;
270 {
271 if (sc1->brt > sc2->brt)
272 return(-1);
273 if (sc1->brt < sc2->brt)
274 return(1);
275 return(0);
276 }
277
278
279 void
280 direct(r, f, p) /* add direct component */
281 RAY *r; /* ray that hit surface */
282 void (*f)(); /* direct component coefficient function */
283 char *p; /* data for f */
284 {
285 extern void (*trace)();
286 register int sn;
287 register CONTRIB *scp;
288 SRCINDEX si;
289 int nshadcheck, ncnts;
290 int nhits;
291 double prob, ourthresh, hwt;
292 RAY sr;
293 /* NOTE: srccnt and cntord global so no recursion */
294 if (nsources <= 0)
295 return; /* no sources?! */
296 /* potential contributions */
297 initsrcindex(&si);
298 for (sn = 0; srcray(&sr, r, &si); sn++) {
299 if (sn >= maxcntr) {
300 maxcntr = sn + MAXSPART;
301 srccnt = (CONTRIB *)realloc((void *)srccnt,
302 maxcntr*sizeof(CONTRIB));
303 cntord = (CNTPTR *)realloc((void *)cntord,
304 maxcntr*sizeof(CNTPTR));
305 if ((srccnt == NULL) | (cntord == NULL))
306 error(SYSTEM, "out of memory in direct");
307 }
308 cntord[sn].sndx = sn;
309 scp = srccnt + sn;
310 scp->sno = sr.rsrc;
311 /* compute coefficient */
312 (*f)(scp->coef, p, sr.rdir, si.dom);
313 cntord[sn].brt = bright(scp->coef);
314 if (cntord[sn].brt <= 0.0)
315 continue;
316 VCOPY(scp->dir, sr.rdir);
317 /* compute potential */
318 sr.revf = srcvalue;
319 rayvalue(&sr);
320 copycolor(scp->val, sr.rcol);
321 multcolor(scp->val, scp->coef);
322 cntord[sn].brt = bright(scp->val);
323 }
324 /* sort contributions */
325 qsort(cntord, sn, sizeof(CNTPTR), cntcmp);
326 { /* find last */
327 register int l, m;
328
329 ncnts = l = sn;
330 sn = 0;
331 while ((m = (sn + ncnts) >> 1) != l) {
332 if (cntord[m].brt > 0.0)
333 sn = m;
334 else
335 ncnts = m;
336 l = m;
337 }
338 }
339 if (ncnts == 0)
340 return; /* no contributions! */
341 /* accumulate tail */
342 for (sn = ncnts-1; sn > 0; sn--)
343 cntord[sn-1].brt += cntord[sn].brt;
344 /* compute number to check */
345 nshadcheck = pow((double)ncnts, shadcert) + .5;
346 /* modify threshold */
347 ourthresh = shadthresh / r->rweight;
348 /* test for shadows */
349 for (nhits = 0, hwt = 0.0, sn = 0; sn < ncnts;
350 hwt += (double)source[scp->sno].nhits /
351 (double)source[scp->sno].ntests,
352 sn++) {
353 /* check threshold */
354 if ((sn+nshadcheck>=ncnts ? cntord[sn].brt :
355 cntord[sn].brt-cntord[sn+nshadcheck].brt)
356 < ourthresh*bright(r->rcol))
357 break;
358 scp = srccnt + cntord[sn].sndx;
359 /* test for hit */
360 rayorigin(&sr, r, SHADOW, 1.0);
361 VCOPY(sr.rdir, scp->dir);
362 sr.rsrc = scp->sno;
363 /* keep statistics */
364 if (source[scp->sno].ntests++ > 0xfffffff0) {
365 source[scp->sno].ntests >>= 1;
366 source[scp->sno].nhits >>= 1;
367 }
368 if (localhit(&sr, &thescene) &&
369 ( sr.ro != source[scp->sno].so ||
370 source[scp->sno].sflags & SFOLLOW )) {
371 /* follow entire path */
372 raycont(&sr);
373 rayparticipate(&sr);
374 if (trace != NULL)
375 (*trace)(&sr); /* trace execution */
376 if (bright(sr.rcol) <= FTINY)
377 continue; /* missed! */
378 copycolor(scp->val, sr.rcol);
379 multcolor(scp->val, scp->coef);
380 }
381 /* add contribution if hit */
382 addcolor(r->rcol, scp->val);
383 nhits++;
384 source[scp->sno].nhits++;
385 }
386 /* source hit rate */
387 if (hwt > FTINY)
388 hwt = (double)nhits / hwt;
389 else
390 hwt = 0.5;
391 #ifdef DEBUG
392 sprintf(errmsg, "%d tested, %d untested, %f conditional hit rate\n",
393 sn, ncnts-sn, hwt);
394 eputs(errmsg);
395 #endif
396 /* add in untested sources */
397 for ( ; sn < ncnts; sn++) {
398 scp = srccnt + cntord[sn].sndx;
399 prob = hwt * (double)source[scp->sno].nhits /
400 (double)source[scp->sno].ntests;
401 if (prob > 1.0)
402 prob = 1.0;
403 scalecolor(scp->val, prob);
404 addcolor(r->rcol, scp->val);
405 }
406 }
407
408
409 void
410 srcscatter(r) /* compute source scattering into ray */
411 register RAY *r;
412 {
413 int oldsampndx;
414 int nsamps;
415 RAY sr;
416 SRCINDEX si;
417 double t, d;
418 double re, ge, be;
419 COLOR cvext;
420 int i, j;
421
422 if (r->slights == NULL || r->slights[0] == 0
423 || r->gecc >= 1.-FTINY || r->rot >= FHUGE)
424 return;
425 if (ssampdist <= FTINY || (nsamps = r->rot/ssampdist + .5) < 1)
426 nsamps = 1;
427 #if MAXSSAMP
428 else if (nsamps > MAXSSAMP)
429 nsamps = MAXSSAMP;
430 #endif
431 oldsampndx = samplendx;
432 samplendx = random()&0x7fff; /* randomize */
433 for (i = r->slights[0]; i > 0; i--) { /* for each source */
434 for (j = 0; j < nsamps; j++) { /* for each sample position */
435 samplendx++;
436 t = r->rot * (j+frandom())/nsamps;
437 /* extinction */
438 re = t*colval(r->cext,RED);
439 ge = t*colval(r->cext,GRN);
440 be = t*colval(r->cext,BLU);
441 setcolor(cvext, re > 92. ? 0. : exp(-re),
442 ge > 92. ? 0. : exp(-ge),
443 be > 92. ? 0. : exp(-be));
444 if (intens(cvext) <= FTINY)
445 break; /* too far away */
446 sr.rorg[0] = r->rorg[0] + r->rdir[0]*t;
447 sr.rorg[1] = r->rorg[1] + r->rdir[1]*t;
448 sr.rorg[2] = r->rorg[2] + r->rdir[2]*t;
449 sr.rmax = 0.;
450 initsrcindex(&si); /* sample ray to this source */
451 si.sn = r->slights[i];
452 nopart(&si, &sr);
453 if (!srcray(&sr, NULL, &si) ||
454 sr.rsrc != r->slights[i])
455 continue; /* no path */
456 copycolor(sr.cext, r->cext);
457 copycolor(sr.albedo, r->albedo);
458 sr.gecc = r->gecc;
459 sr.slights = r->slights;
460 rayvalue(&sr); /* eval. source ray */
461 if (bright(sr.rcol) <= FTINY)
462 continue;
463 if (r->gecc <= FTINY) /* compute P(theta) */
464 d = 1.;
465 else {
466 d = DOT(r->rdir, sr.rdir);
467 d = 1. + r->gecc*r->gecc - 2.*r->gecc*d;
468 d = (1. - r->gecc*r->gecc) / (d*sqrt(d));
469 }
470 /* other factors */
471 d *= si.dom * r->rot / (4.*PI*nsamps);
472 multcolor(sr.rcol, r->cext);
473 multcolor(sr.rcol, r->albedo);
474 scalecolor(sr.rcol, d);
475 multcolor(sr.rcol, cvext);
476 addcolor(r->rcol, sr.rcol); /* add it in */
477 }
478 }
479 samplendx = oldsampndx;
480 }
481
482
483 /****************************************************************
484 * The following macros were separated from the m_light() routine
485 * because they are very nasty and difficult to understand.
486 */
487
488 /* illumblock *
489 *
490 * We cannot allow an illum to pass to another illum, because that
491 * would almost certainly constitute overcounting.
492 * However, we do allow an illum to pass to another illum
493 * that is actually going to relay to a virtual light source.
494 * We also prevent an illum from passing to a glow; this provides a
495 * convenient mechanism for defining detailed light source
496 * geometry behind (or inside) an effective radiator.
497 */
498
499 static int
500 weaksrcmat(int obj) /* identify material */
501 {
502 register OBJREC *o = objptr(obj);
503
504 while (!ismaterial(o->otype)) /* find material */
505 o = objptr(o->omod);
506 return((o->otype==MAT_ILLUM)|(o->otype==MAT_GLOW));
507 }
508
509 #define illumblock(m, r) (!(source[r->rsrc].sflags&SVIRTUAL) && \
510 r->rod > 0.0 && \
511 weaksrcmat(source[r->rsrc].so->omod))
512
513 /* wrongsource *
514 *
515 * This source is the wrong source (ie. overcounted) if we are
516 * aimed to a different source than the one we hit and the one
517 * we hit is not an illum that should be passed.
518 */
519
520 #define wrongsource(m, r) (r->rsrc>=0 && source[r->rsrc].so!=r->ro && \
521 (m->otype!=MAT_ILLUM || illumblock(m,r)))
522
523 /* distglow *
524 *
525 * A distant glow is an object that sometimes acts as a light source,
526 * but is too far away from the test point to be one in this case.
527 * (Glows with negative radii should NEVER participate in illumination.)
528 */
529
530 #define distglow(m, r, d) (m->otype==MAT_GLOW && \
531 m->oargs.farg[3] >= -FTINY && \
532 d > m->oargs.farg[3])
533
534 /* badcomponent *
535 *
536 * We must avoid counting light sources in the ambient calculation,
537 * since the direct component is handled separately. Therefore, any
538 * ambient ray which hits an active light source must be discarded.
539 * The same is true for stray specular samples, since the specular
540 * contribution from light sources is calculated separately.
541 */
542
543 #define badcomponent(m, r) (r->crtype&(AMBIENT|SPECULAR) && \
544 !(r->crtype&SHADOW || r->rod < 0.0 || \
545 /* not 100% correct */ distglow(m, r, r->rot)))
546
547 /* passillum *
548 *
549 * An illum passes to another material type when we didn't hit it
550 * on purpose (as part of a direct calculation), or it is relaying
551 * a virtual light source.
552 */
553
554 #define passillum(m, r) (m->otype==MAT_ILLUM && \
555 (r->rsrc<0 || source[r->rsrc].so!=r->ro || \
556 source[r->rsrc].sflags&SVIRTUAL))
557
558 /* srcignore *
559 *
560 * The -dv flag is normally on for sources to be visible.
561 */
562
563 #define srcignore(m, r) !(directvis || r->crtype&SHADOW || \
564 distglow(m, r, raydist(r,PRIMARY)))
565
566
567 int
568 m_light(m, r) /* ray hit a light source */
569 register OBJREC *m;
570 register RAY *r;
571 {
572 /* check for over-counting */
573 if (badcomponent(m, r))
574 return(1);
575 if (wrongsource(m, r))
576 return(1);
577 /* check for passed illum */
578 if (passillum(m, r)) {
579 if (m->oargs.nsargs && strcmp(m->oargs.sarg[0], VOIDID))
580 return(rayshade(r,lastmod(objndx(m),m->oargs.sarg[0])));
581 raytrans(r);
582 return(1);
583 }
584 /* otherwise treat as source */
585 /* check for behind */
586 if (r->rod < 0.0)
587 return(1);
588 /* check for invisibility */
589 if (srcignore(m, r))
590 return(1);
591 /* check for outside spot */
592 if (m->otype==MAT_SPOT && spotout(r, makespot(m)))
593 return(1);
594 /* get distribution pattern */
595 raytexture(r, m->omod);
596 /* get source color */
597 setcolor(r->rcol, m->oargs.farg[0],
598 m->oargs.farg[1],
599 m->oargs.farg[2]);
600 /* modify value */
601 multcolor(r->rcol, r->pcol);
602 return(1);
603 }