tge/engine/lightingSystem/sgObjectShadows.cc
2017-04-17 06:17:10 -06:00

525 lines
13 KiB
C++
Executable File

//-----------------------------------------------
// Synapse Gaming - Lighting System
// Copyright © Synapse Gaming 2003
// Written by John Kabus
//-----------------------------------------------
#include "sceneGraph/sceneGraph.h"
#include "math/mathUtils.h"
#include "game/shapeBase.h"
#include "game/vehicles/wheeledVehicle.h"
#include "lightingSystem/sgLighting.h"
#include "lightingSystem/sgLightingModel.h"
#include "lightingSystem/sgObjectShadows.h"
#define SG_UNUSED_TIMEOUT 2000
#define SG_SHADOW_TIMEOUT 6000
// this can be a long time, only used for cleaning
// up texture memory after an intensive scene (TSE only)...
//#define SG_TEXTURE_TIMEOUT 30000
extern SceneGraph* gClientSceneGraph;
Vector<sgObjectShadows *> sgObjectShadowMonitor::sgAllObjectShadows;
F32 sgObjectShadows::sgCurrentFade = 1.0;
bool sgTimeElapsed(U32 currenttime, U32 &lasttime, U32 period)
{
if(currenttime < lasttime)
{
lasttime = 0;
return false;
}
if((currenttime - lasttime) < period)
return false;
lasttime = currenttime;
return true;
}
//-----------------------------------------------
void sgObjectShadowMonitor::sgRegister(sgObjectShadows *shadows)
{
sgAllObjectShadows.push_back(shadows);
}
void sgObjectShadowMonitor::sgUnregister(sgObjectShadows *shadows)
{
for(U32 i=0; i<sgAllObjectShadows.size(); i++)
{
if(sgAllObjectShadows[i] == shadows)
{
sgAllObjectShadows.erase_fast(i);
return;
}
}
}
void sgObjectShadowMonitor::sgCleanupUnused()
{
static U32 lasttime = 0;
U32 time = Platform::getRealMilliseconds();
if(!sgTimeElapsed(time, lasttime, SG_UNUSED_TIMEOUT))
return;
//Con::warnf("Checking for unused shadows...");
for(U32 i=0; i<sgAllObjectShadows.size(); i++)
sgAllObjectShadows[i]->sgCleanupUnused(time);
//Con::warnf("Done.");
}
//-----------------------------------------------
sgObjectShadows::sgObjectShadows()
{
sgRegistered = false;
sgLastRenderTime = 0;
sgSingleShadowSource.mColor = ColorF(0.5, 0.5, 0.5);
//sgEnable = false;
//sgCanMove = false;
//sgCanRTT = false;
//sgCanSelfShadow = false;
//sgRequestedShadowSize = 64;
//sgFrameSkip = 5;
//sgMaxVisibleDistance = 15.0f;
//sgProjectionDistance = 7.0f;
//sgFirstEntry = sgGetFirstShadowEntry();
}
sgObjectShadows::~sgObjectShadows()
{
sgClearMap();
}
void sgObjectShadows::sgClearMap()
{
sgShadowMultimap *entry = sgGetFirstShadowEntry();
while(entry)
{
if(entry->info)
{
delete entry->info;
entry->info = NULL;
}
entry = entry->linkHigh;
}
// all shadows are deleted, so nothing left to monitor...
if(sgRegistered)
{
sgObjectShadowMonitor::sgUnregister(this);
sgRegistered = false;
}
}
void sgObjectShadows::sgRender(SceneObject *parentobject,
TSShapeInstance *shapeinstance, F32 dist, F32 fogamount,
F32 genericshadowlevel, F32 noshadowlevel, U32 shadownode,
bool move, bool animate)
{
if(!LightManager::sgAllowDynamicShadows())
return;
if(Shadow::getGlobalShadowDetailLevel() < noshadowlevel)
return;
if(shapeinstance->getShape()->subShapeFirstTranslucentObject.empty() ||
shapeinstance->getShape()->subShapeFirstTranslucentObject[0] == 0)
return;
// prior to this no shadows exist, so no resources are used...
if(!sgRegistered)
{
sgObjectShadowMonitor::sgRegister(this);
sgRegistered = true;
}
sgLastRenderTime = Platform::getRealMilliseconds();
ShapeBase *shapebase = dynamic_cast<ShapeBase *>(parentobject);
WheeledVehicle *wheeledvehicle = dynamic_cast<WheeledVehicle *>(parentobject);
LightInfoList lights;
LightManager *lm = gClientSceneGraph->getLightManager();
lm->sgGetBestLights(lights);
// env light must be last!
for(U32 i=0; i<lights.size(); i++)
{
LightInfo *light = lights[i];
if((light->mType != LightInfo::Ambient) &&
(light->mType != LightInfo::Vector))
continue;
U32 il = lights.size() - 1;
if(i == il)
continue;
// swap...
LightInfo *last = lights[il];
lights[il] = light;
lights[i] = last;
break;
}
F32 shadowLen = 10.0f * shapeinstance->getShape()->radius;
Point3F pos = shapeinstance->getShape()->center;
// this is a bit of a hack...move generic shadows towards feet/base of shape
if(Shadow::getGlobalShadowDetailLevel() < genericshadowlevel)
pos *= 0.5f;
S32 shadowNode = shadownode;
if(shadowNode >= 0)
{
// adjust for movement of shape outside of bounding box by tracking this node
Point3F offset;
shapeinstance->mNodeTransforms[shadowNode].getColumn(3,&offset);
offset -= shapeinstance->getShape()->defaultTranslations[shadowNode];
offset.z = 0.0f;
pos += offset;
}
pos.convolve(parentobject->getScale());
parentobject->getRenderTransform().mulP(pos);
U32 usedlights = 0;
for(U32 i=0; i<lights.size(); i++)
{
LightInfo *light = lights[i];
// we want the env light!
if((!LightManager::sgMultipleDynamicShadows) &&
(light->mType != LightInfo::Vector) &&
(light->mType != LightInfo::Ambient))
continue;
// cast shadows?
if(!light->sgCastsShadows)
continue;
// avoid overlapping dynamic and static shadows...
if(((light->mType == LightInfo::SGStaticPoint) ||
(light->mType == LightInfo::SGStaticSpot) ||
(light->mType == LightInfo::Vector)) && (parentobject->getTypeMask() & ShadowCasterObjectType))
continue;
// find the shadow...
Shadow *shadow = sgFindShadow(parentobject, light, shapeinstance);
AssertFatal((shadow), "Shadow not found?");
if(Shadow::getGlobalShadowDetailLevel() < genericshadowlevel)
shadow->setGeneric(true);
else
shadow->setGeneric(false);
shadow->setMoving(move);
shadow->setAnimating(animate);
// Get a real light dir...
// keep this default, it makes the sun shadow darker...
F32 fade = 3.0;
Point3F lightDir;
if(LightManager::sgMultipleDynamicShadows)
{
//if(light->mType == LightInfo::Vector)
// lightDir = light->mDirection;
//else if(light->mType == LightInfo::Ambient)
if((light->mType == LightInfo::Vector) ||
(light->mType == LightInfo::Ambient))
{
lightDir = sgGetShadowLightDirection(parentobject, shadow);
if(usedlights > 0)
fade *= 1.0f / F32(usedlights + 1);
}
else
{
lightDir = parentobject->getPosition() - light->mPos;
F32 len = lightDir.len();
if(len == 0.0f)
continue;
lightDir /= len;
sgLightingModel &model = sgLightingModelManager::sgGetLightingModel(
light->sgLightingModelName);
model.sgSetState(light);
//F32 maxrad = model.sgGetMaxRadius(true, true);
SphereF s;
s.center = parentobject->getPosition();
fade = model.sgScoreLight(light, s);
model.sgResetState();
}
}
else
{
lightDir = sgGetShadowLightDirection(parentobject, shadow);
}
// help out dim lights a little...
if(light->sgAssignedToParticleSystem)
fade *= SG_PARTICLESYSTEMLIGHT_FIXED_INTENSITY;
else
fade *= getMax(light->mColor.red, getMax(light->mColor.green, light->mColor.blue));
fade *= 1.5f;
// prevent dark shadows...
fade = mClampF(fade, 0.0f, 1.0f);
if(fade <= 0.1f)
continue;
usedlights++;
sgObjectShadows::sgCurrentFade = fade;
// pos is where shadow will be centered (in world space)
if(wheeledvehicle)
pos -= lightDir;
Point3F scale = parentobject->getScale();
shadow->setRadius(shapeinstance, scale);
if(!shadow->prepare(pos, lightDir, shadowLen, scale, dist, fogamount, shapeinstance))
continue;
F32 maxscale = getMax(scale.x, getMax(scale.y, scale.z));
if(shadow->needBitmap())
{
shadow->beginRenderToBitmap();
shadow->selectShapeDetail(shapeinstance, dist, maxscale);
shadow->renderToBitmap(shapeinstance, parentobject->getRenderTransform(), pos, scale);
if(shapebase)
{
// render mounted items to shadow bitmap
for(U32 i=0; i<ShapeBase::MaxMountedImages; i++)
{
TSShapeInstance *instance = shapebase->getImageShapeInstance(i);
if(instance)
{
MatrixF mat;
shapebase->getRenderImageTransform(i, &mat);
shadow->selectShapeDetail(instance, dist, maxscale);
shadow->renderToBitmap(instance, mat, pos, Point3F(1,1,1));
}
}
// We only render the first mounted object for now...
ShapeBase *mount = shapebase->getMountedObject(0);
if(mount && mount->getShapeInstance())
{
Point3F linkscale = mount->getScale();
maxscale = getMax(linkscale.x, getMax(linkscale.y, linkscale.z));
shadow->selectShapeDetail(mount->getShapeInstance(), dist, maxscale);
shadow->renderToBitmap(mount->getShapeInstance(),
mount->getRenderTransform(), pos, linkscale);
}
}
if(wheeledvehicle)
{
for(U32 w=0; w<wheeledvehicle->getWheelCount(); w++)
{
WheeledVehicle::Wheel *wheel = wheeledvehicle->getWheel(w);
if(wheel->shapeInstance)
{
MatrixF m = parentobject->getRenderTransform();
MatrixF hub(EulerF(0, 0, wheeledvehicle->getSteering().x * wheel->steering));
Point3F p = wheel->data->pos;
p.z -= wheel->spring->length * wheel->extension;
hub.setColumn(3, p);
m.mul(hub);
MatrixF rot(EulerF(wheel->apos * M_2PI,0,0));
m.mul(rot);
MatrixF wrot(EulerF(0, 0, (wheel->data->pos.x > 0)? M_PI/2: -M_PI/2));
m.mul(wrot);
shadow->selectShapeDetail(wheel->shapeInstance, dist, maxscale);
shadow->renderToBitmap(wheel->shapeInstance, m, pos, scale);
}
}
}
shadow->endRenderToBitmap();
}
shadow->render();
shadow->sgLastRenderTime = sgLastRenderTime;
if(!LightManager::sgMultipleDynamicShadows)
{
//only need to render one here...
break;
}
}
}
const Point3F sgObjectShadows::sgGetShadowLightDirection(SceneObject *obj, Shadow *shadow) const
{
// don't want this to run too fast on newer systems (otherwise shadows snap into place)...
U32 time = Platform::getRealMilliseconds();
if((time - shadow->sgPreviousShadowTime) < SG_DYNAMIC_SHADOW_TIME)
return shadow->sgPreviousShadowLightingVector;
shadow->sgPreviousShadowTime = time;
LightManager *lm = gClientSceneGraph->getLightManager();
// ok get started...
U32 zone = obj->getCurrZone(0);
VectorF vectcomposite = VectorF(0, 0, -1.0f);
if(!LightManager::sgMultipleDynamicShadows)
{
U32 score;
U32 maxscore[2] = {0, 0};
LightInfo *light[2] = {NULL, NULL};
VectorF vector[2] = {VectorF(0, 0, 0), VectorF(0, 0, 0)};
LightInfoList lights;
lm->sgGetBestLights(lights);
for(U32 i=0; i<lights.size(); i++)
{
LightInfo *l = lights[i];
if((l->mType == LightInfo::Ambient) || (l->mType == LightInfo::Vector))
score = l->mScore / SG_LIGHTMANAGER_SUN_PRIORITY;
else if((l->mType == LightInfo::SGStaticPoint) || (l->mType == LightInfo::SGStaticSpot))
score = l->mScore / SG_LIGHTMANAGER_STATIC_PRIORITY;
else
score = l->mScore / SG_LIGHTMANAGER_DYNAMIC_PRIORITY;
if(score > maxscore[0])
{
light[1] = light[0];
maxscore[1] = maxscore[0];
light[0] = l;
maxscore[0] = score;
}
else if(score > maxscore[1])
{
light[1] = l;
maxscore[1] = score;
}
}
for(U32 i=0; i<2; i++)
{
if(!light[i])
break;
if((light[i]->mType == LightInfo::Ambient) || (light[i]->mType == LightInfo::Vector))
{
if(zone == 0)
vector[i] = light[i]->mDirection;
else
vector[i] = Point3F(0, 0, -1.0);
}
else
{
VectorF vect = obj->getPosition() - light[i]->mPos;
vect.normalize();
vector[i] = vect;
}
}
if(light[0])
{
if(light[1])
{
F32 ratio = F32(maxscore[0]) / F32(maxscore[0] + maxscore[1]);
vectcomposite = (vector[0] * ratio) + (vector[1] * (1.0f - ratio));
}
else
vectcomposite = vector[0];
}
}
else
{
if(zone != 0)
vectcomposite = VectorF(0.0f, 0.0f, -1.0f);
else
{
LightInfo *sun = lm->sgGetSpecialLight(LightManager::sgSunLightType);
vectcomposite = sun->mDirection;
}
}
VectorF step = (vectcomposite - shadow->sgPreviousShadowLightingVector) / SG_DYNAMIC_SHADOW_STEPS;
shadow->sgPreviousShadowLightingVector += step;
shadow->sgPreviousShadowLightingVector.normalize();
return shadow->sgPreviousShadowLightingVector;
}
void sgObjectShadows::sgCleanupUnused(U32 time)
{
// wrapped around?
if(time < sgLastRenderTime)
{
sgLastRenderTime = 0;
return;
}
// try to ditch the whole thing first...
if((time - sgLastRenderTime) > SG_SHADOW_TIMEOUT)
{
//Con::warnf("Found a whole set...");
sgClearMap();
return;
}
// no? alright lets try to get rid of some old shadows...
sgShadowMultimap *entry = sgGetFirstShadowEntry();
while(entry)
{
if(entry->info)
{
if(sgTimeElapsed(time, entry->info->sgLastRenderTime, SG_SHADOW_TIMEOUT))
{
//Con::warnf("Found one...");
delete entry->info;
entry->info = NULL;
}
}
entry = entry->linkHigh;
}
}
Shadow *sgObjectShadows::sgFindShadow(SceneObject *parentobject,
LightInfo *light, TSShapeInstance *shapeinstance)
{
sgShadowMultimap *entry = sgShadows.find(sgLightToHash(light));
if(entry->info)
return entry->info;
Shadow *shadow = new Shadow();
entry->info = shadow;
return entry->info;
}
sgShadowMultimap *sgObjectShadows::sgGetFirstShadowEntry()
{
return sgShadows.find(sgosFirstEntryHash);
}