diff --git a/components/nif/node.cpp b/components/nif/node.cpp index 28cd2cdbfe..01c0e1597d 100644 --- a/components/nif/node.cpp +++ b/components/nif/node.cpp @@ -2,13 +2,33 @@ #include +#include + +#include #include +#include #include "data.hpp" #include "exception.hpp" #include "physics.hpp" #include "property.hpp" +namespace +{ + + void triBasedGeomToBtTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data) + { + // FIXME: copying vertices/indices individually is unreasonable + const std::vector& vertices = data.mVertices; + mesh.preallocateVertices(static_cast(vertices.size())); + for (const osg::Vec3f& vertex : vertices) + mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false); + + mesh.preallocateIndices(static_cast(data.mNumTriangles) * 3); + } + +} + namespace Nif { @@ -218,6 +238,82 @@ namespace Nif } } + std::unique_ptr NiTriShape::getCollisionShape() const + { + if (mData.empty() || mData->mVertices.empty()) + return nullptr; + + auto data = static_cast(mData.getPtr()); + if (data->mNumTriangles == 0 || data->mTriangles.empty()) + return nullptr; + + 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]); + + if (mesh->getNumTriangles() == 0) + return nullptr; + + auto shape = std::make_unique(mesh.get(), true); + std::ignore = mesh.release(); + + return shape; + } + + std::unique_ptr NiTriStrips::getCollisionShape() const + { + if (mData.empty() || mData->mVertices.empty()) + return nullptr; + + auto data = static_cast(mData.getPtr()); + if (data->mNumTriangles == 0 || data->mStrips.empty()) + return nullptr; + + 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); + } + } + + if (mesh->getNumTriangles() == 0) + return nullptr; + + auto shape = std::make_unique(mesh.get(), true); + std::ignore = mesh.release(); + + return shape; + } + + std::unique_ptr NiLines::getCollisionShape() const + { + return nullptr; + } + + std::unique_ptr NiParticles::getCollisionShape() const + { + return nullptr; + } + void BSSegmentedTriShape::SegmentData::read(NIFStream* nif) { nif->read(mFlags); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 0aaad40ed4..32746f7a9f 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -8,6 +8,8 @@ #include "base.hpp" +class btCollisionShape; + namespace Nif { @@ -146,6 +148,11 @@ namespace Nif void read(NIFStream* nif) override; void post(Reader& nif) override; + + virtual std::unique_ptr getCollisionShape() const + { + throw std::runtime_error("NiGeometry::getCollisionShape() called on base class"); + } }; // Abstract triangle-based geometry @@ -155,6 +162,7 @@ namespace Nif struct NiTriShape : NiTriBasedGeom { + std::unique_ptr getCollisionShape() const override; }; struct BSSegmentedTriShape : NiTriShape @@ -175,17 +183,20 @@ namespace Nif struct NiTriStrips : NiTriBasedGeom { + std::unique_ptr getCollisionShape() const override; }; struct NiLines : NiTriBasedGeom { + std::unique_ptr getCollisionShape() const override; }; struct NiParticles : NiGeometry { + std::unique_ptr getCollisionShape() const override; }; - struct BSLODTriShape : NiTriBasedGeom + struct BSLODTriShape : NiTriShape { std::array mLOD; void read(NIFStream* nif) override; diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index ec46afec41..96dff80004 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -6,22 +6,15 @@ #include #include -#include - #include - +#include #include - #include - -#include #include #include #include #include -#include - namespace { @@ -32,111 +25,6 @@ namespace return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X'); } - bool isTypeNiGeometry(int type) - { - switch (type) - { - case Nif::RC_NiTriShape: - case Nif::RC_NiTriStrips: - case Nif::RC_BSLODTriShape: - case Nif::RC_BSSegmentedTriShape: - return true; - } - return false; - } - - bool isTypeTriShape(int type) - { - switch (type) - { - case Nif::RC_NiTriShape: - case Nif::RC_BSLODTriShape: - case Nif::RC_BSSegmentedTriShape: - return true; - } - - return false; - } - - void prepareTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriBasedGeomData& data) - { - // FIXME: copying vertices/indices individually is unreasonable - const std::vector& vertices = data.mVertices; - mesh.preallocateVertices(static_cast(vertices.size())); - for (const osg::Vec3f& vertex : vertices) - mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false); - - mesh.preallocateIndices(static_cast(data.mNumTriangles) * 3); - } - - void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data) - { - prepareTriangleMesh(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]); - } - - void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data) - { - prepareTriangleMesh(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); - } - } - } - - template - auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function) - -> decltype(function(static_cast(geometry.mData.get()))) - { - if (isTypeTriShape(geometry.recType)) - { - auto data = static_cast(geometry.mData.getPtr()); - if (data->mTriangles.empty()) - return {}; - - return function(static_cast(*data)); - } - - if (geometry.recType == Nif::RC_NiTriStrips) - { - auto data = static_cast(geometry.mData.getPtr()); - if (data->mStrips.empty()) - return {}; - - return function(static_cast(*data)); - } - - return {}; - } - - std::unique_ptr makeChildMesh(const Nif::NiGeometry& geometry) - { - return handleNiGeometry(geometry, [&](const auto& data) { - auto mesh = std::make_unique(); - fillTriangleMesh(*mesh, data); - return mesh; - }); - } - } namespace NifBullet @@ -336,8 +224,8 @@ namespace NifBullet return; // Otherwise we'll want to notify the user. - Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << mShape->mFileName - << ". Treating it as a common NiTriShape."; + Log(Debug::Info) << "BulletNifLoader: RootCollisionNode is not attached to the root node in " + << mShape->mFileName << ". Treating it as a NiNode."; } else { @@ -349,8 +237,12 @@ namespace NifBullet if (node.recType == Nif::RC_AvoidNode) args.mAvoid = true; - if ((args.mAutogenerated || args.mIsCollisionNode) && isTypeNiGeometry(node.recType)) - handleNiTriShape(static_cast(node), parent, args); + if (args.mAutogenerated || args.mIsCollisionNode) + { + auto geometry = dynamic_cast(&node); + if (geometry) + handleGeometry(*geometry, parent, args); + } // For NiNodes, loop through children if (const Nif::NiNode* ninode = dynamic_cast(&node)) @@ -367,7 +259,7 @@ namespace NifBullet } } - void BulletNifLoader::handleNiTriShape( + void BulletNifLoader::handleGeometry( const Nif::NiGeometry& niGeometry, const Nif::Parent* nodeParent, HandleNodeArgs args) { // This flag comes from BSXFlags @@ -378,20 +270,14 @@ namespace NifBullet if (args.mHasTriMarkers && Misc::StringUtils::ciStartsWith(niGeometry.mName, "Tri EditorMarker")) return; - if (niGeometry.mData.empty() || niGeometry.mData->mVertices.empty()) - return; - if (!niGeometry.mSkin.empty()) args.mAnimated = false; // TODO: handle NiSkinPartition - std::unique_ptr childMesh = makeChildMesh(niGeometry); - if (childMesh == nullptr || childMesh->getNumTriangles() == 0) + std::unique_ptr childShape = niGeometry.getCollisionShape(); + if (childShape == nullptr) return; - auto childShape = std::make_unique(childMesh.get(), true); - std::ignore = childMesh.release(); - osg::Matrixf transform = niGeometry.mTransform.toMatrix(); for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent) transform *= parent->mNiNode.mTransform.toMatrix(); diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index c87c1242de..a80e6fdc3d 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -62,7 +62,7 @@ namespace NifBullet void handleRoot(Nif::FileView nif, const Nif::NiAVObject& node, HandleNodeArgs args); void handleNode(const Nif::NiAVObject& node, const Nif::Parent* parent, HandleNodeArgs args); - void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args); + void handleGeometry(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args); std::unique_ptr mCompoundShape; std::unique_ptr mAvoidCompoundShape;