1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-29 22:20:33 +00:00

Merge branch 'peace_love_and_stencils' into 'master'

Support morrowind stenciling (#6443)

Closes #6443

See merge request OpenMW/openmw!1635
This commit is contained in:
psi29a 2022-02-08 09:42:26 +00:00
commit 01e8ae8981
9 changed files with 57 additions and 26 deletions

View File

@ -129,6 +129,7 @@
Feature #6288: Preserve the "blocked" record flag for referenceable objects. Feature #6288: Preserve the "blocked" record flag for referenceable objects.
Feature #6380: Commas are treated as whitespace in vanilla Feature #6380: Commas are treated as whitespace in vanilla
Feature #6419: Topics shouldn't be greyed out if they can produce another topic reference Feature #6419: Topics shouldn't be greyed out if they can produce another topic reference
Feature #6443: NiStencilProperty is not fully supported
Feature #6534: Shader-based object texture blending Feature #6534: Shader-based object texture blending
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
Task #6264: Remove the old classes in animation.cpp Task #6264: Remove the old classes in animation.cpp

View File

@ -187,7 +187,7 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f));
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderOrder(osg::Camera::PRE_RENDER);
camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static); camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static);

View File

@ -333,12 +333,12 @@ public:
osg::FrameBufferAttachment(postProcessor->getFirstPersonRBProxy()).attach(*state, GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, ext); osg::FrameBufferAttachment(postProcessor->getFirstPersonRBProxy()).attach(*state, GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, ext);
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// color accumulation pass // color accumulation pass
bin->drawImplementation(renderInfo, previous); bin->drawImplementation(renderInfo, previous);
auto primaryFBO = postProcessor->getMsaaFbo() ? postProcessor->getMsaaFbo() : postProcessor->getFbo(); auto primaryFBO = postProcessor->getMsaaFbo() ? postProcessor->getMsaaFbo() : postProcessor->getFbo();
primaryFBO->getAttachment(osg::FrameBufferObject::BufferComponent::DEPTH_BUFFER).attach(*state, GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, ext); primaryFBO->getAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER).attach(*state, GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, ext);
state->pushStateSet(mStateSet); state->pushStateSet(mStateSet);
state->apply(); state->apply();
@ -349,7 +349,7 @@ public:
else else
{ {
// fallback to standard depth clear when we are not rendering our main scene via an intermediate FBO // fallback to standard depth clear when we are not rendering our main scene via an intermediate FBO
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
bin->drawImplementation(renderInfo, previous); bin->drawImplementation(renderInfo, previous);
} }
} }

View File

