diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 7bf01fdb32..7afe7b177e 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -182,8 +182,8 @@ namespace NifBullet if (hasCollisionNode && !hasCollisionShape) mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera; bool generateCollisionShape = !hasCollisionShape; - handleNode(filename, nif.getVersion(), *node, nullptr, 0, generateCollisionShape, isAnimated, - generateCollisionShape, false, mShape->mVisualCollisionType); + handleNode(filename, *node, nullptr, 0, generateCollisionShape, isAnimated, generateCollisionShape, false, + mShape->mVisualCollisionType); } if (mCompoundShape) @@ -268,8 +268,8 @@ namespace NifBullet return true; } - void BulletNifLoader::handleNode(const std::string& fileName, unsigned int nifVersion, const Nif::Node& node, - const Nif::Parent* parent, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid, + void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent, + int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid, Resource::VisualCollisionType& visualCollisionType) { // TODO: allow on-the fly collision switching via toggling this flag @@ -301,7 +301,13 @@ namespace NifBullet << ". Treating it as a common NiTriShape."; // Check for extra data + std::vector extraCollection; for (Nif::ExtraPtr e = node.extra; !e.empty(); e = e->next) + extraCollection.emplace_back(e); + for (const auto& extraNode : node.extralist) + if (!extraNode.empty()) + extraCollection.emplace_back(extraNode); + for (const auto& e : extraCollection) { if (e->recType == Nif::RC_NiStringExtraData) { @@ -326,11 +332,14 @@ namespace NifBullet return; } } + else if (e->recType == Nif::RC_BSXFlags) + { + auto bsxFlags = static_cast(e.getPtr()); + if (bsxFlags->data & 32) // Editor marker flag + return; + } } - if (nifVersion > Nif::NIFFile::NIFVersion::VER_MW && Misc::StringUtils::ciEqual(node.name, "EditorMarker")) - return; - if (isCollisionNode) { // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! @@ -355,8 +364,8 @@ namespace NifBullet continue; assert(std::find(child->parents.begin(), child->parents.end(), ninode) != child->parents.end()); - handleNode(fileName, nifVersion, child.get(), ¤tParent, flags, isCollisionNode, isAnimated, - autogenerated, avoid, visualCollisionType); + handleNode(fileName, child.get(), ¤tParent, flags, isCollisionNode, isAnimated, autogenerated, + avoid, visualCollisionType); } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 5866412de4..6128857cb8 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -52,8 +52,8 @@ namespace NifBullet private: bool findBoundingBox(const Nif::Node& node, const std::string& filename); - void handleNode(const std::string& fileName, unsigned int nifVersion, const Nif::Node& node, - const Nif::Parent* parent, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid, + void handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent, int flags, + bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid, Resource::VisualCollisionType& visualCollisionType); bool hasRootCollisionNode(const Nif::Node& rootNode) const; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 386f4d30a7..84aaf8ca05 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -319,6 +319,19 @@ namespace NifOsg } } + struct HandleNodeArgs + { + unsigned int mNifVersion; + Resource::ImageManager* mImageManager; + SceneUtil::TextKeyMap* mTextKeys; + std::vector mBoundTextures = {}; + int mAnimFlags = 0; + bool mSkipMeshes = false; + bool mHasMarkers = false; + bool mHasAnimatedParents = false; + osg::Node* mRootNode = nullptr; + }; + osg::ref_ptr load(Nif::FileView nif, Resource::ImageManager* imageManager) { const size_t numRoots = nif.numRoots(); @@ -341,8 +354,10 @@ namespace NifOsg created->setDataVariance(osg::Object::STATIC); for (const Nif::Node* root : roots) { - auto node = handleNode(nif.getVersion(), root, nullptr, nullptr, imageManager, - std::vector(), 0, false, false, false, &textkeys->mTextKeys); + auto node = handleNode(root, nullptr, nullptr, + { .mNifVersion = nif.getVersion(), + .mImageManager = imageManager, + .mTextKeys = &textkeys->mTextKeys }); created->addChild(node); } if (mHasNightDayLabel) @@ -598,19 +613,11 @@ namespace NifOsg return node; } - osg::ref_ptr handleNode(unsigned int nifVersion, const Nif::Node* nifNode, const Nif::Parent* parent, - osg::Group* parentNode, Resource::ImageManager* imageManager, std::vector boundTextures, - int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, SceneUtil::TextKeyMap* textKeys, - osg::Node* rootNode = nullptr) + osg::ref_ptr handleNode( + const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode, HandleNodeArgs args) { - if (rootNode) - { - if (Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) - return nullptr; - if (nifVersion > Nif::NIFFile::NIFVersion::VER_MW && !Loader::getShowMarkers() - && Misc::StringUtils::ciEqual(nifNode->name, "EditorMarker")) - return nullptr; - } + if (args.mRootNode && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) + return nullptr; osg::ref_ptr node = createNode(nifNode); @@ -624,8 +631,8 @@ namespace NifOsg if (parentNode) parentNode->addChild(node); - if (!rootNode) - rootNode = node; + if (!args.mRootNode) + args.mRootNode = node; // The original NIF record index is used for a variety of features: // - finding the correct emitter node for a particle system @@ -644,10 +651,10 @@ namespace NifOsg for (const auto& e : extraCollection) { - if (e->recType == Nif::RC_NiTextKeyExtraData && textKeys) + if (e->recType == Nif::RC_NiTextKeyExtraData && args.mTextKeys) { const Nif::NiTextKeyExtraData* tk = static_cast(e.getPtr()); - extractTextKeys(tk, *textKeys); + extractTextKeys(tk, *args.mTextKeys); } else if (e->recType == Nif::RC_NiStringExtraData) { @@ -660,7 +667,7 @@ namespace NifOsg if (sd->string == "MRK" && !Loader::getShowMarkers()) { // Marker objects. These meshes are only visible in the editor. - hasMarkers = true; + args.mHasMarkers = true; } else if (sd->string == "BONE") { @@ -672,10 +679,16 @@ namespace NifOsg Misc::OsgUserValues::sExtraData, sd->string.substr(extraDataIdentifer.length())); } } + else if (e->recType == Nif::RC_BSXFlags) + { + auto bsxFlags = static_cast(e.getPtr()); + if (bsxFlags->data & 32) // Editor marker flag + args.mHasMarkers = true; + } } if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode) - animflags = nifNode->flags; + args.mAnimFlags = nifNode->flags; if (nifNode->recType == Nif::RC_NiSortAdjustNode) { @@ -699,7 +712,7 @@ namespace NifOsg // We still need to animate the hidden bones so the physics system can access them if (nifNode->recType == Nif::RC_RootCollisionNode) { - skipMeshes = true; + args.mSkipMeshes = true; node->setNodeMask(Loader::getHiddenNodeMask()); } @@ -716,8 +729,8 @@ namespace NifOsg } if (!hasVisController) - skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating - // collision shapes + args.mSkipMeshes = true; // skip child meshes, but still create the child node hierarchy for + // animating collision shapes node->setNodeMask(Loader::getHiddenNodeMask()); } @@ -727,37 +740,44 @@ namespace NifOsg osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; - applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); + applyNodeProperties(nifNode, node, composite, args.mImageManager, args.mBoundTextures, args.mAnimFlags); const bool isGeometry = isTypeGeometry(nifNode->recType); - if (isGeometry && !skipMeshes) + if (isGeometry && !args.mSkipMeshes) { - const bool isMarker = hasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "tri editormarker"); - if (!isMarker && !Misc::StringUtils::ciStartsWith(nifNode->name, "shadow") - && !Misc::StringUtils::ciStartsWith(nifNode->name, "tri shadow")) + bool skip; + if (args.mNifVersion <= Nif::NIFFile::NIFVersion::VER_MW) + { + skip = (args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "tri editormarker")) + || Misc::StringUtils::ciStartsWith(nifNode->name, "shadow") + || Misc::StringUtils::ciStartsWith(nifNode->name, "tri shadow"); + } + else + skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "EditorMarker"); + if (!skip) { Nif::NiSkinInstancePtr skin = static_cast(nifNode)->skin; if (skin.empty()) - handleGeometry(nifNode, parent, node, composite, boundTextures, animflags); + handleGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags); else - handleSkinnedGeometry(nifNode, parent, node, composite, boundTextures, animflags); + handleSkinnedGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags); if (!nifNode->controller.empty()) - handleMeshControllers(nifNode, node, composite, boundTextures, animflags); + handleMeshControllers(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags); } } if (nifNode->recType == Nif::RC_NiParticles) - handleParticleSystem(nifNode, parent, node, composite, animflags); + handleParticleSystem(nifNode, parent, node, composite, args.mAnimFlags); if (composite->getNumControllers() > 0) { osg::Callback* cb = composite; if (composite->getNumControllers() == 1) cb = composite->getController(0); - if (animflags & Nif::NiNode::AnimFlag_AutoPlay) + if (args.mAnimFlags & Nif::NiNode::AnimFlag_AutoPlay) node->addCullCallback(cb); else node->addUpdateCallback( @@ -765,10 +785,10 @@ namespace NifOsg } bool isAnimated = false; - handleNodeControllers(nifNode, node, animflags, isAnimated); - hasAnimatedParents |= isAnimated; + handleNodeControllers(nifNode, node, args.mAnimFlags, isAnimated); + args.mHasAnimatedParents |= isAnimated; // Make sure empty nodes and animated shapes are not optimized away so the physics system can find them. - if (isAnimated || (hasAnimatedParents && ((skipMeshes || hasMarkers) || isGeometry))) + if (isAnimated || (args.mHasAnimatedParents && ((args.mSkipMeshes || args.mHasMarkers) || isGeometry))) node->setDataVariance(osg::Object::DYNAMIC); // LOD and Switch nodes must be wrapped by a transform (the current node) to support transformations @@ -809,8 +829,7 @@ namespace NifOsg const Nif::Parent currentParent{ *ninode, parent }; for (const auto& child : children) if (!child.empty()) - handleNode(nifVersion, child.getPtr(), ¤tParent, currentNode, imageManager, boundTextures, - animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode); + handleNode(child.getPtr(), ¤tParent, currentNode, args); // Propagate effects to the the direct subgraph instead of the node itself // This simulates their "affected node list" which Morrowind appears to replace with the subgraph (?) @@ -819,7 +838,7 @@ namespace NifOsg if (!effect.empty()) { osg::ref_ptr effectStateSet = new osg::StateSet; - if (handleEffect(effect.getPtr(), effectStateSet, imageManager)) + if (handleEffect(effect.getPtr(), effectStateSet, args.mImageManager)) for (unsigned int i = 0; i < currentNode->getNumChildren(); ++i) currentNode->getChild(i)->getOrCreateStateSet()->merge(*effectStateSet); }