diff --git a/CHANGELOG.md b/CHANGELOG.md index 27147172b0..de117145e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Bug #5870: Disposing of actors who were selected in the console doesn't deselect them like vanilla Bug #5883: Immobile creatures don't cause water ripples Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load + Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex Bug #6313: Followers with high Fight can turn hostile Bug #6427: Enemy health bar disappears before damaging effect ends Bug #6550: Cloned body parts don't inherit texture effects diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 066da7b607..a12a49a466 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -586,7 +586,7 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_shape_with_triangle_mesh_shape) + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_static_shape) { Nif::NIFFile file("test.nif"); file.mRoots.push_back(&mNiTriShape); @@ -596,14 +596,18 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } TEST_F(TestBulletNifLoader, - for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape) + for_tri_shape_root_node_with_bounds_should_return_static_shape_with_bounds_but_with_null_collision_shape) { mNiTriShape.hasBounds = true; mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; @@ -623,7 +627,7 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape) + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_static_shape) { mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); @@ -636,13 +640,17 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape) + TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_static_shape) { mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiNode2) })); mNiNode2.parents.push_back(&mNiNode); @@ -657,13 +665,17 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes) + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_static_shape_with_all_meshes) { mNiTriShape.parents.push_back(&mNiNode); mNiTriShape2.parents.push_back(&mNiNode); @@ -677,16 +689,22 @@ namespace const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); - triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr triangles2(new btTriangleMesh(false)); + triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + compound->addChildShape( + btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles2.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } TEST_F(TestBulletNifLoader, - for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape) + for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_static_shape) { mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance); mNiTriShape.parents.push_back(&mNiNode); @@ -700,14 +718,16 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, - for_tri_shape_root_node_and_filename_starting_with_x_should_return_shape_with_compound_shape) + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_animated_shape) { copy(mTransform, mNiTriShape.trafo); mNiTriShape.trafo.scale = 3; @@ -731,8 +751,7 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, - for_tri_shape_child_node_and_filename_starting_with_x_should_return_shape_with_compound_shape) + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_animated_shape) { copy(mTransform, mNiTriShape.trafo); mNiTriShape.trafo.scale = 3; @@ -759,8 +778,8 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, - for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_shape_with_compound_shape) + TEST_F( + TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_animated_shape) { copy(mTransform, mNiTriShape.trafo); mNiTriShape.trafo.scale = 3; @@ -801,7 +820,7 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_shape_with_compound_shape) + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_animated_shape) { mController.recType = Nif::RC_NiKeyframeController; mController.flags |= Nif::Controller::Flag_Active; @@ -831,8 +850,7 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, - for_two_tri_shape_children_nodes_where_one_with_controller_should_return_shape_with_compound_shape) + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_animated_shape) { mController.recType = Nif::RC_NiKeyframeController; mController.flags |= Nif::Controller::Flag_Active; @@ -856,10 +874,9 @@ namespace const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); - triangles->addTriangle( - btVector3(4, 8, 12), btVector3(16, 8, 12), btVector3(16, 18.5309906005859375, 6.246893405914306640625)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); - mesh->setLocalScaling(btVector3(1, 1, 1)); + mesh->setLocalScaling(btVector3(12, 12, 12)); std::unique_ptr triangles2(new btTriangleMesh(false)); triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); @@ -867,11 +884,11 @@ namespace mesh2->setLocalScaling(btVector3(12, 12, 12)); std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform2, mesh.release()); shape->addChildShape(mResultTransform2, mesh2.release()); - shape->addChildShape(btTransform::getIdentity(), mesh.release()); Resource::BulletShape expected; expected.mCollisionShape.reset(shape.release()); - expected.mAnimatedShapes = { { -1, 0 } }; + expected.mAnimatedShapes = { { -1, 1 } }; EXPECT_EQ(*result, expected); } @@ -921,8 +938,11 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); Resource::BulletShape expected; - expected.mAvoidCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), false)); + expected.mAvoidCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } @@ -980,8 +1000,12 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); + expected.mVisualCollisionType = Resource::VisualCollisionType::Camera; EXPECT_EQ(*result, expected); @@ -1005,8 +1029,11 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Camera; EXPECT_EQ(*result, expected); @@ -1029,8 +1056,11 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Default; EXPECT_EQ(*result, expected); @@ -1054,8 +1084,11 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Default; EXPECT_EQ(*result, expected); @@ -1085,8 +1118,11 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); expected.mVisualCollisionType = Resource::VisualCollisionType::Camera; EXPECT_EQ(*result, expected); @@ -1133,8 +1169,11 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } @@ -1154,7 +1193,7 @@ namespace EXPECT_EQ(*result, expected); } - TEST_F(TestBulletNifLoader, for_tri_strips_root_node_should_return_shape_with_triangle_mesh_shape) + TEST_F(TestBulletNifLoader, for_tri_strips_root_node_should_return_static_shape) { Nif::NIFFile file("test.nif"); file.mRoots.push_back(&mNiTriStrips); @@ -1165,8 +1204,11 @@ namespace std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); triangles->addTriangle(btVector3(1, 0, 0), btVector3(0, 1, 0), btVector3(1, 1, 0)); + std::unique_ptr compound(new btCompoundShape); + compound->addChildShape(btTransform::getIdentity(), new Resource::TriangleMeshShape(triangles.release(), true)); + Resource::BulletShape expected; - expected.mCollisionShape.reset(new Resource::TriangleMeshShape(triangles.release(), true)); + expected.mCollisionShape.reset(compound.release()); EXPECT_EQ(*result, expected); } diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index aa5922e79c..546f9e5279 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -40,7 +40,7 @@ namespace return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X'); } - void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf& transform) + void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data) { const std::vector& vertices = data.vertices; const std::vector& triangles = data.triangles; @@ -49,13 +49,13 @@ namespace for (std::size_t i = 0; i < triangles.size(); i += 3) { - mesh.addTriangle(Misc::Convert::toBullet(vertices[triangles[i + 0]] * transform), - Misc::Convert::toBullet(vertices[triangles[i + 1]] * transform), - Misc::Convert::toBullet(vertices[triangles[i + 2]] * transform)); + mesh.addTriangle(Misc::Convert::toBullet(vertices[triangles[i + 0]]), + Misc::Convert::toBullet(vertices[triangles[i + 1]]), + Misc::Convert::toBullet(vertices[triangles[i + 2]])); } } - void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf& transform) + void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data) { mesh.preallocateVertices(static_cast(data.vertices.size())); mesh.preallocateIndices(static_cast(data.mNumTriangles)); @@ -75,9 +75,9 @@ namespace c = strip[i]; if (a == b || b == c || a == c) continue; - const btVector3 vertexA = Misc::Convert::toBullet(data.vertices[a] * transform); - const btVector3 vertexB = Misc::Convert::toBullet(data.vertices[b] * transform); - const btVector3 vertexC = Misc::Convert::toBullet(data.vertices[c] * transform); + const btVector3 vertexA = Misc::Convert::toBullet(data.vertices[a]); + const btVector3 vertexB = Misc::Convert::toBullet(data.vertices[b]); + const btVector3 vertexC = Misc::Convert::toBullet(data.vertices[c]); if (i % 2 == 0) mesh.addTriangle(vertexA, vertexB, vertexC); else @@ -117,22 +117,11 @@ namespace return {}; } - std::monostate fillTriangleMesh( - std::unique_ptr& mesh, const Nif::NiGeometry& geometry, const osg::Matrixf& transform) - { - return handleNiGeometry(geometry, [&](const auto& data) { - if (mesh == nullptr) - mesh = std::make_unique(false); - fillTriangleMesh(*mesh, data, transform); - return std::monostate{}; - }); - } - std::unique_ptr makeChildMesh(const Nif::NiGeometry& geometry) { return handleNiGeometry(geometry, [&](const auto& data) { auto mesh = std::make_unique(); - fillTriangleMesh(*mesh, data, osg::Matrixf()); + fillTriangleMesh(*mesh, data); return mesh; }); } @@ -147,8 +136,7 @@ namespace NifBullet mShape = new Resource::BulletShape; mCompoundShape.reset(); - mStaticMesh.reset(); - mAvoidStaticMesh.reset(); + mAvoidCompoundShape.reset(); mShape->mFileHash = nif.getHash(); @@ -208,30 +196,10 @@ namespace NifBullet } if (mCompoundShape) - { - if (mStaticMesh != nullptr && mStaticMesh->getNumTriangles() > 0) - { - btTransform trans; - trans.setIdentity(); - std::unique_ptr child - = std::make_unique(mStaticMesh.get(), true); - mCompoundShape->addChildShape(trans, child.get()); - std::ignore = child.release(); - std::ignore = mStaticMesh.release(); - } mShape->mCollisionShape = std::move(mCompoundShape); - } - else if (mStaticMesh != nullptr && mStaticMesh->getNumTriangles() > 0) - { - mShape->mCollisionShape.reset(new Resource::TriangleMeshShape(mStaticMesh.get(), true)); - std::ignore = mStaticMesh.release(); - } - if (mAvoidStaticMesh != nullptr && mAvoidStaticMesh->getNumTriangles() > 0) - { - mShape->mAvoidCollisionShape.reset(new Resource::TriangleMeshShape(mAvoidStaticMesh.get(), false)); - std::ignore = mAvoidStaticMesh.release(); - } + if (mAvoidCompoundShape) + mShape->mAvoidCollisionShape = std::move(mAvoidCompoundShape); return mShape; } @@ -409,36 +377,39 @@ namespace NifBullet if (!niGeometry.skin.empty()) isAnimated = false; - if (isAnimated) - { - std::unique_ptr childMesh = makeChildMesh(niGeometry); - if (childMesh == nullptr || childMesh->getNumTriangles() == 0) - return; + std::unique_ptr childMesh = makeChildMesh(niGeometry); + if (childMesh == nullptr || childMesh->getNumTriangles() == 0) + return; + auto childShape = std::make_unique(childMesh.get(), true); + std::ignore = childMesh.release(); + + float scale = niGeometry.trafo.scale; + for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent) + scale *= parent->mNiNode.trafo.scale; + osg::Quat q = transform.getRotate(); + osg::Vec3f v = transform.getTrans(); + childShape->setLocalScaling(btVector3(scale, scale, scale)); + + btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z())); + + if (!avoid) + { if (!mCompoundShape) mCompoundShape.reset(new btCompoundShape); - auto childShape = std::make_unique(childMesh.get(), true); - std::ignore = childMesh.release(); - - float scale = niGeometry.trafo.scale; - for (const Nif::Parent* parent = nodeParent; parent != nullptr; parent = parent->mParent) - scale *= parent->mNiNode.trafo.scale; - osg::Quat q = transform.getRotate(); - osg::Vec3f v = transform.getTrans(); - childShape->setLocalScaling(btVector3(scale, scale, scale)); - - btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z())); - - mShape->mAnimatedShapes.emplace(niGeometry.recIndex, mCompoundShape->getNumChildShapes()); - + if (isAnimated) + mShape->mAnimatedShapes.emplace(niGeometry.recIndex, mCompoundShape->getNumChildShapes()); mCompoundShape->addChildShape(trans, childShape.get()); - std::ignore = childShape.release(); } - else if (avoid) - fillTriangleMesh(mAvoidStaticMesh, niGeometry, transform); else - fillTriangleMesh(mStaticMesh, niGeometry, transform); + { + if (!mAvoidCompoundShape) + mAvoidCompoundShape.reset(new btCompoundShape); + mAvoidCompoundShape->addChildShape(trans, childShape.get()); + } + + std::ignore = childShape.release(); } } // namespace NifBullet diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 947f93eadf..28cc2c5246 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -64,10 +64,7 @@ namespace NifBullet bool isAnimated, bool avoid); std::unique_ptr mCompoundShape; - - std::unique_ptr mStaticMesh; - - std::unique_ptr mAvoidStaticMesh; + std::unique_ptr mAvoidCompoundShape; osg::ref_ptr mShape; };