Stereo friendly StateSetUpdater

(cherry picked from commit 496b3aef88b8fd867dcdd23a6ca43144573b1b2f)

Stereo friendly water

(cherry picked from commit 0e22c55e48a7f965367d3d430c1bef5357b22748)

Option to disable per view mapping.

Include memory header

De-hardcode settings and buffers.

formatting error

Update water.cpp (whitespace)

Update water.cpp (more whitespace)

include render order

c array -> c++ array
dont-compose-content
madsbuvi 4 years ago
parent 0433a53883
commit 41c08b1c3b

@ -324,8 +324,8 @@ namespace MWRender
rttCamera->setUpdateCallback(new NoTraverseCallback); rttCamera->setUpdateCallback(new NoTraverseCallback);
rttCamera->addChild(mSceneRoot); rttCamera->addChild(mSceneRoot);
rttCamera->addChild(mWater->getReflectionCamera()); rttCamera->addChild(mWater->getReflectionNode());
rttCamera->addChild(mWater->getRefractionCamera()); rttCamera->addChild(mWater->getRefractionNode());
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI)); rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));

@ -25,6 +25,7 @@
#include <components/resource/imagemanager.hpp> #include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/rtt.hpp>
#include <components/sceneutil/shadow.hpp> #include <components/sceneutil/shadow.hpp>
#include <components/sceneutil/util.hpp> #include <components/sceneutil/util.hpp>
#include <components/sceneutil/waterutil.hpp> #include <components/sceneutil/waterutil.hpp>
@ -261,65 +262,43 @@ osg::ref_ptr<osg::Image> readPngImage (const std::string& file)
return result.getImage(); return result.getImage();
} }
class Refraction : public SceneUtil::RTTNode
class Refraction : public osg::Camera
{ {
public: public:
Refraction() Refraction(uint32_t rttSize)
: RTTNode(rttSize, rttSize, 1, false)
{ {
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water"); mClipCullNode = new ClipCullNode;
setRenderOrder(osg::Camera::PRE_RENDER, 1); }
setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); void setDefaults(osg::Camera* camera) override
setReferenceFrame(osg::Camera::RELATIVE_RF); {
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); camera->setReferenceFrame(osg::Camera::RELATIVE_RF);
osg::Camera::setName("RefractionCamera"); camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
setCullCallback(new InheritViewPointCallback); camera->setName("RefractionCamera");
setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); camera->addCullCallback(new InheritViewPointCallback);
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting|Mask_Groundcover);
setNodeMask(Mask_RenderToTexture); camera->setCullMask(Mask_Effect | Mask_Scene | Mask_Object | Mask_Static | Mask_Terrain | Mask_Actor | Mask_ParticleSystem | Mask_Sky | Mask_Sun | Mask_Player | Mask_Lighting | Mask_Groundcover);
setViewport(0, 0, rttSize, rttSize);
// No need for Update traversal since the scene is already updated as part of the main scene graph
// A double update would mess with the light collection (in addition to being plain redundant)
setUpdateCallback(new NoTraverseCallback);
// No need for fog here, we are already applying fog on the water surface itself as well as underwater fog // No need for fog here, we are already applying fog on the water surface itself as well as underwater fog
// assign large value to effectively turn off fog // assign large value to effectively turn off fog
// shaders don't respect glDisable(GL_FOG) // shaders don't respect glDisable(GL_FOG)
osg::ref_ptr<osg::Fog> fog (new osg::Fog); osg::ref_ptr<osg::Fog> fog(new osg::Fog);
fog->setStart(10000000); fog->setStart(10000000);
fog->setEnd(10000000); fog->setEnd(10000000);
getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); camera->getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
mClipCullNode = new ClipCullNode; camera->addChild(mClipCullNode);
osg::Camera::addChild(mClipCullNode); camera->setNodeMask(Mask_RenderToTexture);
mRefractionTexture = new osg::Texture2D;
mRefractionTexture->setTextureSize(rttSize, rttSize);
mRefractionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
mRefractionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
mRefractionTexture->setInternalFormat(GL_RGB);
mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mRefractionTexture);
mRefractionDepthTexture = new osg::Texture2D;
mRefractionDepthTexture->setTextureSize(rttSize, rttSize);
mRefractionDepthTexture->setSourceFormat(GL_DEPTH_COMPONENT);
mRefractionDepthTexture->setInternalFormat(GL_DEPTH_COMPONENT24);
mRefractionDepthTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
mRefractionDepthTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);
mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);
if (Settings::Manager::getFloat("refraction scale", "Water") != 1) // TODO: to be removed with issue #5709 if (Settings::Manager::getFloat("refraction scale", "Water") != 1) // TODO: to be removed with issue #5709
SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet()); SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet());
}
void apply(osg::Camera* camera) override
{
camera->setViewMatrix(mViewMatrix);
} }
void setScene(osg::Node* scene) void setScene(osg::Node* scene)
@ -332,74 +311,53 @@ public:
void setWaterLevel(float waterLevel) void setWaterLevel(float waterLevel)
{ {
const float refractionScale = std::min(1.0f,std::max(0.0f, const float refractionScale = std::min(1.0f, std::max(0.0f,
Settings::Manager::getFloat("refraction scale", "Water"))); Settings::Manager::getFloat("refraction scale", "Water")));
setViewMatrix(osg::Matrix::scale(1,1,refractionScale) * mViewMatrix = osg::Matrix::scale(1, 1, refractionScale) *
osg::Matrix::translate(0,0,(1.0 - refractionScale) * waterLevel)); osg::Matrix::translate(0, 0, (1.0 - refractionScale) * waterLevel);
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,-1), osg::Vec3d(0,0, waterLevel)));
}
osg::Texture2D* getRefractionTexture() const
{
return mRefractionTexture.get();
}
osg::Texture2D* getRefractionDepthTexture() const mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0, 0, -1), osg::Vec3d(0, 0, waterLevel)));
{
return mRefractionDepthTexture.get();
} }
private: private:
osg::ref_ptr<ClipCullNode> mClipCullNode; osg::ref_ptr<ClipCullNode> mClipCullNode;
osg::ref_ptr<osg::Texture2D> mRefractionTexture;
osg::ref_ptr<osg::Texture2D> mRefractionDepthTexture;
osg::ref_ptr<osg::Node> mScene; osg::ref_ptr<osg::Node> mScene;
osg::Matrix mViewMatrix{ osg::Matrix::identity() };
}; };
class Reflection : public osg::Camera class Reflection : public SceneUtil::RTTNode
{ {
public: public:
Reflection(bool isInterior) Reflection(uint32_t rttSize, bool isInterior)
: RTTNode(rttSize, rttSize, 0, false)
{ {
setRenderOrder(osg::Camera::PRE_RENDER);
setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
setReferenceFrame(osg::Camera::RELATIVE_RF);
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
osg::Camera::setName("ReflectionCamera");
setCullCallback(new InheritViewPointCallback);
setInterior(isInterior); setInterior(isInterior);
setNodeMask(Mask_RenderToTexture); mClipCullNode = new ClipCullNode;
}
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
setViewport(0, 0, rttSize, rttSize);
// No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph
// A double update would mess with the light collection (in addition to being plain redundant)
setUpdateCallback(new NoTraverseCallback);
mReflectionTexture = new osg::Texture2D;
mReflectionTexture->setTextureSize(rttSize, rttSize);
mReflectionTexture->setInternalFormat(GL_RGB);
mReflectionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mReflectionTexture); void setDefaults(osg::Camera* camera) override
{
camera->setReferenceFrame(osg::Camera::RELATIVE_RF);
camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
camera->setName("ReflectionCamera");
camera->addCullCallback(new InheritViewPointCallback);
// XXX: should really flip the FrontFace on each renderable instead of forcing clockwise. // XXX: should really flip the FrontFace on each renderable instead of forcing clockwise.
osg::ref_ptr<osg::FrontFace> frontFace (new osg::FrontFace); osg::ref_ptr<osg::FrontFace> frontFace(new osg::FrontFace);
frontFace->setMode(osg::FrontFace::CLOCKWISE); frontFace->setMode(osg::FrontFace::CLOCKWISE);
getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON); camera->getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
mClipCullNode = new ClipCullNode; camera->addChild(mClipCullNode);
osg::Camera::addChild(mClipCullNode); camera->setNodeMask(Mask_RenderToTexture);
SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet()); SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet());
}
void apply(osg::Camera* camera) override
{
camera->setViewMatrix(mViewMatrix);
camera->setCullMask(mNodeMask);
} }
void setInterior(bool isInterior) void setInterior(bool isInterior)
@ -409,16 +367,16 @@ public:
unsigned int extraMask = 0; unsigned int extraMask = 0;
if(reflectionDetail >= 1) extraMask |= Mask_Terrain; if(reflectionDetail >= 1) extraMask |= Mask_Terrain;
if(reflectionDetail >= 2) extraMask |= Mask_Static; if(reflectionDetail >= 2) extraMask |= Mask_Static;
if(reflectionDetail >= 3) extraMask |= Mask_Effect|Mask_ParticleSystem|Mask_Object; if(reflectionDetail >= 3) extraMask |= Mask_Effect | Mask_ParticleSystem | Mask_Object;
if(reflectionDetail >= 4) extraMask |= Mask_Player|Mask_Actor; if(reflectionDetail >= 4) extraMask |= Mask_Player | Mask_Actor;
if(reflectionDetail >= 5) extraMask |= Mask_Groundcover; if(reflectionDetail >= 5) extraMask |= Mask_Groundcover;
setCullMask(Mask_Scene|Mask_Sky|Mask_Lighting|extraMask); mNodeMask = Mask_Scene | Mask_Sky | Mask_Lighting | extraMask;
} }
void setWaterLevel(float waterLevel) void setWaterLevel(float waterLevel)
{ {
setViewMatrix(osg::Matrix::scale(1,1,-1) * osg::Matrix::translate(0,0,2 * waterLevel)); mViewMatrix = osg::Matrix::scale(1, 1, -1) * osg::Matrix::translate(0, 0, 2 * waterLevel);
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,1), osg::Vec3d(0,0,waterLevel))); mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, waterLevel)));
} }
void setScene(osg::Node* scene) void setScene(osg::Node* scene)
@ -429,15 +387,11 @@ public:
mClipCullNode->addChild(scene); mClipCullNode->addChild(scene);
} }
osg::Texture2D* getReflectionTexture() const
{
return mReflectionTexture.get();
}
private: private:
osg::ref_ptr<osg::Texture2D> mReflectionTexture;
osg::ref_ptr<ClipCullNode> mClipCullNode; osg::ref_ptr<ClipCullNode> mClipCullNode;
osg::ref_ptr<osg::Node> mScene; osg::ref_ptr<osg::Node> mScene;
osg::Node::NodeMask mNodeMask;
osg::Matrix mViewMatrix{ osg::Matrix::identity() };
}; };
/// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported. /// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported.
@ -474,6 +428,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
, mTop(0) , mTop(0)
, mInterior(false) , mInterior(false)
, mCullCallback(nullptr) , mCullCallback(nullptr)
, mShaderWaterStateSetUpdater(nullptr)
{ {
mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem)); mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem));
@ -528,22 +483,31 @@ void Water::setCullCallback(osg::Callback* callback)
void Water::updateWaterMaterial() void Water::updateWaterMaterial()
{ {
if (mShaderWaterStateSetUpdater)
{
mWaterNode->removeCullCallback(mShaderWaterStateSetUpdater);
mShaderWaterStateSetUpdater = nullptr;
}
if (mReflection) if (mReflection)
{ {
mReflection->removeChildren(0, mReflection->getNumChildren());
mParent->removeChild(mReflection); mParent->removeChild(mReflection);
mReflection = nullptr; mReflection = nullptr;
} }
if (mRefraction) if (mRefraction)
{ {
mRefraction->removeChildren(0, mRefraction->getNumChildren());
mParent->removeChild(mRefraction); mParent->removeChild(mRefraction);
mRefraction = nullptr; mRefraction = nullptr;
} }
mWaterNode->setStateSet(nullptr);
mWaterGeom->setStateSet(nullptr);
mWaterGeom->setUpdateCallback(nullptr);
if (Settings::Manager::getBool("shader", "Water")) if (Settings::Manager::getBool("shader", "Water"))
{ {
mReflection = new Reflection(mInterior); unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
mReflection = new Reflection(rttSize, mInterior);
mReflection->setWaterLevel(mTop); mReflection->setWaterLevel(mTop);
mReflection->setScene(mSceneRoot); mReflection->setScene(mSceneRoot);
if (mCullCallback) if (mCullCallback)
@ -552,7 +516,7 @@ void Water::updateWaterMaterial()
if (Settings::Manager::getBool("refraction", "Water")) if (Settings::Manager::getBool("refraction", "Water"))
{ {
mRefraction = new Refraction; mRefraction = new Refraction(rttSize);
mRefraction->setWaterLevel(mTop); mRefraction->setWaterLevel(mTop);
mRefraction->setScene(mSceneRoot); mRefraction->setScene(mSceneRoot);
if (mCullCallback) if (mCullCallback)
@ -560,7 +524,7 @@ void Water::updateWaterMaterial()
mParent->addChild(mRefraction); mParent->addChild(mRefraction);
} }
createShaderWaterStateSet(mWaterGeom, mReflection, mRefraction); createShaderWaterStateSet(mWaterNode, mReflection, mRefraction);
} }
else else
createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha")); createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha"));
@ -568,12 +532,12 @@ void Water::updateWaterMaterial()
updateVisible(); updateVisible();
} }
osg::Camera *Water::getReflectionCamera() osg::Node *Water::getReflectionNode()
{ {
return mReflection; return mReflection;
} }
osg::Camera *Water::getRefractionCamera() osg::Node* Water::getRefractionNode()
{ {
return mRefraction; return mRefraction;
} }
@ -620,17 +584,74 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
sceneManager->setForceShaders(oldValue); sceneManager->setForceShaders(oldValue);
} }
class ShaderWaterStateSetUpdater : public SceneUtil::StateSetUpdater
{
public:
ShaderWaterStateSetUpdater(Water* water, Reflection* reflection, Refraction* refraction, osg::ref_ptr<osg::Program> program, osg::ref_ptr<osg::Texture2D> normalMap)
: mWater(water)
, mReflection(reflection)
, mRefraction(refraction)
, mProgram(program)
, mNormalMap(normalMap)
{
}
void setDefaults(osg::StateSet* stateset) override
{
stateset->addUniform(new osg::Uniform("normalMap", 0));
stateset->setTextureAttributeAndModes(0, mNormalMap, osg::StateAttribute::ON);
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
stateset->setAttributeAndModes(mProgram, osg::StateAttribute::ON);
stateset->addUniform(new osg::Uniform("reflectionMap", 1));
if (mRefraction)
{
stateset->addUniform(new osg::Uniform("refractionMap", 2));
stateset->addUniform(new osg::Uniform("refractionDepthMap", 3));
stateset->setRenderBinDetails(MWRender::RenderBin_Default, "RenderBin");
}
else
{
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin");
osg::ref_ptr<osg::Depth> depth(new osg::Depth);
depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
}
}
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
stateset->setTextureAttributeAndModes(1, mReflection->getColorTexture(cv), osg::StateAttribute::ON);
if (mRefraction)
{
stateset->setTextureAttributeAndModes(2, mRefraction->getColorTexture(cv), osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(3, mRefraction->getDepthTexture(cv), osg::StateAttribute::ON);
}
}
private:
Water* mWater;
Reflection* mReflection;
Refraction* mRefraction;
osg::ref_ptr<osg::Program> mProgram;
osg::ref_ptr<osg::Texture2D> mNormalMap;
};
void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction) void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction)
{ {
// use a define map to conditionally compile the shader // use a define map to conditionally compile the shader
std::map<std::string, std::string> defineMap; std::map<std::string, std::string> defineMap;
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(refraction ? "1" : "0"))); defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(mRefraction ? "1" : "0")));
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager(); Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
osg::ref_ptr<osg::Shader> vertexShader (shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX)); osg::ref_ptr<osg::Shader> vertexShader(shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX));
osg::ref_ptr<osg::Shader> fragmentShader (shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT)); osg::ref_ptr<osg::Shader> fragmentShader(shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT));
osg::ref_ptr<osg::Program> program = shaderMgr.getProgram(vertexShader, fragmentShader);
osg::ref_ptr<osg::Texture2D> normalMap (new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png"))); osg::ref_ptr<osg::Texture2D> normalMap(new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png")));
if (normalMap->getImage()) if (normalMap->getImage())
normalMap->getImage()->flipVertical(); normalMap->getImage()->flipVertical();
@ -640,46 +661,14 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
osg::ref_ptr<osg::StateSet> shaderStateset = new osg::StateSet;
shaderStateset->addUniform(new osg::Uniform("normalMap", 0));
shaderStateset->addUniform(new osg::Uniform("reflectionMap", 1));
shaderStateset->setTextureAttributeAndModes(0, normalMap, osg::StateAttribute::ON);
shaderStateset->setTextureAttributeAndModes(1, reflection->getReflectionTexture(), osg::StateAttribute::ON);
if (refraction)
{
shaderStateset->setTextureAttributeAndModes(2, refraction->getRefractionTexture(), osg::StateAttribute::ON);
shaderStateset->setTextureAttributeAndModes(3, refraction->getRefractionDepthTexture(), osg::StateAttribute::ON);
shaderStateset->addUniform(new osg::Uniform("refractionMap", 2));
shaderStateset->addUniform(new osg::Uniform("refractionDepthMap", 3));
shaderStateset->setRenderBinDetails(MWRender::RenderBin_Default, "RenderBin");
}
else
{
shaderStateset->setMode(GL_BLEND, osg::StateAttribute::ON);
shaderStateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin");
osg::ref_ptr<osg::Depth> depth (new osg::Depth);
depth->setWriteMask(false);
shaderStateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
}
shaderStateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
osg::ref_ptr<osg::Program> program (new osg::Program);
program->addShader(vertexShader);
program->addShader(fragmentShader);
auto method = mResourceSystem->getSceneManager()->getLightingMethod(); auto method = mResourceSystem->getSceneManager()->getLightingMethod();
if (method == SceneUtil::LightingMethod::SingleUBO) if (method == SceneUtil::LightingMethod::SingleUBO)
program->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer)); program->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer));
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
node->setStateSet(shaderStateset);
mRainIntensityUpdater = new RainIntensityUpdater(); mRainIntensityUpdater = new RainIntensityUpdater();
node->setUpdateCallback(mRainIntensityUpdater); node->setUpdateCallback(mRainIntensityUpdater);
mShaderWaterStateSetUpdater = new ShaderWaterStateSetUpdater(this, mReflection, mRefraction, program, normalMap);
node->addCullCallback(mShaderWaterStateSetUpdater);
} }
void Water::processChangedSettings(const Settings::CategorySettingVector& settings) void Water::processChangedSettings(const Settings::CategorySettingVector& settings)
@ -693,13 +682,11 @@ Water::~Water()
if (mReflection) if (mReflection)
{ {
mReflection->removeChildren(0, mReflection->getNumChildren());
mParent->removeChild(mReflection); mParent->removeChild(mReflection);
mReflection = nullptr; mReflection = nullptr;
} }
if (mRefraction) if (mRefraction)
{ {
mRefraction->removeChildren(0, mRefraction->getNumChildren());
mParent->removeChild(mRefraction); mParent->removeChild(mRefraction);
mRefraction = nullptr; mRefraction = nullptr;
} }

