1 /*
2 * shadow.c
3 *
4 * Copyright (C) 1989, 1991, Craig E. Kolb
5 * All rights reserved.
6 *
7 * This software may be freely copied, modified, and redistributed
8 * provided that this copyright notice is preserved on all copies.
9 *
10 * You may not distribute this software, in whole or in part, as part of
11 * any commercial product without the express consent of the authors.
12 *
13 * There is no warranty or other guarantee of fitness of this software
14 * for any purpose. It is provided solely "as is".
15 *
16 * $Id: shadow.c,v 4.0.1.1 91/09/29 15:40:57 cek Exp Locker: cek $
17 *
18 * $Log: shadow.c,v $
19 * Revision 4.0.1.1 91/09/29 15:40:57 cek
20 * patch1: ShadowOptions was incorrectly externed.
21 *
22 * Revision 4.0 91/07/17 14:35:34 kolb
23 * Initial version.
24 *
25 */
26 #include "libobj/geom.h"
27 #include "libsurf/surface.h"
28 #include "light.h"
29
30 /*
31 * Shadow stats.
32 * External functions have read access via ShadowStats().
33 */
34 static unsigned long ShadowRays, ShadowHits, CacheMisses, CacheHits;
35 /*
36 * Options controlling how shadowing information is determined.
37 * Set by external modules via ShadowSetOptions().
38 */
39 static long ShadowOptions;
40
41 void LightCacheHit();
42
43 /*
44 * Trace ray from point of intersection to a light. If an intersection
45 * occurs at a distance less than "dist" (the distance to the
46 * light source), then the point is in shadow, and TRUE is returned.
47 * Otherwise, the brightness/color of the light is computed ('result'),
48 * and FALSE is returned.
49 */
50 int
Shadowed(result,color,cache,ray,dist,noshadow)51 Shadowed(result, color, cache, ray, dist, noshadow)
52 Color *result, *color; /* resultant intensity, light color */
53 ShadowCache *cache; /* shadow cache for light */
54 Ray *ray; /* ray, origin on surface, dir towards light */
55 Float dist; /* distance from pos to light source */
56 int noshadow; /* If TRUE, no shadow ray is cast. */
57 {
58 int i, smooth, enter;
59 HitList hitlist;
60 Ray tmpray;
61 ShadowCache *cp;
62 Vector hitpos, norm, gnorm;
63 Surface surf, *sptr, *prevsurf;
64 Float s, totaldist, statten;
65 Color res;
66
67 if (noshadow || NOSHADOWS(ShadowOptions)) {
68 *result = *color;
69 return FALSE;
70 }
71
72 ShadowRays++;
73 s = dist;
74 cp = &cache[ray->depth];
75 /*
76 * Check shadow cache. SHADOWCACHE() is implied.
77 */
78 if (cp->obj) {
79 /*
80 * Transform ray to the space of the cached primitive.
81 */
82 tmpray = *ray;
83 if (cp->dotrans)
84 s *= RayTransform(&tmpray, &cp->trans);
85 /*
86 * s = distance to light source in 'primitive space'.
87 * Intersect ray with cached object.
88 */
89 if (cp->obj->animtrans) {
90 /*
91 * Geom has animated transformation --
92 * call intersect so that the transformation
93 * is resolved properly.
94 */
95 if (intersect(cp->obj, &tmpray, &hitlist,
96 SHADOW_EPSILON, &s)) {
97 CacheHits++;
98 return TRUE;
99 }
100 } else if (IsAggregate(cp->obj)) {
101 if ((*cp->obj->methods->intersect)(cp->obj->obj,
102 &tmpray, &hitlist, SHADOW_EPSILON, &s)) {
103 CacheHits++;
104 return TRUE;
105 }
106 } else if ((*cp->obj->methods->intersect)(cp->obj->obj,
107 &tmpray, SHADOW_EPSILON, &s)) {
108 /* Hit cached object. */
109 CacheHits++;
110 return TRUE;
111 }
112 /*
113 * Did not hit anything -- zero out the cache.
114 */
115 CacheMisses++;
116 /*
117 * Transformed -- reset s for use below.
118 */
119 s = dist;
120 cp->obj = (Geom *)NULL;
121 cp->dotrans = FALSE;
122 }
123
124 hitlist.nodes = 0;
125 if (!TraceRay(ray, &hitlist, SHADOW_EPSILON, &s)) {
126 /* Shadow ray didn't hit anything. */
127 *result = *color;
128 return FALSE;
129 }
130
131 /*
132 * Otherwise, we've hit something.
133 */
134 ShadowHits++;
135
136 /*
137 * If we're not worrying about transparent objects...
138 * This is ugly due to the fact that we have to find
139 * the surface associated with the object that was hit.
140 * GetShadingSurf() will always return a non-null value.
141 *
142 * ***NOTE**
143 * The transparency of the surface is checked below without
144 * applying textures, if any, to it. This means that if
145 * an object may be made trasparent by a texture, its
146 * surface should have non-zero transparency *before* texturing
147 * as well.
148 */
149 if (!SHADOWTRANSP(ShadowOptions)) {
150 if (SHADOWCACHE(ShadowOptions))
151 LightCacheHit(&hitlist, cp);
152 return TRUE;
153 }
154
155 /*
156 * We've hit a transparent object. Attenuate the color of the light
157 * source and continue the ray until we hit background or a
158 * non-transparent object. Note that this is incorrect if DefIndex or
159 * any of the indices of refraction of the surfaces differ.
160 */
161
162 totaldist = 0.;
163 prevsurf = (Surface *)NULL;
164 res = *color;
165
166 do {
167 /*
168 * Get a pointer to the surface to be used
169 * for shading...
170 */
171 sptr = GetShadingSurf(&hitlist);
172 if (sptr->transp < EPSILON) {
173 if (SHADOWCACHE(ShadowOptions))
174 LightCacheHit(&hitlist, cp);
175 return TRUE;
176 }
177 /*
178 * Take specular transmission attenuation from
179 * previous intersection into account.
180 */
181 if (prevsurf) {
182 if (prevsurf->statten != 1.) {
183 statten = pow(prevsurf->statten, s - totaldist);
184 ColorScale(statten, res, &res);
185 }
186 }
187 /*
188 * Perform texturing and the like in case surface
189 * transparency is modulated.
190 */
191 /* copy the surface to be used... */
192 surf = *sptr;
193 enter = ComputeSurfProps(&hitlist, ray, &hitpos,
194 &norm, &gnorm, &surf, &smooth);
195 if (enter)
196 prevsurf = &surf;
197 else
198 prevsurf = (Surface *)NULL;
199 /*
200 * Attenuate light source by body color of surface.
201 */
202 ColorScale(surf.transp, res, &res);
203 ColorMultiply(res, surf.body, &res);
204 /*
205 * Return if attenuation becomes large.
206 * In this case, the light was attenuated to nothing,
207 * so we can't cache anything...
208 */
209 if (res.r < EPSILON && res.g < EPSILON && res.b < EPSILON)
210 return TRUE;
211 /*
212 * Min distance is previous max.
213 */
214 totaldist = s + EPSILON;
215 /*
216 * Max distance is dist to light source
217 */
218 s = dist;
219 /*
220 * Trace ray starting at new origin and in the
221 * same direction.
222 */
223 hitlist.nodes = 0;
224 } while (TraceRay(ray, &hitlist, totaldist, &s));
225
226 *result = res;
227 return FALSE;
228 }
229
230 void
ShadowStats(shadowrays,shadowhit,cachehit,cachemiss)231 ShadowStats(shadowrays, shadowhit, cachehit, cachemiss)
232 unsigned long *shadowrays, *shadowhit, *cachehit, *cachemiss;
233 {
234 *shadowrays = ShadowRays;
235 *shadowhit = ShadowHits;
236 *cachehit = CacheHits;
237 *cachemiss = CacheMisses;
238 }
239
240 void
ShadowSetOptions(options)241 ShadowSetOptions(options)
242 long options;
243 {
244 ShadowOptions = options;
245 }
246
247 void
LightCacheHit(hitlist,cache)248 LightCacheHit(hitlist, cache)
249 HitList *hitlist;
250 ShadowCache *cache;
251 {
252 HitNode *np;
253 int i, n;
254 extern long ShadowOptions;
255
256 i = 0;
257
258 if (SHADOWCSG(ShadowOptions)) {
259 /*
260 * There's possibly a CSG object in the
261 * hitlist, so we can't simply cache the
262 * primitive that was hit. Find the
263 * object lowest in hit that's not part
264 * of a CSG object, and cache it.
265 */
266 i = FirstCSGGeom(hitlist);
267 }
268
269 if (SHADOWBLUR(ShadowOptions)) {
270 /*
271 * Something might be animated --
272 * gotta cache the puppy.
273 */
274 n = FirstAnimatedGeom(hitlist);
275 if (n > i)
276 i = n;
277 }
278
279 /*
280 * Compute total world-->cached object
281 * transformation and store in cache->trans.
282 */
283 /*
284 * Find the first transformation...
285 */
286 np = &hitlist->data[i];
287 cache->obj = np->obj;
288 /*
289 * If the cached object is animated, then we don't
290 * want to include the object's transformation(s)
291 * in cache->trans (it's taken care of in shadowed()
292 * by calling intersect).
293 */
294 if (cache->obj->animtrans) {
295 i++;
296 np++;
297 }
298 cache->dotrans = FALSE;
299 while (i < hitlist->nodes -1) {
300 if (np->obj->trans) {
301 if (cache->dotrans) {
302 MatrixMult(
303 &np->obj->trans->itrans,
304 &cache->trans,
305 &cache->trans);
306 } else {
307 MatrixCopy(
308 &np->obj->trans->itrans,
309 &cache->trans);
310 cache->dotrans = TRUE;
311 }
312 }
313 i++;
314 np++;
315 }
316 }
317