diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b7fd36f27..3ebd3b07e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,7 @@ Feature #6443: NiStencilProperty is not fully supported Feature #6534: Shader-based object texture blending Feature #6592: Missing support for NiTriShape particle emitters + Feature #6600: Support NiSortAdjustNode Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings Task #6264: Remove the old classes in animation.cpp Task #6553: Simplify interpreter instruction registration diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 8b17680cf3..0a1acde5b5 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -31,7 +31,8 @@ using CreateRecord = std::unique_ptr (*)(); ///These are all the record types we know how to read. static std::map makeFactory() { - return { + return + { {"NiNode" , &construct }, {"NiSwitchNode" , &construct }, {"NiLODNode" , &construct }, @@ -147,7 +148,7 @@ static std::map makeFactory() {"bhkRigidBody" , &construct }, {"bhkRigidBodyT" , &construct }, {"BSLightingShaderProperty" , &construct }, - {"NiSortAdjustNode" , &construct }, + {"NiSortAdjustNode" , &construct }, {"NiClusterAccumulator" , &construct }, {"NiAlphaAccumulator" , &construct }, }; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index f18a639aae..133c393ab5 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -142,7 +142,8 @@ enum RecordType RC_bhkRigidBodyT, RC_BSLightingShaderProperty, RC_NiClusterAccumulator, - RC_NiAlphaAccumulator + RC_NiAlphaAccumulator, + RC_NiSortAdjustNode }; /// Base class for all records diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 73bd2068fb..8e0a5c08ac 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -220,6 +220,9 @@ namespace NifOsg bool mHasHerbalismLabel = false; bool mHasStencilProperty = false; + const Nif::NiSortAdjustNode* mPushedSorter = nullptr; + const Nif::NiSortAdjustNode* mLastAppliedNoInheritSorter = nullptr; + // This is used to queue emitters that weren't attached to their node yet. std::vector>> mEmitterQueue; @@ -310,10 +313,6 @@ namespace NifOsg if (mHasHerbalismLabel) 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. handleQueuedParticleEmitters(created, nif); @@ -598,6 +597,22 @@ namespace NifOsg if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode) animflags = nifNode->flags; + if (nifNode->recType == Nif::RC_NiSortAdjustNode) + { + auto sortNode = static_cast(nifNode); + + if (sortNode->mSubSorter.empty()) + { + Log(Debug::Warning) << "Empty accumulator found in '" << nifNode->recName << "' node " << nifNode->recIndex; + } + else + { + if (mPushedSorter && !mPushedSorter->mSubSorter.empty() && mPushedSorter->mMode != Nif::NiSortAdjustNode::SortingMode_Inherit) + mLastAppliedNoInheritSorter = mPushedSorter; + mPushedSorter = sortNode; + } + } + // Hide collision shapes, but don't skip the subgraph // We still need to animate the hidden bones so the physics system can access them if (nifNode->recType == Nif::RC_RootCollisionNode) @@ -2000,6 +2015,13 @@ namespace NifOsg mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); bool hasMatCtrl = false; + bool hasSortAlpha = false; + osg::StateSet* blendFuncStateSet = nullptr; + + auto setBin_Transparent = [] (osg::StateSet* ss) { ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); }; + auto setBin_BackToFront = [] (osg::StateSet* ss) { ss->setRenderBinDetails(0, "SORT_BACK_TO_FRONT"); }; + auto setBin_Traversal = [] (osg::StateSet* ss) { ss->setRenderBinDetails(2, "TraversalOrderBin"); }; + auto setBin_Inherit = [] (osg::StateSet* ss) { ss->setRenderBinToInherit(); }; int lightmode = 1; float emissiveMult = 1.f; @@ -2077,17 +2099,23 @@ namespace NifOsg bool noSort = (alphaprop->flags>>13)&1; if (!noSort) { - node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - node->getOrCreateStateSet()->setNestRenderBins(false); + hasSortAlpha = true; + if (!mPushedSorter) + setBin_Transparent(node->getStateSet()); } else - node->getOrCreateStateSet()->setRenderBinToInherit(); + { + if (!mPushedSorter) + setBin_Inherit(node->getStateSet()); + } } else if (osg::StateSet* stateset = node->getStateSet()) { stateset->removeAttribute(osg::StateAttribute::BLENDFUNC); stateset->removeMode(GL_BLEND); - stateset->setRenderBinToInherit(); + blendFuncStateSet = stateset; + if (!mPushedSorter) + blendFuncStateSet->setRenderBinToInherit(); } if((alphaprop->flags>>9)&1) @@ -2152,7 +2180,10 @@ namespace NifOsg mat->setColorMode(osg::Material::OFF); } - if (!hasMatCtrl && mat->getColorMode() == osg::Material::OFF + if (!mPushedSorter && !hasSortAlpha && mHasStencilProperty) + setBin_Traversal(node->getOrCreateStateSet()); + + if (!mPushedSorter && !hasMatCtrl && mat->getColorMode() == osg::Material::OFF && mat->getEmission(osg::Material::FRONT_AND_BACK) == osg::Vec4f(0,0,0,1) && mat->getDiffuse(osg::Material::FRONT_AND_BACK) == osg::Vec4f(1,1,1,1) && mat->getAmbient(osg::Material::FRONT_AND_BACK) == osg::Vec4f(1,1,1,1) @@ -2171,6 +2202,51 @@ namespace NifOsg stateset->addUniform(new osg::Uniform("emissiveMult", emissiveMult)); if (specStrength != 1.f) stateset->addUniform(new osg::Uniform("specStrength", specStrength)); + + if (!mPushedSorter) + return; + + auto assignBin = [&] (int mode, int type) { + if (mode == Nif::NiSortAdjustNode::SortingMode_Off) + { + setBin_Traversal(stateset); + return; + } + + if (type == Nif::RC_NiAlphaAccumulator) + { + if (hasSortAlpha) + setBin_BackToFront(stateset); + else + setBin_Traversal(stateset); + } + else if (type == Nif::RC_NiClusterAccumulator) + setBin_BackToFront(stateset); + else + Log(Debug::Error) << "Unrecognized NiAccumulator in " << mFilename; + }; + + switch (mPushedSorter->mMode) + { + case Nif::NiSortAdjustNode::SortingMode_Inherit: + { + if (mLastAppliedNoInheritSorter) + assignBin(mLastAppliedNoInheritSorter->mMode, mLastAppliedNoInheritSorter->mSubSorter->recType); + else + assignBin(mPushedSorter->mMode, Nif::RC_NiAlphaAccumulator); + break; + } + case Nif::NiSortAdjustNode::SortingMode_Off: + { + setBin_Traversal(stateset); + break; + } + case Nif::NiSortAdjustNode::SortingMode_Subsort: + { + assignBin(mPushedSorter->mMode, mPushedSorter->mSubSorter->recType); + break; + } + } } };