@ -72,6 +72,7 @@ namespace MWRender
bool mInterior; bool mInterior;
osg::Callback* mCullCallback; osg::Callback* mCullCallback;
osg::ref_ptr<osg::NodeCallback> mShaderWaterStateSetUpdater;
osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY); osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY);
void updateVisible(); void updateVisible();
@ -116,8 +117,8 @@ namespace MWRender
void update(float dt); void update(float dt);
osg::Camera *getReflectionCamera(); osg::Node* getReflectionNode();
osg::Camera *getRefractionCamera(); osg::Node* getRefractionNode();
void processChangedSettings(const Settings::CategorySettingVector& settings); void processChangedSettings(const Settings::CategorySettingVector& settings);
}; };

@ -56,7 +56,7 @@ add_component_dir (shader
add_component_dir (sceneutil add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller rtt
screencapture screencapture
) )

@ -0,0 +1,110 @@
#include "rtt.hpp"
#include "util.hpp"
#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/Texture2D>
#include <osgUtil/CullVisitor>
#include <components/settings/settings.hpp>
namespace SceneUtil
{
// RTTNode's cull callback
class CullCallback : public osg::NodeCallback
{
public:
CullCallback(RTTNode* group)
: mGroup(group) {}
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
mGroup->cull(cv);
}
RTTNode* mGroup;
};
RTTNode::RTTNode(uint32_t textureWidth, uint32_t textureHeight, int renderOrderNum, bool doPerViewMapping)
: mTextureWidth(textureWidth)
, mTextureHeight(textureHeight)
, mRenderOrderNum(renderOrderNum)
, mDoPerViewMapping(doPerViewMapping)
{
addCullCallback(new CullCallback(this));
setCullingActive(false);
}
RTTNode::~RTTNode()
{
}
void RTTNode::cull(osgUtil::CullVisitor* cv)
{
auto* vdd = getViewDependentData(cv);
apply(vdd->mCamera);
vdd->mCamera->accept(*cv);
}
osg::Texture* RTTNode::getColorTexture(osgUtil::CullVisitor* cv)
{
return getViewDependentData(cv)->mCamera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._texture;
}
osg::Texture* RTTNode::getDepthTexture(osgUtil::CullVisitor* cv)
{
return getViewDependentData(cv)->mCamera->getBufferAttachmentMap()[osg::Camera::DEPTH_BUFFER]._texture;
}
RTTNode::ViewDependentData* RTTNode::getViewDependentData(osgUtil::CullVisitor* cv)
{
if (!mDoPerViewMapping)
// Always setting it to null is an easy way to disable per-view mapping when mDoPerViewMapping is false.
// This is safe since the visitor is never dereferenced.
cv = nullptr;
if (mViewDependentDataMap.count(cv) == 0)
{
auto camera = new osg::Camera();
mViewDependentDataMap[cv].reset(new ViewDependentData);
mViewDependentDataMap[cv]->mCamera = camera;
camera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrderNum);
camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
camera->setViewport(0, 0, mTextureWidth, mTextureHeight);
setDefaults(mViewDependentDataMap[cv]->mCamera.get());
// Create any buffer attachments not added in setDefaults
if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
{
auto colorBuffer = new osg::Texture2D;
colorBuffer->setTextureSize(mTextureWidth, mTextureHeight);
colorBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
colorBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
colorBuffer->setInternalFormat(GL_RGB);
colorBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
colorBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
camera->attach(osg::Camera::COLOR_BUFFER, colorBuffer);
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, colorBuffer);
}
if (camera->getBufferAttachmentMap().count(osg::Camera::DEPTH_BUFFER) == 0)
{
auto depthBuffer = new osg::Texture2D;
depthBuffer->setTextureSize(mTextureWidth, mTextureHeight);
depthBuffer->setSourceFormat(GL_DEPTH_COMPONENT);
depthBuffer->setInternalFormat(GL_DEPTH_COMPONENT24);
depthBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
depthBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
depthBuffer->setSourceType(GL_UNSIGNED_INT);
depthBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
depthBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
camera->attach(osg::Camera::DEPTH_BUFFER, depthBuffer);
}
}
return mViewDependentDataMap[cv].get();
}
}

