Skip to content

Commit

Permalink
Merge branch 'bullettotheheart' into 'master'
Browse files Browse the repository at this point in the history
BulletNifLoader: Move NiGeometry triangulation to NiGeometry

See merge request OpenMW/openmw!3613
  • Loading branch information
Assumeru committed Nov 29, 2023
2 parents 76172c7 + 9a43ca2 commit fe67407
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 128 deletions.
96 changes: 96 additions & 0 deletions components/nif/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,33 @@

#include <cstdint>

#include <BulletCollision/CollisionShapes/btTriangleMesh.h>

#include <components/misc/convert.hpp>
#include <components/misc/strings/algorithm.hpp>
#include <components/resource/bulletshape.hpp>

#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<osg::Vec3f>& vertices = data.mVertices;
mesh.preallocateVertices(static_cast<int>(vertices.size()));
for (const osg::Vec3f& vertex : vertices)
mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false);

mesh.preallocateIndices(static_cast<int>(data.mNumTriangles) * 3);
}

}

namespace Nif
{

Expand Down Expand Up @@ -218,6 +238,82 @@ namespace Nif
}
}

std::unique_ptr<btCollisionShape> NiTriShape::getCollisionShape() const
{
if (mData.empty() || mData->mVertices.empty())
return nullptr;

auto data = static_cast<const NiTriShapeData*>(mData.getPtr());
if (data->mNumTriangles == 0 || data->mTriangles.empty())
return nullptr;

auto mesh = std::make_unique<btTriangleMesh>();
triBasedGeomToBtTriangleMesh(*mesh, *data);
const std::vector<unsigned short>& 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<Resource::TriangleMeshShape>(mesh.get(), true);
std::ignore = mesh.release();

return shape;
}

std::unique_ptr<btCollisionShape> NiTriStrips::getCollisionShape() const
{
if (mData.empty() || mData->mVertices.empty())
return nullptr;

auto data = static_cast<const NiTriStripsData*>(mData.getPtr());
if (data->mNumTriangles == 0 || data->mStrips.empty())
return nullptr;

auto mesh = std::make_unique<btTriangleMesh>();
triBasedGeomToBtTriangleMesh(*mesh, *data);
for (const std::vector<unsigned short>& 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<Resource::TriangleMeshShape>(mesh.get(), true);
std::ignore = mesh.release();

return shape;
}

std::unique_ptr<btCollisionShape> NiLines::getCollisionShape() const
{
return nullptr;
}

std::unique_ptr<btCollisionShape> NiParticles::getCollisionShape() const
{
return nullptr;
}

void BSSegmentedTriShape::SegmentData::read(NIFStream* nif)
{
nif->read(mFlags);
Expand Down
13 changes: 12 additions & 1 deletion components/nif/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "base.hpp"

class btCollisionShape;

namespace Nif
{

Expand Down Expand Up @@ -146,6 +148,11 @@ namespace Nif

void read(NIFStream* nif) override;
void post(Reader& nif) override;

virtual std::unique_ptr<btCollisionShape> getCollisionShape() const
{
throw std::runtime_error("NiGeometry::getCollisionShape() called on base class");
}
};

// Abstract triangle-based geometry
Expand All @@ -155,6 +162,7 @@ namespace Nif

struct NiTriShape : NiTriBasedGeom
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};

struct BSSegmentedTriShape : NiTriShape
Expand All @@ -175,17 +183,20 @@ namespace Nif

struct NiTriStrips : NiTriBasedGeom
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};

struct NiLines : NiTriBasedGeom
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};

struct NiParticles : NiGeometry
{
std::unique_ptr<btCollisionShape> getCollisionShape() const override;
};

struct BSLODTriShape : NiTriBasedGeom
struct BSLODTriShape : NiTriShape
{
std::array<uint32_t, 3> mLOD;
void read(NIFStream* nif) override;
Expand Down
138 changes: 12 additions & 126 deletions components/nifbullet/bulletnifloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,15 @@
#include <variant>
#include <vector>

#include <BulletCollision/CollisionShapes/btTriangleMesh.h>

#include <components/debug/debuglog.hpp>

#include <components/files/conversion.hpp>
#include <components/misc/convert.hpp>

#include <components/misc/strings/algorithm.hpp>

#include <components/nif/data.hpp>
#include <components/nif/extra.hpp>
#include <components/nif/nifstream.hpp>
#include <components/nif/node.hpp>
#include <components/nif/parent.hpp>

#include <components/files/conversion.hpp>

namespace
{

Expand All @@ -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<osg::Vec3f>& vertices = data.mVertices;
mesh.preallocateVertices(static_cast<int>(vertices.size()));
for (const osg::Vec3f& vertex : vertices)
mesh.findOrAddVertex(Misc::Convert::toBullet(vertex), false);

mesh.preallocateIndices(static_cast<int>(data.mNumTriangles) * 3);
}

void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data)
{
prepareTriangleMesh(mesh, data);
const std::vector<unsigned short>& 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<unsigned short>& 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 <class Function>
auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function)
-> decltype(function(static_cast<const Nif::NiTriShapeData&>(geometry.mData.get())))
{
if (isTypeTriShape(geometry.recType))
{
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.mData.getPtr());
if (data->mTriangles.empty())
return {};

return function(static_cast<const Nif::NiTriShapeData&>(*data));
}

if (geometry.recType == Nif::RC_NiTriStrips)
{
auto data = static_cast<const Nif::NiTriStripsData*>(geometry.mData.getPtr());
if (data->mStrips.empty())
return {};

return function(static_cast<const Nif::NiTriStripsData&>(*data));
}

return {};
}

std::unique_ptr<btTriangleMesh> makeChildMesh(const Nif::NiGeometry& geometry)
{
return handleNiGeometry(geometry, [&](const auto& data) {
auto mesh = std::make_unique<btTriangleMesh>();
fillTriangleMesh(*mesh, data);
return mesh;
});
}

}

namespace NifBullet
Expand Down Expand Up @@ -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
{
Expand All @@ -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<const Nif::NiGeometry&>(node), parent, args);
if (args.mAutogenerated || args.mIsCollisionNode)
{
auto geometry = dynamic_cast<const Nif::NiGeometry*>(&node);
if (geometry)
handleGeometry(*geometry, parent, args);
}

// For NiNodes, loop through children
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&node))
Expand All @@ -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
Expand All @@ -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<btTriangleMesh> childMesh = makeChildMesh(niGeometry);
if (childMesh == nullptr || childMesh->getNumTriangles() == 0)
std::unique_ptr<btCollisionShape> childShape = niGeometry.getCollisionShape();
if (childShape == nullptr)
return;

auto childShape = std::make_unique<Resource::TriangleMeshShape>(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();
Expand Down
2 changes: 1 addition & 1 deletion components/nifbullet/bulletnifloader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<btCompoundShape, Resource::DeleteCollisionShape> mCompoundShape;
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mAvoidCompoundShape;
Expand Down

0 comments on commit fe67407

Please sign in to comment.