//----------------------------------------------- // 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 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; }*/ }