From 6e7661ca879661bbabc6333fd376e4b1cb49f621 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 3 Dec 2023 17:44:49 +0300 Subject: [PATCH 1/2] BulletNifLoader: Handle only the first child of NiSwitchNode and NiFltAnimationNode To prevent duplicated collisions in general cases when the node states are similar or only one child is ever active. For NiLODNode this is definitely not going to work --- components/nifbullet/bulletnifloader.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 96dff80004..2c7dd1b1c4 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -250,11 +250,16 @@ namespace NifBullet const Nif::Parent currentParent{ *ninode, parent }; for (const auto& child : ninode->mChildren) { - if (child.empty()) - continue; - - assert(std::find(child->mParents.begin(), child->mParents.end(), ninode) != child->mParents.end()); - handleNode(child.get(), ¤tParent, args); + if (!child.empty()) + { + assert(std::find(child->mParents.begin(), child->mParents.end(), ninode) != child->mParents.end()); + handleNode(child.get(), ¤tParent, args); + } + // For NiSwitchNodes and NiFltAnimationNodes, only use the first child + // TODO: must synchronize with the rendering scene graph somehow + // Doing this for NiLODNodes is unsafe (the first level might not be the closest) + if (node.recType == Nif::RC_NiSwitchNode || node.recType == Nif::RC_NiFltAnimationNode) + break; } } } From b93291840ee3c0996576ffabef29ff550a056a87 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 3 Dec 2023 18:20:05 +0300 Subject: [PATCH 2/2] BulletNifLoader: Handle NiSkinPartition Add NiSkinPartition recovery helper method --- apps/openmw_test_suite/nif/node.hpp | 1 + components/nif/data.cpp | 11 +++ components/nif/data.hpp | 2 + components/nif/node.cpp | 104 +++++++++++++++++------ components/nifbullet/bulletnifloader.cpp | 1 - components/nifosg/nifloader.cpp | 12 +-- 6 files changed, 91 insertions(+), 40 deletions(-) diff --git a/apps/openmw_test_suite/nif/node.hpp b/apps/openmw_test_suite/nif/node.hpp index 76cc6ac687..4e21698501 100644 --- a/apps/openmw_test_suite/nif/node.hpp +++ b/apps/openmw_test_suite/nif/node.hpp @@ -53,6 +53,7 @@ namespace Nif::Testing { value.mData = NiSkinDataPtr(nullptr); value.mRoot = NiAVObjectPtr(nullptr); + value.mPartitions = NiSkinPartitionPtr(nullptr); } inline void init(NiTimeController& value) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index e653959bbc..fa66435aee 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -290,6 +290,17 @@ namespace Nif } } + const Nif::NiSkinPartition* NiSkinInstance::getPartitions() const + { + const Nif::NiSkinPartition* partitions = nullptr; + if (!mPartitions.empty()) + partitions = mPartitions.getPtr(); + else if (!mData.empty() && !mData->mPartitions.empty()) + partitions = mData->mPartitions.getPtr(); + + return partitions; + } + void BSDismemberSkinInstance::read(NIFStream* nif) { NiSkinInstance::read(nif); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 75c18d657a..c9daeef4d4 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -208,6 +208,8 @@ namespace Nif void read(NIFStream* nif) override; void post(Reader& nif) override; + + const Nif::NiSkinPartition* getPartitions() const; }; struct BSDismemberSkinInstance : public NiSkinInstance diff --git a/components/nif/node.cpp b/components/nif/node.cpp index 01c0e1597d..b91f143d00 100644 --- a/components/nif/node.cpp +++ b/components/nif/node.cpp @@ -27,6 +27,37 @@ namespace mesh.preallocateIndices(static_cast(data.mNumTriangles) * 3); } + void trianglesToBtTriangleMesh(btTriangleMesh& mesh, const std::vector& triangles) + { + for (std::size_t i = 0; i < triangles.size(); i += 3) + mesh.addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]); + } + + void stripsToBtTriangleMesh(btTriangleMesh& mesh, const std::vector>& strips) + { + for (const auto& strip : strips) + { + if (strip.size() < 3) + continue; + + unsigned short a; + unsigned short b = strip[0]; + unsigned short c = strip[1]; + for (size_t i = 2; i < strip.size(); i++) + { + a = b; + b = c; + c = strip[i]; + if (a == b || b == c || a == c) + continue; + if (i % 2 == 0) + mesh.addTriangleIndices(a, b, c); + else + mesh.addTriangleIndices(a, c, b); + } + } + } + } namespace Nif @@ -243,15 +274,33 @@ namespace Nif if (mData.empty() || mData->mVertices.empty()) return nullptr; + std::vector*> triangleLists; + std::vector>*> stripsLists; auto data = static_cast(mData.getPtr()); - if (data->mNumTriangles == 0 || data->mTriangles.empty()) - return nullptr; + const Nif::NiSkinPartition* partitions = nullptr; + if (!mSkin.empty()) + partitions = mSkin->getPartitions(); + if (partitions) + { + triangleLists.reserve(partitions->mPartitions.size()); + stripsLists.reserve(partitions->mPartitions.size()); + for (auto& partition : partitions->mPartitions) + { + triangleLists.push_back(&partition.mTrueTriangles); + stripsLists.push_back(&partition.mTrueStrips); + } + } + else if (data->mNumTriangles != 0) + triangleLists.push_back(&data->mTriangles); + + // This makes a perhaps dangerous assumption that NiSkinPartition will never have more than 65536 triangles. auto mesh = std::make_unique(); triBasedGeomToBtTriangleMesh(*mesh, *data); - const std::vector& triangles = data->mTriangles; - for (std::size_t i = 0; i < triangles.size(); i += 3) - mesh->addTriangleIndices(triangles[i + 0], triangles[i + 1], triangles[i + 2]); + for (const auto triangles : triangleLists) + trianglesToBtTriangleMesh(*mesh, *triangles); + for (const auto strips : stripsLists) + stripsToBtTriangleMesh(*mesh, *strips); if (mesh->getNumTriangles() == 0) return nullptr; @@ -267,33 +316,32 @@ namespace Nif if (mData.empty() || mData->mVertices.empty()) return nullptr; + std::vector*> triangleLists; + std::vector>*> stripsLists; auto data = static_cast(mData.getPtr()); - if (data->mNumTriangles == 0 || data->mStrips.empty()) - return nullptr; + const Nif::NiSkinPartition* partitions = nullptr; + if (!mSkin.empty()) + partitions = mSkin->getPartitions(); + + if (partitions) + { + triangleLists.reserve(partitions->mPartitions.size()); + stripsLists.reserve(partitions->mPartitions.size()); + for (auto& partition : partitions->mPartitions) + { + triangleLists.push_back(&partition.mTrueTriangles); + stripsLists.push_back(&partition.mTrueStrips); + } + } + else if (data->mNumTriangles != 0) + stripsLists.push_back(&data->mStrips); auto mesh = std::make_unique(); triBasedGeomToBtTriangleMesh(*mesh, *data); - for (const std::vector& strip : data->mStrips) - { - if (strip.size() < 3) - continue; - - unsigned short a; - unsigned short b = strip[0]; - unsigned short c = strip[1]; - for (size_t i = 2; i < strip.size(); i++) - { - a = b; - b = c; - c = strip[i]; - if (a == b || b == c || a == c) - continue; - if (i % 2 == 0) - mesh->addTriangleIndices(a, b, c); - else - mesh->addTriangleIndices(a, c, b); - } - } + for (const auto triangles : triangleLists) + trianglesToBtTriangleMesh(*mesh, *triangles); + for (const auto strips : stripsLists) + stripsToBtTriangleMesh(*mesh, *strips); if (mesh->getNumTriangles() == 0) return nullptr; diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 2c7dd1b1c4..0bba9ee1a2 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -277,7 +277,6 @@ namespace NifBullet if (!niGeometry.mSkin.empty()) args.mAnimated = false; - // TODO: handle NiSkinPartition std::unique_ptr childShape = niGeometry.getCollisionShape(); if (childShape == nullptr) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 436f2e1d34..fba831d2a2 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1381,17 +1381,7 @@ namespace NifOsg if (!niGeometry->mSkin.empty()) { const Nif::NiSkinInstance* skin = niGeometry->mSkin.getPtr(); - const Nif::NiSkinData* data = nullptr; - const Nif::NiSkinPartition* partitions = nullptr; - if (!skin->mData.empty()) - { - data = skin->mData.getPtr(); - if (!data->mPartitions.empty()) - partitions = data->mPartitions.getPtr(); - } - if (!partitions && !skin->mPartitions.empty()) - partitions = skin->mPartitions.getPtr(); - + const Nif::NiSkinPartition* partitions = skin->getPartitions(); hasPartitions = partitions != nullptr; if (hasPartitions) {