tge/engine/sceneGraph/lightManager.cc
2025-02-17 23:17:30 -06:00

427 lines
13 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/lightManager.h"
#include "dgl/dgl.h"
#include "console/console.h"
#include "console/simBase.h"
#include "sceneGraph/sceneGraph.h"
#include "console/consoleTypes.h"
namespace {
static GLfloat zeroColor[] = {0.f, 0.f, 0.f, 0.f};
static GLfloat whiteColor[] = {1.f, 1.f, 1.f, 1.f};
// global settings for the lightmanager
static ColorF gOpenGLLightingAmbientColor(0.f, 0.f, 0.f, 0.f);
static ColorF gOpenGLMaterialAmbientColor(0.f, 0.f, 0.f, 0.f);
static ColorF gOpenGLMaterialDiffuseColor(1.f, 1.f, 1.f, 1.f);
static S32 gOpenGLMaxHardwareLights = LightManager::DefaultMaxLights;
bool colorIsZero(const ColorF & col)
{
return((col.red == 0.f) && (col.green == 0.f) && (col.blue == 0.f));
}
}
//------------------------------------------------------------------------------
// Class LightManager
//------------------------------------------------------------------------------
ConsoleFunction( resetLighting, void, 1, 1, "Resets GL lighting.")
{
if(!gClientSceneGraph)
{
Con::errorf(ConsoleLogEntry::General, "LightManager::cResetLighting: no client scenegraph!");
return;
}
LightManager * lighting = gClientSceneGraph->getLightManager();
if(!lighting)
{
Con::errorf(ConsoleLogEntry::General, "LightManager::cResetLighting: lightmanager not found!");
return;
}
lighting->resetGL();
}
LightManager::LightManager()
{
mGLInitialized = false;
mGLLightsInstalled = false;
mGLLightCount = 0;
mAmbientLightColor.set(0.f,0.f,0.f);
mVectorLightsAttenuation = 1.f;
mVectorLightsEnabled = true;
mSunLight = NULL;
#ifdef DEBUG
Con::addVariable("$pref::OpenGL::lightingAmbientColor", TypeColorF, &gOpenGLLightingAmbientColor);
Con::addVariable("$pref::OpenGL::materialAmbientColor", TypeColorF, &gOpenGLMaterialAmbientColor);
Con::addVariable("$pref::OpenGL::materialDiffuseColor", TypeColorF, &gOpenGLMaterialDiffuseColor);
#endif
Con::addVariable("$pref::OpenGL::maxHardwareLights", TypeS32, &gOpenGLMaxHardwareLights);
}
void LightManager::setMaxGLLights(U32 max)
{
// ask gl for max light value, and check if this is greater..
GLint glMaxLights = 0;
glGetIntegerv(GL_MAX_LIGHTS, &glMaxLights);
// clamp to the number gl returned
if(max > glMaxLights)
{
Con::errorf(ConsoleLogEntry::General, "LightManager::setMaxGLLights: too many lights.");
max = glMaxLights;
}
mMaxGLLights = max;
// cleanup any installed lights
if(mGLLightsInstalled)
uninstallGLLights();
}
// will crash if called prior to PlatformVideo:: initialization!
void LightManager::resetGL()
{
if(!mGLInitialized)
return;
if(mGLLightsInstalled)
uninstallGLLights();
// set ambient color
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (const F32 *)gOpenGLMaterialAmbientColor);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (const F32 *)gOpenGLMaterialDiffuseColor);
setMaxGLLights(gOpenGLMaxHardwareLights);
}
void LightManager::registerLights(bool lightingScene)
{
if(!mGLInitialized)
{
mGLInitialized = true;
resetGL();
}
if(mGLLightsInstalled)
{
Con::warnf(ConsoleLogEntry::General, "LightManager::registerLights: lights are already installed.");
uninstallGLLights();
}
// clear out the light list
mLights.clear();
// walk the lightlist and query objects for their lights
SimSet * lightSet = Sim::getLightSet();
for(SimObject ** itr = lightSet->begin(); itr != lightSet->end(); itr++)
(*itr)->registerLights(this, lightingScene);
}
void LightManager::addLight(LightInfo * light)
{
mLights.push_back(light);
if(light->mType == LightInfo::Vector)
mSunLight = light;
}
void LightManager::removeLight(LightInfo * light)
{
for(U32 i = 0; i < mLights.size(); i++)
{
if(mLights[i] == light)
{
mLights.erase_fast(i);
break;
}
}
if(mSunLight == light)
mSunLight = NULL;
}
//--------------------------------------------------------------------------
U32 LightManager::installGLLights(const SphereF & sphere)
{
// score the lights:
for(U32 i = 0; i < mLights.size(); i++)
{
if(!mVectorLightsEnabled && (mLights[i]->mType == LightInfo::Vector))
mLights[i]->mScore = 0;
else
scoreLight(mLights[i], sphere);
}
return(installGLLights());
}
U32 LightManager::installGLLights(const Box3F & box)
{
SphereF sphere;
box.getCenter(&sphere.center);
sphere.radius = Point3F(box.max - sphere.center).len();
return(installGLLights(sphere));
}
//--------------------------------------------------------------------------
U32 LightManager::installGLLights()
{
AssertFatal(!mGLLightsInstalled, "LightManager::installGLLights: lights are already installed!");
mGLLightsInstalled = true;
// sort them
dQsort(mLights.address(), mLights.size(), sizeof(LightInfo*), compareLights);
// grab the best ones...
U32 count = 0;
while(count < mLights.size())
{
if(count >= mMaxGLLights)
break;
if(!mLights[count]->mScore)
break;
installGLLight(mLights[count++]);
}
// enable ambient and lighting always:
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (const F32 *)ColorF(gOpenGLLightingAmbientColor + mAmbientLightColor));
glEnable(GL_LIGHTING);
return(count);
}
//--------------------------------------------------------------------------
// - restores ambient light changes
// - restores vector light enabled flag
void LightManager::uninstallGLLights()
{
if(!mGLLightsInstalled)
{
Con::errorf(ConsoleLogEntry::General, "LightManager::uninstallGLLights: no lights installed!");
return;
}
mGLLightsInstalled = false;
// restore values:
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (const F32*)gOpenGLLightingAmbientColor);
mAmbientLightColor.set(0.f, 0.f, 0.f);
mVectorLightsAttenuation = 1.f;
mVectorLightsEnabled = true;
glDisable(GL_LIGHTING);
for(U32 i = 0; i < mGLLightCount; i++)
glDisable(GL_LIGHT0 + i);
mGLLightCount = 0;
}
void LightManager::installGLLight(LightInfo * lightInfo)
{
AssertFatal(mGLLightCount < mMaxGLLights, "LightManager::installGLLight: too many lights installed");
GLfloat lightColor[] = {lightInfo->mColor.red, lightInfo->mColor.green, lightInfo->mColor.blue, 1.f};
GLfloat ambientColor[] = {lightInfo->mAmbient.red, lightInfo->mAmbient.green, lightInfo->mAmbient.blue, 1.f};
GLfloat lightPos[] = {lightInfo->mPos.x, lightInfo->mPos.y, lightInfo->mPos.z, 1.f};
GLfloat lightDir[] = {-lightInfo->mDirection.x, -lightInfo->mDirection.y, -lightInfo->mDirection.z, 0.f};
U32 light = GL_LIGHT0 + mGLLightCount++;
glEnable(light);
switch(lightInfo->mType)
{
case LightInfo::Spot:
// set the light params
glLightfv(light, GL_POSITION, (const GLfloat*)lightPos);
glLightfv(light, GL_SPOT_DIRECTION, (const GLfloat*)lightDir);
glLightfv(light, GL_DIFFUSE, (const GLfloat*)lightColor);
glLightfv(light, GL_AMBIENT, (const GLfloat*)zeroColor);
glLightfv(light, GL_SPECULAR, (const GLfloat*)lightColor);
break;
// falls through to LightInfo::Ambient
case LightInfo::Vector:
{
F32 ambientFactor = (ambientColor[0] + ambientColor[1] + ambientColor[2]) / 3.f;
F32 factor = mClampF(mVectorLightsAttenuation - ambientFactor, 0.f, 1.f);
// attenuate (for in shadow...)
for(U32 i = 0; i < 3; i++)
lightColor[i] *= factor;
}
case LightInfo::Ambient:
// set the light params
glLightfv(light, GL_POSITION, (const GLfloat*)lightDir);
glLightfv(light, GL_DIFFUSE, (const GLfloat*)lightColor);
glLightfv(light, GL_AMBIENT, (const GLfloat*)ambientColor);
glLightfv(light, GL_SPECULAR, (const GLfloat*)lightColor);
break;
case LightInfo::Point:
// set the light params
glLightfv(light, GL_POSITION, (const GLfloat*)lightPos);
glLightfv(light, GL_DIFFUSE, (const GLfloat*)lightColor);
glLightfv(light, GL_AMBIENT, (const GLfloat*)zeroColor);
glLightfv(light, GL_SPECULAR, (const GLfloat*)lightColor);
glLightf(light, GL_SPOT_CUTOFF, 180.f);
glLightf(light, GL_CONSTANT_ATTENUATION, 0.f);
glLightf(light, GL_LINEAR_ATTENUATION, 1.0f);
glLightf(light, GL_QUADRATIC_ATTENUATION, 0.f);
break;
}
}
//------------------------------------------------------------------------------
void LightManager::getBestLights(const PlaneF * testPlanes, const U32 numPlanes,
const Point3F & pos, LightInfoList & lightList, const U32 max)
{
for(U32 i = 0; i < mLights.size(); i++)
scoreLight(mLights[i], testPlanes, numPlanes, pos);
fillLightList(lightList, max);
}
void LightManager::getBestLights(const SphereF & sphere, LightInfoList & lightList, const U32 max)
{
// score the lights
for(U32 i = 0; i < mLights.size(); i++)
scoreLight(mLights[i], sphere);
fillLightList(lightList, max);
}
void LightManager::getBestLights(const Box3F & box, LightInfoList & lightList, const U32 max)
{
SphereF sphere;
box.getCenter(&sphere.center);
sphere.radius = Point3F(box.max - sphere.center).len();
getBestLights(sphere, lightList, max);
}
const Point3F LightManager::getShadowLightDirection() const
{
if(mSunLight)
return mSunLight->mDirection;
else
// Do a lame default...
return Point3F(0.573, 0.573, -0.573);
}
//------------------------------------------------------------------------------
void LightManager::fillLightList(LightInfoList & lightList, const U32 max)
{
dQsort(mLights.address(), mLights.size(), sizeof(LightInfo*), compareLights);
// grab the best ones...
U32 count = 0;
for(U32 i = 0; i < mLights.size(); i++)
{
if(!mLights[i]->mScore)
continue;
if(count++ >= max)
break;
lightList.push_back(mLights[i]);
}
}
//------------------------------------------------------------------------------
// 0 not lit
// 1->100 point/spot lights
// 100->200 vector/ambient lights
void LightManager::scoreLight(LightInfo * lightInfo, const SphereF & sphere) const
{
switch(lightInfo->mType)
{
case LightInfo::Ambient:
case LightInfo::Vector:
{
// 100 + (color_intensity * 100)
float intensity = (lightInfo->mColor.red + lightInfo->mAmbient.red) * 0.346f +
(lightInfo->mColor.green + lightInfo->mAmbient.green) * 0.588f +
(lightInfo->mColor.blue + lightInfo->mAmbient.blue) * 0.070f;
lightInfo->mScore = 100 + S32(100.f * mClampF(intensity, 0.f, 1.f));
break;
}
case LightInfo::Point:
{
Point3F diff = lightInfo->mPos - sphere.center;
F32 lenSq = diff.lenSquared();
F32 sumRadiusSq = (sphere.radius + lightInfo->mRadius) * (sphere.radius + lightInfo->mRadius);
if(lenSq > sumRadiusSq)
{
lightInfo->mScore = 0;
break;
}
F32 radSq = sphere.radius * sphere.radius;
lightInfo->mScore = 1 + S32(99.f * (getMin((sumRadiusSq - lenSq), radSq) / radSq));
break;
}
default:
lightInfo->mScore = 1;
break;
}
}
void LightManager::scoreLight(LightInfo* lightInfo,
const PlaneF* /*testPlanes*/,
const U32 /*numPlanes*/,
const Point3F& /*pos*/) const
{
switch(lightInfo->mType)
{
case LightInfo::Ambient:
case LightInfo::Vector:
{
// 100 + (color_intensity * 100)
float intensity = (lightInfo->mColor.red + lightInfo->mAmbient.red) * 0.346f +
(lightInfo->mColor.green + lightInfo->mAmbient.green) * 0.588f +
(lightInfo->mColor.blue + lightInfo->mAmbient.blue) * 0.070f;
lightInfo->mScore = 100 + S32(100.f * mClampF(intensity, 0.f, 1.f));
break;
}
case LightInfo::Point:
{
lightInfo->mScore = 1;
break;
}
default:
lightInfo->mScore = 1;
break;
}
}
//------------------------------------------------------------------------------
S32 QSORT_CALLBACK LightManager::compareLights(const void* a, const void* b)
{
return((*(LightInfo**)b)->mScore - (*(LightInfo**)a)->mScore);
}