mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 03:15:32 +00:00
Read NIF bounding volume data correctly
This commit is contained in:
parent
18a95377eb
commit
df9667e923
4 changed files with 178 additions and 43 deletions
|
@ -373,6 +373,7 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default)
|
||||
{
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -422,11 +423,13 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -444,12 +447,14 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -467,16 +472,19 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.boundXYZ = osg::Vec3f(4, 5, 6);
|
||||
mNiNode.boundPos = osg::Vec3f(-4, -5, -6);
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiNode.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -494,20 +502,24 @@ namespace
|
|||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
mNode2.hasBounds = true;
|
||||
mNode2.boundXYZ = osg::Vec3f(4, 5, 6);
|
||||
mNode2.boundPos = osg::Vec3f(-4, -5, -6);
|
||||
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.boundPos = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -524,21 +536,25 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds)
|
||||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
mNode2.hasBounds = true;
|
||||
mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
|
||||
mNode2.boundXYZ = osg::Vec3f(4, 5, 6);
|
||||
mNode2.boundPos = osg::Vec3f(-4, -5, -6);
|
||||
mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
|
||||
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
|
||||
|
||||
mNiNode.hasBounds = true;
|
||||
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.boundPos = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9);
|
||||
mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9);
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
|
||||
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
|
||||
const auto result = mLoader.load(mNifFile);
|
||||
|
||||
Resource::BulletShape expected;
|
||||
|
@ -555,8 +571,9 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape)
|
||||
{
|
||||
mNode.hasBounds = true;
|
||||
mNode.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNode.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
|
||||
|
@ -588,8 +605,9 @@ namespace
|
|||
TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape)
|
||||
{
|
||||
mNiTriShape.hasBounds = true;
|
||||
mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3);
|
||||
mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3);
|
||||
mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
|
||||
mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3);
|
||||
mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3);
|
||||
|
||||
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
|
||||
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));
|
||||
|
|
|
@ -16,6 +16,117 @@ namespace Nif
|
|||
|
||||
struct NiNode;
|
||||
|
||||
struct NiBoundingVolume
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
SPHERE_BV = 0,
|
||||
BOX_BV = 1,
|
||||
CAPSULE_BV = 2,
|
||||
LOZENGE_BV = 3,
|
||||
UNION_BV = 4,
|
||||
HALFSPACE_BV = 5
|
||||
};
|
||||
|
||||
struct NiSphereBV
|
||||
{
|
||||
osg::Vec3f center;
|
||||
float radius{0.f};
|
||||
};
|
||||
|
||||
struct NiBoxBV
|
||||
{
|
||||
osg::Vec3f center;
|
||||
Matrix3 axis;
|
||||
osg::Vec3f extents;
|
||||
};
|
||||
|
||||
struct NiCapsuleBV
|
||||
{
|
||||
osg::Vec3f center, axis;
|
||||
float extent{0.f}, radius{0.f};
|
||||
};
|
||||
|
||||
struct NiLozengeBV
|
||||
{
|
||||
float radius{0.f}, extent0{0.f}, extent1{0.f};
|
||||
osg::Vec3f center, axis0, axis1;
|
||||
};
|
||||
|
||||
struct NiHalfSpaceBV
|
||||
{
|
||||
osg::Vec3f center, normal;
|
||||
};
|
||||
|
||||
unsigned int type;
|
||||
NiSphereBV sphere;
|
||||
NiBoxBV box;
|
||||
NiCapsuleBV capsule;
|
||||
NiLozengeBV lozenge;
|
||||
std::vector<NiBoundingVolume> children;
|
||||
NiHalfSpaceBV plane;
|
||||
void read(NIFStream* nif)
|
||||
{
|
||||
type = nif->getUInt();
|
||||
switch (type)
|
||||
{
|
||||
case SPHERE_BV:
|
||||
{
|
||||
sphere.center = nif->getVector3();
|
||||
sphere.radius = nif->getFloat();
|
||||
break;
|
||||
}
|
||||
case BOX_BV:
|
||||
{
|
||||
box.center = nif->getVector3();
|
||||
box.axis = nif->getMatrix3();
|
||||
box.extents = nif->getVector3();
|
||||
break;
|
||||
}
|
||||
case CAPSULE_BV:
|
||||
{
|
||||
capsule.center = nif->getVector3();
|
||||
capsule.axis = nif->getVector3();
|
||||
capsule.extent = nif->getFloat();
|
||||
capsule.radius = nif->getFloat();
|
||||
break;
|
||||
}
|
||||
case LOZENGE_BV:
|
||||
{
|
||||
lozenge.radius = nif->getFloat();
|
||||
lozenge.extent0 = nif->getFloat();
|
||||
lozenge.extent1 = nif->getFloat();
|
||||
lozenge.center = nif->getVector3();
|
||||
lozenge.axis0 = nif->getVector3();
|
||||
lozenge.axis1 = nif->getVector3();
|
||||
break;
|
||||
}
|
||||
case UNION_BV:
|
||||
{
|
||||
unsigned int numChildren = nif->getUInt();
|
||||
if (numChildren == 0)
|
||||
break;
|
||||
children.resize(numChildren);
|
||||
for (NiBoundingVolume& child : children)
|
||||
child.read(nif);
|
||||
break;
|
||||
}
|
||||
case HALFSPACE_BV:
|
||||
{
|
||||
plane.center = nif->getVector3();
|
||||
plane.normal = nif->getVector3();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "Unhandled NiBoundingVolume type: " << type;
|
||||
nif->file->fail(error.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** A Node is an object that's part of the main NIF tree. It has
|
||||
parent node (unless it's the root), and transformation (location
|
||||
and rotation) relative to it's parent.
|
||||
|
@ -31,9 +142,7 @@ public:
|
|||
|
||||
// Bounding box info
|
||||
bool hasBounds{false};
|
||||
osg::Vec3f boundPos;
|
||||
Matrix3 boundRot;
|
||||
osg::Vec3f boundXYZ; // Box size
|
||||
NiBoundingVolume bounds;
|
||||
|
||||
void read(NIFStream *nif) override
|
||||
{
|
||||
|
@ -48,13 +157,8 @@ public:
|
|||
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
||||
hasBounds = nif->getBoolean();
|
||||
if(hasBounds)
|
||||
{
|
||||
nif->getInt(); // always 1
|
||||
boundPos = nif->getVector3();
|
||||
boundRot = nif->getMatrix3();
|
||||
boundXYZ = nif->getVector3();
|
||||
}
|
||||
if (hasBounds)
|
||||
bounds.read(nif);
|
||||
// Reference to the collision object in Gamebryo files.
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
||||
nif->skip(4);
|
||||
|
|
|
@ -132,13 +132,14 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
|||
if ((node = dynamic_cast<Nif::Node*>(r)))
|
||||
break;
|
||||
}
|
||||
const std::string filename = nif.getFilename();
|
||||
if (!node)
|
||||
{
|
||||
warn("Found no root nodes in NIF.");
|
||||
warn("Found no root nodes in NIF file " + filename);
|
||||
return mShape;
|
||||
}
|
||||
|
||||
if (findBoundingBox(node))
|
||||
if (findBoundingBox(node, filename))
|
||||
{
|
||||
const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents);
|
||||
const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate);
|
||||
|
@ -158,7 +159,6 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
|||
|
||||
// files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource).
|
||||
// assume all nodes in the file will be animated
|
||||
const auto filename = nif.getFilename();
|
||||
const bool isAnimated = pathFileNameStartsWithX(filename);
|
||||
|
||||
handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated);
|
||||
|
@ -194,12 +194,25 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
|
|||
|
||||
// Find a boundingBox in the node hierarchy.
|
||||
// Return: use bounding box for collision?
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::Node* node)
|
||||
bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string& filename)
|
||||
{
|
||||
if (node->hasBounds)
|
||||
{
|
||||
mShape->mCollisionBoxHalfExtents = node->boundXYZ;
|
||||
mShape->mCollisionBoxTranslate = node->boundPos;
|
||||
unsigned int type = node->bounds.type;
|
||||
switch (type)
|
||||
{
|
||||
case Nif::NiBoundingVolume::Type::BOX_BV:
|
||||
mShape->mCollisionBoxHalfExtents = node->bounds.box.extents;
|
||||
mShape->mCollisionBoxTranslate = node->bounds.box.center;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
std::stringstream warning;
|
||||
warning << "Unsupported NiBoundingVolume type " << type << " in node " << node->recIndex;
|
||||
warning << " in file " << filename;
|
||||
warn(warning.str());
|
||||
}
|
||||
}
|
||||
|
||||
if (node->flags & Nif::NiNode::Flag_BBoxCollision)
|
||||
{
|
||||
|
@ -215,7 +228,7 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node)
|
|||
{
|
||||
if(!list[i].empty())
|
||||
{
|
||||
bool found = findBoundingBox (list[i].getPtr());
|
||||
bool found = findBoundingBox (list[i].getPtr(), filename);
|
||||
if (found)
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class BulletNifLoader
|
|||
public:
|
||||
void warn(const std::string &msg)
|
||||
{
|
||||
Log(Debug::Warning) << "NIFLoader: Warn:" << msg;
|
||||
Log(Debug::Warning) << "NIFLoader: Warn: " << msg;
|
||||
}
|
||||
|
||||
void fail(const std::string &msg)
|
||||
|
@ -52,7 +52,7 @@ public:
|
|||
osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file);
|
||||
|
||||
private:
|
||||
bool findBoundingBox(const Nif::Node* node);
|
||||
bool findBoundingBox(const Nif::Node* node, const std::string& filename);
|
||||
|
||||
void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode,
|
||||
bool isAnimated=false, bool autogenerated=false, bool avoid=false);
|
||||
|
|
Loading…
Reference in a new issue