mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-09 09:39:53 +00:00
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
This commit is contained in:
parent
0433a53883
commit
41c08b1c3b
@ -324,8 +324,8 @@ namespace MWRender
|
||||
rttCamera->setUpdateCallback(new NoTraverseCallback);
|
||||
rttCamera->addChild(mSceneRoot);
|
||||
|
||||
rttCamera->addChild(mWater->getReflectionCamera());
|
||||
rttCamera->addChild(mWater->getRefractionCamera());
|
||||
rttCamera->addChild(mWater->getReflectionNode());
|
||||
rttCamera->addChild(mWater->getRefractionNode());
|
||||
|
||||
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
|
||||
#include <components/sceneutil/rtt.hpp>
|
||||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/waterutil.hpp>
|
||||
@ -261,65 +262,43 @@ osg::ref_ptr<osg::Image> readPngImage (const std::string& file)
|
||||
return result.getImage();
|
||||
}
|
||||
|
||||
|
||||
class Refraction : public osg::Camera
|
||||
class Refraction : public SceneUtil::RTTNode
|
||||
{
|
||||
public:
|
||||
Refraction()
|
||||
Refraction(uint32_t rttSize)
|
||||
: RTTNode(rttSize, rttSize, 1, false)
|
||||
{
|
||||
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
||||
setRenderOrder(osg::Camera::PRE_RENDER, 1);
|
||||
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("RefractionCamera");
|
||||
setCullCallback(new InheritViewPointCallback);
|
||||
setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
mClipCullNode = new ClipCullNode;
|
||||
}
|
||||
|
||||
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);
|
||||
setViewport(0, 0, rttSize, rttSize);
|
||||
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("RefractionCamera");
|
||||
camera->addCullCallback(new InheritViewPointCallback);
|
||||
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
|
||||
// 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);
|
||||
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);
|
||||
|
||||
// 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
|
||||
// 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->setEnd(10000000);
|
||||
getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
||||
camera->getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
||||
|
||||
mClipCullNode = new ClipCullNode;
|
||||
osg::Camera::addChild(mClipCullNode);
|
||||
|
||||
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);
|
||||
camera->addChild(mClipCullNode);
|
||||
camera->setNodeMask(Mask_RenderToTexture);
|
||||
|
||||
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)
|
||||
@ -332,74 +311,53 @@ public:
|
||||
|
||||
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")));
|
||||
|
||||
setViewMatrix(osg::Matrix::scale(1,1,refractionScale) *
|
||||
osg::Matrix::translate(0,0,(1.0 - refractionScale) * waterLevel));
|
||||
mViewMatrix = osg::Matrix::scale(1, 1, refractionScale) *
|
||||
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
|
||||
{
|
||||
return mRefractionDepthTexture.get();
|
||||
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0, 0, -1), osg::Vec3d(0, 0, waterLevel)));
|
||||
}
|
||||
|
||||
private:
|
||||
osg::ref_ptr<ClipCullNode> mClipCullNode;
|
||||
osg::ref_ptr<osg::Texture2D> mRefractionTexture;
|
||||
osg::ref_ptr<osg::Texture2D> mRefractionDepthTexture;
|
||||
osg::ref_ptr<osg::Node> mScene;
|
||||
osg::Matrix mViewMatrix{ osg::Matrix::identity() };
|
||||
};
|
||||
|
||||
class Reflection : public osg::Camera
|
||||
class Reflection : public SceneUtil::RTTNode
|
||||
{
|
||||
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);
|
||||
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.
|
||||
osg::ref_ptr<osg::FrontFace> frontFace (new osg::FrontFace);
|
||||
osg::ref_ptr<osg::FrontFace> frontFace(new osg::FrontFace);
|
||||
frontFace->setMode(osg::FrontFace::CLOCKWISE);
|
||||
getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
|
||||
camera->getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
|
||||
|
||||
mClipCullNode = new ClipCullNode;
|
||||
osg::Camera::addChild(mClipCullNode);
|
||||
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)
|
||||
@ -409,16 +367,16 @@ public:
|
||||
unsigned int extraMask = 0;
|
||||
if(reflectionDetail >= 1) extraMask |= Mask_Terrain;
|
||||
if(reflectionDetail >= 2) extraMask |= Mask_Static;
|
||||
if(reflectionDetail >= 3) extraMask |= Mask_Effect|Mask_ParticleSystem|Mask_Object;
|
||||
if(reflectionDetail >= 4) extraMask |= Mask_Player|Mask_Actor;
|
||||
if(reflectionDetail >= 3) extraMask |= Mask_Effect | Mask_ParticleSystem | Mask_Object;
|
||||
if(reflectionDetail >= 4) extraMask |= Mask_Player | Mask_Actor;
|
||||
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)
|
||||
{
|
||||
setViewMatrix(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)));
|
||||
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)));
|
||||
}
|
||||
|
||||
void setScene(osg::Node* scene)
|
||||
@ -429,15 +387,11 @@ public:
|
||||
mClipCullNode->addChild(scene);
|
||||
}
|
||||
|
||||
osg::Texture2D* getReflectionTexture() const
|
||||
{
|
||||
return mReflectionTexture.get();
|
||||
}
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Texture2D> mReflectionTexture;
|
||||
osg::ref_ptr<ClipCullNode> mClipCullNode;
|
||||
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.
|
||||
@ -474,6 +428,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
||||
, mTop(0)
|
||||
, mInterior(false)
|
||||
, mCullCallback(nullptr)
|
||||
, mShaderWaterStateSetUpdater(nullptr)
|
||||
{
|
||||
mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem));
|
||||
|
||||
@ -528,22 +483,31 @@ void Water::setCullCallback(osg::Callback* callback)
|
||||
|
||||
void Water::updateWaterMaterial()
|
||||
{
|
||||
if (mShaderWaterStateSetUpdater)
|
||||
{
|
||||
mWaterNode->removeCullCallback(mShaderWaterStateSetUpdater);
|
||||
mShaderWaterStateSetUpdater = nullptr;
|
||||
}
|
||||
if (mReflection)
|
||||
{
|
||||
mReflection->removeChildren(0, mReflection->getNumChildren());
|
||||
mParent->removeChild(mReflection);
|
||||
mReflection = nullptr;
|
||||
}
|
||||
if (mRefraction)
|
||||
{
|
||||
mRefraction->removeChildren(0, mRefraction->getNumChildren());
|
||||
mParent->removeChild(mRefraction);
|
||||
mRefraction = nullptr;
|
||||
}
|
||||
|
||||
mWaterNode->setStateSet(nullptr);
|
||||
mWaterGeom->setStateSet(nullptr);
|
||||
mWaterGeom->setUpdateCallback(nullptr);
|
||||
|
||||
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->setScene(mSceneRoot);
|
||||
if (mCullCallback)
|
||||
@ -552,7 +516,7 @@ void Water::updateWaterMaterial()
|
||||
|
||||
if (Settings::Manager::getBool("refraction", "Water"))
|
||||
{
|
||||
mRefraction = new Refraction;
|
||||
mRefraction = new Refraction(rttSize);
|
||||
mRefraction->setWaterLevel(mTop);
|
||||
mRefraction->setScene(mSceneRoot);
|
||||
if (mCullCallback)
|
||||
@ -560,7 +524,7 @@ void Water::updateWaterMaterial()
|
||||
mParent->addChild(mRefraction);
|
||||
}
|
||||
|
||||
createShaderWaterStateSet(mWaterGeom, mReflection, mRefraction);
|
||||
createShaderWaterStateSet(mWaterNode, mReflection, mRefraction);
|
||||
}
|
||||
else
|
||||
createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha"));
|
||||
@ -568,12 +532,12 @@ void Water::updateWaterMaterial()
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
osg::Camera *Water::getReflectionCamera()
|
||||
osg::Node *Water::getReflectionNode()
|
||||
{
|
||||
return mReflection;
|
||||
}
|
||||
|
||||
osg::Camera *Water::getRefractionCamera()
|
||||
osg::Node* Water::getRefractionNode()
|
||||
{
|
||||
return mRefraction;
|
||||
}
|
||||
@ -620,17 +584,74 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
|
||||
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)
|
||||
{
|
||||
// use a define map to conditionally compile the shader
|
||||
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();
|
||||
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> 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::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())
|
||||
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::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();
|
||||
if (method == SceneUtil::LightingMethod::SingleUBO)
|
||||
program->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer));
|
||||
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
||||
|
||||
node->setStateSet(shaderStateset);
|
||||
|
||||
mRainIntensityUpdater = new RainIntensityUpdater();
|
||||
node->setUpdateCallback(mRainIntensityUpdater);
|
||||
|
||||
mShaderWaterStateSetUpdater = new ShaderWaterStateSetUpdater(this, mReflection, mRefraction, program, normalMap);
|
||||
node->addCullCallback(mShaderWaterStateSetUpdater);
|
||||
}
|
||||
|
||||
void Water::processChangedSettings(const Settings::CategorySettingVector& settings)
|
||||
@ -693,13 +682,11 @@ Water::~Water()
|
||||
|
||||
if (mReflection)
|
||||
{
|
||||
mReflection->removeChildren(0, mReflection->getNumChildren());
|
||||
mParent->removeChild(mReflection);
|
||||
mReflection = nullptr;
|
||||
}
|
||||
if (mRefraction)
|
||||
{
|
||||
mRefraction->removeChildren(0, mRefraction->getNumChildren());
|
||||
mParent->removeChild(mRefraction);
|
||||
mRefraction = nullptr;
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ namespace MWRender
|
||||
bool mInterior;
|
||||
|
||||
osg::Callback* mCullCallback;
|
||||
osg::ref_ptr<osg::NodeCallback> mShaderWaterStateSetUpdater;
|
||||
|
||||
osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY);
|
||||
void updateVisible();
|
||||
@ -116,8 +117,8 @@ namespace MWRender
|
||||
|
||||
void update(float dt);
|
||||
|
||||
osg::Camera *getReflectionCamera();
|
||||
osg::Camera *getRefractionCamera();
|
||||
osg::Node* getReflectionNode();
|
||||
osg::Node* getRefractionNode();
|
||||
|
||||
void processChangedSettings(const Settings::CategorySettingVector& settings);
|
||||
};
|
||||
|
@ -56,7 +56,7 @@ add_component_dir (shader
|
||||
add_component_dir (sceneutil
|
||||
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
||||
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
|
||||
)
|
||||
|
||||
|
110
components/sceneutil/rtt.cpp
Normal file
110
components/sceneutil/rtt.cpp
Normal file
@ -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();
|
||||
}
|
||||
}
|
68
components/sceneutil/rtt.hpp
Normal file
68
components/sceneutil/rtt.hpp
Normal file
@ -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)
|
||||
{
|
||||
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)
|
||||
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
|
||||
else
|
||||
mStateSets[i] = new osg::StateSet;
|
||||
setDefaults(mStateSets[i]);
|
||||
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
|
||||
setDefaults(mStateSetsUpdate[i]);
|
||||
}
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::StateSet> stateset = mStateSets[nv->getTraversalNumber()%2];
|
||||
osg::ref_ptr<osg::StateSet> stateset = mStateSetsUpdate[nv->getTraversalNumber() % 2];
|
||||
apply(stateset, nv);
|
||||
|
||||
if (!isCullVisitor)
|
||||
node->setStateSet(stateset);
|
||||
else
|
||||
static_cast<osgUtil::CullVisitor*>(nv)->pushStateSet(stateset);
|
||||
|
||||
node->setStateSet(stateset);
|
||||
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)
|
||||
static_cast<osgUtil::CullVisitor*>(nv)->popStateSet();
|
||||
osg::StateSet* StateSetUpdater::getCvDependentStateset(osgUtil::CullVisitor* cv)
|
||||
{
|
||||
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()
|
||||
{
|
||||
mStateSets[0] = nullptr;
|
||||
mStateSets[1] = nullptr;
|
||||
mStateSetsUpdate[0] = nullptr;
|
||||
mStateSetsUpdate[1] = nullptr;
|
||||
mStateSetsCull.clear();
|
||||
}
|
||||
|
||||
StateSetUpdater::StateSetUpdater()
|
||||
|
@ -3,6 +3,14 @@
|
||||
|
||||
#include <osg/NodeCallback>
|
||||
|
||||
#include <map>
|
||||
#include <array>
|
||||
|
||||
namespace osgUtil
|
||||
{
|
||||
class CullVisitor;
|
||||
}
|
||||
|
||||
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
|
||||
/// 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.
|
||||
/// @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.
|
||||
/// @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 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
|
||||
{
|
||||
public:
|
||||
@ -40,7 +52,12 @@ namespace SceneUtil
|
||||
void reset();
|
||||
|
||||
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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user