Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
64 kB · 1554 lines
C++
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555#include "../nif/node.hpp"
#include <components/bullethelpers/processtrianglecallback.hpp>#include <components/misc/convert.hpp>#include <components/nif/data.hpp>#include <components/nif/extra.hpp>#include <components/nif/node.hpp>#include <components/nifbullet/bulletnifloader.hpp>
#include <BulletCollision/CollisionShapes/btBoxShape.h>#include <BulletCollision/CollisionShapes/btCompoundShape.h>#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
#include <gmock/gmock.h>#include <gtest/gtest.h>
#include <algorithm>#include <span>#include <type_traits>
namespace{ template <class T> bool compareObjects(const T* lhs, const T* rhs) { return (!lhs && !rhs) || (lhs && rhs && *lhs == *rhs); }
std::vector<btVector3> getTriangles(const btBvhTriangleMeshShape& shape) { std::vector<btVector3> result; auto callback = BulletHelpers::makeProcessTriangleCallback([&](btVector3* triangle, int, int) { for (std::size_t i = 0; i < 3; ++i) result.push_back(triangle[i]); }); btVector3 aabbMin; btVector3 aabbMax; shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); shape.processAllTriangles(&callback, aabbMin, aabbMax); return result; }
bool isNear(btScalar lhs, btScalar rhs) { return std::abs(lhs - rhs) <= 1e-5; }
bool isNear(const btVector3& lhs, const btVector3& rhs) { return std::equal(static_cast<const btScalar*>(lhs), static_cast<const btScalar*>(lhs) + 3, static_cast<const btScalar*>(rhs), [](btScalar l, btScalar r) { return isNear(l, r); }); }
bool isNear(const btMatrix3x3& lhs, const btMatrix3x3& rhs) { for (int i = 0; i < 3; ++i) if (!isNear(lhs[i], rhs[i])) return false; return true; }
bool isNear(const btTransform& lhs, const btTransform& rhs) { return isNear(lhs.getOrigin(), rhs.getOrigin()) && isNear(lhs.getBasis(), rhs.getBasis()); }
bool isNear(std::span<const btVector3> lhs, std::span<const btVector3> rhs) { if (lhs.size() != rhs.size()) return false; return std::equal( lhs.begin(), lhs.end(), rhs.begin(), [](const btVector3& l, const btVector3& r) { return isNear(l, r); }); }
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){ return stream << "btVector3 {" << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getX() << ", " << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getY() << ", " << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.getZ() << "}";}
static std::ostream& operator<<(std::ostream& stream, const btMatrix3x3& value){ stream << "btMatrix3x3 {"; for (int i = 0; i < 3; ++i) stream << value.getRow(i) << ", "; return stream << "}";}
static std::ostream& operator<<(std::ostream& stream, const btTransform& value){ return stream << "btTransform {" << value.getBasis() << ", " << value.getOrigin() << "}";}
static std::ostream& operator<<(std::ostream& stream, const btCollisionShape* value);
static std::ostream& operator<<(std::ostream& stream, const btCompoundShape& value){ stream << "btCompoundShape {" << value.getLocalScaling() << ", "; stream << "{"; for (int i = 0; i < value.getNumChildShapes(); ++i) stream << value.getChildShape(i) << ", "; stream << "},"; stream << "{"; for (int i = 0; i < value.getNumChildShapes(); ++i) stream << value.getChildTransform(i) << ", "; stream << "}"; return stream << "}";}
static std::ostream& operator<<(std::ostream& stream, const btBoxShape& value){ return stream << "btBoxShape {" << value.getLocalScaling() << ", " << value.getHalfExtentsWithoutMargin() << "}";}
namespace Resource{
static std::ostream& operator<<(std::ostream& stream, const TriangleMeshShape& value) { stream << "Resource::TriangleMeshShape {" << value.getLocalScaling() << ", " << value.usesQuantizedAabbCompression() << ", " << value.getOwnsBvh() << ", {"; auto callback = BulletHelpers::makeProcessTriangleCallback([&](btVector3* triangle, int, int) { for (std::size_t i = 0; i < 3; ++i) stream << triangle[i] << ", "; }); btVector3 aabbMin; btVector3 aabbMax; value.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); value.processAllTriangles(&callback, aabbMin, aabbMax); return stream << "}}"; }
static std::ostream& operator<<(std::ostream& stream, const ScaledTriangleMeshShape& value) { return stream << "Resource::ScaledTriangleMeshShape {" << value.getLocalScaling() << ", " << value.getChildShape() << "}"; }
static bool operator==(const CollisionBox& l, const CollisionBox& r) { const auto tie = [](const CollisionBox& v) { return std::tie(v.mExtents, v.mCenter); }; return tie(l) == tie(r); }
static std::ostream& operator<<(std::ostream& stream, const CollisionBox& value) { return stream << "CollisionBox {" << WriteVec3f{ value.mExtents } << ", " << WriteVec3f{ value.mCenter } << "}"; }
}
static std::ostream& operator<<(std::ostream& stream, const btCollisionShape& value){ switch (value.getShapeType()) { case COMPOUND_SHAPE_PROXYTYPE: return stream << static_cast<const btCompoundShape&>(value); case BOX_SHAPE_PROXYTYPE: return stream << static_cast<const btBoxShape&>(value); case TRIANGLE_MESH_SHAPE_PROXYTYPE: if (const auto casted = dynamic_cast<const Resource::TriangleMeshShape*>(&value)) return stream << *casted; break; case SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE: if (const auto casted = dynamic_cast<const Resource::ScaledTriangleMeshShape*>(&value)) return stream << *casted; break; } return stream << "btCollisionShape {" << value.getShapeType() << "}";}
static std::ostream& operator<<(std::ostream& stream, const btCollisionShape* value){ return value ? stream << "&" << *value : stream << "nullptr";}
namespace std{ static std::ostream& operator<<(std::ostream& stream, const map<int, int>& value) { stream << "std::map<int, int> {"; for (const auto& v : value) stream << "{" << v.first << ", " << v.second << "},"; return stream << "}"; }}
namespace Resource{ static bool operator==(const Resource::BulletShape& lhs, const Resource::BulletShape& rhs) { return compareObjects(lhs.mCollisionShape.get(), rhs.mCollisionShape.get()) && compareObjects(lhs.mAvoidCollisionShape.get(), rhs.mAvoidCollisionShape.get()) && lhs.mCollisionBox == rhs.mCollisionBox && lhs.mVisualCollisionType == rhs.mVisualCollisionType && lhs.mAnimatedShapes == rhs.mAnimatedShapes; }
static std::ostream& operator<<(std::ostream& stream, Resource::VisualCollisionType value) { switch (value) { case Resource::VisualCollisionType::None: return stream << "Resource::VisualCollisionType::None"; case Resource::VisualCollisionType::Default: return stream << "Resource::VisualCollisionType::Default"; case Resource::VisualCollisionType::Camera: return stream << "Resource::VisualCollisionType::Camera"; } return stream << static_cast<std::underlying_type_t<Resource::VisualCollisionType>>(value); }
static std::ostream& operator<<(std::ostream& stream, const Resource::BulletShape& value) { return stream << "Resource::BulletShape {" << value.mCollisionShape.get() << ", " << value.mAvoidCollisionShape.get() << ", " << value.mCollisionBox << ", " << value.mAnimatedShapes << ", " << value.mVisualCollisionType << "}"; }}
static bool operator==(const btCollisionShape& lhs, const btCollisionShape& rhs);
static bool operator==(const btCompoundShape& lhs, const btCompoundShape& rhs){ if (lhs.getNumChildShapes() != rhs.getNumChildShapes() || lhs.getLocalScaling() != rhs.getLocalScaling()) return false; for (int i = 0; i < lhs.getNumChildShapes(); ++i) { if (!compareObjects(lhs.getChildShape(i), rhs.getChildShape(i)) || !isNear(lhs.getChildTransform(i), rhs.getChildTransform(i))) return false; } return true;}
static bool operator==(const btBoxShape& lhs, const btBoxShape& rhs){ return isNear(lhs.getLocalScaling(), rhs.getLocalScaling()) && lhs.getHalfExtentsWithoutMargin() == rhs.getHalfExtentsWithoutMargin();}
static bool operator==(const btBvhTriangleMeshShape& lhs, const btBvhTriangleMeshShape& rhs){ return isNear(lhs.getLocalScaling(), rhs.getLocalScaling()) && lhs.usesQuantizedAabbCompression() == rhs.usesQuantizedAabbCompression() && lhs.getOwnsBvh() == rhs.getOwnsBvh() && isNear(getTriangles(lhs), getTriangles(rhs));}
static bool operator==(const btScaledBvhTriangleMeshShape& lhs, const btScaledBvhTriangleMeshShape& rhs){ return isNear(lhs.getLocalScaling(), rhs.getLocalScaling()) && compareObjects(lhs.getChildShape(), rhs.getChildShape());}
static bool operator==(const btCollisionShape& lhs, const btCollisionShape& rhs){ if (lhs.getShapeType() != rhs.getShapeType()) return false; switch (lhs.getShapeType()) { case COMPOUND_SHAPE_PROXYTYPE: return static_cast<const btCompoundShape&>(lhs) == static_cast<const btCompoundShape&>(rhs); case BOX_SHAPE_PROXYTYPE: return static_cast<const btBoxShape&>(lhs) == static_cast<const btBoxShape&>(rhs); case TRIANGLE_MESH_SHAPE_PROXYTYPE: if (const auto lhsCasted = dynamic_cast<const Resource::TriangleMeshShape*>(&lhs)) if (const auto rhsCasted = dynamic_cast<const Resource::TriangleMeshShape*>(&rhs)) return *lhsCasted == *rhsCasted; return false; case SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE: if (const auto lhsCasted = dynamic_cast<const Resource::ScaledTriangleMeshShape*>(&lhs)) if (const auto rhsCasted = dynamic_cast<const Resource::ScaledTriangleMeshShape*>(&rhs)) return *lhsCasted == *rhsCasted; return false; } return false;}
namespace{ using namespace testing; using namespace Nif::Testing; using NifBullet::BulletNifLoader;
constexpr VFS::Path::NormalizedView testNif("test.nif"); constexpr VFS::Path::NormalizedView xtestNif("xtest.nif");
void copy(const btTransform& src, Nif::NiTransform& dst) { dst.mTranslation = Misc::Convert::makeOsgVec3f(src.getOrigin()); for (int row = 0; row < 3; ++row) for (int column = 0; column < 3; ++column) dst.mRotation.mValues[row][column] = static_cast<float>(src.getBasis().getRow(row)[column]); }
struct TestBulletNifLoader : Test { BulletNifLoader mLoader; Nif::NiAVObject mNode; Nif::NiAVObject mNode2; Nif::NiNode mNiNode; Nif::NiNode mNiNode2; Nif::NiNode mNiNode3; Nif::NiTriShapeData mNiTriShapeData; Nif::NiTriShape mNiTriShape; Nif::NiTriShapeData mNiTriShapeData2; Nif::NiTriShape mNiTriShape2; Nif::NiTriStripsData mNiTriStripsData; Nif::NiTriStrips mNiTriStrips; Nif::NiSkinInstance mNiSkinInstance; Nif::NiStringExtraData mNiStringExtraData; Nif::NiStringExtraData mNiStringExtraData2; Nif::NiIntegerExtraData mNiIntegerExtraData; Nif::NiTimeController mController; btTransform mTransform{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3) }; btTransform mTransformScale2{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(2, 4, 6) }; btTransform mTransformScale3{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(3, 6, 9) }; btTransform mTransformScale4{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(4, 8, 12) }; const std::string mHash = "hash";
TestBulletNifLoader() { init(mNode); init(mNode2); init(mNiNode); init(mNiNode2); init(mNiNode3); init(mNiTriShape); init(mNiTriShape2); init(mNiTriStrips); init(mNiSkinInstance); init(mNiStringExtraData); init(mNiStringExtraData2); init(mController);
mNiTriShapeData.mRecordType = Nif::RC_NiTriShapeData; mNiTriShapeData.mVertices = { osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0) }; mNiTriShapeData.mNumTriangles = 1; mNiTriShapeData.mTriangles = { 0, 1, 2 }; mNiTriShape.mData = Nif::NiGeometryDataPtr(&mNiTriShapeData);
mNiTriShapeData2.mRecordType = Nif::RC_NiTriShapeData; mNiTriShapeData2.mVertices = { osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1) }; mNiTriShapeData2.mNumTriangles = 1; mNiTriShapeData2.mTriangles = { 0, 1, 2 }; mNiTriShape2.mData = Nif::NiGeometryDataPtr(&mNiTriShapeData2);
mNiTriStripsData.mRecordType = Nif::RC_NiTriStripsData; mNiTriStripsData.mVertices = { osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0), osg::Vec3f(0, 1, 0) }; mNiTriStripsData.mNumTriangles = 2; mNiTriStripsData.mStrips = { { 0, 1, 2, 3 } }; mNiTriStrips.mData = Nif::NiGeometryDataPtr(&mNiTriStripsData); } };
TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default) { Nif::NIFFile file(testNif); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); EXPECT_EQ(result->mFileName, "test.nif"); EXPECT_EQ(result->mFileHash, mHash); }
TEST_F(TestBulletNifLoader, should_ignore_nullptr_root) { Nif::NIFFile file(testNif); file.mRoots.push_back(nullptr); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default) { Nif::NIFFile file(testNif); file.mRoots.push_back(&mNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_default_root_collision_node_nif_node_should_return_default) { mNode.mRecordType = Nif::RC_RootCollisionNode;
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_default_root_nif_node_and_filename_starting_with_x_should_return_default) { Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_root_bounding_box_should_return_shape_with_bounding_box_data) { mNode.mName = "Bounding Box"; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3);
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_child_bounding_box_should_return_shape_with_bounding_box_data) { mNode.mName = "Bounding Box"; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_root_with_bounds_and_child_bounding_box_should_use_bounding_box) { mNode.mName = "Bounding Box"; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode);
mNiNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNiNode.mBounds.mBox.mExtents = osg::Vec3f(4, 5, 6); mNiNode.mBounds.mBox.mCenter = osg::Vec3f(-4, -5, -6); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
EXPECT_EQ(*result, expected); }
TEST_F( TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_one_is_bounding_box_use_bounding_box) { mNode.mName = "Bounding Box"; mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode);
mNode2.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode2.mBounds.mBox.mExtents = osg::Vec3f(4, 5, 6); mNode2.mBounds.mBox.mCenter = osg::Vec3f(-4, -5, -6); mNode2.mParents.push_back(&mNiNode);
mNiNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNiNode.mBounds.mBox.mExtents = osg::Vec3f(7, 8, 9); mNiNode.mBounds.mBox.mCenter = osg::Vec3f(-7, -8, -9); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); expected.mCollisionBox.mCenter = osg::Vec3f(-1, -2, -3);
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_second_is_bounding_box_use_bounding_box) { mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3); mNode.mParents.push_back(&mNiNode);
mNode2.mName = "Bounding Box"; mNode2.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode2.mBounds.mBox.mExtents = osg::Vec3f(4, 5, 6); mNode2.mBounds.mBox.mCenter = osg::Vec3f(-4, -5, -6); mNode2.mParents.push_back(&mNiNode);
mNiNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNiNode.mBounds.mBox.mExtents = osg::Vec3f(7, 8, 9); mNiNode.mBounds.mBox.mCenter = osg::Vec3f(-7, -8, -9); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNode), Nif::NiAVObjectPtr(&mNode2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(4, 5, 6); expected.mCollisionBox.mCenter = osg::Vec3f(-4, -5, -6);
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_should_return_shape_with_null_collision_shape) { mNode.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNode.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3); mNode.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3);
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_static_shape) { Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiTriShape); file.mHash = mHash;
const auto result = mLoader.load(file);
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); auto triShape = std::make_unique<Resource::TriangleMeshShape>(triangles.release(), true); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(triShape.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_static_shape) { mNiTriShape.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNiTriShape.mBounds.mBox.mExtents = osg::Vec3f(1, 2, 3); mNiTriShape.mBounds.mBox.mCenter = osg::Vec3f(-1, -2, -3);
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiTriShape); file.mHash = mHash;
const auto result = mLoader.load(file);
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); auto triShape = std::make_unique<Resource::TriangleMeshShape>(triangles.release(), true); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(triShape.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_static_shape) { mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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); auto triShape = std::make_unique<Resource::TriangleMeshShape>(triangles.release(), true); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(triShape.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_static_shape) { mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiNode2) }; mNiNode2.mParents.push_back(&mNiNode); mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiTriShape.mParents.push_back(&mNiNode2);
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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); auto triShape = std::make_unique<Resource::TriangleMeshShape>(triangles.release(), true); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(triShape.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_static_shape_with_all_meshes) { mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape2.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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); auto triShape = std::make_unique<Resource::TriangleMeshShape>(triangles.release(), true); auto triShape2 = std::make_unique<Resource::TriangleMeshShape>(triangles2.release(), true);
compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(triShape.release(), btVector3(1, 1, 1))); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(triShape2.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_static_shape) { mNiTriShape.mSkin = Nif::NiSkinInstancePtr(&mNiSkinInstance); mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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); auto triShape = std::make_unique<Resource::TriangleMeshShape>(triangles.release(), true); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(triShape.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_animated_shape) { copy(mTransform, mNiTriShape.mTransform); mNiTriShape.mTransform.mScale = 3;
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiTriShape); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> shape(new btCompoundShape); shape->addChildShape(mTransform, new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(3, 3, 3))); Resource::BulletShape expected; expected.mCollisionShape.reset(shape.release()); expected.mAnimatedShapes = { { -1, 0 } };
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_animated_shape) { copy(mTransform, mNiTriShape.mTransform); mNiTriShape.mTransform.mScale = 3; mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mTransform.mScale = 4;
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> shape(new btCompoundShape); shape->addChildShape( mTransformScale4, new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(12, 12, 12))); Resource::BulletShape expected; expected.mCollisionShape.reset(shape.release()); expected.mAnimatedShapes = { { -1, 0 } };
EXPECT_EQ(*result, expected); }
TEST_F( TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_animated_shape) { copy(mTransform, mNiTriShape.mTransform); mNiTriShape.mTransform.mScale = 3; mNiTriShape.mParents.push_back(&mNiNode);
copy(mTransform, mNiTriShape2.mTransform); mNiTriShape2.mTransform.mScale = 3; mNiTriShape2.mParents.push_back(&mNiNode);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
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<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));
std::unique_ptr<btCompoundShape> shape(new btCompoundShape); shape->addChildShape(mTransform, new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(3, 3, 3))); shape->addChildShape(mTransform, new Resource::ScaledTriangleMeshShape(mesh2.release(), btVector3(3, 3, 3))); Resource::BulletShape expected; expected.mCollisionShape.reset(shape.release()); expected.mAnimatedShapes = { { -1, 0 } };
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_animated_shape) { mController.mRecordType = Nif::RC_NiKeyframeController; mController.mFlags |= Nif::NiTimeController::Flag_Active; copy(mTransform, mNiTriShape.mTransform); mNiTriShape.mTransform.mScale = 3; mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mController = Nif::NiTimeControllerPtr(&mController); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mTransform.mScale = 4;
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> shape(new btCompoundShape); shape->addChildShape( mTransformScale4, new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(12, 12, 12))); Resource::BulletShape expected; expected.mCollisionShape.reset(shape.release()); expected.mAnimatedShapes = { { -1, 0 } };
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_animated_shape) { mController.mRecordType = Nif::RC_NiKeyframeController; mController.mFlags |= Nif::NiTimeController::Flag_Active; copy(mTransform, mNiTriShape.mTransform); mNiTriShape.mTransform.mScale = 3; mNiTriShape.mParents.push_back(&mNiNode); copy(mTransform, mNiTriShape2.mTransform); mNiTriShape2.mTransform.mScale = 3; mNiTriShape2.mParents.push_back(&mNiNode); mNiTriShape2.mController = Nif::NiTimeControllerPtr(&mController); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape2), }; mNiNode.mTransform.mScale = 4;
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
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<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));
std::unique_ptr<btCompoundShape> shape(new btCompoundShape); shape->addChildShape( mTransformScale4, new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(12, 12, 12))); shape->addChildShape( mTransformScale4, new Resource::ScaledTriangleMeshShape(mesh2.release(), btVector3(12, 12, 12))); Resource::BulletShape expected; expected.mCollisionShape.reset(shape.release()); expected.mAnimatedShapes = { { -1, 1 } };
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, should_add_static_mesh_to_existing_compound_mesh) { mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiTriShape2); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
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<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));
std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1))); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh2.release(), btVector3(1, 1, 1)));
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.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mRecordType = Nif::RC_AvoidNode;
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true));
std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1))); Resource::BulletShape expected; expected.mAvoidCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape) { mNiTriShape.mData = Nif::NiGeometryDataPtr(nullptr); mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape) { auto data = static_cast<Nif::NiTriShapeData*>(mNiTriShape.mData.getPtr()); data->mTriangles.clear(); mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_root_node_with_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision) { mNiStringExtraData.mData = "NCC__"; mNiStringExtraData.mRecordType = Nif::RC_NiStringExtraData; mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
expected.mVisualCollisionType = Resource::VisualCollisionType::Camera;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_root_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision) { mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData2.mData = "NCC__"; mNiStringExtraData2.mRecordType = Nif::RC_NiStringExtraData; mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Camera;
EXPECT_EQ(*result, expected); }
TEST_F( TestBulletNifLoader, for_root_node_with_extra_data_string_starting_with_nc_should_return_shape_with_nocollision) { mNiStringExtraData.mData = "NC___"; mNiStringExtraData.mRecordType = Nif::RC_NiStringExtraData; mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Default;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_root_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_nocollision) { mNiStringExtraData.mNext = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData2.mData = "NC___"; mNiStringExtraData2.mRecordType = Nif::RC_NiStringExtraData; mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Default;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_should_ignore_extra_data) { mNiStringExtraData.mData = "NC___"; mNiStringExtraData.mRecordType = Nif::RC_NiStringExtraData; mNiTriShape.mExtra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_empty_root_collision_node_without_nc_should_return_shape_with_cameraonly_collision) { Nif::NiTriShape niTriShape; Nif::NiNode emptyCollisionNode; init(niTriShape); init(emptyCollisionNode);
niTriShape.mData = Nif::NiGeometryDataPtr(&mNiTriShapeData); niTriShape.mParents.push_back(&mNiNode);
emptyCollisionNode.mRecordType = Nif::RC_RootCollisionNode; emptyCollisionNode.mParents.push_back(&mNiNode);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&niTriShape), Nif::NiAVObjectPtr(&emptyCollisionNode) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Camera;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision_for_markers) { mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mName = "EditorMarker"; mNiIntegerExtraData.mData = 34; // BSXFlags "has collision" | "editor marker" mNiIntegerExtraData.mRecordType = Nif::RC_BSXFlags; mNiNode.mExtraList.push_back(Nif::ExtraPtr(&mNiIntegerExtraData)); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash; file.mVersion = Nif::NIFStream::generateVersion(10, 0, 1, 0);
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, mrk_editor_marker_flag_disables_collision_for_markers) { mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mName = "Tri EditorMarker"; mNiStringExtraData.mData = "MRK"; mNiStringExtraData.mRecordType = Nif::RC_NiStringExtraData; mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, for_tri_strips_root_node_should_return_static_shape) { Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiTriStrips); file.mHash = mHash;
const auto result = mLoader.load(file);
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)); std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, should_ignore_tri_strips_data_with_empty_strips) { mNiTriStripsData.mStrips.clear();
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiTriStrips); file.mHash = mHash;
const auto result = mLoader.load(file);
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.mStrips.front() = { 0, 1 };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiTriStrips); file.mHash = mHash;
const auto result = mLoader.load(file);
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.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mRecordType = Nif::RC_AvoidNode; mNiTriStripsData.mStrips.front() = { 0, 1 };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiTriStrips); file.mHash = mHash;
const auto result = mLoader.load(file);
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.mStrips.front() = { 0, 1 }; mNiTriStrips.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriStrips) };
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
const Resource::BulletShape expected;
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, should_not_add_static_mesh_with_no_triangles_to_compound_shape) { mNiTriStripsData.mStrips.front() = { 0, 1 }; mNiTriShape.mParents.push_back(&mNiNode); mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiTriStrips); file.mHash = mHash;
const auto result = mLoader.load(file);
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<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release()); expected.mAnimatedShapes = { { -1, 0 } };
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, should_handle_node_with_multiple_parents) { copy(mTransform, mNiTriShape.mTransform); mNiTriShape.mTransform.mScale = 4; mNiTriShape.mParents = { &mNiNode, &mNiNode2 }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mTransform.mScale = 2; mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode2.mTransform.mScale = 3;
Nif::NIFFile file(xtestNif); file.mRoots.push_back(&mNiNode); file.mRoots.push_back(&mNiNode2); file.mHash = mHash;
const auto result = mLoader.load(file);
std::unique_ptr<btTriangleMesh> triangles1(new btTriangleMesh(false)); triangles1->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); std::unique_ptr<Resource::TriangleMeshShape> mesh1(new Resource::TriangleMeshShape(triangles1.release(), true)); std::unique_ptr<btTriangleMesh> triangles2(new btTriangleMesh(false)); triangles2->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); std::unique_ptr<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true)); std::unique_ptr<btCompoundShape> shape(new btCompoundShape); shape->addChildShape( mTransformScale2, new Resource::ScaledTriangleMeshShape(mesh1.release(), btVector3(8, 8, 8))); shape->addChildShape( mTransformScale3, new Resource::ScaledTriangleMeshShape(mesh2.release(), btVector3(12, 12, 12))); Resource::BulletShape expected; expected.mCollisionShape.reset(shape.release()); expected.mAnimatedShapes = { { -1, 0 } };
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, dont_assign_invalid_bounding_box_extents) { copy(mTransform, mNiTriShape.mTransform); mNiTriShape.mTransform.mScale = 10; mNiTriShape.mParents.push_back(&mNiNode);
mNiTriShape2.mName = "Bounding Box"; mNiTriShape2.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV; mNiTriShape2.mBounds.mBox.mExtents = osg::Vec3f(-1, -2, -3); mNiTriShape2.mParents.push_back(&mNiNode);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode);
const auto result = mLoader.load(file);
const bool extentsUnassigned = std::ranges::all_of(result->mCollisionBox.mExtents._v, [](float extent) { return extent == 0.f; });
EXPECT_EQ(extentsUnassigned, true); }
TEST_F(TestBulletNifLoader, no_rcn_flag_with_immediate_rcn_uses_rcn_for_collision) { Nif::NiNode rcn; init(rcn); rcn.mRecordType = Nif::RC_RootCollisionNode;
mNiTriShape.mParents.push_back(&mNiNode); rcn.mParents.push_back(&mNiNode); mNiTriShape2.mParents.push_back(&rcn);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&rcn) }; rcn.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, no_rcn_flag_with_nested_rcn_ignores_rcn_and_uses_all_for_collision) { Nif::NiNode rcn; init(rcn); rcn.mRecordType = Nif::RC_RootCollisionNode;
mNiTriShape.mParents.push_back(&mNiNode); mNiNode2.mParents.push_back(&mNiNode); rcn.mParents.push_back(&mNiNode2); mNiTriShape2.mParents.push_back(&rcn);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiNode2) }; mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&rcn) }; rcn.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
std::unique_ptr<btTriangleMesh> triangles1(new btTriangleMesh(false)); triangles1->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); std::unique_ptr<Resource::TriangleMeshShape> mesh1(new Resource::TriangleMeshShape(triangles1.release(), true));
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<Resource::TriangleMeshShape> mesh2(new Resource::TriangleMeshShape(triangles2.release(), true));
std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh1.release(), btVector3(1, 1, 1))); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh2.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, rcn_flag_with_nested_rcn_uses_nested_rcn_for_collision) { Nif::NiNode rcn; init(rcn); rcn.mRecordType = Nif::RC_RootCollisionNode;
mNiStringExtraData.mData = "RCN"; mNiStringExtraData.mRecordType = Nif::RC_NiStringExtraData; mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.mParents.push_back(&mNiNode); mNiNode2.mParents.push_back(&mNiNode); rcn.mParents.push_back(&mNiNode2); mNiTriShape2.mParents.push_back(&rcn);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiNode2) }; mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&rcn) }; rcn.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }
TEST_F(TestBulletNifLoader, rcn_flag_with_multiple_nested_rcns_uses_last_rcn) { Nif::NiNode rcn1; init(rcn1); rcn1.mRecordType = Nif::RC_RootCollisionNode;
Nif::NiNode rcn2; init(rcn2); rcn2.mRecordType = Nif::RC_RootCollisionNode;
mNiStringExtraData.mData = "RCN"; mNiStringExtraData.mRecordType = Nif::RC_NiStringExtraData; mNiNode.mExtra = Nif::ExtraPtr(&mNiStringExtraData);
mNiNode2.mParents.push_back(&mNiNode); mNiNode3.mParents.push_back(&mNiNode);
rcn1.mParents.push_back(&mNiNode2); mNiTriShape.mParents.push_back(&rcn1);
rcn2.mParents.push_back(&mNiNode3); mNiTriShape2.mParents.push_back(&rcn2);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiNode2), Nif::NiAVObjectPtr(&mNiNode3) }; mNiNode2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&rcn1) }; mNiNode3.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&rcn2) }; rcn1.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; rcn2.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape2) };
Nif::NIFFile file(testNif); file.mRoots.push_back(&mNiNode); file.mHash = mHash;
const auto result = mLoader.load(file);
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); std::unique_ptr<Resource::TriangleMeshShape> mesh(new Resource::TriangleMeshShape(triangles.release(), true)); std::unique_ptr<btCompoundShape> compound(new btCompoundShape); compound->addChildShape( btTransform::getIdentity(), new Resource::ScaledTriangleMeshShape(mesh.release(), btVector3(1, 1, 1)));
Resource::BulletShape expected; expected.mCollisionShape.reset(compound.release());
EXPECT_EQ(*result, expected); }}