More formatting, OpenCS cells are unbroken

pull/593/head
glassmancody.info 4 years ago
parent 142c6d2993
commit 690995988b

@ -83,7 +83,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
defines["clamp"] = "1"; // Clamp lighting defines["clamp"] = "1"; // Clamp lighting
defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind
defines["radialFog"] = "0"; defines["radialFog"] = "0";
defines["ffpLighting"] = "0"; defines["ffpLighting"] = "1";
for (const auto& define : shadowDefines) for (const auto& define : shadowDefines)
defines[define.first] = define.second; defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);

@ -199,8 +199,8 @@ namespace MWRender
, mFieldOfViewOverridden(false) , mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f) , mFieldOfViewOverride(0.f)
{ {
auto lightingModelString = Settings::Manager::getString("lighting method", "Shaders"); auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
bool usingFFPLighting = lightingModelString == "legacy" && SceneUtil::LightManager::isValidLightingModelString(lightingModelString); bool usingFFPLighting = lightingMethod == SceneUtil::LightingMethod::FFP;
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
@ -217,9 +217,9 @@ namespace MWRender
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1); resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1);
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(!forceShaders || usingFFPLighting); osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(!forceShaders || usingFFPLighting);
// Let LightManager choose which backend to use based, mostly depends on support for UBOs // Let LightManager choose which backend to use based on our hint, mostly depends on support for UBOs
resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod()); resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod()); resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
@ -262,7 +262,7 @@ namespace MWRender
float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93; float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93;
globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f); globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f);
globalDefines["groundcoverFadeEnd"] = std::to_string(groundcoverDistance); globalDefines["groundcoverFadeEnd"] = std::to_string(groundcoverDistance);
// It is unnecessary to stop/start the viewer as no frames are being rendered yet. // It is unnecessary to stop/start the viewer as no frames are being rendered yet.
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines); mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);

