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) TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default)
{ {
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0)); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0));
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
const auto result = mLoader.load(mNifFile); const auto result = mLoader.load(mNifFile);
Resource::BulletShape expected; Resource::BulletShape expected;
@ -422,11 +423,13 @@ namespace
{ {
mNode.hasBounds = true; mNode.hasBounds = true;
mNode.flags |= Nif::NiNode::Flag_BBoxCollision; mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
mNode.boundXYZ = osg::Vec3f(1, 2, 3); mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.boundPos = osg::Vec3f(-1, -2, -3); 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, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode));
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
const auto result = mLoader.load(mNifFile); const auto result = mLoader.load(mNifFile);
Resource::BulletShape expected; Resource::BulletShape expected;
@ -444,12 +447,14 @@ namespace
{ {
mNode.hasBounds = true; mNode.hasBounds = true;
mNode.flags |= Nif::NiNode::Flag_BBoxCollision; mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
mNode.boundXYZ = osg::Vec3f(1, 2, 3); mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.boundPos = osg::Vec3f(-1, -2, -3); 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)})); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
const auto result = mLoader.load(mNifFile); const auto result = mLoader.load(mNifFile);
Resource::BulletShape expected; Resource::BulletShape expected;
@ -467,16 +472,19 @@ namespace
{ {
mNode.hasBounds = true; mNode.hasBounds = true;
mNode.flags |= Nif::NiNode::Flag_BBoxCollision; mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
mNode.boundXYZ = osg::Vec3f(1, 2, 3); mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.boundPos = osg::Vec3f(-1, -2, -3); mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
mNiNode.hasBounds = true; mNiNode.hasBounds = true;
mNiNode.boundXYZ = osg::Vec3f(4, 5, 6); mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiNode.boundPos = osg::Vec3f(-4, -5, -6); 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)})); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
const auto result = mLoader.load(mNifFile); const auto result = mLoader.load(mNifFile);
Resource::BulletShape expected; Resource::BulletShape expected;
@ -494,20 +502,24 @@ namespace
{ {
mNode.hasBounds = true; mNode.hasBounds = true;
mNode.flags |= Nif::NiNode::Flag_BBoxCollision; mNode.flags |= Nif::NiNode::Flag_BBoxCollision;
mNode.boundXYZ = osg::Vec3f(1, 2, 3); mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.boundPos = osg::Vec3f(-1, -2, -3); mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
mNode2.hasBounds = true; mNode2.hasBounds = true;
mNode2.boundXYZ = osg::Vec3f(4, 5, 6); mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode2.boundPos = osg::Vec3f(-4, -5, -6); mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
mNiNode.hasBounds = true; mNiNode.hasBounds = true;
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiNode.boundPos = osg::Vec3f(-7, -8, -9); 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)})); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
const auto result = mLoader.load(mNifFile); const auto result = mLoader.load(mNifFile);
Resource::BulletShape expected; 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) 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.hasBounds = true;
mNode.boundXYZ = osg::Vec3f(1, 2, 3); mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.boundPos = osg::Vec3f(-1, -2, -3); mNode.bounds.box.extents = osg::Vec3f(1, 2, 3);
mNode.bounds.box.center = osg::Vec3f(-1, -2, -3);
mNode2.hasBounds = true; mNode2.hasBounds = true;
mNode2.flags |= Nif::NiNode::Flag_BBoxCollision; mNode2.flags |= Nif::NiNode::Flag_BBoxCollision;
mNode2.boundXYZ = osg::Vec3f(4, 5, 6); mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode2.boundPos = osg::Vec3f(-4, -5, -6); mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6);
mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6);
mNiNode.hasBounds = true; mNiNode.hasBounds = true;
mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiNode.boundPos = osg::Vec3f(-7, -8, -9); 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)})); mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)}));
EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode));
EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif"));
const auto result = mLoader.load(mNifFile); const auto result = mLoader.load(mNifFile);
Resource::BulletShape expected; 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) 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.hasBounds = true;
mNode.boundXYZ = osg::Vec3f(1, 2, 3); mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNode.boundPos = osg::Vec3f(-1, -2, -3); 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, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); 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) TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape)
{ {
mNiTriShape.hasBounds = true; mNiTriShape.hasBounds = true;
mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3); mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV;
mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3); 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, numRoots()).WillOnce(Return(1));
EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape));

@ -16,6 +16,117 @@ namespace Nif
struct NiNode; 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 /** 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 parent node (unless it's the root), and transformation (location
and rotation) relative to it's parent. and rotation) relative to it's parent.
@ -31,9 +142,7 @@ public:
// Bounding box info // Bounding box info
bool hasBounds{false}; bool hasBounds{false};
osg::Vec3f boundPos; NiBoundingVolume bounds;
Matrix3 boundRot;
osg::Vec3f boundXYZ; // Box size
void read(NIFStream *nif) override void read(NIFStream *nif) override
{ {
@ -48,13 +157,8 @@ public:
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0)) if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
hasBounds = nif->getBoolean(); hasBounds = nif->getBoolean();
if(hasBounds) if (hasBounds)
{ bounds.read(nif);
nif->getInt(); // always 1
boundPos = nif->getVector3();
boundRot = nif->getMatrix3();
boundXYZ = nif->getVector3();
}
// Reference to the collision object in Gamebryo files. // Reference to the collision object in Gamebryo files.
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0)) if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
nif->skip(4); 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))) if ((node = dynamic_cast<Nif::Node*>(r)))
break; break;
} }
const std::string filename = nif.getFilename();
if (!node) if (!node)
{ {
warn("Found no root nodes in NIF."); warn("Found no root nodes in NIF file " + filename);
return mShape; return mShape;
} }
if (findBoundingBox(node)) if (findBoundingBox(node, filename))
{ {
const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents); const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents);
const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate); 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). // 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 // assume all nodes in the file will be animated
const auto filename = nif.getFilename();
const bool isAnimated = pathFileNameStartsWithX(filename); const bool isAnimated = pathFileNameStartsWithX(filename);
handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated); 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. // Find a boundingBox in the node hierarchy.
// Return: use bounding box for collision? // 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) if (node->hasBounds)
{ {
mShape->mCollisionBoxHalfExtents = node->boundXYZ; unsigned int type = node->bounds.type;
mShape->mCollisionBoxTranslate = node->boundPos; 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) if (node->flags & Nif::NiNode::Flag_BBoxCollision)
{ {
@ -215,7 +228,7 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node)
{ {
if(!list[i].empty()) if(!list[i].empty())
{ {
bool found = findBoundingBox (list[i].getPtr()); bool found = findBoundingBox (list[i].getPtr(), filename);
if (found) if (found)
return true; return true;
} }

@ -40,7 +40,7 @@ class BulletNifLoader
public: public:
void warn(const std::string &msg) void warn(const std::string &msg)
{ {
Log(Debug::Warning) << "NIFLoader: Warn:" << msg; Log(Debug::Warning) << "NIFLoader: Warn: " << msg;
} }
void fail(const std::string &msg) void fail(const std::string &msg)
@ -52,7 +52,7 @@ public:
osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file); osg::ref_ptr<Resource::BulletShape> load(const Nif::File& file);
private: 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, void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode,
bool isAnimated=false, bool autogenerated=false, bool avoid=false); bool isAnimated=false, bool autogenerated=false, bool avoid=false);

Loading…
Cancel
Save