diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index ff72bc3eb..63f6aa6b4 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -31,12 +31,17 @@ namespace } }; + TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_throw_exception) + { + EXPECT_THROW(mBuilder.create(), std::invalid_argument); + } + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape) { btTriangleMesh mesh; mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); btBvhTriangleMeshShape shape(&mesh, true); - mBuilder.addObject(shape, btTransform::getIdentity()); + mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); const auto recastMesh = mBuilder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 0, -1, @@ -51,7 +56,7 @@ namespace btTriangleMesh mesh; mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); btBvhTriangleMeshShape shape(&mesh, true); - mBuilder.addObject(shape, + mBuilder.addObject(static_cast(shape), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); const auto recastMesh = mBuilder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ @@ -66,7 +71,7 @@ namespace { const std::array heightfieldData {{0, 0, 0, 0}}; btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); - mBuilder.addObject(shape, btTransform::getIdentity()); + mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); const auto recastMesh = mBuilder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ -0.5, 0, -0.5, @@ -78,4 +83,121 @@ namespace })); EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2, 3, 4, 5})); } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles) + { + btBoxShape shape(btVector3(1, 1, 2)); + mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = mBuilder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + 1, 2, 1, + -1, 2, 1, + 1, 2, -1, + -1, 2, -1, + 1, -2, 1, + -1, -2, 1, + 1, -2, -1, + -1, -2, -1, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({ + 0, 2, 3, + 3, 1, 0, + 0, 4, 6, + 6, 2, 0, + 0, 1, 5, + 5, 4, 0, + 7, 5, 1, + 1, 3, 7, + 7, 3, 2, + 2, 6, 7, + 7, 6, 4, + 4, 5, 7, + })); + } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_compound_shape) + { + btTriangleMesh mesh1; + mesh1.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + btBvhTriangleMeshShape triangle1(&mesh1, true); + btBoxShape box(btVector3(1, 1, 2)); + btTriangleMesh mesh2; + mesh2.addTriangle(btVector3(1, 1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + btBvhTriangleMeshShape triangle2(&mesh2, true); + btCompoundShape shape; + shape.addChildShape(btTransform::getIdentity(), &triangle1); + shape.addChildShape(btTransform::getIdentity(), &box); + shape.addChildShape(btTransform::getIdentity(), &triangle2); + mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = mBuilder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + 1, 0, -1, + -1, 0, 1, + -1, 0, -1, + 1, 2, 1, + -1, 2, 1, + 1, 2, -1, + -1, 2, -1, + 1, -2, 1, + -1, -2, 1, + 1, -2, -1, + -1, -2, -1, + 1, 0, -1, + -1, 0, 1, + 1, 0, 1, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({ + 0, 1, 2, + 3, 5, 6, + 6, 4, 3, + 3, 7, 9, + 9, 5, 3, + 3, 4, 8, + 8, 7, 3, + 10, 8, 4, + 4, 6, 10, + 10, 6, 5, + 5, 9, 10, + 10, 9, 7, + 7, 8, 10, + 11, 12, 13, + })); + } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape) + { + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + btBvhTriangleMeshShape triangle(&mesh, true); + btCompoundShape shape; + shape.addChildShape(btTransform::getIdentity(), &triangle); + mBuilder.addObject(static_cast(shape), + btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); + const auto recastMesh = mBuilder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + 2, 3, 0, + 0, 3, 4, + 0, 3, 0, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); + } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape_with_transformed_bhv_triangle_shape) + { + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + btBvhTriangleMeshShape triangle(&mesh, true); + btCompoundShape shape; + shape.addChildShape(btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), + &triangle); + mBuilder.addObject(static_cast(shape), + btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); + const auto recastMesh = mBuilder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + 3, 12, 2, + 1, 12, 10, + 1, 12, 2, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); + } } diff --git a/components/detournavigator/chunkytrimesh.cpp b/components/detournavigator/chunkytrimesh.cpp index 9402569fc..1b7f53845 100644 --- a/components/detournavigator/chunkytrimesh.cpp +++ b/components/detournavigator/chunkytrimesh.cpp @@ -1,4 +1,5 @@ #include "chunkytrimesh.hpp" +#include "exceptions.hpp" #include @@ -119,7 +120,7 @@ namespace DetourNavigator const auto trianglesCount = indices.size() / 3; if (trianglesCount == 0) - return; + throw InvalidArgument("ChunkyTriMesh tris should contain at least 3 values"); const auto nchunks = (trianglesCount + trisPerChunk - 1) / trisPerChunk; diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index eb44dbcd5..1e0d2dfc8 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -7,9 +7,13 @@ #include +#include #include #include #include +#include + +#include namespace { @@ -35,6 +39,8 @@ namespace DetourNavigator return addObject(static_cast(shape), transform); else if (shape.isConcave()) return addObject(static_cast(shape), transform); + else if (shape.getShapeType() == BOX_SHAPE_PROXYTYPE) + return addObject(static_cast(shape), transform); std::ostringstream message; message << "Unsupported shape type: " << BroadphaseNativeTypes(shape.getShapeType()); throw InvalidArgument(message.str()); @@ -51,7 +57,7 @@ namespace DetourNavigator return addObject(shape, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { for (std::size_t i = 3; i > 0; --i) - addVertex(transform(triangle[i - 1])); + addTriangleVertex(transform(triangle[i - 1])); })); } @@ -60,10 +66,40 @@ namespace DetourNavigator return addObject(shape, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { for (std::size_t i = 0; i < 3; ++i) - addVertex(transform(triangle[i])); + addTriangleVertex(transform(triangle[i])); })); } + void RecastMeshBuilder::addObject(const btBoxShape& shape, const btTransform& transform) + { + const auto indexOffset = int(mVertices.size() / 3); + + for (int vertex = 0; vertex < shape.getNumVertices(); ++vertex) + { + btVector3 position; + shape.getVertex(vertex, position); + addVertex(transform(position)); + } + + static const std::array indices {{ + 0, 2, 3, + 3, 1, 0, + 0, 4, 6, + 6, 2, 0, + 0, 1, 5, + 5, 4, 0, + 7, 5, 1, + 1, 3, 7, + 7, 3, 2, + 2, 6, 7, + 7, 6, 4, + 4, 5, 7, + }}; + + std::transform(indices.begin(), indices.end(), std::back_inserter(mIndices), + [&] (int index) { return index + indexOffset; }); + } + std::shared_ptr RecastMeshBuilder::create() const { return std::make_shared(mIndices, mVertices, mSettings); @@ -83,10 +119,15 @@ namespace DetourNavigator shape.processAllTriangles(&callback, aabbMin, aabbMax); } + void RecastMeshBuilder::addTriangleVertex(const btVector3& worldPosition) + { + mIndices.push_back(static_cast(mVertices.size() / 3)); + addVertex(worldPosition); + } + void RecastMeshBuilder::addVertex(const btVector3& worldPosition) { const auto navMeshPosition = toNavMeshCoordinates(mSettings, makeOsgVec3f(worldPosition)); - mIndices.push_back(static_cast(mIndices.size())); mVertices.push_back(navMeshPosition.x()); mVertices.push_back(navMeshPosition.y()); mVertices.push_back(navMeshPosition.z()); diff --git a/components/detournavigator/recastmeshbuilder.hpp b/components/detournavigator/recastmeshbuilder.hpp index 5010a95e0..28ca1f713 100644 --- a/components/detournavigator/recastmeshbuilder.hpp +++ b/components/detournavigator/recastmeshbuilder.hpp @@ -3,6 +3,7 @@ #include "recastmesh.hpp" +class btBoxShape; class btCollisionShape; class btCompoundShape; class btConcaveShape; @@ -26,6 +27,8 @@ namespace DetourNavigator void addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform); + void addObject(const btBoxShape& shape, const btTransform& transform); + std::shared_ptr create() const; void reset(); @@ -37,6 +40,8 @@ namespace DetourNavigator void addObject(const btConcaveShape& shape, btTriangleCallback&& callback); + void addTriangleVertex(const btVector3& worldPosition); + void addVertex(const btVector3& worldPosition); }; }