@ -0,0 +1,68 @@
#ifndef OPENMW_RTT_H
#define OPENMW_RTT_H
#include <osg/Node>
#include <map>
#include <memory>
namespace osg
{
class Texture2D;
class Camera;
}
namespace osgUtil
{
class CullVisitor;
}
namespace SceneUtil
{
/// @brief Implements per-view RTT operations.
/// @par With a naive RTT implementation, subsequent views of multiple views will overwrite the results of the previous views, leading to
/// the results of the last view being broadcast to all views. An error in all cases where the RTT result depends on the view.
/// @par If using an RTTNode this is solved by mapping RTT operations to CullVisitors, which will be unique per view. This requires
/// instancing one camera per view, and traversing only the camera mapped to that CV during cull traversals.
/// @par Camera settings should be effectuated by overriding the setDefaults() and apply() methods, following a pattern similar to SceneUtil::StateSetUpdater
/// @par When using the RTT texture in your statesets, it is recommended to use SceneUtil::StateSetUpdater as a cull callback to handle this as the appropriate
/// textures can be retrieved during SceneUtil::StateSetUpdater::Apply()
/// @par For any of COLOR_BUFFER or DEPTH_BUFFER not added during setDefaults(), RTTNode will attach a default buffer. The default color buffer has an internal format of GL_RGB.
/// The default depth buffer has internal format GL_DEPTH_COMPONENT24, source format GL_DEPTH_COMPONENT, and source type GL_UNSIGNED_INT. Default wrap is CLAMP_TO_EDGE and filter LINEAR.
class RTTNode : public osg::Node
{
public:
RTTNode(uint32_t textureWidth, uint32_t textureHeight, int renderOrderNum, bool doPerViewMapping);
~RTTNode();
osg::Texture* getColorTexture(osgUtil::CullVisitor* cv);
osg::Texture* getDepthTexture(osgUtil::CullVisitor* cv);
/// Apply state - to override in derived classes
/// @note Due to the view mapping approach you *have* to apply all camera settings, even if they have not changed since the last frame.
virtual void setDefaults(osg::Camera* camera) {};
/// Set default settings - optionally override in derived classes
virtual void apply(osg::Camera* camera) {};
void cull(osgUtil::CullVisitor* cv);
private:
struct ViewDependentData
{
osg::ref_ptr<osg::Camera> mCamera;
};
ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv);
typedef std::map< osgUtil::CullVisitor*, std::unique_ptr<ViewDependentData> > ViewDependentDataMap;
ViewDependentDataMap mViewDependentDataMap;
uint32_t mTextureWidth;
uint32_t mTextureHeight;
int mRenderOrderNum;
bool mDoPerViewMapping;
};
}
#endif

