| 1 | greg | 1.1 | /* Copyright (c) 1986 Regents of the University of California */ | 
| 2 |  |  |  | 
| 3 |  |  | #ifndef lint | 
| 4 |  |  | static char SCCSid[] = "$SunId$ LBL"; | 
| 5 |  |  | #endif | 
| 6 |  |  |  | 
| 7 |  |  | /* | 
| 8 |  |  | *  source.c - routines dealing with illumination sources. | 
| 9 |  |  | * | 
| 10 |  |  | *     8/20/85 | 
| 11 |  |  | */ | 
| 12 |  |  |  | 
| 13 |  |  | #include  "ray.h" | 
| 14 |  |  |  | 
| 15 | greg | 1.4 | #include  "octree.h" | 
| 16 |  |  |  | 
| 17 | greg | 1.1 | #include  "source.h" | 
| 18 |  |  |  | 
| 19 |  |  | #include  "otypes.h" | 
| 20 |  |  |  | 
| 21 |  |  | #include  "cone.h" | 
| 22 |  |  |  | 
| 23 |  |  | #include  "face.h" | 
| 24 |  |  |  | 
| 25 |  |  | #include  "random.h" | 
| 26 |  |  |  | 
| 27 |  |  |  | 
| 28 |  |  | extern double  dstrsrc;                 /* source distribution amount */ | 
| 29 | greg | 1.4 | extern double  shadthresh;              /* relative shadow threshold */ | 
| 30 | greg | 1.12 | extern double  shadcert;                /* shadow testing certainty */ | 
| 31 | greg | 1.1 |  | 
| 32 | greg | 1.4 | SRCREC  *source = NULL;                 /* our list of sources */ | 
| 33 | greg | 1.1 | int  nsources = 0;                      /* the number of sources */ | 
| 34 |  |  |  | 
| 35 |  |  |  | 
| 36 |  |  | marksources()                   /* find and mark source objects */ | 
| 37 |  |  | { | 
| 38 |  |  | register OBJREC  *o, *m; | 
| 39 |  |  | register int  i; | 
| 40 |  |  |  | 
| 41 |  |  | for (i = 0; i < nobjects; i++) { | 
| 42 |  |  |  | 
| 43 |  |  | o = objptr(i); | 
| 44 |  |  |  | 
| 45 |  |  | if (o->omod == OVOID) | 
| 46 |  |  | continue; | 
| 47 |  |  |  | 
| 48 |  |  | m = objptr(o->omod); | 
| 49 |  |  |  | 
| 50 |  |  | if (m->otype != MAT_LIGHT && | 
| 51 |  |  | m->otype != MAT_ILLUM && | 
| 52 | greg | 1.6 | m->otype != MAT_GLOW && | 
| 53 |  |  | m->otype != MAT_SPOT) | 
| 54 | greg | 1.1 | continue; | 
| 55 |  |  |  | 
| 56 | greg | 1.6 | if (m->oargs.nfargs != (m->otype == MAT_GLOW ? 4 : | 
| 57 |  |  | m->otype == MAT_SPOT ? 7 : 3)) | 
| 58 | greg | 1.1 | objerror(m, USER, "bad # arguments"); | 
| 59 |  |  |  | 
| 60 | greg | 1.6 | if (m->otype == MAT_GLOW && | 
| 61 |  |  | o->otype != OBJ_SOURCE && | 
| 62 |  |  | m->oargs.farg[3] <= FTINY) | 
| 63 | greg | 1.1 | continue;                       /* don't bother */ | 
| 64 |  |  |  | 
| 65 | greg | 1.4 | if (source == NULL) | 
| 66 |  |  | source = (SRCREC *)malloc(sizeof(SRCREC)); | 
| 67 |  |  | else | 
| 68 |  |  | source = (SRCREC *)realloc((char *)source, | 
| 69 |  |  | (unsigned)(nsources+1)*sizeof(SRCREC)); | 
| 70 |  |  | if (source == NULL) | 
| 71 |  |  | error(SYSTEM, "out of memory in marksources"); | 
| 72 | greg | 1.1 |  | 
| 73 | greg | 1.4 | newsource(&source[nsources], o); | 
| 74 | greg | 1.1 |  | 
| 75 | greg | 1.6 | if (m->otype == MAT_GLOW) { | 
| 76 |  |  | source[nsources].sflags |= SPROX; | 
| 77 |  |  | source[nsources].sl.prox = m->oargs.farg[3]; | 
| 78 |  |  | if (o->otype == OBJ_SOURCE) | 
| 79 |  |  | source[nsources].sflags |= SSKIP; | 
| 80 |  |  | } else if (m->otype == MAT_SPOT) { | 
| 81 |  |  | source[nsources].sflags |= SSPOT; | 
| 82 |  |  | source[nsources].sl.s = makespot(m); | 
| 83 |  |  | } | 
| 84 | greg | 1.1 | nsources++; | 
| 85 |  |  | } | 
| 86 |  |  | } | 
| 87 |  |  |  | 
| 88 |  |  |  | 
| 89 |  |  | newsource(src, so)                      /* add a source to the array */ | 
| 90 | greg | 1.4 | register SRCREC  *src; | 
| 91 | greg | 1.1 | register OBJREC  *so; | 
| 92 |  |  | { | 
| 93 |  |  | double  cos(), tan(), sqrt(); | 
| 94 |  |  | double  theta; | 
| 95 |  |  | FACE  *f; | 
| 96 |  |  | CONE  *co; | 
| 97 |  |  | int  j; | 
| 98 |  |  | register int  i; | 
| 99 |  |  |  | 
| 100 |  |  | src->sflags = 0; | 
| 101 | greg | 1.6 | src->nhits = 1; src->ntests = 2;        /* start probability = 1/2 */ | 
| 102 | greg | 1.1 | src->so = so; | 
| 103 |  |  |  | 
| 104 |  |  | switch (so->otype) { | 
| 105 |  |  | case OBJ_SOURCE: | 
| 106 |  |  | if (so->oargs.nfargs != 4) | 
| 107 |  |  | objerror(so, USER, "bad arguments"); | 
| 108 |  |  | src->sflags |= SDISTANT; | 
| 109 |  |  | VCOPY(src->sloc, so->oargs.farg); | 
| 110 |  |  | if (normalize(src->sloc) == 0.0) | 
| 111 |  |  | objerror(so, USER, "zero direction"); | 
| 112 |  |  | theta = PI/180.0/2.0 * so->oargs.farg[3]; | 
| 113 |  |  | if (theta <= FTINY) | 
| 114 |  |  | objerror(so, USER, "zero size"); | 
| 115 |  |  | src->ss = theta >= PI/4 ? 1.0 : tan(theta); | 
| 116 |  |  | src->ss2 = 2.0*PI * (1.0 - cos(theta)); | 
| 117 |  |  | break; | 
| 118 |  |  | case OBJ_SPHERE: | 
| 119 |  |  | VCOPY(src->sloc, so->oargs.farg); | 
| 120 |  |  | src->ss = so->oargs.farg[3]; | 
| 121 |  |  | src->ss2 = PI * src->ss * src->ss; | 
| 122 |  |  | break; | 
| 123 |  |  | case OBJ_FACE: | 
| 124 |  |  | /* get the face */ | 
| 125 |  |  | f = getface(so); | 
| 126 |  |  | /* find the center */ | 
| 127 |  |  | for (j = 0; j < 3; j++) { | 
| 128 |  |  | src->sloc[j] = 0.0; | 
| 129 |  |  | for (i = 0; i < f->nv; i++) | 
| 130 |  |  | src->sloc[j] += VERTEX(f,i)[j]; | 
| 131 |  |  | src->sloc[j] /= f->nv; | 
| 132 |  |  | } | 
| 133 |  |  | if (!inface(src->sloc, f)) | 
| 134 |  |  | objerror(so, USER, "cannot hit center"); | 
| 135 |  |  | src->ss = sqrt(f->area / PI); | 
| 136 |  |  | src->ss2 = f->area; | 
| 137 |  |  | break; | 
| 138 |  |  | case OBJ_RING: | 
| 139 |  |  | /* get the ring */ | 
| 140 |  |  | co = getcone(so, 0); | 
| 141 |  |  | VCOPY(src->sloc, CO_P0(co)); | 
| 142 |  |  | if (CO_R0(co) > 0.0) | 
| 143 |  |  | objerror(so, USER, "cannot hit center"); | 
| 144 |  |  | src->ss = CO_R1(co); | 
| 145 |  |  | src->ss2 = PI * src->ss * src->ss; | 
| 146 |  |  | break; | 
| 147 |  |  | default: | 
| 148 |  |  | objerror(so, USER, "illegal material"); | 
| 149 |  |  | } | 
| 150 |  |  | } | 
| 151 |  |  |  | 
| 152 |  |  |  | 
| 153 | greg | 1.6 | SPOT * | 
| 154 |  |  | makespot(m)                     /* make a spotlight */ | 
| 155 |  |  | register OBJREC  *m; | 
| 156 |  |  | { | 
| 157 |  |  | extern double  cos(); | 
| 158 |  |  | register SPOT  *ns; | 
| 159 |  |  |  | 
| 160 |  |  | if ((ns = (SPOT *)malloc(sizeof(SPOT))) == NULL) | 
| 161 |  |  | error(SYSTEM, "out of memory in makespot"); | 
| 162 |  |  | ns->siz = 2.0*PI * (1.0 - cos(PI/180.0/2.0 * m->oargs.farg[3])); | 
| 163 |  |  | VCOPY(ns->aim, m->oargs.farg+4); | 
| 164 |  |  | if ((ns->flen = normalize(ns->aim)) == 0.0) | 
| 165 |  |  | objerror(m, USER, "zero focus vector"); | 
| 166 |  |  | return(ns); | 
| 167 |  |  | } | 
| 168 |  |  |  | 
| 169 |  |  |  | 
| 170 | greg | 1.1 | double | 
| 171 |  |  | srcray(sr, r, sn)               /* send a ray to a source, return domega */ | 
| 172 |  |  | register RAY  *sr;              /* returned source ray */ | 
| 173 |  |  | RAY  *r;                        /* ray which hit object */ | 
| 174 |  |  | register int  sn;               /* source number */ | 
| 175 |  |  | { | 
| 176 |  |  | register double  *norm = NULL;  /* plane normal */ | 
| 177 |  |  | double  ddot;                   /* (distance times) cosine */ | 
| 178 |  |  | FVECT  vd; | 
| 179 |  |  | double  d; | 
| 180 |  |  | register int  i; | 
| 181 |  |  |  | 
| 182 | greg | 1.4 | if (source[sn].sflags & SSKIP) | 
| 183 | greg | 1.1 | return(0.0);                    /* skip this source */ | 
| 184 |  |  |  | 
| 185 |  |  | rayorigin(sr, r, SHADOW, 1.0);          /* ignore limits */ | 
| 186 |  |  |  | 
| 187 |  |  | sr->rsrc = sn;                          /* remember source */ | 
| 188 |  |  | /* get source direction */ | 
| 189 | greg | 1.4 | if (source[sn].sflags & SDISTANT) | 
| 190 | greg | 1.1 | /* constant direction */ | 
| 191 | greg | 1.4 | VCOPY(sr->rdir, source[sn].sloc); | 
| 192 | greg | 1.1 | else {                                  /* compute direction */ | 
| 193 |  |  | for (i = 0; i < 3; i++) | 
| 194 | greg | 1.4 | sr->rdir[i] = source[sn].sloc[i] - sr->rorg[i]; | 
| 195 | greg | 1.1 |  | 
| 196 | greg | 1.4 | if (source[sn].so->otype == OBJ_FACE) | 
| 197 |  |  | norm = getface(source[sn].so)->norm; | 
| 198 |  |  | else if (source[sn].so->otype == OBJ_RING) | 
| 199 |  |  | norm = getcone(source[sn].so,0)->ad; | 
| 200 | greg | 1.1 |  | 
| 201 | greg | 1.2 | if (norm != NULL && (ddot = -DOT(sr->rdir, norm)) <= FTINY) | 
| 202 | greg | 1.1 | return(0.0);            /* behind surface! */ | 
| 203 |  |  | } | 
| 204 |  |  | if (dstrsrc > FTINY) { | 
| 205 |  |  | /* distribute source direction */ | 
| 206 |  |  | for (i = 0; i < 3; i++) | 
| 207 | greg | 1.4 | vd[i] = dstrsrc * source[sn].ss * (1.0 - 2.0*frandom()); | 
| 208 | greg | 1.1 |  | 
| 209 |  |  | if (norm != NULL) {             /* project offset */ | 
| 210 |  |  | d = DOT(vd, norm); | 
| 211 |  |  | for (i = 0; i < 3; i++) | 
| 212 |  |  | vd[i] -= d * norm[i]; | 
| 213 |  |  | } | 
| 214 |  |  | for (i = 0; i < 3; i++)         /* offset source direction */ | 
| 215 |  |  | sr->rdir[i] += vd[i]; | 
| 216 |  |  |  | 
| 217 | greg | 1.4 | } else if (source[sn].sflags & SDISTANT) | 
| 218 | greg | 1.1 | /* already normalized */ | 
| 219 | greg | 1.4 | return(source[sn].ss2); | 
| 220 | greg | 1.1 |  | 
| 221 |  |  | if ((d = normalize(sr->rdir)) == 0.0) | 
| 222 |  |  | /* at source! */ | 
| 223 |  |  | return(0.0); | 
| 224 |  |  |  | 
| 225 | greg | 1.4 | if (source[sn].sflags & SDISTANT) | 
| 226 | greg | 1.1 | /* domega constant */ | 
| 227 | greg | 1.4 | return(source[sn].ss2); | 
| 228 | greg | 1.1 |  | 
| 229 |  |  | else { | 
| 230 | greg | 1.6 | /* check proximity */ | 
| 231 |  |  | if (source[sn].sflags & SPROX && | 
| 232 |  |  | d > source[sn].sl.prox) | 
| 233 |  |  | return(0.0); | 
| 234 | greg | 1.1 |  | 
| 235 |  |  | if (norm != NULL) | 
| 236 |  |  | ddot /= d; | 
| 237 |  |  | else | 
| 238 |  |  | ddot = 1.0; | 
| 239 | greg | 1.6 | /* check angle */ | 
| 240 |  |  | if (source[sn].sflags & SSPOT) { | 
| 241 |  |  | if (source[sn].sl.s->siz < 2.0*PI * | 
| 242 |  |  | (1.0 + DOT(source[sn].sl.s->aim,sr->rdir))) | 
| 243 |  |  | return(0.0); | 
| 244 |  |  | d += source[sn].sl.s->flen; | 
| 245 |  |  | } | 
| 246 | greg | 1.1 | /* return domega */ | 
| 247 | greg | 1.4 | return(ddot*source[sn].ss2/(d*d)); | 
| 248 | greg | 1.1 | } | 
| 249 |  |  | } | 
| 250 |  |  |  | 
| 251 |  |  |  | 
| 252 |  |  | sourcehit(r)                    /* check to see if ray hit distant source */ | 
| 253 |  |  | register RAY  *r; | 
| 254 |  |  | { | 
| 255 |  |  | int  first, last; | 
| 256 |  |  | register int  i; | 
| 257 |  |  |  | 
| 258 |  |  | if (r->rsrc >= 0) {             /* check only one if aimed */ | 
| 259 |  |  | first = last = r->rsrc; | 
| 260 |  |  | } else {                        /* otherwise check all */ | 
| 261 |  |  | first = 0; last = nsources-1; | 
| 262 |  |  | } | 
| 263 |  |  | for (i = first; i <= last; i++) | 
| 264 | greg | 1.4 | if (source[i].sflags & SDISTANT) | 
| 265 | greg | 1.1 | /* | 
| 266 |  |  | * Check to see if ray is within | 
| 267 |  |  | * solid angle of source. | 
| 268 |  |  | */ | 
| 269 | greg | 1.4 | if (2.0*PI * (1.0 - DOT(source[i].sloc,r->rdir)) | 
| 270 |  |  | <= source[i].ss2) { | 
| 271 |  |  | r->ro = source[i].so; | 
| 272 |  |  | if (!(source[i].sflags & SSKIP)) | 
| 273 | greg | 1.1 | break; | 
| 274 |  |  | } | 
| 275 |  |  |  | 
| 276 |  |  | if (r->ro != NULL) { | 
| 277 |  |  | for (i = 0; i < 3; i++) | 
| 278 |  |  | r->ron[i] = -r->rdir[i]; | 
| 279 |  |  | r->rod = 1.0; | 
| 280 | greg | 1.3 | r->rofs = 1.0; setident4(r->rofx); | 
| 281 |  |  | r->robs = 1.0; setident4(r->robx); | 
| 282 | greg | 1.1 | return(1); | 
| 283 |  |  | } | 
| 284 |  |  | return(0); | 
| 285 |  |  | } | 
| 286 |  |  |  | 
| 287 |  |  |  | 
| 288 | greg | 1.4 | static int | 
| 289 |  |  | cntcmp(sc1, sc2)                        /* contribution compare (descending) */ | 
| 290 | greg | 1.9 | register CNTPTR  *sc1, *sc2; | 
| 291 | greg | 1.4 | { | 
| 292 |  |  | if (sc1->brt > sc2->brt) | 
| 293 |  |  | return(-1); | 
| 294 |  |  | if (sc1->brt < sc2->brt) | 
| 295 |  |  | return(1); | 
| 296 |  |  | return(0); | 
| 297 |  |  | } | 
| 298 |  |  |  | 
| 299 |  |  |  | 
| 300 |  |  | direct(r, f, p)                         /* add direct component */ | 
| 301 |  |  | RAY  *r;                        /* ray that hit surface */ | 
| 302 |  |  | int  (*f)();                    /* direct component coefficient function */ | 
| 303 |  |  | char  *p;                       /* data for f */ | 
| 304 |  |  | { | 
| 305 | greg | 1.12 | extern double  pow(); | 
| 306 | greg | 1.4 | register int  sn; | 
| 307 |  |  | register CONTRIB  *srccnt; | 
| 308 | greg | 1.9 | register CNTPTR  *cntord; | 
| 309 | greg | 1.12 | int  nshadcheck, ncnts; | 
| 310 | greg | 1.10 | double  prob, ourthresh, hwt, test2, hit2; | 
| 311 | greg | 1.4 | RAY  sr; | 
| 312 |  |  |  | 
| 313 | greg | 1.9 | srccnt = (CONTRIB *)malloc(nsources*sizeof(CONTRIB)); | 
| 314 |  |  | cntord = (CNTPTR *)malloc(nsources*sizeof(CNTPTR)); | 
| 315 |  |  | if (srccnt == NULL || cntord == NULL) | 
| 316 | greg | 1.4 | error(SYSTEM, "out of memory in direct"); | 
| 317 | greg | 1.12 | /* compute number to check */ | 
| 318 |  |  | nshadcheck = pow((double)nsources, shadcert) + .5; | 
| 319 | greg | 1.8 | /* modify threshold */ | 
| 320 |  |  | ourthresh = shadthresh / r->rweight; | 
| 321 | greg | 1.4 | /* potential contributions */ | 
| 322 |  |  | for (sn = 0; sn < nsources; sn++) { | 
| 323 | greg | 1.9 | cntord[sn].sno = sn; | 
| 324 |  |  | cntord[sn].brt = 0.0; | 
| 325 | greg | 1.4 | /* get source ray */ | 
| 326 |  |  | if ((srccnt[sn].dom = srcray(&sr, r, sn)) == 0.0) | 
| 327 |  |  | continue; | 
| 328 |  |  | VCOPY(srccnt[sn].dir, sr.rdir); | 
| 329 |  |  | /* compute coefficient */ | 
| 330 |  |  | (*f)(srccnt[sn].val, p, srccnt[sn].dir, srccnt[sn].dom); | 
| 331 | greg | 1.9 | cntord[sn].brt = bright(srccnt[sn].val); | 
| 332 | greg | 1.15 | if (cntord[sn].brt <= 0.0) | 
| 333 | greg | 1.4 | continue; | 
| 334 |  |  | /* compute intersection */ | 
| 335 |  |  | if (!( source[sn].sflags & SDISTANT ? | 
| 336 |  |  | sourcehit(&sr) : | 
| 337 |  |  | (*ofun[source[sn].so->otype].funp) | 
| 338 |  |  | (source[sn].so, &sr) )) | 
| 339 |  |  | continue; | 
| 340 |  |  | /* compute contribution */ | 
| 341 |  |  | rayshade(&sr, sr.ro->omod); | 
| 342 |  |  | multcolor(srccnt[sn].val, sr.rcol); | 
| 343 | greg | 1.9 | cntord[sn].brt = bright(srccnt[sn].val); | 
| 344 | greg | 1.4 | } | 
| 345 |  |  | /* sort contributions */ | 
| 346 | greg | 1.9 | qsort(cntord, nsources, sizeof(CNTPTR), cntcmp); | 
| 347 | greg | 1.13 | {                                       /* find last */ | 
| 348 |  |  | register int  l, m; | 
| 349 |  |  |  | 
| 350 |  |  | sn = 0; ncnts = l = nsources; | 
| 351 |  |  | while ((m = (sn + ncnts) >> 1) != l) { | 
| 352 |  |  | if (cntord[m].brt > 0.0) | 
| 353 |  |  | sn = m; | 
| 354 |  |  | else | 
| 355 |  |  | ncnts = m; | 
| 356 |  |  | l = m; | 
| 357 |  |  | } | 
| 358 |  |  | } | 
| 359 | greg | 1.12 | /* accumulate tail */ | 
| 360 |  |  | for (sn = ncnts-1; sn > 0; sn--) | 
| 361 |  |  | cntord[sn-1].brt += cntord[sn].brt; | 
| 362 |  |  | /* start with prob=.5 */ | 
| 363 |  |  | hit2 = 0.5; test2 = 1.0; | 
| 364 | greg | 1.10 | /* test for shadows */ | 
| 365 | greg | 1.12 | for (sn = 0; sn < ncnts; sn++) { | 
| 366 | greg | 1.10 | /* check threshold */ | 
| 367 | greg | 1.12 | if ((sn+nshadcheck>=ncnts ? cntord[sn].brt : | 
| 368 |  |  | cntord[sn].brt-cntord[sn+nshadcheck].brt) < | 
| 369 |  |  | ourthresh*bright(r->rcol)) | 
| 370 | greg | 1.4 | break; | 
| 371 |  |  | /* get statistics */ | 
| 372 | greg | 1.9 | hwt = (double)source[cntord[sn].sno].nhits / | 
| 373 |  |  | (double)source[cntord[sn].sno].ntests; | 
| 374 | greg | 1.4 | test2 += hwt; | 
| 375 | greg | 1.9 | source[cntord[sn].sno].ntests++; | 
| 376 | greg | 1.4 | /* test for hit */ | 
| 377 |  |  | rayorigin(&sr, r, SHADOW, 1.0); | 
| 378 | greg | 1.9 | VCOPY(sr.rdir, srccnt[cntord[sn].sno].dir); | 
| 379 | greg | 1.4 | if (localhit(&sr, &thescene) && | 
| 380 | greg | 1.9 | sr.ro != source[cntord[sn].sno].so) { | 
| 381 | greg | 1.4 | /* check for transmission */ | 
| 382 | greg | 1.14 | rayshade(&sr, sr.ro->omod); | 
| 383 | greg | 1.5 | if (bright(sr.rcol) <= FTINY) | 
| 384 | greg | 1.4 | continue;       /* missed! */ | 
| 385 | greg | 1.9 | (*f)(srccnt[cntord[sn].sno].val, p, | 
| 386 |  |  | srccnt[cntord[sn].sno].dir, | 
| 387 |  |  | srccnt[cntord[sn].sno].dom); | 
| 388 |  |  | multcolor(srccnt[cntord[sn].sno].val, sr.rcol); | 
| 389 | greg | 1.4 | } | 
| 390 |  |  | /* add contribution if hit */ | 
| 391 | greg | 1.9 | addcolor(r->rcol, srccnt[cntord[sn].sno].val); | 
| 392 | greg | 1.4 | hit2 += hwt; | 
| 393 | greg | 1.9 | source[cntord[sn].sno].nhits++; | 
| 394 | greg | 1.4 | } | 
| 395 | greg | 1.7 | /* weighted hit rate */ | 
| 396 |  |  | hwt = hit2 / test2; | 
| 397 | greg | 1.4 | #ifdef DEBUG | 
| 398 | greg | 1.12 | sprintf(errmsg, "%d tested, %d untested, %f hit rate\n", | 
| 399 |  |  | sn, ncnts-sn, hwt); | 
| 400 |  |  | eputs(errmsg); | 
| 401 | greg | 1.4 | #endif | 
| 402 |  |  | /* add in untested sources */ | 
| 403 | greg | 1.12 | for ( ; sn < ncnts; sn++) { | 
| 404 | greg | 1.9 | prob = hwt * (double)source[cntord[sn].sno].nhits / | 
| 405 |  |  | (double)source[cntord[sn].sno].ntests; | 
| 406 |  |  | scalecolor(srccnt[cntord[sn].sno].val, prob); | 
| 407 |  |  | addcolor(r->rcol, srccnt[cntord[sn].sno].val); | 
| 408 | greg | 1.4 | } | 
| 409 | greg | 1.10 |  | 
| 410 | greg | 1.4 | free(srccnt); | 
| 411 | greg | 1.9 | free(cntord); | 
| 412 | greg | 1.4 | } | 
| 413 |  |  |  | 
| 414 |  |  |  | 
| 415 | greg | 1.1 | #define  wrongsource(m, r)      (m->otype!=MAT_ILLUM && \ | 
| 416 |  |  | r->rsrc>=0 && \ | 
| 417 | greg | 1.4 | source[r->rsrc].so!=r->ro) | 
| 418 | greg | 1.1 |  | 
| 419 |  |  | #define  badambient(m, r)       ((r->crtype&(AMBIENT|SHADOW))==AMBIENT && \ | 
| 420 | greg | 1.6 | !(r->rtype&REFLECTED) &&        /* hack! */\ | 
| 421 |  |  | !(m->otype==MAT_GLOW&&r->rot>m->oargs.farg[3])) | 
| 422 | greg | 1.1 |  | 
| 423 |  |  | #define  passillum(m, r)        (m->otype==MAT_ILLUM && \ | 
| 424 | greg | 1.4 | !(r->rsrc>=0&&source[r->rsrc].so==r->ro)) | 
| 425 | greg | 1.1 |  | 
| 426 |  |  |  | 
| 427 |  |  | m_light(m, r)                   /* ray hit a light source */ | 
| 428 |  |  | register OBJREC  *m; | 
| 429 |  |  | register RAY  *r; | 
| 430 |  |  | { | 
| 431 |  |  | /* check for behind */ | 
| 432 |  |  | if (r->rod < 0.0) | 
| 433 |  |  | return; | 
| 434 |  |  | /* check for over-counting */ | 
| 435 |  |  | if (wrongsource(m, r) || badambient(m, r)) | 
| 436 |  |  | return; | 
| 437 |  |  | /* check for passed illum */ | 
| 438 |  |  | if (passillum(m, r)) { | 
| 439 |  |  |  | 
| 440 |  |  | if (m->oargs.nsargs < 1 || !strcmp(m->oargs.sarg[0], VOIDID)) | 
| 441 |  |  | raytrans(r); | 
| 442 |  |  | else | 
| 443 |  |  | rayshade(r, modifier(m->oargs.sarg[0])); | 
| 444 |  |  |  | 
| 445 |  |  | /* otherwise treat as source */ | 
| 446 |  |  | } else { | 
| 447 |  |  | /* get distribution pattern */ | 
| 448 |  |  | raytexture(r, m->omod); | 
| 449 |  |  | /* get source color */ | 
| 450 |  |  | setcolor(r->rcol, m->oargs.farg[0], | 
| 451 |  |  | m->oargs.farg[1], | 
| 452 |  |  | m->oargs.farg[2]); | 
| 453 |  |  | /* modify value */ | 
| 454 |  |  | multcolor(r->rcol, r->pcol); | 
| 455 |  |  | } | 
| 456 |  |  | } | 
| 457 |  |  |  | 
| 458 |  |  |  | 
| 459 |  |  | o_source() {}           /* intersection with a source is done elsewhere */ |