ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/source.c
Revision: 2.65
Committed: Thu May 28 09:03:54 2015 UTC (8 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.64: +4 -5 lines
Log Message:
Moved source obstructor initialization out of source checking loop(!)

File Contents

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