Merge branch 'fix_6841' into 'master'

Treat empty `RootCollisionNode` in NIF as NC flag and generate VisualOnly collision shape

Closes #6841

See merge request OpenMW/openmw!2084
LTO-timing^2
psi29a 3 years ago
commit aaaeed572a

@ -187,6 +187,7 @@ namespace Resource
return compareObjects(lhs.mCollisionShape.get(), rhs.mCollisionShape.get())
&& compareObjects(lhs.mAvoidCollisionShape.get(), rhs.mAvoidCollisionShape.get())
&& lhs.mCollisionBox == rhs.mCollisionBox
&& lhs.mCollisionType == rhs.mCollisionType
&& lhs.mAnimatedShapes == rhs.mAnimatedShapes;
}
@ -197,6 +198,7 @@ namespace Resource
<< value.mAvoidCollisionShape.get() << ", "
<< value.mCollisionBox << ", "
<< value.mAnimatedShapes
<< ", collisionType=" << value.mCollisionType
<< "}";
}
}
@ -1034,7 +1036,7 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_equal_ncc_should_return_shape_with_cameraonly_collision)
{
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.string = "NC___";
mNiStringExtraData2.string = "NCC__";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);
mNiTriShape.parents.push_back(&mNiNode);
@ -1054,7 +1056,6 @@ namespace
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_nocollision)
{
mNiStringExtraData.string = "NC___";
@ -1100,6 +1101,36 @@ namespace
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.data = Nif::NiGeometryDataPtr(&mNiTriShapeData);
niTriShape.parents.push_back(&mNiNode);
emptyCollisionNode.recType = Nif::RC_RootCollisionNode;
emptyCollisionNode.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>(
{Nif::NodePtr(&niTriShape), Nif::NodePtr(&emptyCollisionNode)}));
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);
std::unique_ptr<btTriangleMesh> triangles(new btTriangleMesh(false));
triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0));
Resource::BulletShape expected;
expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true));
expected.mCollisionType = Resource::BulletShape::CollisionType::Camera;
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape)
{
mNiStringExtraData.string = "MRK";

@ -220,8 +220,12 @@ osg::ref_ptr<Resource::BulletShape> BulletNifLoader::load(const Nif::File& nif)
// from the collision data present in every root node.
for (const Nif::Node* node : roots)
{
bool autogenerated = hasAutoGeneratedCollision(*node);
handleNode(filename, *node, nullptr, 0, autogenerated, isAnimated, autogenerated, false, mShape->mCollisionType);
bool hasCollisionNode = hasRootCollisionNode(*node);
bool hasCollisionShape = hasCollisionNode && !collisionShapeIsEmpty(*node);
if (hasCollisionNode && !hasCollisionShape)
mShape->mCollisionType = Resource::BulletShape::CollisionType::Camera;
bool generateCollisionShape = !hasCollisionShape;
handleNode(filename, *node, nullptr, 0, generateCollisionShape, isAnimated, generateCollisionShape, false, mShape->mCollisionType);
}
if (mCompoundShape)
@ -295,18 +299,37 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node& node, const std::string&
return false;
}
bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node& rootNode)
bool BulletNifLoader::hasRootCollisionNode(const Nif::Node& rootNode) const
{
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&rootNode))
{
const Nif::NodeList &list = ninode->children;
for(size_t i = 0;i < list.length();i++)
{
if(!list[i].empty())
{
if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode)
return false;
}
if(list[i].empty())
continue;
if (list[i].getPtr()->recType == Nif::RC_RootCollisionNode)
return true;
}
}
return false;
}
bool BulletNifLoader::collisionShapeIsEmpty(const Nif::Node& rootNode) const
{
if (const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(&rootNode))
{
const Nif::NodeList &list = ninode->children;
for(size_t i = 0;i < list.length();i++)
{
if(list[i].empty())
continue;
const Nif::Node* childNode = list[i].getPtr();
if (childNode->recType != Nif::RC_RootCollisionNode)
continue;
const Nif::NiNode* niChildnode = static_cast<const Nif::NiNode*>(childNode); // RootCollisionNode is always a NiNode
if (childNode->hasBounds || niChildnode->children.length() > 0)
return false;
}
}
return true;
@ -319,6 +342,11 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& n
if (node.recType == Nif::RC_NiCollisionSwitch && !node.collisionActive())
return;
// If RootCollisionNode is empty we treat it as NCC flag and autogenerate collision shape as there was no RootCollisionNode.
// So ignoring it here if `autogenerated` is true and collisionType was set to `Camera`.
if (node.recType == Nif::RC_RootCollisionNode && autogenerated && collisionType == Resource::BulletShape::CollisionType::Camera)
return;
// Accumulate the flags from all the child nodes. This works for all
// the flags we currently use, at least.
flags |= node.flags;

@ -59,7 +59,8 @@ private:
void handleNode(const std::string& fileName, const Nif::Node& node,const Nif::Parent* parent, int flags,
bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid, unsigned int& cameraOnlyCollision);
bool hasAutoGeneratedCollision(const Nif::Node& rootNode);
bool hasRootCollisionNode(const Nif::Node& rootNode) const;
bool collisionShapeIsEmpty(const Nif::Node& rootNode) const;
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, const osg::Matrixf& transform,
bool isAnimated, bool avoid);

Loading…
Cancel
Save