@ -1,5 +1,7 @@
#include "postprocessor.hpp" #include "postprocessor.hpp"
#include <SDL_opengl_glext.h>
#include <osg/Group> #include <osg/Group>
#include <osg/Camera> #include <osg/Camera>
#include <osg/Callback> #include <osg/Callback>
@ -155,7 +157,7 @@ namespace MWRender
PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode) PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode)
: mViewer(viewer) : mViewer(viewer)
, mRootNode(new osg::Group) , mRootNode(new osg::Group)
, mDepthFormat(GL_DEPTH_COMPONENT24) , mDepthFormat(GL_DEPTH24_STENCIL8_EXT)
{ {
bool softParticles = Settings::Manager::getBool("soft particles", "Shaders"); bool softParticles = Settings::Manager::getBool("soft particles", "Shaders");
@ -183,9 +185,9 @@ namespace MWRender
if (SceneUtil::AutoDepth::isReversed()) if (SceneUtil::AutoDepth::isReversed())
{ {
if (osg::isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float")) if (osg::isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float"))
mDepthFormat = GL_DEPTH_COMPONENT32F; mDepthFormat = GL_DEPTH32F_STENCIL8;
else if (osg::isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float")) else if (osg::isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float"))
mDepthFormat = GL_DEPTH_COMPONENT32F_NV; mDepthFormat = GL_DEPTH32F_STENCIL8_NV;
else else
{ {
// TODO: Once we have post-processing implemented we want to skip this return and continue with setup. // TODO: Once we have post-processing implemented we want to skip this return and continue with setup.
@ -213,7 +215,7 @@ namespace MWRender
mViewer->getCamera()->addCullCallback(new CullCallback); mViewer->getCamera()->addCullCallback(new CullCallback);
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex); mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex);
mViewer->getCamera()->attach(osg::Camera::DEPTH_BUFFER, mDepthTex); mViewer->getCamera()->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, mDepthTex);
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this)); mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
mViewer->getCamera()->setUserData(this); mViewer->getCamera()->setUserData(this);
@ -236,7 +238,7 @@ namespace MWRender
mFbo = new osg::FrameBufferObject; mFbo = new osg::FrameBufferObject;
mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(mSceneTex)); mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(mSceneTex));
mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(mDepthTex)); mFbo->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTex));
// When MSAA is enabled we must first render to a render buffer, then // When MSAA is enabled we must first render to a render buffer, then
// blit the result to the FBO which is either passed to the main frame // blit the result to the FBO which is either passed to the main frame
@ -247,7 +249,7 @@ namespace MWRender
osg::ref_ptr<osg::RenderBuffer> colorRB = new osg::RenderBuffer(width, height, mSceneTex->getInternalFormat(), samples); osg::ref_ptr<osg::RenderBuffer> colorRB = new osg::RenderBuffer(width, height, mSceneTex->getInternalFormat(), samples);
osg::ref_ptr<osg::RenderBuffer> depthRB = new osg::RenderBuffer(width, height, mDepthTex->getInternalFormat(), samples); osg::ref_ptr<osg::RenderBuffer> depthRB = new osg::RenderBuffer(width, height, mDepthTex->getInternalFormat(), samples);
mMsaaFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(colorRB)); mMsaaFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(colorRB));
mMsaaFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(depthRB)); mMsaaFbo->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(depthRB));
} }
if (const auto depthProxy = std::getenv("OPENMW_ENABLE_DEPTH_CLEAR_PROXY")) if (const auto depthProxy = std::getenv("OPENMW_ENABLE_DEPTH_CLEAR_PROXY"))
@ -264,8 +266,8 @@ namespace MWRender
{ {
mDepthTex = new osg::Texture2D; mDepthTex = new osg::Texture2D;
mDepthTex->setTextureSize(width, height); mDepthTex->setTextureSize(width, height);
mDepthTex->setSourceFormat(GL_DEPTH_COMPONENT); mDepthTex->setSourceFormat(GL_DEPTH_STENCIL_EXT);
mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT : GL_UNSIGNED_INT); mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT_32_UNSIGNED_INT_24_8_REV : GL_UNSIGNED_INT_24_8_EXT);
mDepthTex->setInternalFormat(mDepthFormat); mDepthTex->setInternalFormat(mDepthFormat);
mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);

View File

@ -538,6 +538,8 @@ namespace MWRender
SceneUtil::setCameraClearDepth(mViewer->getCamera()); SceneUtil::setCameraClearDepth(mViewer->getCamera());
updateProjectionMatrix(); updateProjectionMatrix();
mViewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
} }
RenderingManager::~RenderingManager() RenderingManager::~RenderingManager()

View File

@ -348,7 +348,7 @@ namespace MWRender
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & ~(Mask_GUI|Mask_FirstPerson)); rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & ~(Mask_GUI|Mask_FirstPerson));
rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
renderCameraToImage(rttCamera.get(),image,w,h); renderCameraToImage(rttCamera.get(),image,w,h);
} }

View File

