From 5c4e6c3f576d6ce3efb5828b5d8192c236a3e4d1 Mon Sep 17 00:00:00 2001 From: madsbuvi Date: Sun, 24 Jan 2021 10:42:21 +0100 Subject: [PATCH] Stereo friendly water (cherry picked from commit 0e22c55e48a7f965367d3d430c1bef5357b22748) --- apps/openmw/mwrender/screenshotmanager.cpp | 4 +- apps/openmw/mwrender/water.cpp | 271 ++++++++++----------- apps/openmw/mwrender/water.hpp | 5 +- components/CMakeLists.txt | 2 +- components/sceneutil/rtt.cpp | 103 ++++++++ components/sceneutil/rtt.hpp | 62 +++++ 6 files changed, 297 insertions(+), 150 deletions(-) create mode 100644 components/sceneutil/rtt.cpp create mode 100644 components/sceneutil/rtt.hpp diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index 89b225da4..4d4ef4018 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -312,8 +312,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)); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index e786ce937..f9f5e476c 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -228,65 +229,42 @@ osg::ref_ptr readPngImage (const std::string& file) return result.getImage(); } - -class Refraction : public osg::Camera +class Refraction : public SceneUtil::RTTNode { public: Refraction() { - 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); - 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); // 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 fog (new osg::Fog); + osg::ref_ptr 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); - - attach(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) @@ -302,71 +280,49 @@ public: 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(); - } - private: osg::ref_ptr mClipCullNode; - osg::ref_ptr mRefractionTexture; - osg::ref_ptr mRefractionDepthTexture; osg::ref_ptr mScene; + osg::Matrix mViewMatrix{ osg::Matrix::identity() }; }; -class Reflection : public osg::Camera +class Reflection : public SceneUtil::RTTNode { public: Reflection(bool isInterior) { - 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); - - attach(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 frontFace (new osg::FrontFace); + osg::ref_ptr 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) @@ -378,12 +334,12 @@ public: if(reflectionDetail >= 2) extraMask |= Mask_Static; if(reflectionDetail >= 3) extraMask |= Mask_Effect|Mask_ParticleSystem|Mask_Object; if(reflectionDetail >= 4) extraMask |= Mask_Player|Mask_Actor; - 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)); + 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))); } @@ -395,15 +351,11 @@ public: mClipCullNode->addChild(scene); } - osg::Texture2D* getReflectionTexture() const - { - return mReflectionTexture.get(); - } - private: - osg::ref_ptr mReflectionTexture; osg::ref_ptr mClipCullNode; osg::ref_ptr mScene; + osg::Node::NodeMask mNodeMask; + osg::Matrix mViewMatrix{ osg::Matrix::identity() }; }; /// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported. @@ -439,6 +391,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)); @@ -500,19 +453,26 @@ osg::Uniform *Water::getRainIntensityUniform() 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); @@ -532,7 +492,7 @@ void Water::updateWaterMaterial() mParent->addChild(mRefraction); } - createShaderWaterStateSet(mWaterGeom, mReflection, mRefraction); + createShaderWaterStateSet(mWaterNode, mReflection, mRefraction); } else createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha")); @@ -540,12 +500,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; } @@ -590,17 +550,75 @@ 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 program, osg::ref_ptr 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->addUniform(mWater->getRainIntensityUniform()); + 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 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(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 mProgram; + osg::ref_ptr mNormalMap; +}; + void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction) { // use a define map to conditionally compile the shader std::map 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 vertexShader (shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX)); - osg::ref_ptr fragmentShader (shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT)); + osg::ref_ptr vertexShader(shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX)); + osg::ref_ptr fragmentShader(shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT)); + osg::ref_ptr program = shaderMgr.getProgram(vertexShader, fragmentShader); - osg::ref_ptr normalMap (new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png"))); + osg::ref_ptr normalMap(new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png"))); if (normalMap->getImage()) normalMap->getImage()->flipVertical(); @@ -610,43 +628,8 @@ 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 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 depth (new osg::Depth); - depth->setWriteMask(false); - shaderStateset->setAttributeAndModes(depth, osg::StateAttribute::ON); - } - - shaderStateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - - shaderStateset->addUniform(mRainIntensityUniform.get()); - - osg::ref_ptr program (new osg::Program); - program->addShader(vertexShader); - program->addShader(fragmentShader); - shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); - - node->setStateSet(shaderStateset); - node->setUpdateCallback(nullptr); + mShaderWaterStateSetUpdater = new ShaderWaterStateSetUpdater(this, mReflection, mRefraction, program, normalMap); + node->addCullCallback(mShaderWaterStateSetUpdater); } void Water::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -660,13 +643,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; } diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 3787ef426..4cf4de886 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -72,6 +72,7 @@ namespace MWRender bool mInterior; osg::Callback* mCullCallback; + osg::ref_ptr mShaderWaterStateSetUpdater; osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY); void updateVisible(); @@ -115,8 +116,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); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 832fc611f..ca9fb1a21 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -52,7 +52,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 ) add_component_dir (nif diff --git a/components/sceneutil/rtt.cpp b/components/sceneutil/rtt.cpp new file mode 100644 index 000000000..8c4d2d695 --- /dev/null +++ b/components/sceneutil/rtt.cpp @@ -0,0 +1,103 @@ +#include "rtt.hpp" + +#include +#include +#include +#include + +#include + +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(nv); + mGroup->cull(cv); + } + RTTNode* mGroup; + }; + + // RTT camera + class RTTCamera : public osg::Camera + { + public: + RTTCamera() + { + setRenderOrder(osg::Camera::PRE_RENDER, 1); + setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + + + unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water"); + setViewport(0, 0, rttSize, rttSize); + + mColorBuffer = new osg::Texture2D; + mColorBuffer->setTextureSize(rttSize, rttSize); + mColorBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mColorBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + mColorBuffer->setInternalFormat(GL_RGB); + mColorBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + mColorBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + attach(osg::Camera::COLOR_BUFFER, mColorBuffer); + + mDepthBuffer = new osg::Texture2D; + mDepthBuffer->setTextureSize(rttSize, rttSize); + mDepthBuffer->setSourceFormat(GL_DEPTH_COMPONENT); + mDepthBuffer->setInternalFormat(GL_DEPTH_COMPONENT24); + mDepthBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mDepthBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + mDepthBuffer->setSourceType(GL_UNSIGNED_INT); + mDepthBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + mDepthBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + attach(osg::Camera::DEPTH_BUFFER, mDepthBuffer); + } + + osg::ref_ptr mColorBuffer; + osg::ref_ptr mDepthBuffer; + }; + + RTTNode::RTTNode() + { + addCullCallback(new CullCallback(this)); + } + + RTTNode::~RTTNode() + { + } + + void RTTNode::cull(osgUtil::CullVisitor* cv) + { + auto* vdd = getViewDependentData(cv); + apply(vdd->mCamera); + vdd->mCamera->accept(*cv); + } + + osg::Texture2D* RTTNode::getColorTexture(osgUtil::CullVisitor* cv) + { + return getViewDependentData(cv)->mCamera->mColorBuffer.get(); + } + + osg::Texture2D* RTTNode::getDepthTexture(osgUtil::CullVisitor* cv) + { + return getViewDependentData(cv)->mCamera->mDepthBuffer.get(); + } + + RTTNode::ViewDependentData* RTTNode::getViewDependentData(osgUtil::CullVisitor* cv) + { + if (mViewDependentDataMap.count(cv) == 0) + { + mViewDependentDataMap[cv].reset(new ViewDependentData); + mViewDependentDataMap[cv]->mCamera = new RTTCamera(); + setDefaults(mViewDependentDataMap[cv]->mCamera.get()); + } + + return mViewDependentDataMap[cv].get(); + } +} diff --git a/components/sceneutil/rtt.hpp b/components/sceneutil/rtt.hpp new file mode 100644 index 000000000..afb840873 --- /dev/null +++ b/components/sceneutil/rtt.hpp @@ -0,0 +1,62 @@ +#ifndef OPENMW_RTT_H +#define OPENMW_RTT_H + +#include + +#include + +namespace osg +{ + class Texture2D; +} + +namespace osgUtil +{ + class CullVisitor; +} + +namespace SceneUtil +{ + class RTTCamera; + + /// @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() + class RTTNode : public osg::Node + { + public: + RTTNode(); + ~RTTNode(); + + osg::Texture2D* getColorTexture(osgUtil::CullVisitor* cv); + + osg::Texture2D* 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 mCamera; + }; + + ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv); + + typedef std::map< osgUtil::CullVisitor*, std::unique_ptr > ViewDependentDataMap; + ViewDependentDataMap mViewDependentDataMap; + }; +} +#endif