From ec9a1b0d0526c9aba7d7cdd0e68789ab0e747d50 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Jul 2018 08:24:30 +0400 Subject: [PATCH] Handle RootCollisionNode, attached to non-root node (bug #4311) --- CHANGELOG.md | 1 + components/nifbullet/bulletnifloader.cpp | 42 ++++++++++++++++-------- components/nifbullet/bulletnifloader.hpp | 4 +-- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b62bf1a..1d6a20cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4293: Faction members are not aware of faction ownerships in barter Bug #4307: World cleanup should remove dead bodies only if death animation is finished + Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4368: Settings window ok button doesn't have key focus by default diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index be5a7d9d6..6f8c8f2c0 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -94,13 +94,16 @@ osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr& } else { - bool autogenerated = hasAutoGeneratedCollision(node); - // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated const bool isAnimated = pathFileNameStartsWithX(nif->getFilename()); - handleNode(node, 0, autogenerated, isAnimated, autogenerated); + // If the mesh has RootCollisionNode, attached to actual root node, use it as collision mesh + const Nif::Node* rootCollisionNode = getCollisionNode(node); + if (rootCollisionNode) + handleNode(nif->getFilename(), rootCollisionNode, 0, false, isAnimated, false); + else + handleNode(nif->getFilename(), node, 0, true, isAnimated, true); if (mCompoundShape) { @@ -153,25 +156,34 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, int flags) return false; } -bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode) +const Nif::Node* BulletNifLoader::getCollisionNode(const Nif::Node* rootNode) { const Nif::NiNode *ninode = dynamic_cast(rootNode); if(ninode) { + // If root NiNode has only other NiNode as child, consider it as a wrapper, not as actual root node const Nif::NodeList &list = ninode->children; - for(size_t i = 0;i < list.length();i++) + if (list.length() == 1 && + rootNode->recType == Nif::RC_NiNode && + list[0].getPtr()->recType == Nif::RC_NiNode) { - if(!list[i].empty()) - { - if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode) - return false; - } + return getCollisionNode(list[0].getPtr()); + } + + for(size_t i = 0; i < list.length(); i++) + { + if(list[i].empty()) + continue; + + const Nif::Node* childNode = list[i].getPtr(); + if(childNode->recType == Nif::RC_RootCollisionNode) + return childNode; } } - return true; + return nullptr; } -void BulletNifLoader::handleNode(const Nif::Node *node, int flags, +void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *node, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated) { // Accumulate the flags from all the child nodes. This works for all @@ -184,6 +196,9 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); + if (node->recType == Nif::RC_RootCollisionNode && autogenerated) + std::cerr << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape." << std::endl; + // Don't collide with AvoidNode shapes if(node->recType == Nif::RC_AvoidNode) flags |= 0x800; @@ -212,7 +227,6 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, // Marker can still have collision if the model explicitely specifies it via a RootCollisionNode. return; } - } } @@ -235,7 +249,7 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); + handleNode(fileName, list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index fff51933f..8fd9cc2a1 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -55,9 +55,9 @@ public: private: bool findBoundingBox(const Nif::Node* node, int flags = 0); - void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); + void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); - bool hasAutoGeneratedCollision(const Nif::Node *rootNode); + const Nif::Node* getCollisionNode(const Nif::Node* rootNode); void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated);