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

616 lines
18 KiB
C++
Executable File

//-----------------------------------------------
// Synapse Gaming - Lighting System
// Copyright © Synapse Gaming 2003
// Written by John Kabus
//-----------------------------------------------
#include "dgl/dgl.h"
#include "lightingSystem/sgLighting.h"
#include "lightingSystem/sgLightingModel.h"
#include "lightingSystem/sgLightManager.h"
#include "platform/profiler.h"
//#define SG_TEST_LIGHTING_TEXTURE
#define SG_LIGHTING_MIN_CUTOFF 0.00392f
#define SG_DYNAMIC_LIGHTING_TEXTURE_SIZE 16
#define SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE F32(SG_DYNAMIC_LIGHTING_TEXTURE_SIZE / 2)
Vector<sgLightingModel *> sgLightingModelManager::sgLightingModels;
sgLightingModelStock sgLightingModelManager::sgDefaultModel;
sgLightingModelAdvanced sgLightingModelManager::sgAdvancedModel;
sgLightingModelInverseSquare sgLightingModelManager::sgInverseSquare;
sgLightingModelInverseSquareFastFalloff sgLightingModelManager::sgInverseSquareFastFalloff;
sgLightingModelNearLinear sgLightingModelManager::sgNearLinear;
sgLightingModelNearLinearFastFalloff sgLightingModelManager::sgNearLinearFastFalloff;
sgLightingModel *sgLightingModelManager::sgSunlightModel = &sgLightingModelManager::sgAdvancedModel;
#ifdef SG_TEST_LIGHTING_TEXTURE
GFXTexHandle sgTestLightingTexture;
#endif
ConsoleFunction(sgLightingModelCount, S32, 1, 1, "Get the number of Lighting Pack Lighting Models.")
{
return sgLightingModelManager::sgGetLightingModelCount();
}
ConsoleFunction(sgLightingModelName, const char *, 2, 2, "Get the name of the Lighting Pack Lighting Model.")
{
U32 index;
index = dAtoi(argv[1]);
return sgLightingModelManager::sgGetLightingModelName(index);
}
//-----------------------------------------------
//-----------------------------------------------
sgLightingModel::sgLightingModel()
{
sgStateSet = false;
sgStateInitLM = false;
sgLight = NULL;
sgLightingModelName[0] = 0;
sgLightingModelManager::sgRegisterLightingModel(this);
}
//-----------------------------------------------
//-----------------------------------------------
// only for TSE's consistent lighting models...
// TGE's vertex lighting requires the base
// class method sgLightingModelGLBase::sgScoreLight...
/*
F32 sgLightingModelAdvanced::sgScoreLight(LightInfo *light, const SphereF &sphere)
{
sgLightingModel::sgScoreLight(light, sphere);
F32 radsq = (light->mRadius * light->mRadius);
if(radsq <= 0.0f)
return 0.0f;
VectorF dist = light->mPos - sphere.center;
F32 distsq = dist.lenSquared();
return getMax((1 - (distsq / radsq)), 0.0f);
}
*/
bool sgLightingModelAdvanced::sgCanIlluminate(const Box3F &box)
{
AssertFatal((sgStateSet == true), "sgLightingModel: State not properly set.");
// can we skip the hard stuff?
if((sgLight->mType == LightInfo::Vector) || (sgLight->mType == LightInfo::Ambient))
return true;
// get the min dist...
Point3F pos = box.getClosestPoint(sgLight->mPos);
Point3F nor = sgLight->mPos - pos;
F32 distsq = mDot(nor, nor);
bool res = (distsq < (sgLight->mRadius * sgLight->mRadius));
return res;
}
void sgLightingModelAdvanced::sgSetState(LightInfo *light)
{
sgLightingModelGLBase::sgSetState(light);
if(sgLight->mRadius > 0.0f)
sgLinearAttenuation = (6.0f / sgLight->mRadius);
else
sgLinearAttenuation = 0.0f;
}
void sgLightingModelAdvanced::sgInitStateLM()
{
sgLightingModelGLBase::sgInitStateLM();
maxlightdistancesquared = sgLight->mRadius * sgLight->mRadius;
}
void sgLightingModelAdvanced::sgLightingLM(const Point3F &point, VectorF normal, ColorF &diffuse, ColorF &ambient, Point3F &lightingnormal)
{
// do not call sgLightingModelGLBase::sgLightingLM!!!
// we're overriding the method!!!
sgLightingModel::sgLightingLM(point, normal, diffuse, ambient, lightingnormal);
Point3F lightvector;
F32 distsquared;
F32 spotlightingangle;
// do this in lightmap...
//diffuse = ColorF(0.0, 0.0, 0.0);
//ambient = ColorF(0.0, 0.0, 0.0);
//lightingnormal = Point3F(0.0, 0.0, 0.0);
if(sgLight->mType != LightInfo::Vector)
{
// get the distance squared from the light to the
// light map texel world space coord.
lightvector = sgLight->mPos - point;
distsquared = lightvector.lenSquared();
if(distsquared >= maxlightdistancesquared)
return;
}
else
{
// directional lighting is always close enough
// just setup the needed variables...
lightvector = sgLight->mDirection * -1.0f;
}
// step two: find the spotlight amount...
// vector lights are already normallized...
if(sgLight->mType != LightInfo::Vector)
lightvector.normalize();
lightingnormal = lightvector;
// is it a spotlight? is it pointing this way?
if(sgLight->mType == LightInfo::SGStaticSpot)
{
spotlightingangle = mDot(lightvector, sgLight->mDirection);
if(spotlightingangle < spotanglecos)
return;
}
else
spotlightingangle = 1.0f;
// get the lighting angle amount...
// values coming from terrain are not normalized...
normal.normalize();
F32 angleamount = mDot(lightvector, normal);
F32 lightamount = 1.0f;
if(sgLight->sgUseNormals)
lightamount *= angleamount;
F32 lightfalloff = 1.0f;
lightamount = getMax(getMin(lightamount, 1.0f), 0.0f);
if(sgLight->mType != LightInfo::Vector)
{
// apply one of the point light lighting models...
lightfalloff = getMax((1 - (distsquared / maxlightdistancesquared)), 0.0f);
if(sgLight->mType == LightInfo::SGStaticSpot)
{
// apply the spotlight lighting model...
spotlightingangle = mClampF(((spotlightingangle - spotamountouter) / spotamountinner), 0.0f, 1.0f);
if(sgLight->sgSmoothSpotLight)
spotlightingangle *= getMin((spotlightingangle * 1.2f), 1.0f);
lightfalloff *= spotlightingangle;
}
lightamount *= lightfalloff;
}
lightamount *= (1.0f - sgLight->sgLocalAmbientAmount);
lightamount = getMax(getMin(lightamount, 1.0f), 0.0f);
diffuse = sgLight->mColor * lightamount;
if(sgLight->sgDoubleSidedAmbient || (angleamount > 0.0f))
{
lightfalloff *= sgLight->sgLocalAmbientAmount;
lightfalloff = getMax(getMin(lightfalloff, 1.0f), 0.0f);
ambient = sgLight->mColor * lightfalloff;
}
}
//-----------------------------------------------
//-----------------------------------------------
F32 sgLightingModelGLBase::sgScoreLight(LightInfo *light, const SphereF &sphere)
{
sgLightingModel::sgScoreLight(light, sphere);
if((sgLight->mType == LightInfo::Vector) || (sgLight->mType == LightInfo::Ambient))
return 0.5f;
VectorF vect = light->mPos - sphere.center;
F32 distsq = vect.lenSquared();
F32 dist = 0.0f;
if(sgLinearAttenuation > 0.0f)
{
dist = mSqrt(distsq);
}
F32 amount = (sgConstantAttenuation + (sgLinearAttenuation * dist) + (sgQuadraticAttenuation * distsq));
amount = getMax(amount, 0.000001f);
if(amount != 0.0f)
amount = 1.0f / amount;
amount = getMax(amount, 0.0f);
return amount;
}
bool sgLightingModelGLBase::sgCanIlluminate(const Box3F &box)
{
AssertFatal((sgStateSet == true), "sgLightingModel: State not properly set.");
// can we skip the hard stuff?
if((sgLight->mType == LightInfo::Vector) || (sgLight->mType == LightInfo::Ambient))
return true;
// get the min dist...
Point3F pos = box.getClosestPoint(sgLight->mPos);
Point3F nor = sgLight->mPos - pos;
F32 distsq = mDot(nor, nor);
F32 intensity = 0.0f;
if(distsq > 0.0f)
{
// run the basic lighting equation...
intensity = sgConstantAttenuation;
intensity += (sgQuadraticAttenuation * distsq);
if(sgLinearAttenuation > 0.0f)
intensity += (sgLinearAttenuation * mSqrt(distsq));
}
else
{
intensity = 1.0f;
}
// this seems fine...
if(intensity <= 0.0f)
return false;
intensity = 1.0f / intensity;
ColorF col = sgLight->mColor * intensity;
if((col.red < SG_MIN_LEXEL_INTENSITY) && (col.green < SG_MIN_LEXEL_INTENSITY) &&
(col.blue < SG_MIN_LEXEL_INTENSITY))
return false;
return true;
}
F32 sgLightingModelGLBase::sgGetMaxRadius(bool speedoverquality, bool glstyle)
{
AssertFatal((sgStateSet == true), "sgLightingModel: State not properly set.");
if((sgLight->mType == LightInfo::Vector) || (sgLight->mType == LightInfo::Ambient))
return 1000000.0f;
// get started...
// don't use F32_MAX, this value will be added to stuff...
const F32 infinity = 1.0e+6F;
F32 adjustedvalue;
if(speedoverquality)
adjustedvalue = (1.0f / SG_MIN_LEXEL_INTENSITY_SPEED_OVER_QUALITY) - sgConstantAttenuation;
else
adjustedvalue = (1.0f / SG_MIN_LEXEL_INTENSITY) - sgConstantAttenuation;
// early out?
if(adjustedvalue <= 0.0f)
return 0.0f;
// this is the default even when L and Q are both populated...
if(sgLinearAttenuation > 0.0f)
return adjustedvalue / sgLinearAttenuation;
if(sgQuadraticAttenuation > 0.0f)
return mSqrt(adjustedvalue / sgQuadraticAttenuation);
if(sgConstantAttenuation > 0.0f)
return infinity;
return 0.0f;
}
void sgLightingModelGLBase::sgSetState(LightInfo *light)
{
sgLightingModel::sgSetState(light);
sgConstantAttenuation = 0.0f;
sgLinearAttenuation = 0.0f;
sgQuadraticAttenuation = 0.0f;
sgLightParamDiffuse = Point4F(light->mColor.red * light->sgDTSLightingOcclusionAdjust,
light->mColor.green * light->sgDTSLightingOcclusionAdjust,
light->mColor.blue * light->sgDTSLightingOcclusionAdjust,
1.0f);
sgLightParamAmbient = Point4F(light->mAmbient.red, light->mAmbient.green, light->mAmbient.blue, 1.0f);
sgLightParamPosition = Point4F(light->mPos.x, light->mPos.y, light->mPos.z, 1.0f);
sgLightParamDirection = Point4F(-light->mDirection.x, -light->mDirection.y, -light->mDirection.z, 0.0f);
}
void sgLightingModelGLBase::sgLightingGL(S32 gllight)
{
sgLightingModel::sgLightingGL(gllight);
const GLfloat black[] = {0.0f, 0.0f, 0.0f, 0.0f};
if((sgLight->mType == LightInfo::Vector) || (sgLight->mType == LightInfo::Ambient))
{
F32 ambientFactor = (sgLightParamAmbient[0] + sgLightParamAmbient[1] + sgLightParamAmbient[2]) / 3.0f;
F32 factor = mClampF(1.0f - ambientFactor, 0.f, 1.f);
// attenuate (for in shadow...)
sgLightParamDiffuse.x *= factor;
sgLightParamDiffuse.y *= factor;
sgLightParamDiffuse.z *= factor;
glLightfv(gllight, GL_POSITION, (const GLfloat*)&sgLightParamDirection.x);
// do not add this back in!!!
// the DX wrapper is a bastard!!!
//glLightfv(gllight, GL_SPOT_DIRECTION, (const GLfloat*)black);
glLightfv(gllight, GL_DIFFUSE, (const GLfloat*)&sgLightParamDiffuse.x);
glLightfv(gllight, GL_AMBIENT, (const GLfloat*)&sgLightParamAmbient.x);
glLightfv(gllight, GL_SPECULAR, (const GLfloat*)black);
LightManager::sgSetAmbientSelfIllumination(sgLight, &sgLightParamDiffuse.x, &sgLightParamAmbient.x);
glLightf(gllight, GL_SPOT_CUTOFF, 180.f);
glLightf(gllight, GL_CONSTANT_ATTENUATION, 0.0f);
glLightf(gllight, GL_QUADRATIC_ATTENUATION, 0.0f);
glLightf(gllight, GL_LINEAR_ATTENUATION, 1.0f);
}
else
{
glLightfv(gllight, GL_POSITION, (const GLfloat*)&sgLightParamPosition.x);
// must be here and only set spots!!!
// the DX wrapper is a bastard!!!
if((sgLight->mType == LightInfo::SGStaticSpot) || (sgLight->mType == LightInfo::Spot))
glLightfv(gllight, GL_SPOT_DIRECTION, (const GLfloat*)&sgLightParamDirection.x);
glLightfv(gllight, GL_DIFFUSE, (const GLfloat*)&sgLightParamDiffuse.x);
glLightfv(gllight, GL_AMBIENT, (const GLfloat*)black);
glLightfv(gllight, GL_SPECULAR, (const GLfloat*)black);
if((sgLight->mType == LightInfo::SGStaticSpot) || (sgLight->mType == LightInfo::Spot))
glLightf(gllight, GL_SPOT_CUTOFF, (sgLight->sgSpotAngle * 0.5f));
else
glLightf(gllight, GL_SPOT_CUTOFF, 180.f);
glLightf(gllight, GL_CONSTANT_ATTENUATION, sgConstantAttenuation);
glLightf(gllight, GL_QUADRATIC_ATTENUATION, sgQuadraticAttenuation);
glLightf(gllight, GL_LINEAR_ATTENUATION, sgLinearAttenuation);
}
}
void sgLightingModelGLBase::sgInitStateLM()
{
sgLightingModel::sgInitStateLM();
spotanglecos = mCos(mDegToRad(sgLight->sgSpotAngle * 0.5f));
spotamountinner = 1.0f - spotanglecos;
spotamountouter = spotanglecos;
}
void sgLightingModelGLBase::sgLightingLM(const Point3F &point, VectorF normal, ColorF &diffuse, ColorF &ambient, Point3F &lightingnormal)
{
sgLightingModel::sgLightingLM(point, normal, diffuse, ambient, lightingnormal);
F32 anglefalloff = 0.0f;
// for speed set on exit - wrong! too often unset...
// do this in lightmap...
//diffuse = ColorF(0.0, 0.0, 0.0);
//ambient = ColorF(0.0, 0.0, 0.0);
//lightingnormal = Point3F(0.0, 0.0, 0.0);
// get the lighting vector squared...
if(sgLight->mType == LightInfo::Vector)
lightingnormal = sgLight->mDirection * -1.0f;
else
lightingnormal = sgLight->mPos - point;
// early out 1 - is surface facing light without double sided ambient (for terrain)?
anglefalloff = mDot(lightingnormal, normal);
if((anglefalloff <= 0.0f) && !(sgLight->sgDoubleSidedAmbient &&
((sgLight->sgLocalAmbientAmount >= SG_LIGHTING_MIN_CUTOFF) ||
(sgLight->mType == LightInfo::Vector))))
{
// do this in lightmap...
//diffuse = ColorF(0.0, 0.0, 0.0);
//ambient = ColorF(0.0, 0.0, 0.0);
//lightingnormal = Point3F(0.0, 0.0, 0.0);
return;
}
F32 dist = 0.0f;
F32 distsq = 0.0f;
F32 distfalloff = 1.0f;
F32 spotlightangle = 1.0f;
// so far so good - now get the dist falloff...
if(sgLight->mType != LightInfo::Vector)
{
bool didsqrt = false;
// need this...
distsq = lightingnormal.lenSquared();
// only need now for linear...
if(sgLinearAttenuation > 0.0f)
{
dist = mSqrt(distsq);
didsqrt = true;
}
distfalloff = (sgConstantAttenuation + (sgLinearAttenuation * dist) + (sgQuadraticAttenuation * distsq));
if(distfalloff > 0.0f)
distfalloff = 1.0f / distfalloff;
distfalloff = getMax(distfalloff, 0.0f);
// early out 2 - too far away?
if(distfalloff <= SG_LIGHTING_MIN_CUTOFF)
{
// do this in lightmap...
//diffuse = ColorF(0.0, 0.0, 0.0);
//ambient = ColorF(0.0, 0.0, 0.0);
//lightingnormal = Point3F(0.0, 0.0, 0.0);
return;
}
// get it now...
if(!didsqrt)
dist = mSqrt(distsq);
// normalize...
if(dist != 0.0f)
lightingnormal /= dist;
// safe to bailout here, ambient only follows spot...
if(sgLight->mType == LightInfo::SGStaticSpot)
{
spotlightangle = mDot(lightingnormal, sgLight->mDirection);
if(spotlightangle < spotanglecos)
{
//diffuse = ColorF(0.0, 0.0, 0.0);
//ambient = ColorF(0.0, 0.0, 0.0);
//lightingnormal = Point3F(0.0, 0.0, 0.0);
return;
}
}
}
// values coming from terrain are not normalized...
// lightingnormal is already normalized...
if((anglefalloff > 0.0f) && sgLight->sgUseNormals)
{
normal.normalize();
anglefalloff = mDot(lightingnormal, normal);
}
anglefalloff = getMax(getMin(anglefalloff, 1.0f), 0.0f);
if(sgLight->mType == LightInfo::SGStaticSpot)
{
// apply the spotlight lighting model...
spotlightangle = mClampF(((spotlightangle - spotamountouter) / spotamountinner), 0.0f, 1.0f);
if(sgLight->sgSmoothSpotLight)
spotlightangle *= getMin((spotlightangle * 1.2f), 1.0f);
}
if((sgLight->mType == LightInfo::Vector) && (anglefalloff < 0.0f))
anglefalloff = anglefalloff;
F32 ambientlightingamount = distfalloff * spotlightangle;
if(anglefalloff > 0.0f)
{
F32 diffuselightingamount = ambientlightingamount;
if(sgLight->sgUseNormals)
diffuselightingamount *= anglefalloff;
diffuselightingamount *= (1 - sgLight->sgLocalAmbientAmount);
diffuselightingamount = getMax(getMin(diffuselightingamount, 1.0f), 0.0f);
diffuse = sgLight->mColor * diffuselightingamount;
}
if(sgLight->sgDoubleSidedAmbient || (anglefalloff > 0.0f))
{
if(sgLight->mType == LightInfo::Vector)
{
ambient = sgLight->mAmbient;
}
else
{
ambientlightingamount *= sgLight->sgLocalAmbientAmount;
ambientlightingamount = getMax(getMin(ambientlightingamount, 1.0f), 0.0f);
ambient = sgLight->mColor * ambientlightingamount;
}
}
/*Point3F lightvectorsq;
Point3F lightvector;
Point3F lightvectorfordist;
F32 spotlightingangle;
//F32 anglefalloff = 0;
F32 dist;
F32 distsq;
// for speed set on exit...
diffuse = ColorF(0.0, 0.0, 0.0);
ambient = ColorF(0.0, 0.0, 0.0);
lightingnormal = Point3F(0.0, 0.0, 0.0);
// get light vector...
if(sgLight->mType == LightInfo::Vector)
lightvector = sgLight->mDirection * -1;
else
{
lightvector = sgLight->mPos - point;
// need this later...
lightvectorfordist = lightvector;
// vector lights are already normallized...
lightvector.normalize();
}
lightingnormal = lightvector;
// is it a spotlight? is it pointing this way?
if(sgLight->mType == LightInfo::SGStaticSpot)
{
spotlightingangle = mDot(lightvector, sgLight->mDirection);
if(spotlightingangle < spotanglecos)
return;
}
else
{
// default...
spotlightingangle = 1.0;
}
// get the attenuation info...
if(sgLight->mType != LightInfo::Vector)
{
if(sgQuadraticAttenuation > 0.0f)
distsq = lightvectorfordist.lenSquared();
// should never use linear...
if(sgLinearAttenuation > 0.0f)
dist = lightvectorfordist.len();
}
// get the lighting angle amount...
F32 angleamount = mDot(lightvector, normal);
F32 lightamount = angleamount;
F32 lightfalloff = 1.0;
lightamount = getMax(getMin(lightamount, 1.0f), 0.0f);
if(sgLight->mType != LightInfo::Vector)
{
// ahhh the lighting model...
lightfalloff = (sgConstantAttenuation + (sgLinearAttenuation * dist) + (sgQuadraticAttenuation * distsq));
if(lightfalloff != 0.0f)
lightfalloff = 1 / lightfalloff;
lightfalloff = getMax(lightfalloff, 0.0f);
if(sgLight->mType == LightInfo::SGStaticSpot)
{
// apply the spotlight lighting model...
spotlightingangle = mClampF(((spotlightingangle - spotamountouter) / spotamountinner), 0.0f, 1.0f);
if(sgLight->sgLightInfoData.sgSmoothSpotLight)
spotlightingangle *= getMin((spotlightingangle * 1.2), 1.0);
lightfalloff *= spotlightingangle;
}
lightamount *= lightfalloff;
}
lightamount *= (1 - sgLight->sgLightInfoData.sgLocalAmbientAmount);
lightamount = getMax(getMin(lightamount, 1.0f), 0.0f);
diffuse = sgLight->mColor * lightamount;
if(sgLight->sgLightInfoData.sgDoubleSidedAmbient || (angleamount > 0.0f))
{
lightfalloff *= sgLight->sgLightInfoData.sgLocalAmbientAmount;
lightfalloff = getMax(getMin(lightfalloff, 1.0f), 0.0f);
ambient = sgLight->mColor * lightfalloff;
}*/
}