Back to index

radiance  4R0+20100331
source.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char RCSid[] = "$Id: source.c,v 2.57 2008/07/17 18:26:01 greg Exp $";
00003 #endif
00004 /*
00005  *  source.c - routines dealing with illumination sources.
00006  *
00007  *  External symbols declared in source.h
00008  */
00009 
00010 #include  "ray.h"
00011 #include  "otypes.h"
00012 #include  "rtotypes.h"
00013 #include  "source.h"
00014 #include  "random.h"
00015 
00016 extern double  ssampdist;          /* scatter sampling distance */
00017 
00018 #ifndef MAXSSAMP
00019 #define MAXSSAMP     16            /* maximum samples per ray */
00020 #endif
00021 
00022 /*
00023  * Structures used by direct()
00024  */
00025 
00026 typedef struct {
00027        int  sno;            /* source number */
00028        FVECT  dir;          /* source direction */
00029        COLOR  coef;         /* material coefficient */
00030        COLOR  val;          /* contribution */
00031 }  CONTRIB;          /* direct contribution */
00032 
00033 typedef struct {
00034        int  sndx;           /* source index (to CONTRIB array) */
00035        float  brt;          /* brightness (for comparison) */
00036 }  CNTPTR;           /* contribution pointer */
00037 
00038 static CONTRIB  *srccnt;           /* source contributions in direct() */
00039 static CNTPTR  *cntord;                   /* source ordering in direct() */
00040 static int  maxcntr = 0;           /* size of contribution arrays */
00041 
00042 static int cntcmp(const void *p1, const void *p2);
00043 
00044 
00045 extern OBJREC *                    /* find an object's actual material */
00046 findmaterial(register OBJREC *o)
00047 {
00048        while (!ismaterial(o->otype)) {
00049               if (o->otype == MOD_ALIAS && o->oargs.nsargs) {
00050                      OBJECT  aobj;
00051                      OBJREC  *ao;
00052                      aobj = lastmod(objndx(o), o->oargs.sarg[0]);
00053                      if (aobj < 0)
00054                             objerror(o, USER, "bad reference");
00055                      ao = objptr(aobj);
00056                      if (ismaterial(ao->otype))
00057                             return(ao);
00058                      if (ao->otype == MOD_ALIAS) {
00059                             o = ao;
00060                             continue;
00061                      }
00062               }
00063               if (o->omod == OVOID)
00064                      return(NULL);
00065               o = objptr(o->omod);
00066        }
00067        return(o);           /* mixtures will return NULL */
00068 }
00069 
00070 
00071 extern void
00072 marksources(void)                  /* find and mark source objects */
00073 {
00074        int  foundsource = 0;
00075        int  i;
00076        register OBJREC  *o, *m;
00077        register int  ns;
00078                                    /* initialize dispatch table */
00079        initstypes();
00080                                    /* find direct sources */
00081        for (i = 0; i < nsceneobjs; i++) {
00082        
00083               o = objptr(i);
00084 
00085               if (!issurface(o->otype) || o->omod == OVOID)
00086                      continue;
00087                                    /* find material */
00088               m = findmaterial(objptr(o->omod));
00089               if (m == NULL)
00090                      continue;
00091               if (m->otype == MAT_CLIP) {
00092                      markclip(m);  /* special case for antimatter */
00093                      continue;
00094               }
00095               if (!islight(m->otype))
00096                      continue;     /* not source modifier */
00097        
00098               if (m->oargs.nfargs != (m->otype == MAT_GLOW ? 4 :
00099                             m->otype == MAT_SPOT ? 7 : 3))
00100                      objerror(m, USER, "bad # arguments");
00101 
00102               if (m->otype == MAT_GLOW &&
00103                             o->otype != OBJ_SOURCE &&
00104                             m->oargs.farg[3] <= FTINY)
00105                      continue;                   /* don't bother */
00106               if (m->oargs.farg[0] <= FTINY && m->oargs.farg[1] <= FTINY &&
00107                             m->oargs.farg[2] <= FTINY)
00108                      continue;                   /* don't bother */
00109 
00110               if (sfun[o->otype].of == NULL ||
00111                             sfun[o->otype].of->setsrc == NULL)
00112                      objerror(o, USER, "illegal material");
00113 
00114               if ((ns = newsource()) < 0)
00115                      goto memerr;
00116 
00117               setsource(&source[ns], o);
00118 
00119               if (m->otype == MAT_GLOW) {
00120                      source[ns].sflags |= SPROX;
00121                      source[ns].sl.prox = m->oargs.farg[3];
00122                      if (source[ns].sflags & SDISTANT)
00123                             source[ns].sflags |= SSKIP;
00124               } else if (m->otype == MAT_SPOT) {
00125                      source[ns].sflags |= SSPOT;
00126                      if ((source[ns].sl.s = makespot(m)) == NULL)
00127                             goto memerr;
00128                      if (source[ns].sflags & SFLAT &&
00129                             !checkspot(source[ns].sl.s,source[ns].snorm)) {
00130                             objerror(o, WARNING,
00131                                    "invalid spotlight direction");
00132                             source[ns].sflags |= SSKIP;
00133                      }
00134               }
00135 #if  SHADCACHE
00136               initobscache(ns);
00137 #endif
00138               if (!(source[ns].sflags & SSKIP))
00139                      foundsource++;
00140        }
00141        if (!foundsource) {
00142               error(WARNING, "no light sources found");
00143               return;
00144        }
00145        markvirtuals();                    /* find and add virtual sources */
00146                             /* allocate our contribution arrays */
00147        maxcntr = nsources + MAXSPART;     /* start with this many */
00148        srccnt = (CONTRIB *)malloc(maxcntr*sizeof(CONTRIB));
00149        cntord = (CNTPTR *)malloc(maxcntr*sizeof(CNTPTR));
00150        if ((srccnt == NULL) | (cntord == NULL))
00151               goto memerr;
00152        return;
00153 memerr:
00154        error(SYSTEM, "out of memory in marksources");
00155 }
00156 
00157 
00158 extern void
00159 freesources(void)                  /* free all source structures */
00160 {
00161        if (nsources > 0) {
00162 #if SHADCACHE
00163               while (nsources--)
00164                      freeobscache(&source[nsources]);
00165 #endif
00166               free((void *)source);
00167               source = NULL;
00168               nsources = 0;
00169        }
00170        if (maxcntr <= 0)
00171               return;
00172        free((void *)srccnt);
00173        srccnt = NULL;
00174        free((void *)cntord);
00175        cntord = NULL;
00176        maxcntr = 0;
00177 }
00178 
00179 
00180 extern int
00181 srcray(                            /* send a ray to a source, return domega */
00182        register RAY  *sr,          /* returned source ray */
00183        RAY  *r,                    /* ray which hit object */
00184        SRCINDEX  *si               /* source sample index */
00185 )
00186 {
00187     double  d;                            /* distance to source */
00188     register SRCREC  *srcp;
00189 
00190     rayorigin(sr, SHADOW, r, NULL);              /* ignore limits */
00191 
00192     while ((d = nextssamp(sr, si)) != 0.0) {
00193        sr->rsrc = si->sn;                 /* remember source */
00194        srcp = source + si->sn;
00195        if (srcp->sflags & SDISTANT) {
00196               if (srcp->sflags & SSPOT && spotout(sr, srcp->sl.s))
00197                      continue;
00198               return(1);           /* sample OK */
00199        }
00200                             /* local source */
00201                                           /* check proximity */
00202        if (srcp->sflags & SPROX && d > srcp->sl.prox)
00203               continue;
00204                                           /* check angle */
00205        if (srcp->sflags & SSPOT) {
00206               if (spotout(sr, srcp->sl.s))
00207                      continue;
00208                                    /* adjust solid angle */
00209               si->dom *= d*d;
00210               d += srcp->sl.s->flen;
00211               si->dom /= d*d;
00212        }
00213        return(1);                  /* sample OK */
00214     }
00215     return(0);                     /* no more samples */
00216 }
00217 
00218 
00219 extern void
00220 srcvalue(                   /* punch ray to source and compute value */
00221        register RAY  *r
00222 )
00223 {
00224        register SRCREC  *sp;
00225 
00226        sp = &source[r->rsrc];
00227        if (sp->sflags & SVIRTUAL) {       /* virtual source */
00228                                    /* check intersection */
00229               if (!(*ofun[sp->so->otype].funp)(sp->so, r))
00230                      return;
00231               if (!rayshade(r, r->ro->omod))     /* compute contribution */
00232                      goto nomat;
00233               rayparticipate(r);
00234               return;
00235        }
00236                                    /* compute intersection */
00237        if (sp->sflags & SDISTANT ? sourcehit(r) :
00238                      (*ofun[sp->so->otype].funp)(sp->so, r)) {
00239               if (sp->sa.success >= 0)
00240                      sp->sa.success++;
00241               if (!rayshade(r, r->ro->omod))     /* compute contribution */
00242                      goto nomat;
00243               rayparticipate(r);
00244               return;
00245        }
00246                                    /* we missed our mark! */
00247        if (sp->sa.success < 0)
00248               return;                     /* bitched already */
00249        sp->sa.success -= AIMREQT;
00250        if (sp->sa.success >= 0)
00251               return;                     /* leniency */
00252        sprintf(errmsg, "aiming failure for light source \"%s\"",
00253                      sp->so->oname);
00254        error(WARNING, errmsg);            /* issue warning */
00255        return;
00256 nomat:
00257        objerror(r->ro, USER, "material not found");
00258 }
00259 
00260 
00261 static int
00262 transillum(                 /* check if material is transparent illum */
00263        OBJECT obj
00264 )
00265 {
00266        OBJREC *m = findmaterial(objptr(obj));
00267        
00268        if (m == NULL)
00269               return(1);
00270        if (m->otype != MAT_ILLUM)
00271               return(0);
00272        return(!m->oargs.nsargs || !strcmp(m->oargs.sarg[0], VOIDID));
00273 }
00274 
00275 
00276 extern int
00277 sourcehit(                  /* check to see if ray hit distant source */
00278        register RAY  *r
00279 )
00280 {
00281        int  glowsrc = -1;
00282        int  transrc = -1;
00283        int  first, last;
00284        register int  i;
00285 
00286        if (r->rsrc >= 0) {         /* check only one if aimed */
00287               first = last = r->rsrc;
00288        } else {                    /* otherwise check all */
00289               first = 0; last = nsources-1;
00290        }
00291        for (i = first; i <= last; i++) {
00292               if ((source[i].sflags & (SDISTANT|SVIRTUAL)) != SDISTANT)
00293                      continue;
00294               /*
00295                * Check to see if ray is within
00296                * solid angle of source.
00297                */
00298               if (2.*PI*(1. - DOT(source[i].sloc,r->rdir)) > source[i].ss2)
00299                      continue;
00300                                    /* is it the only possibility? */
00301               if (first == last) {
00302                      r->ro = source[i].so;
00303                      break;
00304               }
00305               /*
00306                * If it's a glow or transparent illum, just remember it.
00307                */
00308               if (source[i].sflags & SSKIP) {
00309                      glowsrc = i;
00310                      continue;
00311               }
00312               if (transillum(source[i].so->omod)) {
00313                      transrc = i;
00314                      continue;
00315               }
00316               r->ro = source[i].so;       /* otherwise, use first hit */
00317               break;
00318        }
00319        /*
00320         * Do we need fallback?
00321         */
00322        if (r->ro == NULL) {
00323               if (transrc >= 0 && r->crtype & (AMBIENT|SPECULAR))
00324                      return(0);    /* avoid overcounting */
00325               if (glowsrc >= 0)
00326                      r->ro = source[glowsrc].so;
00327               else
00328                      return(0);    /* nothing usable */
00329        }
00330        /*
00331         * Make assignments.
00332         */
00333        r->robj = objndx(r->ro);
00334        for (i = 0; i < 3; i++)
00335               r->ron[i] = -r->rdir[i];
00336        r->rod = 1.0;
00337        r->pert[0] = r->pert[1] = r->pert[2] = 0.0;
00338        r->uv[0] = r->uv[1] = 0.0;
00339        r->rox = NULL;
00340        return(1);
00341 }
00342 
00343 
00344 static int
00345 cntcmp(                            /* contribution compare (descending) */
00346        const void *p1,
00347        const void *p2
00348 )
00349 {
00350        register const CNTPTR  *sc1 = (const CNTPTR *)p1;
00351        register const CNTPTR  *sc2 = (const CNTPTR *)p2;
00352 
00353        if (sc1->brt > sc2->brt)
00354               return(-1);
00355        if (sc1->brt < sc2->brt)
00356               return(1);
00357        return(0);
00358 }
00359 
00360 
00361 extern void
00362 direct(                                   /* add direct component */
00363        RAY  *r,                    /* ray that hit surface */
00364        srcdirf_t *f,               /* direct component coefficient function */
00365        void  *p                    /* data for f */
00366 )
00367 {
00368        register int  sn;
00369        register CONTRIB  *scp;
00370        SRCINDEX  si;
00371        int  nshadcheck, ncnts;
00372        int  nhits;
00373        double  prob, ourthresh, hwt;
00374        RAY  sr;
00375                      /* NOTE: srccnt and cntord global so no recursion */
00376        if (nsources <= 0)
00377               return;              /* no sources?! */
00378                                           /* potential contributions */
00379        initsrcindex(&si);
00380        for (sn = 0; srcray(&sr, r, &si); sn++) {
00381               if (sn >= maxcntr) {
00382                      maxcntr = sn + MAXSPART;
00383                      srccnt = (CONTRIB *)realloc((void *)srccnt,
00384                                    maxcntr*sizeof(CONTRIB));
00385                      cntord = (CNTPTR *)realloc((void *)cntord,
00386                                    maxcntr*sizeof(CNTPTR));
00387                      if ((srccnt == NULL) | (cntord == NULL))
00388                             error(SYSTEM, "out of memory in direct");
00389               }
00390               cntord[sn].sndx = sn;
00391               scp = srccnt + sn;
00392               scp->sno = sr.rsrc;
00393                                           /* compute coefficient */
00394               (*f)(scp->coef, p, sr.rdir, si.dom);
00395               cntord[sn].brt = intens(scp->coef);
00396               if (cntord[sn].brt <= 0.0)
00397                      continue;
00398 #if SHADCACHE
00399                                           /* check shadow cache */
00400               if (si.np == 1 && srcblocked(&sr)) {
00401                      cntord[sn].brt = 0.0;
00402                      continue;
00403               }
00404 #endif
00405               VCOPY(scp->dir, sr.rdir);
00406               copycolor(sr.rcoef, scp->coef);
00407                                           /* compute potential */
00408               sr.revf = srcvalue;
00409               rayvalue(&sr);
00410               multcolor(sr.rcol, sr.rcoef);
00411               copycolor(scp->val, sr.rcol);
00412               cntord[sn].brt = bright(sr.rcol);
00413        }
00414                                           /* sort contributions */
00415        qsort(cntord, sn, sizeof(CNTPTR), cntcmp);
00416        {                                  /* find last */
00417               register int  l, m;
00418 
00419               ncnts = l = sn;
00420               sn = 0;
00421               while ((m = (sn + ncnts) >> 1) != l) {
00422                      if (cntord[m].brt > 0.0)
00423                             sn = m;
00424                      else
00425                             ncnts = m;
00426                      l = m;
00427               }
00428        }
00429        if (ncnts == 0)
00430               return;              /* no contributions! */
00431                                                 /* accumulate tail */
00432         for (sn = ncnts-1; sn > 0; sn--)
00433                 cntord[sn-1].brt += cntord[sn].brt;
00434                                           /* compute number to check */
00435        nshadcheck = pow((double)ncnts, shadcert) + .5;
00436                                           /* modify threshold */
00437        ourthresh = shadthresh / r->rweight;
00438                                           /* test for shadows */
00439        for (nhits = 0, hwt = 0.0, sn = 0; sn < ncnts;
00440                      hwt += (double)source[scp->sno].nhits /
00441                             (double)source[scp->sno].ntests,
00442                      sn++) {
00443                                           /* check threshold */
00444               if ((sn+nshadcheck>=ncnts ? cntord[sn].brt :
00445                             cntord[sn].brt-cntord[sn+nshadcheck].brt)
00446                             < ourthresh*bright(r->rcol))
00447                      break;
00448               scp = srccnt + cntord[sn].sndx;
00449                                           /* test for hit */
00450               rayorigin(&sr, SHADOW, r, NULL);
00451               copycolor(sr.rcoef, scp->coef);
00452               VCOPY(sr.rdir, scp->dir);
00453               sr.rsrc = scp->sno;
00454                                           /* keep statistics */
00455               if (source[scp->sno].ntests++ > 0xfffffff0) {
00456                      source[scp->sno].ntests >>= 1;
00457                      source[scp->sno].nhits >>= 1;
00458               }
00459               if (localhit(&sr, &thescene) &&
00460                             ( sr.ro != source[scp->sno].so ||
00461                             source[scp->sno].sflags & SFOLLOW )) {
00462                                           /* follow entire path */
00463                      raycont(&sr);
00464                      if (trace != NULL)
00465                             (*trace)(&sr);       /* trace execution */
00466                      if (bright(sr.rcol) <= FTINY) {
00467 #if SHADCACHE
00468                             if ((scp <= srccnt || scp[-1].sno != scp->sno)
00469                                           && (scp >= srccnt+ncnts-1 ||
00470                                               scp[1].sno != scp->sno))
00471                                    srcblocker(&sr);
00472 #endif
00473                             continue;     /* missed! */
00474                      }
00475                      rayparticipate(&sr);
00476                      multcolor(sr.rcol, sr.rcoef);
00477                      copycolor(scp->val, sr.rcol);
00478               } else if (trace != NULL &&
00479                      (source[scp->sno].sflags & (SDISTANT|SVIRTUAL|SFOLLOW))
00480                                           == (SDISTANT|SFOLLOW) &&
00481                             sourcehit(&sr) && rayshade(&sr, sr.ro->omod)) {
00482                      (*trace)(&sr);              /* trace execution */
00483                      /* skip call to rayparticipate() & scp->val update */
00484               }
00485                                           /* add contribution if hit */
00486               addcolor(r->rcol, scp->val);
00487               nhits++;
00488               source[scp->sno].nhits++;
00489        }
00490                                    /* source hit rate */
00491        if (hwt > FTINY)
00492               hwt = (double)nhits / hwt;
00493        else
00494               hwt = 0.5;
00495 #ifdef DEBUG
00496        sprintf(errmsg, "%d tested, %d untested, %f conditional hit rate\n",
00497                      sn, ncnts-sn, hwt);
00498        eputs(errmsg);
00499 #endif
00500                                    /* add in untested sources */
00501        for ( ; sn < ncnts; sn++) {
00502               scp = srccnt + cntord[sn].sndx;
00503               prob = hwt * (double)source[scp->sno].nhits /
00504                             (double)source[scp->sno].ntests;
00505               if (prob < 1.0)
00506                      scalecolor(scp->val, prob);
00507               addcolor(r->rcol, scp->val);
00508        }
00509 }
00510 
00511 
00512 extern void
00513 srcscatter(                 /* compute source scattering into ray */
00514        register RAY  *r
00515 )
00516 {
00517        int  oldsampndx;
00518        int  nsamps;
00519        RAY  sr;
00520        SRCINDEX  si;
00521        double  t, d;
00522        double  re, ge, be;
00523        COLOR  cvext;
00524        int  i, j;
00525 
00526        if (r->slights == NULL || r->slights[0] == 0
00527                      || r->gecc >= 1.-FTINY || r->rot >= FHUGE)
00528               return;
00529        if (ssampdist <= FTINY || (nsamps = r->rot/ssampdist + .5) < 1)
00530               nsamps = 1;
00531 #if MAXSSAMP
00532        else if (nsamps > MAXSSAMP)
00533               nsamps = MAXSSAMP;
00534 #endif
00535        oldsampndx = samplendx;
00536        samplendx = random()&0x7fff;              /* randomize */
00537        for (i = r->slights[0]; i > 0; i--) {     /* for each source */
00538               for (j = 0; j < nsamps; j++) {     /* for each sample position */
00539                      samplendx++;
00540                      t = r->rot * (j+frandom())/nsamps;
00541                                                  /* extinction */
00542                      re = t*colval(r->cext,RED);
00543                      ge = t*colval(r->cext,GRN);
00544                      be = t*colval(r->cext,BLU);
00545                      setcolor(cvext,      re > 92. ? 0. : exp(-re),
00546                                    ge > 92. ? 0. : exp(-ge),
00547                                    be > 92. ? 0. : exp(-be));
00548                      if (intens(cvext) <= FTINY)
00549                             break;               /* too far away */
00550                      sr.rorg[0] = r->rorg[0] + r->rdir[0]*t;
00551                      sr.rorg[1] = r->rorg[1] + r->rdir[1]*t;
00552                      sr.rorg[2] = r->rorg[2] + r->rdir[2]*t;
00553                      sr.rmax = 0.;
00554                      initsrcindex(&si);   /* sample ray to this source */
00555                      si.sn = r->slights[i];
00556                      nopart(&si, &sr);
00557                      if (!srcray(&sr, NULL, &si) ||
00558                                    sr.rsrc != r->slights[i])
00559                             continue;            /* no path */
00560 #if SHADCACHE
00561                      if (srcblocked(&sr))        /* check shadow cache */
00562                             continue;
00563 #endif
00564                      copycolor(sr.cext, r->cext);
00565                      copycolor(sr.albedo, r->albedo);
00566                      sr.gecc = r->gecc;
00567                      sr.slights = r->slights;
00568                      rayvalue(&sr);                     /* eval. source ray */
00569                      if (bright(sr.rcol) <= FTINY) {
00570 #if SHADCACHE
00571                             srcblocker(&sr);     /* add blocker to cache */
00572 #endif
00573                             continue;
00574                      }
00575                      if (r->gecc <= FTINY)              /* compute P(theta) */
00576                             d = 1.;
00577                      else {
00578                             d = DOT(r->rdir, sr.rdir);
00579                             d = 1. + r->gecc*r->gecc - 2.*r->gecc*d;
00580                             d = (1. - r->gecc*r->gecc) / (d*sqrt(d));
00581                      }
00582                                                  /* other factors */
00583                      d *= si.dom * r->rot / (4.*PI*nsamps);
00584                      multcolor(sr.rcol, r->cext);
00585                      multcolor(sr.rcol, r->albedo);
00586                      scalecolor(sr.rcol, d);
00587                      multcolor(sr.rcol, cvext);
00588                      addcolor(r->rcol, sr.rcol); /* add it in */
00589               }
00590        }
00591        samplendx = oldsampndx;
00592 }
00593 
00594 
00595 /****************************************************************
00596  * The following macros were separated from the m_light() routine
00597  * because they are very nasty and difficult to understand.
00598  */
00599 
00600 /* illumblock *
00601  *
00602  * We cannot allow an illum to pass to another illum, because that
00603  * would almost certainly constitute overcounting.
00604  * However, we do allow an illum to pass to another illum
00605  * that is actually going to relay to a virtual light source.
00606  * We also prevent an illum from passing to a glow; this provides a
00607  * convenient mechanism for defining detailed light source
00608  * geometry behind (or inside) an effective radiator.
00609  */
00610 
00611 static int
00612 weaksrcmat(OBJECT obj)             /* identify material */
00613 {
00614        OBJREC *m = findmaterial(objptr(obj));
00615        
00616        if (m == NULL) return(0);
00617        return((m->otype==MAT_ILLUM) | (m->otype==MAT_GLOW));
00618 }
00619 
00620 #define  illumblock(m, r)   (!(source[r->rsrc].sflags&SVIRTUAL) && \
00621                             r->rod > 0.0 && \
00622                             weaksrcmat(source[r->rsrc].so->omod))
00623 
00624 /* wrongsource *
00625  *
00626  * This source is the wrong source (ie. overcounted) if we are
00627  * aimed to a different source than the one we hit and the one
00628  * we hit is not an illum that should be passed.
00629  */
00630 
00631 #define  wrongsource(m, r)  (r->rsrc>=0 && source[r->rsrc].so!=r->ro && \
00632                             (m->otype!=MAT_ILLUM || illumblock(m,r)))
00633 
00634 /* distglow *
00635  *
00636  * A distant glow is an object that sometimes acts as a light source,
00637  * but is too far away from the test point to be one in this case.
00638  * (Glows with negative radii should NEVER participate in illumination.)
00639  */
00640 
00641 #define  distglow(m, r, d)  (m->otype==MAT_GLOW && \
00642                             m->oargs.farg[3] >= -FTINY && \
00643                             d > m->oargs.farg[3])
00644 
00645 /* badcomponent *
00646  *
00647  * We must avoid counting light sources in the ambient calculation,
00648  * since the direct component is handled separately.  Therefore, any
00649  * ambient ray which hits an active light source must be discarded.
00650  * The same is true for stray specular samples, since the specular
00651  * contribution from light sources is calculated separately.
00652  */
00653 
00654 #define  badcomponent(m, r) (r->crtype&(AMBIENT|SPECULAR) && \
00655                             !(r->crtype&SHADOW || r->rod < 0.0 || \
00656               /* not 100% correct */      distglow(m, r, r->rot)))
00657 
00658 /* passillum *
00659  *
00660  * An illum passes to another material type when we didn't hit it
00661  * on purpose (as part of a direct calculation), or it is relaying
00662  * a virtual light source.
00663  */
00664 
00665 #define  passillum(m, r)    (m->otype==MAT_ILLUM && \
00666                             (r->rsrc<0 || source[r->rsrc].so!=r->ro || \
00667                             source[r->rsrc].sflags&SVIRTUAL))
00668 
00669 /* srcignore *
00670  *
00671  * The -dv flag is normally on for sources to be visible.
00672  */
00673 
00674 #define  srcignore(m, r)    !(directvis || r->crtype&SHADOW || \
00675                             distglow(m, r, raydist(r,PRIMARY)))
00676 
00677 
00678 extern int
00679 m_light(                           /* ray hit a light source */
00680        register OBJREC  *m,
00681        register RAY  *r
00682 )
00683 {
00684                                           /* check for over-counting */
00685        if (badcomponent(m, r)) {
00686               setcolor(r->rcoef, 0.0, 0.0, 0.0);
00687               return(1);
00688        }
00689        if (wrongsource(m, r)) {
00690               setcolor(r->rcoef, 0.0, 0.0, 0.0);
00691               return(1);
00692        }
00693                                           /* check for passed illum */
00694        if (passillum(m, r)) {
00695               if (m->oargs.nsargs && strcmp(m->oargs.sarg[0], VOIDID))
00696                      return(rayshade(r,lastmod(objndx(m),m->oargs.sarg[0])));
00697               raytrans(r);
00698               return(1);
00699        }
00700                                           /* check for invisibility */
00701        if (srcignore(m, r)) {
00702               setcolor(r->rcoef, 0.0, 0.0, 0.0);
00703               return(1);
00704        }
00705                                    /* otherwise treat as source */
00706                                           /* check for behind */
00707        if (r->rod < 0.0)
00708               return(1);
00709                                           /* check for outside spot */
00710        if (m->otype==MAT_SPOT && spotout(r, makespot(m)))
00711               return(1);
00712                                           /* get distribution pattern */
00713        raytexture(r, m->omod);
00714                                           /* get source color */
00715        setcolor(r->rcol, m->oargs.farg[0],
00716                        m->oargs.farg[1],
00717                        m->oargs.farg[2]);
00718                                           /* modify value */
00719        multcolor(r->rcol, r->pcol);
00720        return(1);
00721 }