Merge branch 'boundingvolume' into 'master'

Read NIF bounding volume data correctly

See merge request OpenMW/openmw!407
pull/593/head
psi29a 4 years ago
commit 6064d3e741

@ -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…
Cancel
Save