mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 18:35:20 +00:00
Merge branch 'refactor_bullet_nif_loader' into 'master'
Refactor BulletNifLoader See merge request OpenMW/openmw!1344
This commit is contained in:
commit
a5bfec610c
@ -60,6 +60,19 @@ namespace
|
||||
{
|
||||
return isNear(lhs.getOrigin(), rhs.getOrigin()) && isNear(lhs.getBasis(), rhs.getBasis());
|
||||
}
|
||||
|
||||
struct WriteVec3f
|
||||
{
|
||||
osg::Vec3f mValue;
|
||||
|
||||
friend std::ostream& operator <<(std::ostream& stream, const WriteVec3f& value)
|
||||
{
|
||||
return stream << "osg::Vec3f {"
|
||||
<< std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue.x() << ", "
|
||||
<< std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue.y() << ", "
|
||||
<< std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue.z() << "}";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static std::ostream& operator <<(std::ostream& stream, const btVector3& value)
|
||||
@ -122,6 +135,17 @@ static std::ostream& operator <<(std::ostream& stream, const TriangleMeshShape&
|
||||
return stream << "}}";
|
||||
}
|
||||
|
||||
static bool operator ==(const BulletShape::CollisionBox& l, const BulletShape::CollisionBox& r)
|
||||
{
|
||||
const auto tie = [] (const BulletShape::CollisionBox& v) { return std::tie(v.mExtents, v.mCenter); };
|
||||
return tie(l) == tie(r);
|
||||
}
|
||||
|
||||
static std::ostream& operator <<(std::ostream& stream, const BulletShape::CollisionBox& value)
|
||||
{
|
||||
return stream << "CollisionBox {" << WriteVec3f {value.mExtents} << ", " << WriteVec3f {value.mCenter} << "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static std::ostream& operator <<(std::ostream& stream, const btCollisionShape& value)
|
||||
@ -162,8 +186,7 @@ namespace Resource
|
||||
{
|
||||
return compareObjects(lhs.mCollisionShape.get(), rhs.mCollisionShape.get())
|
||||
&& compareObjects(lhs.mAvoidCollisionShape.get(), rhs.mAvoidCollisionShape.get())
|
||||
&& lhs.mCollisionBox.mExtents == rhs.mCollisionBox.mExtents
|
||||
&& lhs.mCollisionBox.mCenter == rhs.mCollisionBox.mCenter
|
||||
&& lhs.mCollisionBox == rhs.mCollisionBox
|
||||
&& lhs.mAnimatedShapes == rhs.mAnimatedShapes;
|
||||
}
|
||||
|
||||
@ -172,8 +195,7 @@ namespace Resource
|
||||
return stream << "Resource::BulletShape {"
|
||||
<< value.mCollisionShape.get() << ", "
|
||||
<< value.mAvoidCollisionShape.get() << ", "
|
||||
<< "osg::Vec3f {" << value.mCollisionBox.mExtents << "}" << ", "
|
||||
<< "osg::Vec3f {" << value.mCollisionBox.mCenter << "}" << ", "
|
||||
<< value.mCollisionBox << ", "
|
||||
<< value.mAnimatedShapes
|
||||
<< "}";
|
||||
}
|
||||
@ -272,6 +294,12 @@ namespace
|
||||
value.recType = Nif::RC_NiTriShape;
|
||||
}
|
||||
|
||||
void init(Nif::NiTriStrips& value)
|
||||
{
|
||||
init(static_cast<Nif::NiGeometry&>(value));
|
||||
value.recType = Nif::RC_NiTriStrips;
|
||||
}
|
||||
|
||||
void init(Nif::NiSkinInstance& value)
|
||||
{
|
||||
value.data = Nif::NiSkinDataPtr(nullptr);
|
||||
@ -330,6 +358,8 @@ namespace
|
||||
Nif::NiTriShape mNiTriShape;
|
||||
Nif::NiTriShapeData mNiTriShapeData2;
|
||||
Nif::NiTriShape mNiTriShape2;
|
||||
Nif::NiTriStripsData mNiTriStripsData;
|
||||
Nif::NiTriStrips mNiTriStrips;
|
||||
Nif::NiSkinInstance mNiSkinInstance;
|
||||
Nif::NiStringExtraData mNiStringExtraData;
|
||||
Nif::NiStringExtraData mNiStringExtraData2;
|
||||
@ -361,6 +391,7 @@ namespace
|
||||
init(mNiNode3);
|
||||
init(mNiTriShape);
|
||||
init(mNiTriShape2);
|
||||
init(mNiTriStrips);
|
||||
init(mNiSkinInstance);
|
||||
init(mNiStringExtraData);
|
||||
init(mNiStringExtraData2);
|
||||
@ -375,6 +406,11 @@ namespace
|
||||
mNiTriShapeData2.vertices = {osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1)};
|
||||
mNiTriShapeData2.triangles = {0, 1, 2};
|
||||
mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2);
|
||||
|
||||
mNiTriStripsData.recType = Nif::RC_NiTriStripsData;
|
||||
mNiTriStripsData.vertices = {osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0), osg::Vec3f(0, 1, 0)};
|
||||
mNiTriStripsData.strips = {{0, 1, 2, 3}};
|
||||
mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriStripsData);
|
||||
}
|
||||
};
|
||||
|
||||
@ -389,6 +425,18 @@ namespace
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, should_ignore_nullptr_root)
|
||||
{
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(nullptr));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default)
|
||||
{
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -458,6 +506,7 @@ namespace
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
mNode.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -483,6 +532,7 @@ namespace
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
mNode.parent = &mNiNode;
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
@ -513,11 +563,13 @@ namespace
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
mNode.parent = &mNiNode;
|
||||
|
||||
mNode2.hasBounds = true;
|
||||
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
mNode2.parent = &mNiNode;
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
@ -547,12 +599,14 @@ namespace
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
mNode.parent = &mNiNode;
|
||||
|
||||
mNode2.hasBounds = true;
|
||||
mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
mNode2.parent = &mNiNode;
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
@ -631,6 +685,7 @@ namespace
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape)
|
||||
{
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -649,7 +704,9 @@ namespace
|
||||
TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape)
|
||||
{
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));
|
||||
mNiNode2.parent = &mNiNode;
|
||||
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
mNiTriShape.parent = &mNiNode2;
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
@ -666,6 +723,8 @@ namespace
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
|
||||
{
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiTriShape2.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
|
||||
Nif::NodePtr(&mNiTriShape),
|
||||
Nif::NodePtr(&mNiTriShape2)
|
||||
@ -688,6 +747,7 @@ namespace
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape)
|
||||
{
|
||||
mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance);
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -756,9 +816,11 @@ namespace
|
||||
{
|
||||
copy(mTransform, mNiTriShape.trafo);
|
||||
mNiTriShape.trafo.scale = 3;
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
|
||||
copy(mTransform, mNiTriShape2.trafo);
|
||||
mNiTriShape2.trafo.scale = 3;
|
||||
mNiTriShape2.parent = &mNiNode;
|
||||
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
|
||||
Nif::NodePtr(&mNiTriShape),
|
||||
@ -825,6 +887,7 @@ namespace
|
||||
mController.flags |= Nif::NiNode::ControllerFlag_Active;
|
||||
copy(mTransform, mNiTriShape.trafo);
|
||||
mNiTriShape.trafo.scale = 3;
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
copy(mTransform, mNiTriShape2.trafo);
|
||||
mNiTriShape2.trafo.scale = 3;
|
||||
mNiTriShape2.parent = &mNiNode;
|
||||
@ -841,7 +904,7 @@ namespace
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(1, 2, 3), btVector3(4, 2, 3), btVector3(4, 4.632747650146484375, 1.56172335147857666015625));
|
||||
triangles->addTriangle(btVector3(4, 8, 12), btVector3(16, 8, 12), btVector3(16, 18.5309906005859375, 6.246893405914306640625));
|
||||
std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
mesh->setLocalScaling(btVector3(1, 1, 1));
|
||||
|
||||
@ -860,8 +923,37 @@ namespace
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, should_add_static_mesh_to_existing_compound_mesh)
|
||||
{
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getRoot(1)).WillOnce(Return(&mNiTriShape2));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles2(new btTriangleMesh(false));
|
||||
triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1));
|
||||
|
||||
std::unique_ptr<btCompoundShape> compound(new btCompoundShape);
|
||||
compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles2.release(), true));
|
||||
|
||||
Resource::BulletShape expected;
|
||||
expected.mCollisionShape.reset(compound.release());
|
||||
expected.mAnimatedShapes = {{-1, 0}};
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape)
|
||||
{
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
mNiNode.recType = Nif::RC_AvoidNode;
|
||||
|
||||
@ -881,6 +973,7 @@ namespace
|
||||
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape)
|
||||
{
|
||||
mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr);
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -897,6 +990,7 @@ namespace
|
||||
{
|
||||
auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr());
|
||||
data->triangles.clear();
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -914,6 +1008,7 @@ namespace
|
||||
mNiStringExtraData.string = "NC___";
|
||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -932,6 +1027,7 @@ namespace
|
||||
mNiStringExtraData2.string = "NC___";
|
||||
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -949,6 +1045,7 @@ namespace
|
||||
mNiStringExtraData.string = "MRK";
|
||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
@ -966,8 +1063,10 @@ namespace
|
||||
mNiStringExtraData.string = "MRK";
|
||||
mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
|
||||
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
|
||||
mNiTriShape.parent = &mNiNode2;
|
||||
mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
mNiNode2.recType = Nif::RC_RootCollisionNode;
|
||||
mNiNode2.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));
|
||||
mNiNode.recType = Nif::RC_NiNode;
|
||||
|
||||
@ -983,4 +1082,134 @@ namespace
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, should_ignore_tri_shape_data_with_mismatching_data_rec_type)
|
||||
{
|
||||
mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriStripsData);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
const Resource::BulletShape expected;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_tri_strips_root_node_should_return_shape_with_triangle_mesh_shape)
|
||||
{
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||
triangles->addTriangle(btVector3(1, 0, 0), btVector3(0, 1, 0), btVector3(1, 1, 0));
|
||||
Resource::BulletShape expected;
|
||||
expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_mismatching_data_rec_type)
|
||||
{
|
||||
mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
const Resource::BulletShape expected;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_empty_strips)
|
||||
{
|
||||
mNiTriStripsData.strips.clear();
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
const Resource::BulletShape expected;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_static_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
|
||||
{
|
||||
mNiTriStripsData.strips.front() = {0, 1};
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
const Resource::BulletShape expected;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_avoid_collision_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
|
||||
{
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
mNiNode.recType = Nif::RC_AvoidNode;
|
||||
mNiTriStripsData.strips.front() = {0, 1};
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
const Resource::BulletShape expected;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, for_animated_mesh_should_ignore_tri_strips_data_with_less_than_3_strips)
|
||||
{
|
||||
mNiTriStripsData.strips.front() = {0, 1};
|
||||
mNiTriStrips.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriStrips)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
const Resource::BulletShape expected;
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, should_not_add_static_mesh_with_no_triangles_to_compound_shape)
|
||||
{
|
||||
mNiTriStripsData.strips.front() = {0, 1};
|
||||
mNiTriShape.parent = &mNiNode;
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getRoot(1)).WillOnce(Return(&mNiTriStrips));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
|
||||
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
|
||||
|
||||
std::unique_ptr<btCompoundShape> compound(new btCompoundShape);
|
||||
compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true));
|
||||
|
||||
Resource::BulletShape expected;
|
||||
expected.mCollisionShape.reset(compound.release());
|
||||
expected.mAnimatedShapes = {{-1, 0}};
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "bulletnifloader.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
#include <BulletCollision/CollisionShapes/btBoxShape.h>
|
||||
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
|
||||
@ -18,11 +20,11 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
osg::Matrixf getWorldTransform(const Nif::Node *node)
|
||||
osg::Matrixf getWorldTransform(const Nif::Node& node)
|
||||
{
|
||||
if(node->parent != nullptr)
|
||||
return node->trafo.toMatrix() * getWorldTransform(node->parent);
|
||||
return node->trafo.toMatrix();
|
||||
if(node.parent != nullptr)
|
||||
return node.trafo.toMatrix() * getWorldTransform(*node.parent);
|
||||
return node.trafo.toMatrix();
|
||||
}
|
||||
|
||||
bool pathFileNameStartsWithX(const std::string& path)
|
||||
@ -99,12 +101,56 @@ void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, co
|
||||
}
|
||||
}
|
||||
|
||||
void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiGeometry* geometry, const osg::Matrixf &transform = osg::Matrixf())
|
||||
template <class Function>
|
||||
auto handleNiGeometry(const Nif::NiGeometry& geometry, Function&& function)
|
||||
-> decltype(function(static_cast<const Nif::NiTriShapeData&>(geometry.data.get())))
|
||||
{
|
||||
if (geometry->recType == Nif::RC_NiTriShape || geometry->recType == Nif::RC_BSLODTriShape)
|
||||
fillTriangleMesh(mesh, static_cast<const Nif::NiTriShapeData&>(geometry->data.get()), transform);
|
||||
else if (geometry->recType == Nif::RC_NiTriStrips)
|
||||
fillTriangleMesh(mesh, static_cast<const Nif::NiTriStripsData&>(geometry->data.get()), transform);
|
||||
if (geometry.recType == Nif::RC_NiTriShape || geometry.recType == Nif::RC_BSLODTriShape)
|
||||
{
|
||||
if (geometry.data->recType != Nif::RC_NiTriShapeData)
|
||||
return {};
|
||||
|
||||
auto data = static_cast<const Nif::NiTriShapeData*>(geometry.data.getPtr());
|
||||
if (data->triangles.empty())
|
||||
return {};
|
||||
|
||||
return function(static_cast<const Nif::NiTriShapeData&>(*data));
|
||||
}
|
||||
|
||||
if (geometry.recType == Nif::RC_NiTriStrips)
|
||||
{
|
||||
if (geometry.data->recType != Nif::RC_NiTriStripsData)
|
||||
return {};
|
||||
|
||||
auto data = static_cast<const Nif::NiTriStripsData*>(geometry.data.getPtr());
|
||||
if (data->strips.empty())
|
||||
return {};
|
||||
|
||||
return function(static_cast<const Nif::NiTriStripsData&>(*data));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::monostate fillTriangleMesh(std::unique_ptr<btTriangleMesh>& mesh, const Nif::NiGeometry& geometry, const osg::Matrixf &transform)
|
||||
{
|
||||
return handleNiGeometry(geometry, [&] (const auto& data)
|
||||
{
|
||||
if (mesh == nullptr)
|
||||
mesh.reset(new btTriangleMesh(false));
|
||||
fillTriangleMesh(*mesh, data, transform);
|
||||
return std::monostate {};
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<btTriangleMesh> makeChildMesh(const Nif::NiGeometry& geometry)
|
||||
{
|
||||
return handleNiGeometry(geometry, [&] (const auto& data)
|
||||
{
|
||||
std::unique_ptr<btTriangleMesh> mesh(new btTriangleMesh);
|
||||
fillTriangleMesh(*mesh, data, osg::Matrixf());
|
||||
return mesh;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -141,7 +187,7 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
||||
// Try to find a valid bounding box first. If one's found for any root node, use that.
|
||||
for (const Nif::Node* node : roots)
|
||||
{
|
||||
if (findBoundingBox(node, filename))
|
||||
if (findBoundingBox(*node, filename))
|
||||
{
|
||||
const btVector3 extents = Misc::Convert::toBullet(mShape->mCollisionBox.mExtents);
|
||||
const btVector3 center = Misc::Convert::toBullet(mShape->mCollisionBox.mCenter);
|
||||
@ -164,13 +210,13 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
||||
// from the collision data present in every root node.
|
||||
for (const Nif::Node* node : roots)
|
||||
{
|
||||
bool autogenerated = hasAutoGeneratedCollision(node);
|
||||
handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated);
|
||||
bool autogenerated = hasAutoGeneratedCollision(*node);
|
||||
handleNode(filename, *node, 0, autogenerated, isAnimated, autogenerated);
|
||||
}
|
||||
|
||||
if (mCompoundShape)
|
||||
{
|
||||
if (mStaticMesh)
|
||||
if (mStaticMesh != nullptr && mStaticMesh->getNumTriangles() > 0)
|
||||
{
|
||||
btTransform trans;
|
||||
trans.setIdentity();
|
||||
@ -181,13 +227,13 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
||||
}
|
||||
mShape->mCollisionShape = std::move(mCompoundShape);
|
||||
}
|
||||
else if (mStaticMesh)
|
||||
else if (mStaticMesh != nullptr && mStaticMesh->getNumTriangles() > 0)
|
||||
{
|
||||
mShape->mCollisionShape.reset(new Resource::TriangleMeshShape(mStaticMesh.get(), true));
|
||||
mStaticMesh.release();
|
||||
}
|
||||
|
||||
if (mAvoidStaticMesh)
|
||||
if (mAvoidStaticMesh != nullptr && mAvoidStaticMesh->getNumTriangles() > 0)
|
||||
{
|
||||
mShape->mAvoidCollisionShape.reset(new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false));
|
||||
mAvoidStaticMesh.release();
|
||||
@ -198,41 +244,40 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
||||
|
||||
// Find a boundingBox in the node hierarchy.
|
||||
// Return: use bounding box for collision?
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string& filename)
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::Node& node, const std::string& filename)
|
||||
{
|
||||
if (node->hasBounds)
|
||||
if (node.hasBounds)
|
||||
{
|
||||
unsigned int type = node->bounds.type;
|
||||
unsigned int type = node.bounds.type;
|
||||
switch (type)
|
||||
{
|
||||
case Nif::NiBoundingVolume::Type::BOX_BV:
|
||||
mShape->mCollisionBox.mExtents = node->bounds.box.extents;
|
||||
mShape->mCollisionBox.mCenter = node->bounds.box.center;
|
||||
mShape->mCollisionBox.mExtents = node.bounds.box.extents;
|
||||
mShape->mCollisionBox.mCenter = node.bounds.box.center;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::stringstream warning;
|
||||
warning << "Unsupported NiBoundingVolume type " << type << " in node " << node->recIndex;
|
||||
warning << "Unsupported NiBoundingVolume type " << type << " in node " << node.recIndex;
|
||||
warning << " in file " << filename;
|
||||
warn(warning.str());
|
||||
}
|
||||
}
|
||||
|
||||
if (node->flags & Nif::NiNode::Flag_BBoxCollision)
|
||||
if (node.flags & Nif::NiNode::Flag_BBoxCollision)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
|
||||
if(ninode)
|
||||
if (const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(&node))
|
||||
{
|
||||
const Nif::NodeList &list = ninode->children;
|
||||
for(size_t i = 0;i < list.length();i++)
|
||||
{
|
||||
if(!list[i].empty())
|
||||
{
|
||||
if (findBoundingBox(list[i].getPtr(), filename))
|
||||
if (findBoundingBox(list[i].get(), filename))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -240,10 +285,9 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string&
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode)
|
||||
bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node& rootNode)
|
||||
{
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(rootNode);
|
||||
if(ninode)
|
||||
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&rootNode))
|
||||
{
|
||||
const Nif::NodeList &list = ninode->children;
|
||||
for(size_t i = 0;i < list.length();i++)
|
||||
@ -258,32 +302,32 @@ bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode)
|
||||
return true;
|
||||
}
|
||||
|
||||
void BulletNifLoader::handleNode(const std::string& fileName, 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, bool avoid)
|
||||
{
|
||||
// TODO: allow on-the fly collision switching via toggling this flag
|
||||
if (node->recType == Nif::RC_NiCollisionSwitch && !(node->flags & Nif::NiNode::Flag_ActiveCollision))
|
||||
if (node.recType == Nif::RC_NiCollisionSwitch && !(node.flags & Nif::NiNode::Flag_ActiveCollision))
|
||||
return;
|
||||
|
||||
// Accumulate the flags from all the child nodes. This works for all
|
||||
// the flags we currently use, at least.
|
||||
flags |= node->flags;
|
||||
flags |= node.flags;
|
||||
|
||||
if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController
|
||||
&& (node->controller->flags & Nif::NiNode::ControllerFlag_Active))
|
||||
if (!node.controller.empty() && node.controller->recType == Nif::RC_NiKeyframeController
|
||||
&& (node.controller->flags & Nif::NiNode::ControllerFlag_Active))
|
||||
isAnimated = true;
|
||||
|
||||
isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode);
|
||||
isCollisionNode = isCollisionNode || (node.recType == Nif::RC_RootCollisionNode);
|
||||
|
||||
// Don't collide with AvoidNode shapes
|
||||
avoid = avoid || (node->recType == Nif::RC_AvoidNode);
|
||||
avoid = avoid || (node.recType == Nif::RC_AvoidNode);
|
||||
|
||||
// We encountered a RootCollisionNode inside autogenerated mesh. It is not right.
|
||||
if (node->recType == Nif::RC_RootCollisionNode && autogenerated)
|
||||
if (node.recType == Nif::RC_RootCollisionNode && autogenerated)
|
||||
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << fileName << ". Treating it as a common NiTriShape.";
|
||||
|
||||
// Check for extra data
|
||||
for (Nif::ExtraPtr e = node->extra; !e.empty(); e = e->next)
|
||||
for (Nif::ExtraPtr e = node.extra; !e.empty(); e = e->next)
|
||||
{
|
||||
if (e->recType == Nif::RC_NiStringExtraData)
|
||||
{
|
||||
@ -310,108 +354,79 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n
|
||||
// NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape!
|
||||
// It must be ignored completely.
|
||||
// (occurs in tr_ex_imp_wall_arch_04.nif)
|
||||
if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape
|
||||
|| node->recType == Nif::RC_NiTriStrips
|
||||
|| node->recType == Nif::RC_BSLODTriShape))
|
||||
if(!node.hasBounds && (node.recType == Nif::RC_NiTriShape
|
||||
|| node.recType == Nif::RC_NiTriStrips
|
||||
|| node.recType == Nif::RC_BSLODTriShape))
|
||||
{
|
||||
handleNiTriShape(node, flags, getWorldTransform(node), isAnimated, avoid);
|
||||
}
|
||||
}
|
||||
|
||||
// For NiNodes, loop through children
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
|
||||
if(ninode)
|
||||
if (const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(&node))
|
||||
{
|
||||
const Nif::NodeList &list = ninode->children;
|
||||
for(size_t i = 0;i < list.length();i++)
|
||||
{
|
||||
if(!list[i].empty())
|
||||
handleNode(fileName, list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated, avoid);
|
||||
if (list[i].empty())
|
||||
continue;
|
||||
|
||||
assert(list[i].get().parent == &node);
|
||||
handleNode(fileName, list[i].get(), flags, isCollisionNode, isAnimated, autogenerated, avoid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, const osg::Matrixf &transform,
|
||||
void BulletNifLoader::handleNiTriShape(const Nif::Node& nifNode, int flags, const osg::Matrixf &transform,
|
||||
bool isAnimated, bool avoid)
|
||||
{
|
||||
assert(nifNode != nullptr);
|
||||
|
||||
// If the object was marked "NCO" earlier, it shouldn't collide with
|
||||
// anything. So don't do anything.
|
||||
if ((flags & 0x800))
|
||||
return;
|
||||
|
||||
auto niGeometry = static_cast<const Nif::NiGeometry*>(nifNode);
|
||||
if (niGeometry->data.empty() || niGeometry->data->vertices.empty())
|
||||
handleNiTriShape(static_cast<const Nif::NiGeometry&>(nifNode), transform, isAnimated, avoid);
|
||||
}
|
||||
|
||||
void BulletNifLoader::handleNiTriShape(const Nif::NiGeometry& niGeometry, const osg::Matrixf &transform,
|
||||
bool isAnimated, bool avoid)
|
||||
{
|
||||
if (niGeometry.data.empty() || niGeometry.data->vertices.empty())
|
||||
return;
|
||||
|
||||
if (niGeometry->recType == Nif::RC_NiTriShape || niGeometry->recType == Nif::RC_BSLODTriShape)
|
||||
{
|
||||
if (niGeometry->data->recType != Nif::RC_NiTriShapeData)
|
||||
return;
|
||||
|
||||
auto data = static_cast<const Nif::NiTriShapeData*>(niGeometry->data.getPtr());
|
||||
if (data->triangles.empty())
|
||||
return;
|
||||
}
|
||||
else if (niGeometry->recType == Nif::RC_NiTriStrips)
|
||||
{
|
||||
if (niGeometry->data->recType != Nif::RC_NiTriStripsData)
|
||||
return;
|
||||
|
||||
auto data = static_cast<const Nif::NiTriStripsData*>(niGeometry->data.getPtr());
|
||||
if (data->strips.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
if (!niGeometry->skin.empty())
|
||||
if (!niGeometry.skin.empty())
|
||||
isAnimated = false;
|
||||
|
||||
if (isAnimated)
|
||||
{
|
||||
std::unique_ptr<btTriangleMesh> childMesh = makeChildMesh(niGeometry);
|
||||
if (childMesh == nullptr || childMesh->getNumTriangles() == 0)
|
||||
return;
|
||||
|
||||
if (!mCompoundShape)
|
||||
mCompoundShape.reset(new btCompoundShape);
|
||||
|
||||
std::unique_ptr<btTriangleMesh> childMesh(new btTriangleMesh);
|
||||
|
||||
fillTriangleMesh(*childMesh, niGeometry);
|
||||
|
||||
std::unique_ptr<Resource::TriangleMeshShape> childShape(new Resource::TriangleMeshShape(childMesh.get(), true));
|
||||
childMesh.release();
|
||||
|
||||
float scale = nifNode->trafo.scale;
|
||||
const Nif::Node* parent = nifNode;
|
||||
while (parent->parent)
|
||||
{
|
||||
parent = parent->parent;
|
||||
float scale = niGeometry.trafo.scale;
|
||||
for (const Nif::Node* parent = niGeometry.parent; parent != nullptr; parent = parent->parent)
|
||||
scale *= parent->trafo.scale;
|
||||
}
|
||||
osg::Quat q = transform.getRotate();
|
||||
osg::Vec3f v = transform.getTrans();
|
||||
childShape->setLocalScaling(btVector3(scale, scale, scale));
|
||||
|
||||
btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z()));
|
||||
|
||||
mShape->mAnimatedShapes.emplace(nifNode->recIndex, mCompoundShape->getNumChildShapes());
|
||||
mShape->mAnimatedShapes.emplace(niGeometry.recIndex, mCompoundShape->getNumChildShapes());
|
||||
|
||||
mCompoundShape->addChildShape(trans, childShape.get());
|
||||
childShape.release();
|
||||
}
|
||||
else if (avoid)
|
||||
{
|
||||
if (!mAvoidStaticMesh)
|
||||
mAvoidStaticMesh.reset(new btTriangleMesh(false));
|
||||
|
||||
fillTriangleMesh(*mAvoidStaticMesh, niGeometry, transform);
|
||||
}
|
||||
fillTriangleMesh(mAvoidStaticMesh, niGeometry, transform);
|
||||
else
|
||||
{
|
||||
if (!mStaticMesh)
|
||||
mStaticMesh.reset(new btTriangleMesh(false));
|
||||
|
||||
// Static shape, just transform all vertices into position
|
||||
fillTriangleMesh(*mStaticMesh, niGeometry, transform);
|
||||
}
|
||||
fillTriangleMesh(mStaticMesh, niGeometry, transform);
|
||||
}
|
||||
|
||||
} // namespace NifBullet
|
||||
|
@ -27,6 +27,7 @@ namespace Nif
|
||||
struct Transformation;
|
||||
struct NiTriShape;
|
||||
struct NiTriStrips;
|
||||
struct NiGeometry;
|
||||
}
|
||||
|
||||
namespace NifBullet
|
||||
@ -52,14 +53,16 @@ public:
|
||||
osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file);
|
||||
|
||||
private:
|
||||
bool findBoundingBox(const Nif::Node* node, const std::string& filename);
|
||||
bool findBoundingBox(const Nif::Node& node, const std::string& filename);
|
||||
|
||||
void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode,
|
||||
void handleNode(const std::string& fileName, const Nif::Node& node, int flags, bool isCollisionNode,
|
||||
bool isAnimated=false, bool autogenerated=false, bool avoid=false);
|
||||
|
||||
bool hasAutoGeneratedCollision(const Nif::Node *rootNode);
|
||||
bool hasAutoGeneratedCollision(const Nif::Node& rootNode);
|
||||
|
||||
void handleNiTriShape(const Nif::Node *nifNode, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid);
|
||||
void handleNiTriShape(const Nif::Node& nifNode, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid);
|
||||
|
||||
void handleNiTriShape(const Nif::NiGeometry& nifNode, const osg::Matrixf& transform, bool isAnimated, bool avoid);
|
||||
|
||||
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mCompoundShape;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user