1 #include <components/nifbullet/bulletnifloader.hpp>
2 #include <components/bullethelpers/processtrianglecallback.hpp>
3 #include <components/nif/node.hpp>
4 
5 #include <BulletCollision/CollisionShapes/btBoxShape.h>
6 #include <BulletCollision/CollisionShapes/btCompoundShape.h>
7 #include <BulletCollision/CollisionShapes/btTriangleMesh.h>
8 
9 #include <gtest/gtest.h>
10 #include <gmock/gmock.h>
11 
12 #include <algorithm>
13 
14 namespace
15 {
16     template <class T>
compareObjects(const T * lhs,const T * rhs)17     bool compareObjects(const T* lhs, const T* rhs)
18     {
19         return (!lhs && !rhs) || (lhs && rhs && *lhs == *rhs);
20     }
21 
getTriangles(const btBvhTriangleMeshShape & shape)22     std::vector<btVector3> getTriangles(const btBvhTriangleMeshShape& shape)
23     {
24         std::vector<btVector3> result;
25         auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) {
26             for (std::size_t i = 0; i < 3; ++i)
27                 result.push_back(triangle[i]);
28         });
29         btVector3 aabbMin;
30         btVector3 aabbMax;
31         shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
32         shape.processAllTriangles(&callback, aabbMin, aabbMax);
33         return result;
34     }
35 
isNear(btScalar lhs,btScalar rhs)36     bool isNear(btScalar lhs, btScalar rhs)
37     {
38         return std::abs(lhs - rhs) <= 1e-5;
39     }
40 
isNear(const btVector3 & lhs,const btVector3 & rhs)41     bool isNear(const btVector3& lhs, const btVector3& rhs)
42     {
43         return std::equal(
44             static_cast<const btScalar*>(lhs),
45             static_cast<const btScalar*>(lhs) + 3,
46             static_cast<const btScalar*>(rhs),
47             [] (btScalar lhs, btScalar rhs) { return isNear(lhs, rhs); }
48         );
49     }
50 
isNear(const btMatrix3x3 & lhs,const btMatrix3x3 & rhs)51     bool isNear(const btMatrix3x3& lhs, const btMatrix3x3& rhs)
52     {
53         for (int i = 0; i < 3; ++i)
54             if (!isNear(lhs[i], rhs[i]))
55                 return false;
56         return true;
57     }
58 
isNear(const btTransform & lhs,const btTransform & rhs)59     bool isNear(const btTransform& lhs, const btTransform& rhs)
60     {
61         return isNear(lhs.getOrigin(), rhs.getOrigin()) && isNear(lhs.getBasis(), rhs.getBasis());
62     }
63 }
64 
operator <<(std::ostream & stream,const btVector3 & value)65 static std::ostream& operator <<(std::ostream& stream, const btVector3& value)
66 {
67     return stream << "btVector3 {"
68         << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getX() << ", "
69         << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getY() << ", "
70         << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getZ() << "}";
71 }
72 
operator <<(std::ostream & stream,const btMatrix3x3 & value)73 static std::ostream& operator <<(std::ostream& stream, const btMatrix3x3& value)
74 {
75     stream << "btMatrix3x3 {";
76     for (int i = 0; i < 3; ++i)
77          stream << value.getRow(i) << ", ";
78     return stream << "}";
79 }
80 
operator <<(std::ostream & stream,const btTransform & value)81 static std::ostream& operator <<(std::ostream& stream, const btTransform& value)
82 {
83     return stream << "btTransform {" << value.getBasis() << ", " << value.getOrigin() << "}";
84 }
85 
86 static std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value);
87 
operator <<(std::ostream & stream,const btCompoundShape & value)88 static std::ostream& operator <<(std::ostream& stream, const btCompoundShape& value)
89 {
90     stream << "btCompoundShape {" << value.getLocalScaling() << ", ";
91     stream << "{";
92     for (int i = 0; i < value.getNumChildShapes(); ++i)
93         stream << value.getChildShape(i) << ", ";
94     stream << "},";
95     stream << "{";
96     for (int i = 0; i < value.getNumChildShapes(); ++i)
97         stream << value.getChildTransform(i) << ", ";
98     stream << "}";
99     return stream << "}";
100 }
101 
operator <<(std::ostream & stream,const btBoxShape & value)102 static std::ostream& operator <<(std::ostream& stream, const btBoxShape& value)
103 {
104     return stream << "btBoxShape {" << value.getLocalScaling() << ", " << value.getHalfExtentsWithoutMargin() << "}";
105 }
106 
107 namespace Resource
108 {
109 
operator <<(std::ostream & stream,const TriangleMeshShape & value)110 static std::ostream& operator <<(std::ostream& stream, const TriangleMeshShape& value)
111 {
112     stream << "Resource::TriangleMeshShape {" << value.getLocalScaling() << ", "
113         << value.usesQuantizedAabbCompression() << ", " << value.getOwnsBvh() << ", {";
114     auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) {
115         for (std::size_t i = 0; i < 3; ++i)
116             stream << triangle[i] << ", ";
117     });
118     btVector3 aabbMin;
119     btVector3 aabbMax;
120     value.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
121     value.processAllTriangles(&callback, aabbMin, aabbMax);
122     return stream << "}}";
123 }
124 
125 }
126 
operator <<(std::ostream & stream,const btCollisionShape & value)127 static std::ostream& operator <<(std::ostream& stream, const btCollisionShape& value)
128 {
129     switch (value.getShapeType())
130     {
131         case COMPOUND_SHAPE_PROXYTYPE:
132             return stream << static_cast<const btCompoundShape&>(value);
133         case BOX_SHAPE_PROXYTYPE:
134             return stream << static_cast<const btBoxShape&>(value);
135         case TRIANGLE_MESH_SHAPE_PROXYTYPE:
136             if (const auto casted = dynamic_cast<const Resource::TriangleMeshShape*>(&value))
137                 return stream << *casted;
138             break;
139     }
140     return stream << "btCollisionShape {" << value.getShapeType() << "}";
141 }
142 
operator <<(std::ostream & stream,const btCollisionShape * value)143 static std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value)
144 {
145     return value ? stream << "&" << *value : stream << "nullptr";
146 }
147 
148 namespace std
149 {
operator <<(std::ostream & stream,const map<int,int> & value)150     static std::ostream& operator <<(std::ostream& stream, const map<int, int>& value)
151     {
152         stream << "std::map<int, int> {";
153         for (const auto& v : value)
154             stream << "{" << v.first << ", " << v.second << "},";
155         return stream << "}";
156     }
157 }
158 
159 namespace Resource
160 {
operator ==(const Resource::BulletShape & lhs,const Resource::BulletShape & rhs)161     static bool operator ==(const Resource::BulletShape& lhs, const Resource::BulletShape& rhs)
162     {
163         return compareObjects(lhs.mCollisionShape, rhs.mCollisionShape)
164             && compareObjects(lhs.mAvoidCollisionShape, rhs.mAvoidCollisionShape)
165             && lhs.mCollisionBox.extents == rhs.mCollisionBox.extents
166             && lhs.mCollisionBox.center == rhs.mCollisionBox.center
167             && lhs.mAnimatedShapes == rhs.mAnimatedShapes;
168     }
169 
operator <<(std::ostream & stream,const Resource::BulletShape & value)170     static std::ostream& operator <<(std::ostream& stream, const Resource::BulletShape& value)
171     {
172         return stream << "Resource::BulletShape {"
173             << value.mCollisionShape << ", "
174             << value.mAvoidCollisionShape << ", "
175             << "osg::Vec3f {" << value.mCollisionBox.extents << "}" << ", "
176             << "osg::Vec3f {" << value.mCollisionBox.center << "}" << ", "
177             << value.mAnimatedShapes
178             << "}";
179     }
180 }
181 
182 static bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs);
183 
operator ==(const btCompoundShape & lhs,const btCompoundShape & rhs)184 static bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs)
185 {
186     if (lhs.getNumChildShapes() != rhs.getNumChildShapes() || lhs.getLocalScaling() != rhs.getLocalScaling())
187         return false;
188     for (int i = 0; i < lhs.getNumChildShapes(); ++i)
189     {
190         if (!compareObjects(lhs.getChildShape(i), rhs.getChildShape(i))
191                 || !isNear(lhs.getChildTransform(i), rhs.getChildTransform(i)))
192             return false;
193     }
194     return true;
195 }
196 
operator ==(const btBoxShape & lhs,const btBoxShape & rhs)197 static bool operator ==(const btBoxShape& lhs, const btBoxShape& rhs)
198 {
199     return isNear(lhs.getLocalScaling(), rhs.getLocalScaling())
200         && lhs.getHalfExtentsWithoutMargin() == rhs.getHalfExtentsWithoutMargin();
201 }
202 
operator ==(const btBvhTriangleMeshShape & lhs,const btBvhTriangleMeshShape & rhs)203 static bool operator ==(const btBvhTriangleMeshShape& lhs, const btBvhTriangleMeshShape& rhs)
204 {
205     return isNear(lhs.getLocalScaling(), rhs.getLocalScaling())
206         && lhs.usesQuantizedAabbCompression() == rhs.usesQuantizedAabbCompression()
207         && lhs.getOwnsBvh() == rhs.getOwnsBvh()
208         && getTriangles(lhs) == getTriangles(rhs);
209 }
210 
operator ==(const btCollisionShape & lhs,const btCollisionShape & rhs)211 static bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs)
212 {
213     if (lhs.getShapeType() != rhs.getShapeType())
214         return false;
215     switch (lhs.getShapeType())
216     {
217         case COMPOUND_SHAPE_PROXYTYPE:
218             return static_cast<const btCompoundShape&>(lhs) == static_cast<const btCompoundShape&>(rhs);
219         case BOX_SHAPE_PROXYTYPE:
220             return static_cast<const btBoxShape&>(lhs) == static_cast<const btBoxShape&>(rhs);
221         case TRIANGLE_MESH_SHAPE_PROXYTYPE:
222             if (const auto lhsCasted = dynamic_cast<const Resource::TriangleMeshShape*>(&lhs))
223                 if (const auto rhsCasted = dynamic_cast<const Resource::TriangleMeshShape*>(&rhs))
224                     return *lhsCasted == *rhsCasted;
225             return false;
226     }
227     return false;
228 }
229 
230 namespace
231 {
232     using namespace testing;
233     using NifBullet::BulletNifLoader;
234 
init(Nif::Transformation & value)235     void init(Nif::Transformation& value)
236     {
237         value = Nif::Transformation::getIdentity();
238     }
239 
init(Nif::Extra & value)240     void init(Nif::Extra& value)
241     {
242         value.next = Nif::ExtraPtr(nullptr);
243     }
244 
init(Nif::Named & value)245     void init(Nif::Named& value)
246     {
247         value.extra = Nif::ExtraPtr(nullptr);
248         value.extralist = Nif::ExtraList();
249         value.controller = Nif::ControllerPtr(nullptr);
250     }
251 
init(Nif::Node & value)252     void init(Nif::Node& value)
253     {
254         init(static_cast<Nif::Named&>(value));
255         value.flags = 0;
256         init(value.trafo);
257         value.hasBounds = false;
258         value.parent = nullptr;
259         value.isBone = false;
260     }
261 
init(Nif::NiGeometry & value)262     void init(Nif::NiGeometry& value)
263     {
264         init(static_cast<Nif::Node&>(value));
265         value.data = Nif::NiGeometryDataPtr(nullptr);
266         value.skin = Nif::NiSkinInstancePtr(nullptr);
267     }
268 
init(Nif::NiTriShape & value)269     void init(Nif::NiTriShape& value)
270     {
271         init(static_cast<Nif::NiGeometry&>(value));
272         value.recType = Nif::RC_NiTriShape;
273     }
274 
init(Nif::NiSkinInstance & value)275     void init(Nif::NiSkinInstance& value)
276     {
277         value.data = Nif::NiSkinDataPtr(nullptr);
278         value.root = Nif::NodePtr(nullptr);
279     }
280 
init(Nif::Controller & value)281     void init(Nif::Controller& value)
282     {
283         value.next = Nif::ControllerPtr(nullptr);
284         value.flags = 0;
285         value.frequency = 0;
286         value.phase = 0;
287         value.timeStart = 0;
288         value.timeStop = 0;
289         value.target = Nif::NamedPtr(nullptr);
290     }
291 
copy(const btTransform & src,Nif::Transformation & dst)292     void copy(const btTransform& src, Nif::Transformation& dst)
293     {
294         dst.pos = osg::Vec3f(src.getOrigin().x(), src.getOrigin().y(), src.getOrigin().z());
295         for (int row = 0; row < 3; ++row)
296             for (int column = 0; column < 3; ++column)
297                 dst.rotation.mValues[column][row] = src.getBasis().getRow(row)[column];
298     }
299 
300     struct NifFileMock : Nif::File
301     {
302         MOCK_METHOD(Nif::Record*, getRecord, (std::size_t), (const, override));
303         MOCK_METHOD(std::size_t, numRecords, (), (const, override));
304         MOCK_METHOD(Nif::Record*, getRoot, (std::size_t), (const, override));
305         MOCK_METHOD(std::size_t, numRoots, (), (const, override));
306         MOCK_METHOD(std::string, getString, (uint32_t), (const, override));
307         MOCK_METHOD(void, setUseSkinning, (bool), (override));
308         MOCK_METHOD(bool, getUseSkinning, (), (const, override));
309         MOCK_METHOD(std::string, getFilename, (), (const, override));
310         MOCK_METHOD(unsigned int, getVersion, (), (const, override));
311         MOCK_METHOD(unsigned int, getUserVersion, (), (const, override));
312         MOCK_METHOD(unsigned int, getBethVersion, (), (const, override));
313     };
314 
315     struct RecordMock : Nif::Record
316     {
317         MOCK_METHOD(void, read, (Nif::NIFStream *nif), (override));
318     };
319 
320     struct TestBulletNifLoader : Test
321     {
322         BulletNifLoader mLoader;
323         const StrictMock<const NifFileMock> mNifFile;
324         Nif::Node mNode;
325         Nif::Node mNode2;
326         Nif::NiNode mNiNode;
327         Nif::NiNode mNiNode2;
328         Nif::NiNode mNiNode3;
329         Nif::NiTriShapeData mNiTriShapeData;
330         Nif::NiTriShape mNiTriShape;
331         Nif::NiTriShapeData mNiTriShapeData2;
332         Nif::NiTriShape mNiTriShape2;
333         Nif::NiSkinInstance mNiSkinInstance;
334         Nif::NiStringExtraData mNiStringExtraData;
335         Nif::NiStringExtraData mNiStringExtraData2;
336         Nif::Controller mController;
337         btTransform mTransform {btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3)};
338         btTransform mResultTransform {
339             btMatrix3x3(
340                 1, 0, 0,
341                 0, 0.82417738437652587890625, 0.56633174419403076171875,
342                 0, -0.56633174419403076171875, 0.82417738437652587890625
343             ),
344             btVector3(1, 2, 3)
345         };
346         btTransform mResultTransform2 {
347             btMatrix3x3(
348                 1, 0, 0,
349                 0, 0.7951543331146240234375, 0.606407105922698974609375,
350                 0, -0.606407105922698974609375, 0.7951543331146240234375
351             ),
352             btVector3(4, 8, 12)
353         };
354 
TestBulletNifLoader__anon6cda9ea70511::TestBulletNifLoader355         TestBulletNifLoader()
356         {
357             init(mNode);
358             init(mNode2);
359             init(mNiNode);
360             init(mNiNode2);
361             init(mNiNode3);
362             init(mNiTriShape);
363             init(mNiTriShape2);
364             init(mNiSkinInstance);
365             init(mNiStringExtraData);
366             init(mNiStringExtraData2);
367             init(mController);
368 
369             mNiTriShapeData.recType = Nif::RC_NiTriShapeData;
370             mNiTriShapeData.vertices = {osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0)};
371             mNiTriShapeData.triangles = {0, 1, 2};
372             mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);
373 
374             mNiTriShapeData2.recType = Nif::RC_NiTriShapeData;
375             mNiTriShapeData2.vertices = {osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1)};
376             mNiTriShapeData2.triangles = {0, 1, 2};
377             mNiTriShape2.data = Nif::NiGeometryDataPtr(&mNiTriShapeData2);
378         }
379     };
380 
TEST_F(TestBulletNifLoader,for_zero_num_roots_should_return_default)381     TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default)
382     {
383         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0));
384         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
385         const auto result = mLoader.load(mNifFile);
386 
387         Resource::BulletShape expected;
388 
389         EXPECT_EQ(*result, expected);
390     }
391 
TEST_F(TestBulletNifLoader,for_default_root_nif_node_should_return_default)392     TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default)
393     {
394         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
395         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
396         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
397         const auto result = mLoader.load(mNifFile);
398 
399         Resource::BulletShape expected;
400 
401         EXPECT_EQ(*result, expected);
402     }
403 
TEST_F(TestBulletNifLoader,for_default_root_collision_node_nif_node_should_return_default)404     TEST_F(TestBulletNifLoader, for_default_root_collision_node_nif_node_should_return_default)
405     {
406         mNode.recType = Nif::RC_RootCollisionNode;
407 
408         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
409         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
410         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
411         const auto result = mLoader.load(mNifFile);
412 
413         Resource::BulletShape expected;
414 
415         EXPECT_EQ(*result, expected);
416     }
417 
TEST_F(TestBulletNifLoader,for_default_root_nif_node_and_filename_starting_with_x_should_return_default)418     TEST_F(TestBulletNifLoader, for_default_root_nif_node_and_filename_starting_with_x_should_return_default)
419     {
420         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
421         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
422         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
423         const auto result = mLoader.load(mNifFile);
424 
425         Resource::BulletShape expected;
426 
427         EXPECT_EQ(*result, expected);
428     }
429 
TEST_F(TestBulletNifLoader,for_root_nif_node_with_bounding_box_should_return_shape_with_compound_shape_and_box_inside)430     TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounding_box_should_return_shape_with_compound_shape_and_box_inside)
431     {
432         mNode.hasBounds = true;
433         mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
434         mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
435         mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
436         mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
437 
438         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
439         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
440         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
441         const auto result = mLoader.load(mNifFile);
442 
443         Resource::BulletShape expected;
444         expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);
445         expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);
446         std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
447         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
448         shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
449         expected.mCollisionShape = shape.release();
450 
451         EXPECT_EQ(*result, expected);
452     }
453 
TEST_F(TestBulletNifLoader,for_child_nif_node_with_bounding_box)454     TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box)
455     {
456         mNode.hasBounds = true;
457         mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
458         mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
459         mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
460         mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
461         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
462 
463         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
464         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
465         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
466         const auto result = mLoader.load(mNifFile);
467 
468         Resource::BulletShape expected;
469         expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);
470         expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);
471         std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
472         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
473         shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
474         expected.mCollisionShape = shape.release();
475 
476         EXPECT_EQ(*result, expected);
477     }
478 
TEST_F(TestBulletNifLoader,for_root_and_child_nif_node_with_bounding_box_but_root_without_flag_should_use_child_bounds)479     TEST_F(TestBulletNifLoader, for_root_and_child_nif_node_with_bounding_box_but_root_without_flag_should_use_child_bounds)
480     {
481         mNode.hasBounds = true;
482         mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
483         mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
484         mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
485         mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
486 
487         mNiNode.hasBounds = true;
488         mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
489         mNiNode.bounds.box.extents = osg::Vec3f(4, 5, 6);
490         mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6);
491         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
492 
493         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
494         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
495         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
496         const auto result = mLoader.load(mNifFile);
497 
498         Resource::BulletShape expected;
499         expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);
500         expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);
501         std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
502         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
503         shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
504         expected.mCollisionShape = shape.release();
505 
506         EXPECT_EQ(*result, expected);
507     }
508 
TEST_F(TestBulletNifLoader,for_root_and_two_children_where_both_with_bounds_but_only_first_with_flag_should_use_first_bounds)509     TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_first_with_flag_should_use_first_bounds)
510     {
511         mNode.hasBounds = true;
512         mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
513         mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
514         mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
515         mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
516 
517         mNode2.hasBounds = true;
518         mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
519         mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
520         mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
521 
522         mNiNode.hasBounds = true;
523         mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
524         mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);
525         mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);
526         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
527 
528         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
529         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
530         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
531         const auto result = mLoader.load(mNifFile);
532 
533         Resource::BulletShape expected;
534         expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);
535         expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);
536         std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(1, 2, 3)));
537         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
538         shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release());
539         expected.mCollisionShape = shape.release();
540 
541         EXPECT_EQ(*result, expected);
542     }
543 
TEST_F(TestBulletNifLoader,for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds)544     TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds)
545     {
546         mNode.hasBounds = true;
547         mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
548         mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
549         mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
550 
551         mNode2.hasBounds = true;
552         mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
553         mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
554         mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
555         mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
556 
557         mNiNode.hasBounds = true;
558         mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
559         mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);
560         mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);
561         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
562 
563         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
564         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
565         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
566         const auto result = mLoader.load(mNifFile);
567 
568         Resource::BulletShape expected;
569         expected.mCollisionBox.extents = osg::Vec3f(4, 5, 6);
570         expected.mCollisionBox.center = osg::Vec3f(-4, -5, -6);
571         std::unique_ptr<btBoxShape> box(new btBoxShape(btVector3(4, 5, 6)));
572         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
573         shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-4, -5, -6)), box.release());
574         expected.mCollisionShape = shape.release();
575 
576         EXPECT_EQ(*result, expected);
577     }
578 
TEST_F(TestBulletNifLoader,for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape)579     TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape)
580     {
581         mNode.hasBounds = true;
582         mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
583         mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
584         mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
585 
586         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
587         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
588         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
589         const auto result = mLoader.load(mNifFile);
590 
591         Resource::BulletShape expected;
592         expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);
593         expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);
594 
595         EXPECT_EQ(*result, expected);
596     }
597 
TEST_F(TestBulletNifLoader,for_tri_shape_root_node_should_return_shape_with_triangle_mesh_shape)598     TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_shape_with_triangle_mesh_shape)
599     {
600         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
601         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
602         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
603         const auto result = mLoader.load(mNifFile);
604 
605         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
606         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
607         Resource::BulletShape expected;
608         expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);
609 
610         EXPECT_EQ(*result, expected);
611     }
612 
TEST_F(TestBulletNifLoader,for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape)613     TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape)
614     {
615         mNiTriShape.hasBounds = true;
616         mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
617         mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3);
618         mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3);
619 
620         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
621         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
622         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
623         const auto result = mLoader.load(mNifFile);
624 
625         Resource::BulletShape expected;
626         expected.mCollisionBox.extents = osg::Vec3f(1, 2, 3);
627         expected.mCollisionBox.center = osg::Vec3f(-1, -2, -3);
628 
629         EXPECT_EQ(*result, expected);
630     }
631 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape)632     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape)
633     {
634         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
635 
636         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
637         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
638         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
639         const auto result = mLoader.load(mNifFile);
640 
641         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
642         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
643         Resource::BulletShape expected;
644         expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);
645 
646         EXPECT_EQ(*result, expected);
647     }
648 
TEST_F(TestBulletNifLoader,for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape)649     TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape)
650     {
651         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));
652         mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
653 
654         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
655         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
656         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
657         const auto result = mLoader.load(mNifFile);
658 
659         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
660         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
661         Resource::BulletShape expected;
662         expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);
663 
664         EXPECT_EQ(*result, expected);
665     }
666 
TEST_F(TestBulletNifLoader,for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes)667     TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
668     {
669         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
670             Nif::NodePtr(&mNiTriShape),
671             Nif::NodePtr(&mNiTriShape2)
672         }));
673 
674         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
675         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
676         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
677         const auto result = mLoader.load(mNifFile);
678 
679         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
680         triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1));
681         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
682         Resource::BulletShape expected;
683         expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);
684 
685         EXPECT_EQ(*result, expected);
686     }
687 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape)688     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape)
689     {
690         mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance);
691         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
692 
693         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
694         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
695         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
696         const auto result = mLoader.load(mNifFile);
697 
698         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
699         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
700         Resource::BulletShape expected;
701         expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);
702 
703         EXPECT_EQ(*result, expected);
704     }
705 
TEST_F(TestBulletNifLoader,for_tri_shape_root_node_and_filename_starting_with_x_should_return_shape_with_compound_shape)706     TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_shape_with_compound_shape)
707     {
708         copy(mTransform, mNiTriShape.trafo);
709         mNiTriShape.trafo.scale = 3;
710 
711         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
712         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
713         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
714         const auto result = mLoader.load(mNifFile);
715 
716         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
717         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
718         std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
719         mesh->setLocalScaling(btVector3(3, 3, 3));
720         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
721         shape->addChildShape(mResultTransform, mesh.release());
722         Resource::BulletShape expected;
723         expected.mCollisionShape = shape.release();
724         expected.mAnimatedShapes = {{-1, 0}};
725 
726         EXPECT_EQ(*result, expected);
727     }
728 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_and_filename_starting_with_x_should_return_shape_with_compound_shape)729     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_shape_with_compound_shape)
730     {
731         copy(mTransform, mNiTriShape.trafo);
732         mNiTriShape.trafo.scale = 3;
733         mNiTriShape.parent = &mNiNode;
734         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
735         mNiNode.trafo.scale = 4;
736 
737         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
738         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
739         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
740         const auto result = mLoader.load(mNifFile);
741 
742         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
743         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
744         std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
745         mesh->setLocalScaling(btVector3(12, 12, 12));
746         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
747         shape->addChildShape(mResultTransform2, mesh.release());
748         Resource::BulletShape expected;
749         expected.mCollisionShape = shape.release();
750         expected.mAnimatedShapes = {{-1, 0}};
751 
752         EXPECT_EQ(*result, expected);
753     }
754 
TEST_F(TestBulletNifLoader,for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_shape_with_compound_shape)755     TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_shape_with_compound_shape)
756     {
757         copy(mTransform, mNiTriShape.trafo);
758         mNiTriShape.trafo.scale = 3;
759 
760         copy(mTransform, mNiTriShape2.trafo);
761         mNiTriShape2.trafo.scale = 3;
762 
763         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
764             Nif::NodePtr(&mNiTriShape),
765             Nif::NodePtr(&mNiTriShape2),
766         }));
767 
768         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
769         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
770         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif"));
771         const auto result = mLoader.load(mNifFile);
772 
773         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
774         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
775         std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
776         mesh->setLocalScaling(btVector3(3, 3, 3));
777 
778         std::unique_ptr<btTriangleMesh> triangles2(new btTriangleMesh(false));
779         triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1));
780         std::unique_ptr<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));
781         mesh2->setLocalScaling(btVector3(3, 3, 3));
782 
783         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
784         shape->addChildShape(mResultTransform, mesh.release());
785         shape->addChildShape(mResultTransform, mesh2.release());
786         Resource::BulletShape expected;
787         expected.mCollisionShape = shape.release();
788         expected.mAnimatedShapes = {{-1, 0}};
789 
790         EXPECT_EQ(*result, expected);
791     }
792 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_with_controller_should_return_shape_with_compound_shape)793     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_shape_with_compound_shape)
794     {
795         mController.recType = Nif::RC_NiKeyframeController;
796         mController.flags |= Nif::NiNode::ControllerFlag_Active;
797         copy(mTransform, mNiTriShape.trafo);
798         mNiTriShape.trafo.scale = 3;
799         mNiTriShape.parent = &mNiNode;
800         mNiTriShape.controller = Nif::ControllerPtr(&mController);
801         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
802         mNiNode.trafo.scale = 4;
803 
804         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
805         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
806         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
807         const auto result = mLoader.load(mNifFile);
808 
809         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
810         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
811         std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
812         mesh->setLocalScaling(btVector3(12, 12, 12));
813         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
814         shape->addChildShape(mResultTransform2, mesh.release());
815         Resource::BulletShape expected;
816         expected.mCollisionShape = shape.release();
817         expected.mAnimatedShapes = {{-1, 0}};
818 
819         EXPECT_EQ(*result, expected);
820     }
821 
TEST_F(TestBulletNifLoader,for_two_tri_shape_children_nodes_where_one_with_controller_should_return_shape_with_compound_shape)822     TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_shape_with_compound_shape)
823     {
824         mController.recType = Nif::RC_NiKeyframeController;
825         mController.flags |= Nif::NiNode::ControllerFlag_Active;
826         copy(mTransform, mNiTriShape.trafo);
827         mNiTriShape.trafo.scale = 3;
828         copy(mTransform, mNiTriShape2.trafo);
829         mNiTriShape2.trafo.scale = 3;
830         mNiTriShape2.parent = &mNiNode;
831         mNiTriShape2.controller = Nif::ControllerPtr(&mController);
832         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({
833             Nif::NodePtr(&mNiTriShape),
834             Nif::NodePtr(&mNiTriShape2),
835         }));
836         mNiNode.trafo.scale = 4;
837 
838         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
839         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
840         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
841         const auto result = mLoader.load(mNifFile);
842 
843         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
844         triangles->addTriangle(btVector3(1, 2, 3), btVector3(4, 2, 3), btVector3(4, 4.632747650146484375, 1.56172335147857666015625));
845         std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
846         mesh->setLocalScaling(btVector3(1, 1, 1));
847 
848         std::unique_ptr<btTriangleMesh> triangles2(new btTriangleMesh(false));
849         triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1));
850         std::unique_ptr<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));
851         mesh2->setLocalScaling(btVector3(12, 12, 12));
852 
853         std::unique_ptr<btCompoundShape> shape(new btCompoundShape);
854         shape->addChildShape(mResultTransform2, mesh2.release());
855         shape->addChildShape(btTransform::getIdentity(), mesh.release());
856         Resource::BulletShape expected;
857         expected.mCollisionShape = shape.release();
858         expected.mAnimatedShapes = {{-1, 0}};
859 
860         EXPECT_EQ(*result, expected);
861     }
862 
TEST_F(TestBulletNifLoader,for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape)863     TEST_F(TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape)
864     {
865         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
866         mNiNode.recType = Nif::RC_AvoidNode;
867 
868         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
869         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
870         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
871         const auto result = mLoader.load(mNifFile);
872 
873         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
874         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
875         Resource::BulletShape expected;
876         expected.mAvoidCollisionShape = new Resource::TriangleMeshShape(triangles.release(), false);
877 
878         EXPECT_EQ(*result, expected);
879     }
880 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape)881     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape)
882     {
883         mNiTriShape.data = Nif::NiGeometryDataPtr(nullptr);
884         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
885 
886         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
887         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
888         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
889         const auto result = mLoader.load(mNifFile);
890 
891         Resource::BulletShape expected;
892 
893         EXPECT_EQ(*result, expected);
894     }
895 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape)896     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape)
897     {
898         auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.data.getPtr());
899         data->triangles.clear();
900         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
901 
902         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
903         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
904         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
905         const auto result = mLoader.load(mNifFile);
906 
907         Resource::BulletShape expected;
908 
909         EXPECT_EQ(*result, expected);
910     }
911 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)912     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)
913     {
914         mNiStringExtraData.string = "NC___";
915         mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
916         mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
917         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
918 
919         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
920         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
921         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
922         const auto result = mLoader.load(mNifFile);
923 
924         Resource::BulletShape expected;
925 
926         EXPECT_EQ(*result, expected);
927     }
928 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)929     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)
930     {
931         mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
932         mNiStringExtraData2.string = "NC___";
933         mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
934         mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
935         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
936 
937         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
938         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
939         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
940         const auto result = mLoader.load(mNifFile);
941 
942         Resource::BulletShape expected;
943 
944         EXPECT_EQ(*result, expected);
945     }
946 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape)947     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape)
948     {
949         mNiStringExtraData.string = "MRK";
950         mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
951         mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
952         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
953 
954         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
955         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
956         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
957         const auto result = mLoader.load(mNifFile);
958 
959         Resource::BulletShape expected;
960 
961         EXPECT_EQ(*result, expected);
962     }
963 
TEST_F(TestBulletNifLoader,for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes)964     TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
965     {
966         mNiStringExtraData.string = "MRK";
967         mNiStringExtraData.recType = Nif::RC_NiStringExtraData;
968         mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
969         mNiNode2.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiTriShape)}));
970         mNiNode2.recType = Nif::RC_RootCollisionNode;
971         mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNiNode2)}));
972         mNiNode.recType = Nif::RC_NiNode;
973 
974         EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
975         EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
976         EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
977         const auto result = mLoader.load(mNifFile);
978 
979         std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
980         triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
981         Resource::BulletShape expected;
982         expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true);
983 
984         EXPECT_EQ(*result, expected);
985     }
986 }
987