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