@ -10,36 +10,57 @@ namespace SceneUtil
void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv) void StateSetUpdater::operator()(osg::Node* node, osg::NodeVisitor* nv)
{ {
bool isCullVisitor = nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR; bool isCullVisitor = nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR;
if (!mStateSets[0])
if (isCullVisitor)
return applyCull(node, static_cast<osgUtil::CullVisitor*>(nv));
else
return applyUpdate(node, nv);
}
void StateSetUpdater::applyUpdate(osg::Node* node, osg::NodeVisitor* nv)
{
if (!mStateSetsUpdate[0])
{ {
for (int i=0; i<2; ++i) for (int i = 0; i < 2; ++i)
{ {
if (!isCullVisitor) mStateSetsUpdate[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults
mStateSets[i] = new osg::StateSet(*node->getOrCreateStateSet(), osg::CopyOp::SHALLOW_COPY); // Using SHALLOW_COPY for StateAttributes, if users want to modify it is their responsibility to set a non-shared one first in setDefaults setDefaults(mStateSetsUpdate[i]);
else
mStateSets[i] = new osg::StateSet;
setDefaults(mStateSets[i]);
} }
} }
osg::ref_ptr<osg::StateSet> stateset = mStateSets[nv->getTraversalNumber()%2]; osg::ref_ptr<osg::StateSet> stateset = mStateSetsUpdate[nv->getTraversalNumber() % 2];
apply(stateset, nv); apply(stateset, nv);
node->setStateSet(stateset);
if (!isCullVisitor)
node->setStateSet(stateset);
else
static_cast<osgUtil::CullVisitor*>(nv)->pushStateSet(stateset);
traverse(node, nv); traverse(node, nv);
}
void StateSetUpdater::applyCull(osg::Node* node, osgUtil::CullVisitor* cv)
{
auto stateset = getCvDependentStateset(cv);
apply(stateset, cv);
cv->pushStateSet(stateset);
traverse(node, cv);
cv->popStateSet();
}
if (isCullVisitor) osg::StateSet* StateSetUpdater::getCvDependentStateset(osgUtil::CullVisitor* cv)
static_cast<osgUtil::CullVisitor*>(nv)->popStateSet(); {
auto it = mStateSetsCull.find(cv);
if (it == mStateSetsCull.end())
{
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
mStateSetsCull.emplace(cv, stateset);
setDefaults(stateset);
return stateset;
}
return it->second;
} }
void StateSetUpdater::reset() void StateSetUpdater::reset()
{ {
mStateSets[0] = nullptr; mStateSetsUpdate[0] = nullptr;
mStateSets[1] = nullptr; mStateSetsUpdate[1] = nullptr;
mStateSetsCull.clear();
} }
StateSetUpdater::StateSetUpdater() StateSetUpdater::StateSetUpdater()

@ -3,6 +3,14 @@
#include <osg/NodeCallback> #include <osg/NodeCallback>
#include <map>
#include <array>
namespace osgUtil
{
class CullVisitor;
}
namespace SceneUtil namespace SceneUtil
{ {
@ -11,11 +19,15 @@ namespace SceneUtil
/// queues up a StateSet that we want to modify for the next frame. To solve this we could set the StateSet to /// queues up a StateSet that we want to modify for the next frame. To solve this we could set the StateSet to
/// DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw /// DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw
/// traversals run in parallel can yield up to 200% framerates. /// traversals run in parallel can yield up to 200% framerates.
/// @par Race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns, /// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet,
/// otherwise it operates on a clone of the node's existing StateSet.
/// @par If set as an UpdateCallback, race conditions are prevented using a "double buffering" scheme - we have two StateSets that take turns,
/// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame. /// one StateSet we can write to, the second one is currently in use by the draw traversal of the last frame.
/// @par Must be set as UpdateCallback or CullCallback on a Node. If set as a CullCallback, the StateSetUpdater operates on an empty StateSet, otherwise it operates on a clone of the node's existing StateSet. /// @par If set as a CullCallback, race conditions are prevented by mapping statesets to cull visitors - OSG has two cull visitors that take turns,
/// allowing the updater to automatically scale for the number of views.
/// @note When used as a CullCallback, StateSetUpdater will have no effect on leaf nodes such as osg::Geometry and must be used on branch nodes only.
/// @note Do not add the same StateSetUpdater to multiple nodes. /// @note Do not add the same StateSetUpdater to multiple nodes.
/// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater. /// @note Do not add multiple StateSetUpdaters on the same Node as they will conflict - instead use the CompositeStateSetUpdater.
class StateSetUpdater : public osg::NodeCallback class StateSetUpdater : public osg::NodeCallback
{ {
public: public:
@ -40,7 +52,12 @@ namespace SceneUtil
void reset(); void reset();
private: private:
osg::ref_ptr<osg::StateSet> mStateSets[2]; void applyCull(osg::Node* node, osgUtil::CullVisitor* cv);
void applyUpdate(osg::Node* node, osg::NodeVisitor* nv);
osg::StateSet* getCvDependentStateset(osgUtil::CullVisitor* cv);
std::array<osg::ref_ptr<osg::StateSet>, 2> mStateSetsUpdate;
std::map<osgUtil::CullVisitor*, osg::ref_ptr<osg::StateSet>> mStateSetsCull;
}; };
/// @brief A variant of the StateSetController that can be made up of multiple controllers all controlling the same target. /// @brief A variant of the StateSetController that can be made up of multiple controllers all controlling the same target.

Loading…
Cancel
Save