mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-29 04:06:40 +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
8 changed files with 383 additions and 179 deletions
|
@ -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);
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
void setDefaults(osg::Camera* camera) override
|
||||||
setNodeMask(Mask_RenderToTexture);
|
{
|
||||||
setViewport(0, 0, rttSize, rttSize);
|
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
|
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);
|
||||||
// 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)));
|
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:
|
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");
|
void setDefaults(osg::Camera* camera) override
|
||||||
setViewport(0, 0, rttSize, rttSize);
|
{
|
||||||
|
camera->setReferenceFrame(osg::Camera::RELATIVE_RF);
|
||||||
// No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph
|
camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
||||||
// A double update would mess with the light collection (in addition to being plain redundant)
|
camera->setName("ReflectionCamera");
|
||||||
setUpdateCallback(new NoTraverseCallback);
|
camera->addCullCallback(new InheritViewPointCallback);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
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)
|
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])
|
|
||||||
{
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::StateSet> stateset = mStateSets[nv->getTraversalNumber()%2];
|
|
||||||
apply(stateset, nv);
|
|
||||||
|
|
||||||
if (!isCullVisitor)
|
|
||||||
node->setStateSet(stateset);
|
|
||||||
else
|
|
||||||
static_cast<osgUtil::CullVisitor*>(nv)->pushStateSet(stateset);
|
|
||||||
|
|
||||||
traverse(node, nv);
|
|
||||||
|
|
||||||
if (isCullVisitor)
|
if (isCullVisitor)
|
||||||
static_cast<osgUtil::CullVisitor*>(nv)->popStateSet();
|
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)
|
||||||
|
{
|
||||||
|
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 = mStateSetsUpdate[nv->getTraversalNumber() % 2];
|
||||||
|
apply(stateset, nv);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
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…
Reference in a new issue