This is shadow.c in view mode; [Download] [Up]
/*
* shadow.c
*
* Copyright (C) 1989, 1991, Craig E. Kolb
* All rights reserved.
*
* This software may be freely copied, modified, and redistributed
* provided that this copyright notice is preserved on all copies.
*
* You may not distribute this software, in whole or in part, as part of
* any commercial product without the express consent of the authors.
*
* There is no warranty or other guarantee of fitness of this software
* for any purpose. It is provided solely "as is".
*
* $Id$
*
* $Log$
*/
#include "libobj/object.h"
#include "libsurf/surface.h"
#include "light.h"
/*
* Shadow stats.
* External functions have read access via ShadowStats().
*/
static unsigned long ShadowRays, ShadowHits, CacheMisses, CacheHits;
/*
* Options controlling how shadowing information is determined.
* Set by external modules via ShadowSetOptions().
*/
static long ShadowOptions;
/*
* Trace ray from point of intersection to a light. If an intersection
* occurs at a distance less than "dist" (the distance to the
* light source), then the point is in shadow, and TRUE is returned.
* Otherwise, the brightness/color of the light is computed ('result'),
* and FALSE is returned.
*/
int
Shadowed(result, color, cache, ray, dist, noshadow)
Color *result, *color; /* resultant intensity, light color */
ShadowCache *cache; /* shadow cache for light */
Ray *ray; /* ray, origin on surface, dir towards light */
Float dist; /* distance from pos to light source */
int noshadow; /* If TRUE, no shadow ray is cast. */
{
Float s;
int i, smooth;
HitList hitlist;
Ray tmpray;
HitNode *np;
ShadowCache *cp;
Vector hitpos, norm, gnorm;
Surface surf, *sptr;
Float atten, totaldist;
if (noshadow || NOSHADOWS(ShadowOptions)) {
*result = *color;
return FALSE;
}
ShadowRays++;
s = dist;
cp = &cache[ray->depth];
/*
* Check shadow cache. SHADOWCACHE() is implied.
*/
if (cp->obj) {
/*
* Transform ray to the space of the cached primitive.
*/
tmpray = *ray;
if (cp->dotrans)
s *= RayTransform(&tmpray, &cp->trans);
/*
* s = distance to light source in 'primitive space'.
* Intersect ray with cached object.
*/
if (IsAggregate(cp->obj)) {
if ((*cp->obj->methods->intersect)(cp->obj->obj,
&tmpray, &hitlist, SHADOW_EPSILON, &s)) {
CacheHits++;
return TRUE;
}
} else if ((*cp->obj->methods->intersect)(cp->obj->obj,
&tmpray, SHADOW_EPSILON, &s)) {
/* Hit cached object. */
CacheHits++;
return TRUE;
}
/*
* Did not hit anything -- zero out the cache.
*/
CacheMisses++;
/*
* Transformed -- reset s for use below.
*/
s = dist;
cp->obj = (Object *)NULL;
cp->dotrans = FALSE;
}
hitlist.nodes = 0;
if (!TraceRay(ray, &hitlist, SHADOW_EPSILON, &s)) {
/* Shadow ray didn't hit anything. */
*result = *color;
return FALSE;
}
/*
* Otherwise, we've hit something.
*/
ShadowHits++;
/*
* If we're not worrying about transparent objects...
* This is ugly due to the fact that we have to find
* the surface associated with the object that was hit.
* GetShadingSurf() will always return a non-null value.
*/
sptr = GetShadingSurf(&hitlist);
if (!SHADOWTRANSP(ShadowOptions) || sptr->transp == 0.) {
if (SHADOWCACHE(ShadowOptions)) {
if (SHADOWCSG(ShadowOptions)) {
/*
* There's possibly a CSG object in the
* hitlist, so we can't simply cache the
* primitive that was hit. Find the
* object lowest in hit that's not part
* of a CSG object, and cache it.
*/
i = FirstCSGObj(&hitlist);
} else {
/*
* Cache the primitive that was hit.
*/
i = 0;
}
/*
* Compute total world-->cached object
* transformation and store in cp->trans.
*/
/*
* Find the first transformation...
*/
np = &hitlist.data[i];
cp->obj = np->obj;
cp->dotrans = FALSE;
while (i < hitlist.nodes -1) {
if (np->obj->trans) {
if (cp->dotrans) {
MatrixMult(
&np->obj->trans->itrans,
&cp->trans,
&cp->trans);
} else {
MatrixCopy(
&np->obj->trans->itrans,
&cp->trans);
cp->dotrans = TRUE;
}
}
i++;
np++;
}
}
return TRUE;
}
/*
* We've hit a transparent object. Attenuate the color of the light
* source and continue the ray until we hit background or a
* non-transparent object. Note that this is incorrect if DefIndex or
* any of the indices of refraction of the surfaces differ.
*
* "atten" is a misnomer.
*/
atten = 1.;
totaldist = 0.;
do {
/*
* If surf.transp is 0 at this point, we could
* cache the object and return 0 directly.
* This would mean that if transp is to be set to nonzero
* by a texture, the surface should also have nonzero
* transp *before* texturing or else shadows won't
* work correctly.
*/
/*
* Perform texturing and the like in case surface
* transparency is modulated.
*/
(void)ComputeSurfProps(&hitlist, &ray, &hitpos,
&norm, &gnorm, &surf, &smooth);
atten *= surf.transp;
/*
* Return if attenuation becomes large ('atten' is small).
*/
if (atten < EPSILON)
return TRUE;
/*
* Max distance is previous max minus previous distance
* traveled.
*/
totaldist = s + EPSILON;
s = dist;
/*
* Trace ray starting at new origin and in the
* same direction.
*/
hitlist.nodes = 0;
} while (TraceRay(ray, &hitlist, totaldist, &s));
ColorScale(atten, *color, result);
return FALSE;
}
void
ShadowStats(shadowrays, shadowhit, cachehit, cachemiss)
unsigned long *shadowrays, *shadowhit, *cachehit, *cachemiss;
{
*shadowrays = ShadowRays;
*shadowhit = ShadowHits;
*cachehit = CacheHits;
*cachemiss = CacheMisses;
}
void
ShadowSetOptions(options)
{
ShadowOptions = options;
if (SHADOWCACHE(ShadowOptions) && SHADOWTRANSP(ShadowOptions)) {
/*
* Ensure that if shadowtransp is true that
* caching is disabled. We don't really have to do
* this, but the code isn't yet in place to handle
* this properly.
*/
ShadowOptions &= ~SHADOW_CACHE;
}
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.