mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-19 22:53:53 +00:00
0edc8fc77d
This more-or-less gets rid of the shadow system's only depencency on FFP stuff. All that remains is it using OSG cameras, which OSG provides a uniform-based implementation of, too, which we can trivially migrate to. This should mean we're not eating any of the ~8 FPP-friendly texture units, which is good as Morrowind models can use all of those on their (although they very rarely do), and instead use some of the ~160 shader-only texture image units. This just requires not calling glEnable(GL_TEXTURE_2D), accomplished by changing setTextureAttributeAndModes to setTextureAttribute. Also changes from using glTexGen and its eye plane matrices to pass the shadow space matrix for each light to explicit uniforms. Thankfully, the maths was a simple combination of the valid region matrix and eye plane matrix maths. As of this commit, I believe this kills shadows in one eye for stereo rendering.
211 lines
8.8 KiB
C++
211 lines
8.8 KiB
C++
#include "shadow.hpp"
|
|
|
|
#include <osgShadow/ShadowSettings>
|
|
#include <osgShadow/ShadowedScene>
|
|
|
|
#include <components/misc/strings/algorithm.hpp>
|
|
#include <components/settings/settings.hpp>
|
|
#include <components/stereo/stereomanager.hpp>
|
|
|
|
#include "mwshadowtechnique.hpp"
|
|
|
|
namespace SceneUtil
|
|
{
|
|
using namespace osgShadow;
|
|
|
|
void ShadowManager::setupShadowSettings()
|
|
{
|
|
mEnableShadows = Settings::Manager::getBool("enable shadows", "Shadows");
|
|
|
|
if (!mEnableShadows)
|
|
{
|
|
mShadowTechnique->disableShadows();
|
|
return;
|
|
}
|
|
|
|
mShadowTechnique->enableShadows();
|
|
|
|
mShadowSettings->setLightNum(0);
|
|
mShadowSettings->setReceivesShadowTraversalMask(~0u);
|
|
|
|
const int numberOfShadowMapsPerLight
|
|
= std::clamp(Settings::Manager::getInt("number of shadow maps", "Shadows"), 1, 8);
|
|
|
|
mShadowSettings->setNumShadowMapsPerLight(numberOfShadowMapsPerLight);
|
|
mShadowSettings->setBaseShadowTextureUnit(osg::GLExtensions::Get(0, false)->glMaxTextureUnits - numberOfShadowMapsPerLight);
|
|
|
|
const float maximumShadowMapDistance = Settings::Manager::getFloat("maximum shadow map distance", "Shadows");
|
|
if (maximumShadowMapDistance > 0)
|
|
{
|
|
const float shadowFadeStart
|
|
= std::clamp(Settings::Manager::getFloat("shadow fade start", "Shadows"), 0.f, 1.f);
|
|
mShadowSettings->setMaximumShadowMapDistance(maximumShadowMapDistance);
|
|
mShadowTechnique->setShadowFadeStart(maximumShadowMapDistance * shadowFadeStart);
|
|
}
|
|
|
|
mShadowSettings->setMinimumShadowMapNearFarRatio(
|
|
Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows"));
|
|
|
|
const std::string& computeSceneBounds = Settings::Manager::getString("compute scene bounds", "Shadows");
|
|
if (Misc::StringUtils::ciEqual(computeSceneBounds, "primitives"))
|
|
mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES);
|
|
else if (Misc::StringUtils::ciEqual(computeSceneBounds, "bounds"))
|
|
mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
|
|
|
int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows");
|
|
mShadowSettings->setTextureSize(osg::Vec2s(mapres, mapres));
|
|
|
|
mShadowTechnique->setSplitPointUniformLogarithmicRatio(
|
|
Settings::Manager::getFloat("split point uniform logarithmic ratio", "Shadows"));
|
|
mShadowTechnique->setSplitPointDeltaBias(Settings::Manager::getFloat("split point bias", "Shadows"));
|
|
|
|
mShadowTechnique->setPolygonOffset(Settings::Manager::getFloat("polygon offset factor", "Shadows"),
|
|
Settings::Manager::getFloat("polygon offset units", "Shadows"));
|
|
|
|
if (Settings::Manager::getBool("use front face culling", "Shadows"))
|
|
mShadowTechnique->enableFrontFaceCulling();
|
|
else
|
|
mShadowTechnique->disableFrontFaceCulling();
|
|
|
|
if (Settings::Manager::getBool("allow shadow map overlap", "Shadows"))
|
|
mShadowSettings->setMultipleShadowMapHint(osgShadow::ShadowSettings::CASCADED);
|
|
else
|
|
mShadowSettings->setMultipleShadowMapHint(osgShadow::ShadowSettings::PARALLEL_SPLIT);
|
|
|
|
if (Settings::Manager::getBool("enable debug hud", "Shadows"))
|
|
mShadowTechnique->enableDebugHUD();
|
|
else
|
|
mShadowTechnique->disableDebugHUD();
|
|
}
|
|
|
|
void ShadowManager::disableShadowsForStateSet(osg::ref_ptr<osg::StateSet> stateset)
|
|
{
|
|
if (!Settings::Manager::getBool("enable shadows", "Shadows"))
|
|
return;
|
|
|
|
const int numberOfShadowMapsPerLight
|
|
= std::clamp(Settings::Manager::getInt("number of shadow maps", "Shadows"), 1, 8);
|
|
|
|
int baseShadowTextureUnit = 8 - numberOfShadowMapsPerLight;
|
|
|
|
osg::ref_ptr<osg::Image> fakeShadowMapImage = new osg::Image();
|
|
fakeShadowMapImage->allocateImage(1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT);
|
|
*(float*)fakeShadowMapImage->data() = std::numeric_limits<float>::infinity();
|
|
osg::ref_ptr<osg::Texture> fakeShadowMapTexture = new osg::Texture2D(fakeShadowMapImage);
|
|
fakeShadowMapTexture->setShadowComparison(true);
|
|
fakeShadowMapTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS);
|
|
for (int i = baseShadowTextureUnit; i < baseShadowTextureUnit + numberOfShadowMapsPerLight; ++i)
|
|
{
|
|
stateset->setTextureAttributeAndModes(i, fakeShadowMapTexture,
|
|
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
|
|
stateset->addUniform(
|
|
new osg::Uniform(("shadowTexture" + std::to_string(i - baseShadowTextureUnit)).c_str(), i));
|
|
stateset->addUniform(
|
|
new osg::Uniform(("shadowTextureUnit" + std::to_string(i - baseShadowTextureUnit)).c_str(), i));
|
|
}
|
|
}
|
|
|
|
ShadowManager::ShadowManager(osg::ref_ptr<osg::Group> sceneRoot, osg::ref_ptr<osg::Group> rootNode,
|
|
unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, unsigned int worldMask,
|
|
Shader::ShaderManager& shaderManager)
|
|
: mShadowedScene(new osgShadow::ShadowedScene)
|
|
, mShadowTechnique(new MWShadowTechnique)
|
|
, mOutdoorShadowCastingMask(outdoorShadowCastingMask)
|
|
, mIndoorShadowCastingMask(indoorShadowCastingMask)
|
|
{
|
|
mShadowedScene->setShadowTechnique(mShadowTechnique);
|
|
Stereo::Manager::instance().setShadowTechnique(mShadowTechnique);
|
|
|
|
mShadowedScene->addChild(sceneRoot);
|
|
rootNode->addChild(mShadowedScene);
|
|
mShadowedScene->setNodeMask(sceneRoot->getNodeMask());
|
|
|
|
mShadowSettings = mShadowedScene->getShadowSettings();
|
|
setupShadowSettings();
|
|
|
|
mShadowTechnique->setupCastingShader(shaderManager);
|
|
mShadowTechnique->setWorldMask(worldMask);
|
|
|
|
enableOutdoorMode();
|
|
}
|
|
|
|
ShadowManager::~ShadowManager()
|
|
{
|
|
Stereo::Manager::instance().setShadowTechnique(nullptr);
|
|
}
|
|
|
|
Shader::ShaderManager::DefineMap ShadowManager::getShadowDefines()
|
|
{
|
|
if (!mEnableShadows)
|
|
return getShadowsDisabledDefines();
|
|
|
|
Shader::ShaderManager::DefineMap definesWithShadows;
|
|
|
|
definesWithShadows["shadows_enabled"] = "1";
|
|
|
|
for (unsigned int i = 0; i < mShadowSettings->getNumShadowMapsPerLight(); ++i)
|
|
definesWithShadows["shadow_texture_unit_list"] += std::to_string(i) + ",";
|
|
// remove extra comma
|
|
definesWithShadows["shadow_texture_unit_list"] = definesWithShadows["shadow_texture_unit_list"].substr(
|
|
0, definesWithShadows["shadow_texture_unit_list"].length() - 1);
|
|
|
|
definesWithShadows["shadowMapsOverlap"]
|
|
= Settings::Manager::getBool("allow shadow map overlap", "Shadows") ? "1" : "0";
|
|
|
|
definesWithShadows["useShadowDebugOverlay"]
|
|
= Settings::Manager::getBool("enable debug overlay", "Shadows") ? "1" : "0";
|
|
|
|
// switch this to reading settings if it's ever exposed to the user
|
|
definesWithShadows["perspectiveShadowMaps"]
|
|
= mShadowSettings->getShadowMapProjectionHint() == ShadowSettings::PERSPECTIVE_SHADOW_MAP ? "1" : "0";
|
|
|
|
definesWithShadows["disableNormalOffsetShadows"]
|
|
= Settings::Manager::getFloat("normal offset distance", "Shadows") == 0.0 ? "1" : "0";
|
|
|
|
definesWithShadows["shadowNormalOffset"]
|
|
= std::to_string(Settings::Manager::getFloat("normal offset distance", "Shadows"));
|
|
|
|
definesWithShadows["limitShadowMapDistance"]
|
|
= Settings::Manager::getFloat("maximum shadow map distance", "Shadows") > 0 ? "1" : "0";
|
|
|
|
return definesWithShadows;
|
|
}
|
|
|
|
Shader::ShaderManager::DefineMap ShadowManager::getShadowsDisabledDefines()
|
|
{
|
|
Shader::ShaderManager::DefineMap definesWithoutShadows;
|
|
|
|
definesWithoutShadows["shadows_enabled"] = "0";
|
|
|
|
definesWithoutShadows["shadow_texture_unit_list"] = "";
|
|
|
|
definesWithoutShadows["shadowMapsOverlap"] = "0";
|
|
|
|
definesWithoutShadows["useShadowDebugOverlay"] = "0";
|
|
|
|
definesWithoutShadows["perspectiveShadowMaps"] = "0";
|
|
|
|
definesWithoutShadows["disableNormalOffsetShadows"] = "0";
|
|
|
|
definesWithoutShadows["shadowNormalOffset"] = "0.0";
|
|
|
|
definesWithoutShadows["limitShadowMapDistance"] = "0";
|
|
|
|
return definesWithoutShadows;
|
|
}
|
|
|
|
void ShadowManager::enableIndoorMode()
|
|
{
|
|
if (Settings::Manager::getBool("enable indoor shadows", "Shadows"))
|
|
mShadowSettings->setCastsShadowTraversalMask(mIndoorShadowCastingMask);
|
|
else
|
|
mShadowTechnique->disableShadows(true);
|
|
}
|
|
|
|
void ShadowManager::enableOutdoorMode()
|
|
{
|
|
if (mEnableShadows)
|
|
mShadowTechnique->enableShadows();
|
|
mShadowSettings->setCastsShadowTraversalMask(mOutdoorShadowCastingMask);
|
|
}
|
|
}
|