@ -218,6 +218,7 @@ namespace NifOsg
bool mHasNightDayLabel = false; bool mHasNightDayLabel = false;
bool mHasHerbalismLabel = false; bool mHasHerbalismLabel = false;
bool mHasStencilProperty = false;
// This is used to queue emitters that weren't attached to their node yet. // This is used to queue emitters that weren't attached to their node yet.
std::vector<std::pair<size_t, osg::ref_ptr<Emitter>>> mEmitterQueue; std::vector<std::pair<size_t, osg::ref_ptr<Emitter>>> mEmitterQueue;
@ -309,6 +310,10 @@ namespace NifOsg
if (mHasHerbalismLabel) if (mHasHerbalismLabel)
created->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel); created->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel);
// When dealing with stencil buffer, draw order is especially sensitive. Make sure such objects are drawn with traversal order.
if (mHasStencilProperty)
created->getOrCreateStateSet()->setRenderBinDetails(2, "TraversalOrderBin");
// Attach particle emitters to their nodes which should all be loaded by now. // Attach particle emitters to their nodes which should all be loaded by now.
handleQueuedParticleEmitters(created, nif); handleQueuedParticleEmitters(created, nif);
@ -334,6 +339,21 @@ namespace NifOsg
void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures, int animflags) void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures, int animflags)
{ {
const Nif::PropertyList& props = nifNode->props; const Nif::PropertyList& props = nifNode->props;
bool hasStencilProperty = false;
for (size_t i = 0; i <props.length(); ++i)
{
if (props[i].empty())
continue;
if (props[i].getPtr()->recType == Nif::RC_NiStencilProperty)
{
hasStencilProperty = true;
break;
}
}
for (size_t i = 0; i <props.length(); ++i) for (size_t i = 0; i <props.length(); ++i)
{ {
if (!props[i].empty()) if (!props[i].empty())
@ -350,14 +370,14 @@ namespace NifOsg
if (props[i].getPtr()->recIndex == mFirstRootTextureIndex) if (props[i].getPtr()->recIndex == mFirstRootTextureIndex)
applyTo->setUserValue("overrideFx", 1); applyTo->setUserValue("overrideFx", 1);
} }
handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags); handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags, hasStencilProperty);
} }
} }
auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode); auto geometry = dynamic_cast<const Nif::NiGeometry*>(nifNode);
// NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property // NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property
if (geometry && !geometry->shaderprop.empty()) if (geometry && !geometry->shaderprop.empty())
handleProperty(geometry->shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, animflags); handleProperty(geometry->shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, animflags, hasStencilProperty);
} }
static void setupController(const Nif::Controller* ctrl, SceneUtil::Controller* toSetup, int animflags) static void setupController(const Nif::Controller* ctrl, SceneUtil::Controller* toSetup, int animflags)
@ -1347,7 +1367,7 @@ namespace NifOsg
case 4: return osg::Stencil::GREATER; case 4: return osg::Stencil::GREATER;
case 5: return osg::Stencil::NOTEQUAL; case 5: return osg::Stencil::NOTEQUAL;
case 6: return osg::Stencil::GEQUAL; case 6: return osg::Stencil::GEQUAL;
case 7: return osg::Stencil::NEVER; // NifSkope says this is GL_ALWAYS, but in MW it's GL_NEVER case 7: return osg::Stencil::ALWAYS;
default: default:
Log(Debug::Info) << "Unexpected stencil function: " << func << " in " << mFilename; Log(Debug::Info) << "Unexpected stencil function: " << func << " in " << mFilename;
return osg::Stencil::NEVER; return osg::Stencil::NEVER;
@ -1772,7 +1792,7 @@ namespace NifOsg
} }
void handleProperty(const Nif::Property *property, void handleProperty(const Nif::Property *property,
osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures, int animflags) osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<unsigned int>& boundTextures, int animflags, bool hasStencilProperty)
{ {
switch (property->recType) switch (property->recType)
{ {
@ -1800,6 +1820,7 @@ namespace NifOsg
if (stencilprop->data.enabled != 0) if (stencilprop->data.enabled != 0)
{ {
mHasStencilProperty = true;
osg::ref_ptr<osg::Stencil> stencil = new osg::Stencil; osg::ref_ptr<osg::Stencil> stencil = new osg::Stencil;
stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc), stencilprop->data.stencilRef, stencilprop->data.stencilMask); stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc), stencilprop->data.stencilRef, stencilprop->data.stencilMask);
stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction)); stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction));
@ -1831,7 +1852,9 @@ namespace NifOsg
osg::ref_ptr<osg::Depth> depth = new osg::Depth; osg::ref_ptr<osg::Depth> depth = new osg::Depth;
// Depth write flag // Depth write flag
depth->setWriteMask((zprop->flags>>1)&1); depth->setWriteMask((zprop->flags>>1)&1);
// Morrowind ignores depth test function // Morrowind ignores depth test function, unless a NiStencilProperty is present, in which case it uses a fixed depth function of GL_ALWAYS.
if (hasStencilProperty)
depth->setFunction(osg::Depth::ALWAYS);
depth = shareAttribute(depth); depth = shareAttribute(depth);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
break; break;
@ -2049,7 +2072,10 @@ namespace NifOsg
bool noSort = (alphaprop->flags>>13)&1; bool noSort = (alphaprop->flags>>13)&1;
if (!noSort) if (!noSort)
{
node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
node->getOrCreateStateSet()->setNestRenderBins(false);
}
else else
node->getOrCreateStateSet()->setRenderBinToInherit(); node->getOrCreateStateSet()->setRenderBinToInherit();
} }