@ -14,8 +14,6 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include "apps/openmw/mwrender/vismask.hpp"
namespace namespace
{ {
/* similar to the boost::hash_combine */ /* similar to the boost::hash_combine */
@ -28,7 +26,8 @@ namespace
bool sortLights(const SceneUtil::LightManager::LightSourceViewBound* left, const SceneUtil::LightManager::LightSourceViewBound* right) bool sortLights(const SceneUtil::LightManager::LightSourceViewBound* left, const SceneUtil::LightManager::LightSourceViewBound* right)
{ {
return left->mViewBound.center().length2() - left->mViewBound.radius2()*81 < right->mViewBound.center().length2() - right->mViewBound.radius2()*81; static auto constexpr illuminationBias = 81.f;
return left->mViewBound.center().length2() - left->mViewBound.radius2()*illuminationBias < right->mViewBound.center().length2() - right->mViewBound.radius2()*illuminationBias;
} }
float getLightRadius(const osg::Light* light) float getLightRadius(const osg::Light* light)
@ -99,8 +98,15 @@ namespace SceneUtil
return (*mData)[3*index+1]; return (*mData)[3*index+1];
} }
auto& getData() { return mData; } auto& getData()
void dirty() { mData->dirty(); } {
return mData;
}
void dirty()
{
mData->dirty();
}
static constexpr int queryBlockSize(int sz) static constexpr int queryBlockSize(int sz)
{ {
@ -135,6 +141,7 @@ namespace SceneUtil
{ {
switch (method) switch (method)
{ {
case LightingMethod::Undefined:
case LightingMethod::FFP: case LightingMethod::FFP:
{ {
break; break;
@ -232,7 +239,7 @@ namespace SceneUtil
bool getModeUsage(ModeUsage & usage) const override bool getModeUsage(ModeUsage & usage) const override
{ {
for (size_t i=0; i<mLights.size(); ++i) for (size_t i = 0; i < mLights.size(); ++i)
usage.usesMode(GL_LIGHT0 + mIndex + i); usage.usesMode(GL_LIGHT0 + mIndex + i);
return true; return true;
} }
@ -254,7 +261,7 @@ namespace SceneUtil
LightStateCache* cache = getLightStateCache(state.getContextID()); LightStateCache* cache = getLightStateCache(state.getContextID());
for (size_t i=0; i<mLights.size(); ++i) for (size_t i = 0; i < mLights.size(); ++i)
{ {
osg::Light* current = cache->lastAppliedLight[i+mIndex]; osg::Light* current = cache->lastAppliedLight[i+mIndex];
if (current != mLights[i].get()) if (current != mLights[i].get())
@ -269,29 +276,28 @@ namespace SceneUtil
void applyLight(GLenum lightNum, const osg::Light* light) const void applyLight(GLenum lightNum, const osg::Light* light) const
{ {
glLightfv( lightNum, GL_AMBIENT, light->getAmbient().ptr() ); glLightfv(lightNum, GL_AMBIENT, light->getAmbient().ptr());
glLightfv( lightNum, GL_DIFFUSE, light->getDiffuse().ptr() ); glLightfv(lightNum, GL_DIFFUSE, light->getDiffuse().ptr());
glLightfv( lightNum, GL_SPECULAR, light->getSpecular().ptr() ); glLightfv(lightNum, GL_SPECULAR, light->getSpecular().ptr());
glLightfv( lightNum, GL_POSITION, light->getPosition().ptr() ); glLightfv(lightNum, GL_POSITION, light->getPosition().ptr());
// TODO: enable this once spot lights are supported // TODO: enable this once spot lights are supported
// need to transform SPOT_DIRECTION by the world matrix? // need to transform SPOT_DIRECTION by the world matrix?
//glLightfv( lightNum, GL_SPOT_DIRECTION, light->getDirection().ptr() ); //glLightfv(lightNum, GL_SPOT_DIRECTION, light->getDirection().ptr());
//glLightf ( lightNum, GL_SPOT_EXPONENT, light->getSpotExponent() ); //glLightf(lightNum, GL_SPOT_EXPONENT, light->getSpotExponent());
//glLightf ( lightNum, GL_SPOT_CUTOFF, light->getSpotCutoff() ); //glLightf(lightNum, GL_SPOT_CUTOFF, light->getSpotCutoff());
glLightf ( lightNum, GL_CONSTANT_ATTENUATION, light->getConstantAttenuation() ); glLightf(lightNum, GL_CONSTANT_ATTENUATION, light->getConstantAttenuation());
glLightf ( lightNum, GL_LINEAR_ATTENUATION, light->getLinearAttenuation() ); glLightf(lightNum, GL_LINEAR_ATTENUATION, light->getLinearAttenuation());
glLightf ( lightNum, GL_QUADRATIC_ATTENUATION, light->getQuadraticAttenuation() ); glLightf(lightNum, GL_QUADRATIC_ATTENUATION, light->getQuadraticAttenuation());
} }
private: private:
size_t mIndex; size_t mIndex;
std::vector<osg::ref_ptr<osg::Light>> mLights; std::vector<osg::ref_ptr<osg::Light>> mLights;
}; };
LightManager* findLightManager(const osg::NodePath& path) LightManager* findLightManager(const osg::NodePath& path)
{ {
for (size_t i=0;i<path.size(); ++i) for (size_t i = 0; i < path.size(); ++i)
{ {
if (LightManager* lightManager = dynamic_cast<LightManager*>(path[i])) if (LightManager* lightManager = dynamic_cast<LightManager*>(path[i]))
return lightManager; return lightManager;
@ -406,8 +412,8 @@ namespace SceneUtil
return stateset; return stateset;
} }
// Cached statesets must be re-validated in case the light indicies change. There is no actual link between // Cached statesets must be revalidated in case the light indices change. There is no actual link between
// a lights ID and the buffer index it will eventually be assigned (or reassigned) to. // a light's ID and the buffer index it will eventually be assigned (or reassigned) to.
void update(osg::StateSet* stateset, const LightManager::LightList& lightList, size_t frameNum) override void update(osg::StateSet* stateset, const LightManager::LightList& lightList, size_t frameNum) override
{ {
int newCount = 0; int newCount = 0;
@ -587,104 +593,88 @@ namespace SceneUtil
LightManager* mLightManager; LightManager* mLightManager;
}; };
const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = {
{"legacy", LightingMethod::FFP}
,{"shaders compatibility", LightingMethod::PerObjectUniform}
,{"shaders", LightingMethod::SingleUBO}
};
bool LightManager::isValidLightingModelString(const std::string& value) bool LightManager::isValidLightingModelString(const std::string& value)
{ {
static const std::unordered_set<std::string> validLightingModels = {"legacy", "default", "experimental"}; return LightManager::mLightingMethodSettingMap.find(value) != LightManager::mLightingMethodSettingMap.end();
return validLightingModels.count(value) != 0; }
LightingMethod LightManager::getLightingMethodFromString(const std::string& value)
{
auto it = LightManager::mLightingMethodSettingMap.find(value);
if (it != LightManager::mLightingMethodSettingMap.end())
return it->second;
else
return LightingMethod::Undefined;
} }
LightManager::LightManager(bool ffp) LightManager::LightManager(bool ffp)
: mStartLight(0) : mStartLight(0)
, mLightingMask(~0u) , mLightingMask(~0u)
, mSun(nullptr) , mSun(nullptr)
, mPointLightRadiusMultiplier(std::clamp(Settings::Manager::getFloat("light bounds multiplier", "Shaders"), 0.f, 10.f)) , mPointLightRadiusMultiplier(1.f)
, mPointLightFadeEnd(0.f)
, mPointLightFadeStart(0.f) , mPointLightFadeStart(0.f)
{ {
mPointLightFadeEnd = std::max(0.f, Settings::Manager::getFloat("maximum light distance", "Shaders")); if (ffp)
if (mPointLightFadeEnd > 0)
{ {
mPointLightFadeStart = std::clamp(Settings::Manager::getFloat("light fade start", "Shaders"), 0.f, 1.f); setLightingMethod(LightingMethod::FFP);
mPointLightFadeStart = mPointLightFadeEnd * mPointLightFadeStart; initFFP(LightManager::mFFPMaxLights);
return;
} }
auto lightingModelString = Settings::Manager::getString("lighting method", "Shaders"); std::string lightingMethodString = Settings::Manager::getString("lighting method", "Shaders");
bool validLightingModel = isValidLightingModelString(lightingModelString); auto lightingMethod = LightManager::getLightingMethodFromString(lightingMethodString);
if (!validLightingModel) if (lightingMethod == LightingMethod::Undefined)
Log(Debug::Error) << "Invalid option for 'lighting model': got '" << lightingModelString
<< "', expected legacy, default, or experimental.";
if (ffp || !validLightingModel)
{ {
setMaxLights(LightManager::mFFPMaxLights); Log(Debug::Error) << "Invalid option for 'lighting method': got '" << lightingMethodString
setLightingMethod(LightingMethod::FFP); << "', expected legacy, shaders compatible, or shaders. Falling back to 'shaders compatible'.";
setLightingMethod(LightingMethod::PerObjectUniform);
}
else
{
setLightingMethod(lightingMethod);
}
for (int i=0; i<getMaxLights(); ++i) mPointLightRadiusMultiplier = std::clamp(Settings::Manager::getFloat("light bounds multiplier", "Shaders"), 0.f, 10.f);
mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light> >()));
setUpdateCallback(new LightManagerUpdateCallback); mPointLightFadeEnd = std::max(0.f, Settings::Manager::getFloat("maximum light distance", "Shaders"));
return; if (mPointLightFadeEnd > 0)
{
mPointLightFadeStart = std::clamp(Settings::Manager::getFloat("light fade start", "Shaders"), 0.f, 1.f);
mPointLightFadeStart = mPointLightFadeEnd * mPointLightFadeStart;
} }
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false); osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
bool supportsUBO = exts && exts->isUniformBufferObjectSupported; bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
bool supportsGPU4 = exts && exts->isGpuShader4Supported; bool supportsGPU4 = exts && exts->isGpuShader4Supported;
if (!supportsUBO) if (getLightingMethod() == LightingMethod::SingleUBO)
Log(Debug::Info) << "GL_ARB_uniform_buffer_object not supported: using fallback uniforms"; {
else if (!supportsGPU4) if (!supportsUBO)
Log(Debug::Info) << "GL_EXT_gpu_shader4 not supported: using fallback uniforms"; Log(Debug::Info) << "GL_ARB_uniform_buffer_object not supported: using fallback uniforms";
else if (!supportsGPU4)
Log(Debug::Info) << "GL_EXT_gpu_shader4 not supported: using fallback uniforms";
}
int targetLights = Settings::Manager::getInt("max lights", "Shaders"); int targetLights = Settings::Manager::getInt("max lights", "Shaders");
auto* stateset = getOrCreateStateSet();
if (!supportsUBO || !supportsGPU4 || lightingModelString == "default") if (!supportsUBO || !supportsGPU4 || getLightingMethod() == LightingMethod::PerObjectUniform)
{ {
setLightingMethod(LightingMethod::PerObjectUniform); setLightingMethod(LightingMethod::PerObjectUniform);
setMaxLights(std::max(2, targetLights)); initPerObjectUniform(targetLights);
mLightUniforms.resize(getMaxLights()+1);
for (size_t i = 0; i < mLightUniforms.size(); ++i)
{
osg::ref_ptr<osg::Uniform> udiffuse = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].diffuse").c_str());
osg::ref_ptr<osg::Uniform> uspecular = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].specular").c_str());
osg::ref_ptr<osg::Uniform> uambient = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].ambient").c_str());
osg::ref_ptr<osg::Uniform> uposition = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].position").c_str());
osg::ref_ptr<osg::Uniform> uattenuation = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].attenuation").c_str());
mLightUniforms[i].emplace(UniformKey::Diffuse, udiffuse);
mLightUniforms[i].emplace(UniformKey::Ambient, uambient);
mLightUniforms[i].emplace(UniformKey::Specular, uspecular);
mLightUniforms[i].emplace(UniformKey::Position, uposition);
mLightUniforms[i].emplace(UniformKey::Attenuation, uattenuation);
stateset->addUniform(udiffuse);
stateset->addUniform(uambient);
stateset->addUniform(uposition);
stateset->addUniform(uattenuation);
// specular isn't used besides sun, complete waste to upload it
if (i == 0)
stateset->addUniform(uspecular);
}
} }
else else
{ {
setLightingMethod(LightingMethod::SingleUBO); initSingleUBO(targetLights);
setMaxLights(std::clamp(targetLights, 2, getMaxLightsInScene() / 2));
for (int i = 0; i < 2; ++i)
{
mLightBuffers[i] = new LightBuffer(getMaxLightsInScene());
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
ubo->setUsage(GL_STREAM_DRAW);
mLightBuffers[i]->getData()->setBufferObject(ubo);
}
stateset->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON);
} }
stateset->addUniform(new osg::Uniform("PointLightCount", 0)); getOrCreateStateSet()->addUniform(new osg::Uniform("PointLightCount", 0));
setUpdateCallback(new LightManagerUpdateCallback); setUpdateCallback(new LightManagerUpdateCallback);
addCullCallback(new LightManagerCullCallback(this)); addCullCallback(new LightManagerCullCallback(this));
@ -726,7 +716,7 @@ namespace SceneUtil
int LightManager::getMaxLightsInScene() const int LightManager::getMaxLightsInScene() const
{ {
static constexpr int max = 16384 / LightBuffer::queryBlockSize(1); static constexpr int max = 16384 / LightBuffer::queryBlockSize(1);
return max; return max;
} }
Shader::ShaderManager::DefineMap LightManager::getLightDefines() const Shader::ShaderManager::DefineMap LightManager::getLightDefines() const
@ -746,6 +736,66 @@ namespace SceneUtil
return defines; return defines;
} }
void LightManager::initFFP(int targetLights)
{
setMaxLights(targetLights);
for (int i = 0; i < getMaxLights(); ++i)
mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light>>()));
setUpdateCallback(new LightManagerUpdateCallback);
}
void LightManager::initPerObjectUniform(int targetLights)
{
auto* stateset = getOrCreateStateSet();
setMaxLights(std::max(2, targetLights));
mLightUniforms.resize(getMaxLights()+1);
for (size_t i = 0; i < mLightUniforms.size(); ++i)
{
osg::ref_ptr<osg::Uniform> udiffuse = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].diffuse").c_str());
osg::ref_ptr<osg::Uniform> uspecular = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].specular").c_str());
osg::ref_ptr<osg::Uniform> uambient = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].ambient").c_str());
osg::ref_ptr<osg::Uniform> uposition = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].position").c_str());
osg::ref_ptr<osg::Uniform> uattenuation = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].attenuation").c_str());
mLightUniforms[i].emplace(UniformKey::Diffuse, udiffuse);
mLightUniforms[i].emplace(UniformKey::Ambient, uambient);
mLightUniforms[i].emplace(UniformKey::Specular, uspecular);
mLightUniforms[i].emplace(UniformKey::Position, uposition);
mLightUniforms[i].emplace(UniformKey::Attenuation, uattenuation);
stateset->addUniform(udiffuse);
stateset->addUniform(uambient);
stateset->addUniform(uposition);
stateset->addUniform(uattenuation);
// specular isn't used besides sun, complete waste to upload it
if (i == 0)
stateset->addUniform(uspecular);
}
}
void LightManager::initSingleUBO(int targetLights)
{
setMaxLights(std::clamp(targetLights, 2, getMaxLightsInScene() / 2));
for (int i = 0; i < 2; ++i)
{
mLightBuffers[i] = new LightBuffer(getMaxLightsInScene());
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
ubo->setUsage(GL_STREAM_DRAW);
mLightBuffers[i]->getData()->setBufferObject(ubo);
}
getOrCreateStateSet()->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON);
}
void LightManager::setLightingMethod(LightingMethod method) void LightManager::setLightingMethod(LightingMethod method)
{ {
mLightingMethod = method; mLightingMethod = method;
@ -760,6 +810,9 @@ namespace SceneUtil
case LightingMethod::PerObjectUniform: case LightingMethod::PerObjectUniform:
mStateSetGenerator = std::make_unique<StateSetGeneratorPerObjectUniform>(); mStateSetGenerator = std::make_unique<StateSetGeneratorPerObjectUniform>();
break; break;
case LightingMethod::Undefined:
mStateSetGenerator = nullptr;
break;
} }
mStateSetGenerator->mLightManager = this; mStateSetGenerator->mLightManager = this;
} }
@ -783,7 +836,7 @@ namespace SceneUtil
// Set default light state to zero // Set default light state to zero
// This is necessary because shaders don't respect glDisable(GL_LIGHTX) so in addition to disabling // This is necessary because shaders don't respect glDisable(GL_LIGHTX) so in addition to disabling
// we'll have to set a light state that has no visible effect // we'll have to set a light state that has no visible effect
for (int i=start; i<getMaxLights(); ++i) for (int i = start; i < getMaxLights(); ++i)
{ {
osg::ref_ptr<DisableLight> defaultLight (new DisableLight(i)); osg::ref_ptr<DisableLight> defaultLight (new DisableLight(i));
getOrCreateStateSet()->setAttributeAndModes(defaultLight, osg::StateAttribute::OFF); getOrCreateStateSet()->setAttributeAndModes(defaultLight, osg::StateAttribute::OFF);
@ -802,7 +855,7 @@ namespace SceneUtil
mLightsInViewSpace.clear(); mLightsInViewSpace.clear();
// Do an occasional cleanup for orphaned lights. // Do an occasional cleanup for orphaned lights.
for (int i=0; i<2; ++i) for (int i = 0; i < 2; ++i)
{ {
if (mStateSetCache[i].size() > 5000) if (mStateSetCache[i].size() > 5000)
mStateSetCache[i].clear(); mStateSetCache[i].clear();
@ -836,7 +889,7 @@ namespace SceneUtil
{ {
// possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists) // possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists)
size_t hash = 0; size_t hash = 0;
for (size_t i=0; i<lightList.size();++i) for (size_t i = 0; i < lightList.size(); ++i)
{ {
auto id = lightList[i]->mLightSource->getId(); auto id = lightList[i]->mLightSource->getId();
hash_combine(hash, id); hash_combine(hash, id);
@ -860,13 +913,10 @@ namespace SceneUtil
mStateSetGenerator->update(found->second, lightList, frameNum); mStateSetGenerator->update(found->second, lightList, frameNum);
return found->second; return found->second;
} }
else
{ auto stateset = mStateSetGenerator->generate(lightList, frameNum);
auto stateset = mStateSetGenerator->generate(lightList, frameNum); stateSetCache.emplace(hash, stateset);
stateSetCache.emplace(hash, stateset); return stateset;
return stateset;
}
return new osg::StateSet;
} }
const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum) const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum)
@ -946,7 +996,7 @@ namespace SceneUtil
{ {
mId = sLightId++; mId = sLightId++;
for (int i=0; i<2; ++i) for (int i = 0; i < 2; ++i)
mLight[i] = new osg::Light(*copy.mLight[i].get(), copyop); mLight[i] = new osg::Light(*copy.mLight[i].get(), copyop);
} }
@ -981,7 +1031,6 @@ namespace SceneUtil
// makes sure we don't update it more than once per frame when rendering with multiple cameras // makes sure we don't update it more than once per frame when rendering with multiple cameras
if (mLastFrameNumber != cv->getTraversalNumber()) if (mLastFrameNumber != cv->getTraversalNumber())
{ {
mLastFrameNumber = cv->getTraversalNumber(); mLastFrameNumber = cv->getTraversalNumber();
// Don't use Camera::getViewMatrix, that one might be relative to another camera! // Don't use Camera::getViewMatrix, that one might be relative to another camera!
@ -994,7 +1043,7 @@ namespace SceneUtil
osg::Transform* transform = node->asTransform(); osg::Transform* transform = node->asTransform();
if (transform) if (transform)
{ {
for (size_t i=0; i<transform->getNumChildren(); ++i) for (size_t i = 0; i < transform->getNumChildren(); ++i)
nodeBound.expandBy(transform->getChild(i)->getBound()); nodeBound.expandBy(transform->getChild(i)->getBound());
} }
else else
@ -1003,7 +1052,7 @@ namespace SceneUtil
transformBoundingSphere(mat, nodeBound); transformBoundingSphere(mat, nodeBound);
mLightList.clear(); mLightList.clear();
for (size_t i=0; i<lights.size(); ++i) for (size_t i = 0; i < lights.size(); ++i)
{ {
const LightManager::LightSourceViewBound& l = lights[i]; const LightManager::LightSourceViewBound& l = lights[i];
@ -1014,7 +1063,6 @@ namespace SceneUtil
mLightList.push_back(&l); mLightList.push_back(&l);
} }
} }
if (!mLightList.empty()) if (!mLightList.empty())
{ {
size_t maxLights = mLightManager->getMaxLights() - mLightManager->getStartLight(); size_t maxLights = mLightManager->getMaxLights() - mLightManager->getStartLight();
@ -1025,7 +1073,7 @@ namespace SceneUtil
{ {
// remove lights culled by this camera // remove lights culled by this camera
LightManager::LightList lightList = mLightList; LightManager::LightList lightList = mLightList;
for (auto it = lightList.begin(); it != lightList.end() && lightList.size() > maxLights; ) for (auto it = lightList.begin(); it != lightList.end() && lightList.size() > maxLights;)
{ {
osg::CullStack::CullingStack& stack = cv->getModelViewCullingStack(); osg::CullStack::CullingStack& stack = cv->getModelViewCullingStack();
@ -1053,6 +1101,7 @@ namespace SceneUtil
else else
stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber(), cv->getCurrentRenderStage()->getInitialViewMatrix()); stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber(), cv->getCurrentRenderStage()->getInitialViewMatrix());
cv->pushStateSet(stateset); cv->pushStateSet(stateset);
return true; return true;
} }

@ -2,12 +2,12 @@
#define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H #define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H
#include <set> #include <set>
#include <cassert>
#include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <unordered_map>
#include <memory> #include <memory>
#include <osg/Light> #include <osg/Light>
#include <osg/Group> #include <osg/Group>
#include <osg/NodeVisitor> #include <osg/NodeVisitor>
#include <osg/observer_ptr> #include <osg/observer_ptr>
@ -18,6 +18,7 @@ namespace osgUtil
{ {
class CullVisitor; class CullVisitor;
} }
namespace SceneUtil namespace SceneUtil
{ {
class LightBuffer; class LightBuffer;
@ -27,7 +28,8 @@ namespace SceneUtil
{ {
FFP, FFP,
SingleUBO, SingleUBO,
PerObjectUniform PerObjectUniform,
Undefined
}; };
void configureStateSetSunOverride(LightingMethod method, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); void configureStateSetSunOverride(LightingMethod method, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
@ -98,8 +100,8 @@ namespace SceneUtil
class LightManager : public osg::Group class LightManager : public osg::Group
{ {
public: public:
static bool isValidLightingModelString(const std::string& value); static bool isValidLightingModelString(const std::string& value);
static LightingMethod getLightingMethodFromString(const std::string& value);
enum class UniformKey enum class UniformKey
{ {
@ -136,7 +138,7 @@ namespace SceneUtil
/// By default, it's ~0u i.e. always on. /// By default, it's ~0u i.e. always on.
/// If you have some views that do not require lighting, then set the Camera's cull mask to not include /// If you have some views that do not require lighting, then set the Camera's cull mask to not include
/// the lightingMask for a much faster cull and rendering. /// the lightingMask for a much faster cull and rendering.
void setLightingMask (size_t mask); void setLightingMask(size_t mask);
size_t getLightingMask() const; size_t getLightingMask() const;
/// Set the first light index that should be used by this manager, typically the number of directional lights in the scene. /// Set the first light index that should be used by this manager, typically the number of directional lights in the scene.
@ -167,7 +169,7 @@ namespace SceneUtil
auto& getDummies() { return mDummies; } auto& getDummies() { return mDummies; }
auto& getLightIndexMap(size_t frameNum) { return mLightIndexMaps[frameNum%2]; } auto& getLightIndexMap(size_t frameNum) { return mLightIndexMaps[frameNum%2]; }
auto& getLightBuffer(size_t frameNum) { return mLightBuffers[frameNum%2]; } auto& getLightBuffer(size_t frameNum) { return mLightBuffers[frameNum%2]; }
auto& getLightUniform(int index, UniformKey key) { return mLightUniforms[index][key]; } auto& getLightUniform(int index, UniformKey key) { return mLightUniforms[index][key]; }
@ -175,10 +177,13 @@ namespace SceneUtil
std::map<std::string, std::string> getLightDefines() const; std::map<std::string, std::string> getLightDefines() const;
private: private:
friend class LightManagerStateAttribute; friend class LightManagerStateAttribute;
friend class LightManagerCullCallback; friend class LightManagerCullCallback;
void initFFP(int targetLights);
void initPerObjectUniform(int targetLights);
void initSingleUBO(int targetLights);
void setLightingMethod(LightingMethod method); void setLightingMethod(LightingMethod method);
void setMaxLights(int value); void setMaxLights(int value);
@ -188,7 +193,7 @@ namespace SceneUtil
using LightSourceViewBoundCollection = std::vector<LightSourceViewBound>; using LightSourceViewBoundCollection = std::vector<LightSourceViewBound>;
std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection> mLightsInViewSpace; std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection> mLightsInViewSpace;
// < Light list hash , StateSet > // < Light list hash , StateSet >
using LightStateSetMap = std::map<size_t, osg::ref_ptr<osg::StateSet>>; using LightStateSetMap = std::map<size_t, osg::ref_ptr<osg::StateSet>>;
LightStateSetMap mStateSetCache[2]; LightStateSetMap mStateSetCache[2];
@ -221,6 +226,8 @@ namespace SceneUtil
int mMaxLights; int mMaxLights;
static constexpr auto mFFPMaxLights = 8; static constexpr auto mFFPMaxLights = 8;
static const std::unordered_map<std::string, LightingMethod> mLightingMethodSettingMap;
}; };
/// To receive lighting, objects must be decorated by a LightListCallback. Light list callbacks must be added via /// To receive lighting, objects must be decorated by a LightListCallback. Light list callbacks must be added via
@ -259,7 +266,8 @@ namespace SceneUtil
size_t mLastFrameNumber; size_t mLastFrameNumber;
LightManager::LightList mLightList; LightManager::LightList mLightList;
std::set<SceneUtil::LightSource*> mIgnoredLightSources; std::set<SceneUtil::LightSource*> mIgnoredLightSources;
}; };
} }
#endif #endif

@ -10,7 +10,6 @@
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
#include <components/sceneutil/lightmanager.hpp> #include <components/sceneutil/lightmanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>

@ -40,7 +40,7 @@ Only affects objects that render with shaders (see 'force shaders' option).
Always affects terrain. Always affects terrain.
Leaving this option at its default makes the lighting compatible with Morrowind's fixed-function method, Leaving this option at its default makes the lighting compatible with Morrowind's fixed-function method,
but the lighting may appear dull and there might be colour shifts. but the lighting may appear dull and there might be colour shifts.
Setting this option to 'false' results in more dynamic lighting. Setting this option to 'false' results in more dynamic lighting.
auto use object normal maps auto use object normal maps
@ -152,32 +152,51 @@ lighting method
--------------- ---------------
:Type: string :Type: string
:Range: legacy|default|experimental :Range: legacy|shaders compatibility|shaders
:Default: default :Default: default
Sets the internal handling of light sources. Sets the internal handling of light sources.
'legacy' is restricted to a maximum of 8 lights per object and guarantees fixed function pipeline compatible lighting. 'legacy' is restricted to 8 lights per object and emulates fixed function
pipeline compatible lighting.
'default' removes the light limit via :ref:`max lights` and follows a new attenuation formula which can drastically reduce light popping and seams. 'shaders compatibility' removes the light limit via :ref:`max lights` and
This mode also enables vertex lighting on groundcover, which is otherwise completely disabled with 'legacy'. follows a modifed attenuation formula which can drastically reduce light popping
It is recommended to use this mode with older hardware, as the technique ensures a range of compatibility equal to that of 'legacy'. and seams. This mode also enables lighting on groundcover and a configurable
light fade. It is recommended to use this with older hardware and a light limit
closer to 8. Because of its wide range of compatibility it is set as the
default.
'experimental' carries all of the benefits that 'legacy' has, but uses a modern approach that allows for a higher 'max lights' count with little to no performance penalties on modern hardware. 'shaders' carries all of the benefits that 'shaders compatibility' does, but
uses a modern approach that allows for a higher :ref:`max lights` count with
little to no performance penalties on modern hardware. It is recommended to use
this mode when supported and where the GPU is not a bottleneck. On some weaker
devices, using this mode along with :ref:`force per pixel lighting` can carry
performance penalties.
Note that when enabled, groundcover lighting is forced to be vertex lighting,
unless normal maps are provided. This is due to some groundcover mods using the
Z-Up Normals technique to avoid some common issues with shading. As a
consequence, per pixel lighting would give undesirable results.
This setting has no effect if :ref:`force shaders` is 'false'.
light bounds multiplier light bounds multiplier
----------------------- -----------------------
:Type: float :Type: float
:Range: 0.0-10.0 :Range: 0.0-10.0
:Default: 2.0 :Default: 1.75
Controls the bounding sphere radius of point lights, which is used to determine if an object should receive lighting from a particular light source. Controls the bounding sphere radius of point lights, which is used to determine
Note, this has no direct effect on the overall illumination of lights. if an object should receive lighting from a particular light source. Note, this
Larger multipliers will allow for smoother transitions of light sources, but may require an increase in :ref:`max lights` and thus carries a performance penalty. has no direct effect on the overall illumination of lights. Larger multipliers
This especially helps with abrupt light popping with handheld light sources such as torches and lanterns. will allow for smoother transitions of light sources, but may require an
increase in :ref:`max lights` and thus carries a performance penalty. This
especially helps with abrupt light popping with handheld light sources such as
torches and lanterns.
It is recommended to keep this at 1.0 if :ref:`lighting method` is set to 'legacy', as the number of lights is fixed in that mode. This setting has no effect if :ref:`lighting method` is 'legacy'.
maximum light distance maximum light distance
---------------------- ----------------------
@ -186,9 +205,11 @@ maximum light distance
:Range: The whole range of 32-bit floating point :Range: The whole range of 32-bit floating point
:Default: 8192 :Default: 8192
The maximum distance from the camera that lights will be illuminated, applies to both interiors and exteriors. The maximum distance from the camera that lights will be illuminated, applies to
A lower distance will improve performance. both interiors and exteriors. A lower distance will improve performance. Set
Set this to a non-positive value to disable fading. this to a non-positive value to disable fading.
This setting has no effect if :ref:`lighting method` is 'legacy'.
light fade start light fade start
---------------- ----------------
@ -199,18 +220,23 @@ light fade start
The fraction of the maximum distance at which lights will begin to fade away. The fraction of the maximum distance at which lights will begin to fade away.
Tweaking it will make the transition proportionally more or less smooth. Tweaking it will make the transition proportionally more or less smooth.
This setting has no effect if the maximum light distance is non-positive.
This setting has no effect if the :ref:`maximum light distance` is non-positive
or :ref:`lighting method` is 'legacy'.
max lights max lights
---------- ----------
:Type: integer :Type: integer
:Range: >=2 :Range: >=2
:Default: 16 :Default: 8
Sets the maximum number of lights that each object can receive lighting from. Sets the maximum number of lights that each object can receive lighting from.
Has no effect if :ref:`force shaders` option is off or :ref:`lighting method` is 'legacy'. In this case the maximum number of lights is fixed at 8. Increasing this too much can cause significant performance loss, especially if
Increasing this too much can cause significant performance loss, especially if :ref:`lighting method` is not set to 'experimental' or :ref:`force per pixel lighting` is on. :ref:`lighting method` is not set to 'shaders' or :ref:`force per pixel
lighting` is on.
This setting has no effect if :ref:`lighting method` is 'legacy'.
antialias alpha test antialias alpha test
--------------------------------------- ---------------------------------------

@ -442,23 +442,32 @@ apply lighting to environment maps = false
# This makes fogging independent from the viewing angle. Shaders will be used to render all objects. # This makes fogging independent from the viewing angle. Shaders will be used to render all objects.
radial fog = false radial fog = false
# Internal handling of lights, values are 'legacy', 'default', 'experimental' # Internal handling of lights, ignored if 'force shaders' is off. "legacy"
lighting method = experimental # provides fixed function pipeline emulation."shaders compatibility" (default)
# uncaps the light limit, enables groundcover lighting, and uses a modified
# Sets the bounding sphere multiplier of light sources, which are used to determine if an object should # attenuation formula to reduce popping and light seams. "shaders" comes with
# receive lighting. Higher values will allow for smoother transitions of light sources, but may have a performance cost and # all these benefits and is meant for larger light limits, but may not be
# requires a higher number of 'max lights' set. It is recommended to keep this at 1.0 with 'legacy' lighting enabled. # supported on older hardware and may be less performant on weaker hardware when
light bounds multiplier = 1.0 # 'force per pixel lighting' is enabled.
lighting method = shaders compatibility
# The distance from the camera at which lights fade away completely. Set to 0 to disable fading.
# Sets the bounding sphere multiplier of light sources, which are used to
# determine if an object should receive lighting. Higher values will allow for
# smoother transitions of light sources, but may have a performance cost and
# requires a higher number of 'max lights' set. It is recommended to keep this
# at 1.0 with 'legacy' lighting enabled.
light bounds multiplier = 1.75
# The distance from the camera at which lights fade away completely.
# Set to 0 to disable fading.
maximum light distance = 8192 maximum light distance = 8192
# Fraction of the maximum distance at which lights begin to gradually fade away. # Fraction of the maximum distance at which lights begin to gradually fade away.
light fade start = 0.85 light fade start = 0.85
# Set maximum number of lights per object. # Set maximum number of lights per object.
# Only used when 'lighting method' is not set to 'legacy' # When 'lighting method' is set to 'legacy', this setting will have no effect.
max lights = 16 max lights = 8
# Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage. # Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage.
# This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. # This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation.

@ -69,7 +69,7 @@ uniform int PointLightCount;
#endif #endif
void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 viewNormal) void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 viewNormal)
{ {
vec3 lightDir = normalize(getLight[0].position.xyz); vec3 lightDir = normalize(getLight[0].position.xyz);
#if @lightingModel == LIGHTING_MODEL_SINGLE_UBO #if @lightingModel == LIGHTING_MODEL_SINGLE_UBO
@ -98,9 +98,9 @@ void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 vi
void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal) void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)
{ {
vec3 lightDir = getLight[lightIndex].position.xyz - viewPos; vec3 lightPos = getLight[lightIndex].position.xyz - viewPos;
float lightDistance = length(lightDir); float lightDistance = length(lightPos);
#if !@ffpLighting #if !@ffpLighting
// This has a *considerable* performance uplift where GPU is a bottleneck // This has a *considerable* performance uplift where GPU is a bottleneck
@ -112,7 +112,7 @@ void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec
} }
#endif #endif
lightDir = normalize(lightDir); lightPos = normalize(lightPos);
#if @ffpLighting #if @ffpLighting
float illumination = clamp(1.0 / (getLight[lightIndex].constantAttenuation + getLight[lightIndex].linearAttenuation * lightDistance + getLight[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0); float illumination = clamp(1.0 / (getLight[lightIndex].constantAttenuation + getLight[lightIndex].linearAttenuation * lightDistance + getLight[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0);
@ -128,8 +128,8 @@ void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec
ambientOut = getLight[lightIndex].ambient.xyz * illumination; ambientOut = getLight[lightIndex].ambient.xyz * illumination;
#endif #endif
float lambert = dot(viewNormal.xyz, lightDir) * illumination; float lambert = dot(viewNormal.xyz, lightPos) * illumination;
#ifndef GROUNDCOVER #ifndef GROUNDCOVER
lambert = max(lambert, 0.0); lambert = max(lambert, 0.0);
#else #else
@ -179,7 +179,7 @@ void doLighting(vec3 viewPos, vec3 viewNormal, out vec3 diffuseLight, out vec3 a
for (int i=1; i <= PointLightCount; ++i) for (int i=1; i <= PointLightCount; ++i)
{ {
perLightPoint(ambientOut, diffuseOut, i, viewPos, viewNormal); perLightPoint(ambientOut, diffuseOut, i, viewPos, viewNormal);
#else #else
for (int i=0; i < PointLightCount; ++i) for (int i=0; i < PointLightCount; ++i)
{ {
perLightPoint(ambientOut, diffuseOut, PointLightIndex[i], viewPos, viewNormal); perLightPoint(ambientOut, diffuseOut, PointLightIndex[i], viewPos, viewNormal);

@ -151,6 +151,8 @@ uniform vec3 nodePosition;
uniform float rainIntensity; uniform float rainIntensity;
#define PER_PIXEL_LIGHTING 0
#include "shadows_fragment.glsl" #include "shadows_fragment.glsl"
#include "lighting.glsl" #include "lighting.glsl"

Loading…
Cancel
Save