diff --git a/CHANGELOG.md b/CHANGELOG.md index a181a35098..64af236875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -129,6 +129,7 @@ Feature #6288: Preserve the "blocked" record flag for referenceable objects. 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 #6443: NiStencilProperty is not fully supported Feature #6534: Shader-based object texture blending Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings Task #6264: Remove the old classes in animation.cpp diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 308762045c..47aeaf53af 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -333,12 +333,12 @@ public: 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 bin->drawImplementation(renderInfo, previous); 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->apply(); @@ -349,7 +349,7 @@ public: else { // 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); } } diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 6db01444ad..afdec4f1b5 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -1,5 +1,7 @@ #include "postprocessor.hpp" +#include + #include #include #include @@ -155,7 +157,7 @@ namespace MWRender PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode) : mViewer(viewer) , mRootNode(new osg::Group) - , mDepthFormat(GL_DEPTH_COMPONENT24) + , mDepthFormat(GL_DEPTH24_STENCIL8_EXT) { bool softParticles = Settings::Manager::getBool("soft particles", "Shaders"); @@ -183,9 +185,9 @@ namespace MWRender if (SceneUtil::AutoDepth::isReversed()) { 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")) - mDepthFormat = GL_DEPTH_COMPONENT32F_NV; + mDepthFormat = GL_DEPTH32F_STENCIL8_NV; else { // 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()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); 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()->setUserData(this); @@ -236,7 +238,7 @@ namespace MWRender mFbo = new osg::FrameBufferObject; 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 // blit the result to the FBO which is either passed to the main frame @@ -247,7 +249,7 @@ namespace MWRender osg::ref_ptr colorRB = new osg::RenderBuffer(width, height, mSceneTex->getInternalFormat(), samples); osg::ref_ptr depthRB = new osg::RenderBuffer(width, height, mDepthTex->getInternalFormat(), samples); 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")) @@ -264,8 +266,8 @@ namespace MWRender { mDepthTex = new osg::Texture2D; mDepthTex->setTextureSize(width, height); - mDepthTex->setSourceFormat(GL_DEPTH_COMPONENT); - mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT : GL_UNSIGNED_INT); + mDepthTex->setSourceFormat(GL_DEPTH_STENCIL_EXT); + mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT_32_UNSIGNED_INT_24_8_REV : GL_UNSIGNED_INT_24_8_EXT); mDepthTex->setInternalFormat(mDepthFormat); mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 90fe0877ec..4ec44f0141 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -538,6 +538,8 @@ namespace MWRender SceneUtil::setCameraClearDepth(mViewer->getCamera()); updateProjectionMatrix(); + + mViewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } RenderingManager::~RenderingManager() diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 36f474a223..db1dbc4d2c 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -218,6 +218,7 @@ namespace NifOsg bool mHasNightDayLabel = false; bool mHasHerbalismLabel = false; + bool mHasStencilProperty = false; // This is used to queue emitters that weren't attached to their node yet. std::vector>> mEmitterQueue; @@ -309,6 +310,18 @@ namespace NifOsg if (mHasHerbalismLabel) created->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel); + // Morrowind is particular about draw order with stenciling, there is no real way around not using traversal order drawing. + static osg::ref_ptr traversalOrderBin; + + if (!traversalOrderBin) + { + traversalOrderBin = new osgUtil::RenderBin(osgUtil::RenderBin::TRAVERSAL_ORDER); + osgUtil::RenderBin::addRenderBinPrototype("TraversalOrder", traversalOrderBin); + } + + if (mHasStencilProperty) + created->getOrCreateStateSet()->setRenderBinDetails(2, "TraversalOrder"); + // Attach particle emitters to their nodes which should all be loaded by now. handleQueuedParticleEmitters(created, nif); @@ -334,6 +347,21 @@ namespace NifOsg void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) { const Nif::PropertyList& props = nifNode->props; + + bool hasStencilProperty = false; + + for (size_t i = 0; i recType == Nif::RC_NiStencilProperty) + { + hasStencilProperty = true; + break; + } + } + for (size_t i = 0; i recIndex == mFirstRootTextureIndex) 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(nifNode); // NiGeometry's NiAlphaProperty doesn't get handled here because it's a drawable property 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) @@ -1347,7 +1375,7 @@ namespace NifOsg case 4: return osg::Stencil::GREATER; case 5: return osg::Stencil::NOTEQUAL; 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: Log(Debug::Info) << "Unexpected stencil function: " << func << " in " << mFilename; return osg::Stencil::NEVER; @@ -1772,7 +1800,7 @@ namespace NifOsg } void handleProperty(const Nif::Property *property, - osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) + osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags, bool hasStencilProperty) { switch (property->recType) { @@ -1800,6 +1828,7 @@ namespace NifOsg if (stencilprop->data.enabled != 0) { + mHasStencilProperty = true; osg::ref_ptr stencil = new osg::Stencil; stencil->setFunction(getStencilFunction(stencilprop->data.compareFunc), stencilprop->data.stencilRef, stencilprop->data.stencilMask); stencil->setStencilFailOperation(getStencilOperation(stencilprop->data.failAction)); @@ -1831,7 +1860,9 @@ namespace NifOsg osg::ref_ptr depth = new osg::Depth; // Depth write flag 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); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); break; @@ -2049,7 +2080,10 @@ namespace NifOsg bool noSort = (alphaprop->flags>>13)&1; if (!noSort) + { node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + node->getOrCreateStateSet()->setNestRenderBins(false); + } else node->getOrCreateStateSet()->setRenderBinToInherit(); }