View File

@ -50,7 +50,7 @@ namespace SceneUtil
osg::Texture* RTTNode::getDepthTexture(osgUtil::CullVisitor* cv) osg::Texture* RTTNode::getDepthTexture(osgUtil::CullVisitor* cv)
{ {
return getViewDependentData(cv)->mCamera->getBufferAttachmentMap()[osg::Camera::DEPTH_BUFFER]._texture; return getViewDependentData(cv)->mCamera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._texture;
} }
RTTNode::ViewDependentData* RTTNode::getViewDependentData(osgUtil::CullVisitor* cv) RTTNode::ViewDependentData* RTTNode::getViewDependentData(osgUtil::CullVisitor* cv)
@ -67,7 +67,7 @@ namespace SceneUtil
mViewDependentDataMap[cv]->mCamera = camera; mViewDependentDataMap[cv]->mCamera = camera;
camera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrderNum); camera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrderNum);
camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
camera->setViewport(0, 0, mTextureWidth, mTextureHeight); camera->setViewport(0, 0, mTextureWidth, mTextureHeight);
SceneUtil::setCameraClearDepth(camera); SceneUtil::setCameraClearDepth(camera);
@ -88,18 +88,18 @@ namespace SceneUtil
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, colorBuffer); SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, colorBuffer);
} }
if (camera->getBufferAttachmentMap().count(osg::Camera::DEPTH_BUFFER) == 0) if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER) == 0)
{ {
auto depthBuffer = new osg::Texture2D; auto depthBuffer = new osg::Texture2D;
depthBuffer->setTextureSize(mTextureWidth, mTextureHeight); depthBuffer->setTextureSize(mTextureWidth, mTextureHeight);
depthBuffer->setSourceFormat(GL_DEPTH_COMPONENT); depthBuffer->setSourceFormat(GL_DEPTH_STENCIL_EXT);
depthBuffer->setInternalFormat(GL_DEPTH_COMPONENT24); depthBuffer->setInternalFormat(GL_DEPTH24_STENCIL8_EXT);
depthBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); depthBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
depthBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); depthBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
depthBuffer->setSourceType(GL_UNSIGNED_INT); depthBuffer->setSourceType(GL_UNSIGNED_INT_24_8_EXT);
depthBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); depthBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
depthBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); depthBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
camera->attach(osg::Camera::DEPTH_BUFFER, depthBuffer); camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, depthBuffer);
} }
} }

View File

@ -27,7 +27,7 @@ namespace SceneUtil
/// @par Camera settings should be effectuated by overriding the setDefaults() and apply() methods, following a pattern similar to SceneUtil::StateSetUpdater /// @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 /// @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() /// 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. /// @par For any of COLOR_BUFFER or PACKED_DEPTH_STENCIL_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. /// 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 class RTTNode : public osg::Node
{ {