support morrowind stenciling

C++20
glassmancody.info 3 years ago
parent 643c1d6aeb
commit bbc9c53423

@ -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

@ -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);
}
}

@ -1,5 +1,7 @@
#include "postprocessor.hpp"
#include <SDL_opengl_glext.h>
#include <osg/Group>
#include <osg/Camera>
#include <osg/Callback>
@ -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<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);
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);

@ -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()

@ -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<std::pair<size_t, osg::ref_ptr<Emitter>>> 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<osgUtil::RenderBin> 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<unsigned int>& boundTextures, int animflags)
{
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)
{
if (!props[i].empty())
@ -350,14 +378,14 @@ namespace NifOsg
if (props[i].getPtr()->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<const Nif::NiGeometry*>(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<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)
{
@ -1800,6 +1828,7 @@ namespace NifOsg
if (stencilprop->data.enabled != 0)
{
mHasStencilProperty = true;
osg::ref_ptr<osg::Stencil> 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<osg::Depth> 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();
}

Loading…
Cancel
Save