mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
Don't discard collision for the entire tree if BSXFlags marker flag is set
This commit is contained in:
parent
5b9a195e3d
commit
2de8c6b3c0
3 changed files with 53 additions and 37 deletions
|
@ -1158,12 +1158,13 @@ namespace
|
|||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision)
|
||||
TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision_for_markers)
|
||||
{
|
||||
mNiIntegerExtraData.data = 32; // BSX flag "editor marker"
|
||||
mNiIntegerExtraData.recType = Nif::RC_BSXFlags;
|
||||
mNiTriShape.extralist.push_back(Nif::ExtraPtr(&mNiIntegerExtraData));
|
||||
mNiTriShape.parents.push_back(&mNiNode);
|
||||
mNiTriShape.name = "EditorMarker";
|
||||
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) }));
|
||||
|
||||
Nif::NIFFile file("test.nif");
|
||||
|
|
|
@ -182,8 +182,10 @@ namespace NifBullet
|
|||
if (hasCollisionNode && !hasCollisionShape)
|
||||
mShape->mVisualCollisionType = Resource::VisualCollisionType::Camera;
|
||||
bool generateCollisionShape = !hasCollisionShape;
|
||||
handleNode(filename, *node, nullptr, 0, generateCollisionShape, isAnimated, generateCollisionShape, false,
|
||||
mShape->mVisualCollisionType);
|
||||
HandleNodeArgs args;
|
||||
args.mAutogenerated = args.mIsCollisionNode = generateCollisionShape;
|
||||
args.mAnimated = isAnimated;
|
||||
handleNode(filename, *node, nullptr, args, mShape->mVisualCollisionType);
|
||||
}
|
||||
|
||||
if (mCompoundShape)
|
||||
|
@ -269,36 +271,37 @@ namespace NifBullet
|
|||
}
|
||||
|
||||
void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent,
|
||||
int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid,
|
||||
Resource::VisualCollisionType& visualCollisionType)
|
||||
HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType)
|
||||
{
|
||||
// TODO: allow on-the fly collision switching via toggling this flag
|
||||
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
|
||||
&& visualCollisionType == Resource::VisualCollisionType::Camera)
|
||||
return;
|
||||
|
||||
// Accumulate the flags from all the child nodes. This works for all
|
||||
// the flags we currently use, at least.
|
||||
flags |= node.flags;
|
||||
|
||||
if (!node.controller.empty() && node.controller->recType == Nif::RC_NiKeyframeController
|
||||
&& node.controller->isActive())
|
||||
isAnimated = true;
|
||||
args.mAnimated = true;
|
||||
|
||||
isCollisionNode = isCollisionNode || (node.recType == Nif::RC_RootCollisionNode);
|
||||
if (node.recType == Nif::RC_RootCollisionNode)
|
||||
{
|
||||
args.mIsCollisionNode = true;
|
||||
if (args.mAutogenerated)
|
||||
{
|
||||
// Encountered a RootCollisionNode inside an autogenerated mesh.
|
||||
|
||||
// Don't collide with AvoidNode shapes
|
||||
avoid = avoid || (node.recType == Nif::RC_AvoidNode);
|
||||
// We treat empty RootCollisionNodes as NCC flag (set collisionType to `Camera`)
|
||||
// and generate the camera collision shape based on rendered geometry.
|
||||
if (visualCollisionType == Resource::VisualCollisionType::Camera)
|
||||
return;
|
||||
|
||||
// We encountered a RootCollisionNode inside autogenerated mesh. It is not right.
|
||||
if (node.recType == Nif::RC_RootCollisionNode && autogenerated)
|
||||
// Otherwise we'll want to notify the user.
|
||||
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << fileName
|
||||
<< ". Treating it as a common NiTriShape.";
|
||||
}
|
||||
}
|
||||
|
||||
// Don't collide with AvoidNode shapes
|
||||
if (node.recType == Nif::RC_AvoidNode)
|
||||
args.mAvoid = true;
|
||||
|
||||
// Check for extra data
|
||||
std::vector<Nif::ExtraPtr> extraCollection;
|
||||
|
@ -326,7 +329,7 @@ namespace NifBullet
|
|||
// No collision.
|
||||
visualCollisionType = Resource::VisualCollisionType::Default;
|
||||
}
|
||||
else if (sd->string == "MRK" && autogenerated)
|
||||
else if (sd->string == "MRK" && args.mAutogenerated)
|
||||
{
|
||||
// Marker can still have collision if the model explicitely specifies it via a RootCollisionNode.
|
||||
return;
|
||||
|
@ -336,11 +339,11 @@ namespace NifBullet
|
|||
{
|
||||
auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
|
||||
if (bsxFlags->data & 32) // Editor marker flag
|
||||
return;
|
||||
args.mHasMarkers = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCollisionNode)
|
||||
if (args.mIsCollisionNode)
|
||||
{
|
||||
// NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape!
|
||||
// It must be ignored completely.
|
||||
|
@ -349,7 +352,7 @@ namespace NifBullet
|
|||
&& (node.recType == Nif::RC_NiTriShape || node.recType == Nif::RC_NiTriStrips
|
||||
|| node.recType == Nif::RC_BSLODTriShape))
|
||||
{
|
||||
handleNiTriShape(static_cast<const Nif::NiGeometry&>(node), parent, isAnimated, avoid);
|
||||
handleNiTriShape(static_cast<const Nif::NiGeometry&>(node), parent, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,20 +367,24 @@ namespace NifBullet
|
|||
continue;
|
||||
|
||||
assert(std::find(child->parents.begin(), child->parents.end(), ninode) != child->parents.end());
|
||||
handleNode(fileName, child.get(), ¤tParent, flags, isCollisionNode, isAnimated, autogenerated,
|
||||
avoid, visualCollisionType);
|
||||
handleNode(fileName, child.get(), ¤tParent, args, visualCollisionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BulletNifLoader::handleNiTriShape(
|
||||
const Nif::NiGeometry& niGeometry, const Nif::Parent* nodeParent, bool isAnimated, bool avoid)
|
||||
const Nif::NiGeometry& niGeometry, const Nif::Parent* nodeParent, HandleNodeArgs args)
|
||||
{
|
||||
// mHasMarkers is specifically BSXFlags editor marker flag.
|
||||
// If this changes, the check must be corrected.
|
||||
if (args.mHasMarkers && Misc::StringUtils::ciStartsWith(niGeometry.name, "EditorMarker"))
|
||||
return;
|
||||
|
||||
if (niGeometry.data.empty() || niGeometry.data->vertices.empty())
|
||||
return;
|
||||
|
||||
if (!niGeometry.skin.empty())
|
||||
isAnimated = false;
|
||||
args.mAnimated = false;
|
||||
|
||||
std::unique_ptr<btTriangleMesh> childMesh = makeChildMesh(niGeometry);
|
||||
if (childMesh == nullptr || childMesh->getNumTriangles() == 0)
|
||||
|
@ -398,12 +405,12 @@ namespace NifBullet
|
|||
for (int j = 0; j < 3; ++j)
|
||||
trans.getBasis()[i][j] = transform(j, i);
|
||||
|
||||
if (!avoid)
|
||||
if (!args.mAvoid)
|
||||
{
|
||||
if (!mCompoundShape)
|
||||
mCompoundShape.reset(new btCompoundShape);
|
||||
|
||||
if (isAnimated)
|
||||
if (args.mAnimated)
|
||||
mShape->mAnimatedShapes.emplace(niGeometry.recIndex, mCompoundShape->getNumChildShapes());
|
||||
mCompoundShape->addChildShape(trans, childShape.get());
|
||||
}
|
||||
|
|
|
@ -52,14 +52,22 @@ namespace NifBullet
|
|||
private:
|
||||
bool findBoundingBox(const Nif::Node& node, const std::string& filename);
|
||||
|
||||
void handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent, int flags,
|
||||
bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid,
|
||||
Resource::VisualCollisionType& visualCollisionType);
|
||||
struct HandleNodeArgs
|
||||
{
|
||||
bool mHasMarkers{ false };
|
||||
bool mAnimated{ false };
|
||||
bool mIsCollisionNode{ false };
|
||||
bool mAutogenerated{ false };
|
||||
bool mAvoid{ false };
|
||||
};
|
||||
|
||||
void handleNode(const std::string& fileName, const Nif::Node& node, const Nif::Parent* parent,
|
||||
HandleNodeArgs args, Resource::VisualCollisionType& visualCollisionType);
|
||||
|
||||
bool hasRootCollisionNode(const Nif::Node& rootNode) const;
|
||||
bool collisionShapeIsEmpty(const Nif::Node& rootNode) const;
|
||||
|
||||
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, bool isAnimated, bool avoid);
|
||||
void handleNiTriShape(const Nif::NiGeometry& nifNode, const Nif::Parent* parent, HandleNodeArgs args);
|
||||
|
||||
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mCompoundShape;
|
||||
std::unique_ptr<btCompoundShape, Resource::DeleteCollisionShape> mAvoidCompoundShape;
|
||||
|
|
Loading…
Reference in a new issue