1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-16 08:29:56 +00:00

Make RecastMesh independent from the order of RecastMeshBuilder calls

To make sure RecastMesh objects are equal if built with the same data but in
different order. Will be used later when there will be more than one place
building RecasMesh objects.
This commit is contained in:
elsid 2021-07-11 21:43:19 +02:00
parent 07c70dfb73
commit af7059373c
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
14 changed files with 335 additions and 316 deletions

View file

@ -87,6 +87,19 @@ namespace
}); });
} }
template <class Random>
Mesh generateMesh(std::size_t triangles, Random& random)
{
std::uniform_real_distribution<float> distribution(0.0, 1.0);
std::vector<float> vertices;
std::vector<int> indices;
std::vector<AreaType> areaTypes;
generateVertices(std::back_inserter(vertices), triangles * 1.98, random);
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.53, random);
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes));
}
template <class Random> template <class Random>
Key generateKey(std::size_t triangles, Random& random) Key generateKey(std::size_t triangles, Random& random)
{ {
@ -94,16 +107,10 @@ namespace
const TilePosition tilePosition = generateTilePosition(10000, random); const TilePosition tilePosition = generateTilePosition(10000, random);
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random); const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random); const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
std::vector<float> vertices; Mesh mesh = generateMesh(triangles, random);
generateVertices(std::back_inserter(vertices), triangles * 1.98, random);
std::vector<int> indices;
generateIndices(std::back_inserter(indices), static_cast<int>(vertices.size() / 3) - 1, vertices.size() * 1.53, random);
std::vector<AreaType> areaTypes;
generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random);
std::vector<RecastMesh::Water> water; std::vector<RecastMesh::Water> water;
generateWater(std::back_inserter(water), 2, random); generateWater(std::back_inserter(water), 2, random);
RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices), RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water));
std::move(areaTypes), std::move(water));
return Key {agentHalfExtents, tilePosition, std::move(recastMesh)}; return Key {agentHalfExtents, tilePosition, std::move(recastMesh)};
} }

View file

@ -128,17 +128,23 @@ namespace
return result; return result;
} }
Mesh makeMesh()
{
std::vector<int> indices {{0, 1, 2}};
std::vector<float> vertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}};
std::vector<AreaType> areaTypes {1, AreaType_ground};
return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes));
}
struct DetourNavigatorNavMeshTilesCacheTest : Test struct DetourNavigatorNavMeshTilesCacheTest : Test
{ {
const osg::Vec3f mAgentHalfExtents {1, 2, 3}; const osg::Vec3f mAgentHalfExtents {1, 2, 3};
const TilePosition mTilePosition {0, 0}; const TilePosition mTilePosition {0, 0};
const std::size_t mGeneration = 0; const std::size_t mGeneration = 0;
const std::size_t mRevision = 0; const std::size_t mRevision = 0;
const std::vector<int> mIndices {{0, 1, 2}}; const Mesh mMesh {makeMesh()};
const std::vector<float> mVertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}};
const std::vector<AreaType> mAreaTypes {1, AreaType_ground};
const std::vector<RecastMesh::Water> mWater {}; const std::vector<RecastMesh::Water> mWater {};
const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater}; const RecastMesh mRecastMesh {mGeneration, mRevision, mMesh, mWater};
std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData {makePeparedNavMeshData(3)}; std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData {makePeparedNavMeshData(3)};
const std::size_t mRecastMeshSize = sizeof(mRecastMesh) + getSize(mRecastMesh); const std::size_t mRecastMeshSize = sizeof(mRecastMesh) + getSize(mRecastMesh);
@ -226,7 +232,7 @@ namespace
const std::size_t maxSize = 1; const std::size_t maxSize = 1;
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mMesh, water};
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh)); EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh));
@ -238,7 +244,7 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mMesh, water};
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
const auto copy = clone(*anotherPreparedNavMeshData); const auto copy = clone(*anotherPreparedNavMeshData);
@ -256,7 +262,7 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mMesh, water};
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
@ -272,13 +278,11 @@ namespace
const auto copy = clone(*mPreparedNavMeshData); const auto copy = clone(*mPreparedNavMeshData);
const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mMesh, leastRecentlySetWater};
mAreaTypes, leastRecentlySetWater};
auto leastRecentlySetData = makePeparedNavMeshData(3); auto leastRecentlySetData = makePeparedNavMeshData(3);
const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mMesh, mostRecentlySetWater};
mAreaTypes, mostRecentlySetWater};
auto mostRecentlySetData = makePeparedNavMeshData(3); auto mostRecentlySetData = makePeparedNavMeshData(3);
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh,
@ -300,14 +304,12 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mMesh, leastRecentlyUsedWater};
mAreaTypes, leastRecentlyUsedWater};
auto leastRecentlyUsedData = makePeparedNavMeshData(3); auto leastRecentlyUsedData = makePeparedNavMeshData(3);
const auto leastRecentlyUsedCopy = clone(*leastRecentlyUsedData); const auto leastRecentlyUsedCopy = clone(*leastRecentlyUsedData);
const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mMesh, mostRecentlyUsedWater};
mAreaTypes, mostRecentlyUsedWater};
auto mostRecentlyUsedData = makePeparedNavMeshData(3); auto mostRecentlyUsedData = makePeparedNavMeshData(3);
const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData); const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData);
@ -341,7 +343,7 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mMesh, water};
auto tooLargeData = makePeparedNavMeshData(10); auto tooLargeData = makePeparedNavMeshData(10);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
@ -355,12 +357,11 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mMesh, anotherWater};
auto anotherData = makePeparedNavMeshData(3); auto anotherData = makePeparedNavMeshData(3);
const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mMesh, tooLargeWater};
mAreaTypes, tooLargeWater};
auto tooLargeData = makePeparedNavMeshData(10); auto tooLargeData = makePeparedNavMeshData(10);
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
@ -380,8 +381,7 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, const RecastMesh anotherRecastMesh {mGeneration, mRevision, mMesh, water};
mAreaTypes, water};
auto anotherData = makePeparedNavMeshData(3); auto anotherData = makePeparedNavMeshData(3);
const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
@ -400,7 +400,7 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mMesh, water};
auto anotherData = makePeparedNavMeshData(3); auto anotherData = makePeparedNavMeshData(3);
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));

View file

@ -11,6 +11,8 @@
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h> #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <BulletCollision/CollisionShapes/btCompoundShape.h> #include <BulletCollision/CollisionShapes/btCompoundShape.h>
#include <DetourCommon.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
@ -50,9 +52,9 @@ namespace
{ {
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>()); EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>());
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>()); EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>());
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>()); EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>());
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape)
@ -64,13 +66,13 @@ namespace
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
1, 0, -1,
-1, 0, 1,
-1, 0, -1, -1, 0, -1,
})); -1, 0, 1,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); 1, 0, -1,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); })) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_bhv_triangle_mesh_shape) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_bhv_triangle_mesh_shape)
@ -85,13 +87,13 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
2, 3, 0,
0, 3, 4,
0, 3, 0, 0, 3, 0,
})); 0, 3, 4,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); 2, 3, 0,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); })) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_heightfield_terrian_shape) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_heightfield_terrian_shape)
@ -101,14 +103,14 @@ namespace
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-0.5, 0, -0.5, -0.5, 0, -0.5,
-0.5, 0, 0.5, -0.5, 0, 0.5,
0.5, 0, -0.5, 0.5, 0, -0.5,
0.5, 0, 0.5, 0.5, 0, 0.5,
})); }));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 2, 1, 3})); EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({0, 1, 2, 2, 1, 3}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground})); EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles)
@ -117,31 +119,31 @@ namespace
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
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, -2, -1,
})) << recastMesh->getVertices(); -1, -2, 1,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({ -1, 2, -1,
0, 2, 3, -1, 2, 1,
3, 1, 0, 1, -2, -1,
0, 4, 6, 1, -2, 1,
6, 2, 0, 1, 2, -1,
0, 1, 5, 1, 2, 1,
5, 4, 0, })) << recastMesh->getMesh().getVertices();
7, 5, 1, EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({
1, 3, 7, 0, 1, 3,
7, 3, 2, 0, 2, 6,
2, 6, 7, 0, 4, 5,
7, 6, 4, 1, 5, 7,
4, 5, 7, 2, 3, 7,
})) << recastMesh->getIndices(); 3, 2, 0,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(12, AreaType_ground)); 4, 6, 7,
5, 1, 0,
6, 4, 0,
7, 3, 1,
7, 5, 4,
7, 6, 2,
})) << recastMesh->getMesh().getIndices();
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>(12, AreaType_ground));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_compound_shape) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_compound_shape)
@ -164,7 +166,7 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-1, -2, -1, -1, -2, -1,
-1, -2, 1, -1, -2, 1,
-1, 0, -1, -1, 0, -1,
@ -177,24 +179,24 @@ namespace
1, 0, 1, 1, 0, 1,
1, 2, -1, 1, 2, -1,
1, 2, 1, 1, 2, 1,
})) << recastMesh->getVertices(); })) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({ EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({
8, 3, 2,
11, 10, 4,
4, 5, 11,
11, 7, 6,
6, 10, 11,
11, 5, 1,
1, 7, 11,
0, 1, 5, 0, 1, 5,
5, 4, 0,
0, 4, 10, 0, 4, 10,
10, 6, 0,
0, 6, 7, 0, 6, 7,
1, 7, 11,
4, 5, 11,
5, 4, 0,
6, 10, 11,
7, 1, 0, 7, 1, 0,
8, 3, 2,
8, 3, 9, 8, 3, 9,
})) << recastMesh->getIndices(); 10, 6, 0,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(14, AreaType_ground)); 11, 5, 1,
11, 7, 6,
11, 10, 4,
})) << recastMesh->getMesh().getIndices();
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>(14, AreaType_ground));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape)
@ -211,13 +213,13 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
2, 3, 0,
0, 3, 4,
0, 3, 0, 0, 3, 0,
})); 0, 3, 4,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); 2, 3, 0,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); })) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape_with_transformed_bhv_triangle_shape) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape_with_transformed_bhv_triangle_shape)
@ -235,13 +237,13 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
3, 12, 2,
1, 12, 10,
1, 12, 2, 1, 12, 2,
})); 1, 12, 10,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); 3, 12, 2,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); })) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, without_bounds_add_bhv_triangle_shape_should_not_filter_by_bounds) TEST_F(DetourNavigatorRecastMeshBuilderTest, without_bounds_add_bhv_triangle_shape_should_not_filter_by_bounds)
@ -257,16 +259,16 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
1, 0, -1,
-1, 0, 1,
-1, 0, -1,
-2, 0, -3,
-3, 0, -2,
-3, 0, -3, -3, 0, -3,
})); -3, 0, -2,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 3, 4, 5})); -2, 0, -3,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(2, AreaType_ground)); -1, 0, -1,
-1, 0, 1,
1, 0, -1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0, 5, 4, 3}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>(2, AreaType_ground));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_bhv_triangle_shape_should_filter_by_bounds) TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_bhv_triangle_shape_should_filter_by_bounds)
@ -285,13 +287,13 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-0.2f, 0, -0.3f,
-0.3f, 0, -0.2f,
-0.3f, 0, -0.3f, -0.3f, 0, -0.3f,
})); -0.3f, 0, -0.2f,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); -0.2f, 0, -0.3f,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); })) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_x_bhv_triangle_shape_should_filter_by_bounds) TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_x_bhv_triangle_shape_should_filter_by_bounds)
@ -310,13 +312,13 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({ EXPECT_THAT(recastMesh->getMesh().getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
0, -0.70710659027099609375, -3.535533905029296875, 0, -0.707106769084930419921875, -3.535533905029296875,
0, 0.707107067108154296875, -3.535533905029296875, 0, 4.44089209850062616169452667236328125e-16, -4.24264049530029296875,
0, 2.384185791015625e-07, -4.24264049530029296875, 0, 0.707106769084930419921875, -3.535533905029296875,
}))); }))) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({0, 2, 1}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_y_bhv_triangle_shape_should_filter_by_bounds) TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_y_bhv_triangle_shape_should_filter_by_bounds)
@ -335,13 +337,13 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({ EXPECT_THAT(recastMesh->getMesh().getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
-3.535533905029296875, -0.70710659027099609375, 0, -4.24264049530029296875, 4.44089209850062616169452667236328125e-16, 0,
-3.535533905029296875, 0.707107067108154296875, 0, -3.535533905029296875, -0.707106769084930419921875, 0,
-4.24264049530029296875, 2.384185791015625e-07, 0, -3.535533905029296875, 0.707106769084930419921875, 0,
}))); }))) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({1, 2, 0}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_z_bhv_triangle_shape_should_filter_by_bounds) TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_z_bhv_triangle_shape_should_filter_by_bounds)
@ -360,13 +362,13 @@ namespace
AreaType_ground AreaType_ground
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({ EXPECT_THAT(recastMesh->getMesh().getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
1.41421353816986083984375, 0, 1.1920928955078125e-07, -1.41421353816986083984375, 0, -1.1102230246251565404236316680908203125e-16,
-1.41421353816986083984375, 0, -1.1920928955078125e-07, 1.1102230246251565404236316680908203125e-16, 0, -1.41421353816986083984375,
1.1920928955078125e-07, 0, -1.41421353816986083984375, 1.41421353816986083984375, 0, 1.1102230246251565404236316680908203125e-16,
}))); }))) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 0, 1}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground})); EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, flags_values_should_be_corresponding_to_added_objects) TEST_F(DetourNavigatorRecastMeshBuilderTest, flags_values_should_be_corresponding_to_added_objects)
@ -389,16 +391,16 @@ namespace
AreaType_null AreaType_null
); );
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
1, 0, -1,
-1, 0, 1,
-1, 0, -1,
-2, 0, -3,
-3, 0, -2,
-3, 0, -3, -3, 0, -3,
})); -3, 0, -2,
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 3, 4, 5})); -2, 0, -3,
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_null})); -1, 0, -1,
-1, 0, 1,
1, 0, -1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0, 5, 4, 3}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_null, AreaType_ground}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_water_then_get_water_should_return_it) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_water_then_get_water_should_return_it)
@ -421,13 +423,13 @@ namespace
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-1, 0, -1, -1, 0, -1,
-1, 0, 1, -1, 0, 1,
1, 0, -1, 1, 0, -1,
1, 0, 1, 1, 0, 1,
})) << recastMesh->getVertices(); })) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({2, 1, 0, 2, 1, 3})); EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0, 2, 1, 3}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground})); EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));
} }
} }

View file

@ -18,7 +18,7 @@ namespace DetourNavigator
file.exceptions(std::ios::failbit | std::ios::badbit); file.exceptions(std::ios::failbit | std::ios::badbit);
file.precision(std::numeric_limits<float>::max_exponent10); file.precision(std::numeric_limits<float>::max_exponent10);
std::size_t count = 0; std::size_t count = 0;
for (auto v : recastMesh.getVertices()) for (float v : recastMesh.getMesh().getVertices())
{ {
if (count % 3 == 0) if (count % 3 == 0)
{ {
@ -31,7 +31,7 @@ namespace DetourNavigator
} }
file << '\n'; file << '\n';
count = 0; count = 0;
for (auto v : recastMesh.getIndices()) for (int v : recastMesh.getMesh().getIndices())
{ {
if (count % 3 == 0) if (count % 3 == 0)
{ {

View file

@ -157,26 +157,26 @@ namespace
throw NavigatorException("Failed to create heightfield for navmesh"); throw NavigatorException("Failed to create heightfield for navmesh");
} }
bool rasterizeSolidObjectsTriangles(rcContext& context, const RecastMesh& recastMesh, const rcConfig& config, bool rasterizeTriangles(rcContext& context, const Mesh& mesh, const rcConfig& config,
rcHeightfield& solid) rcHeightfield& solid)
{ {
std::vector<unsigned char> areas(recastMesh.getAreaTypes().begin(), recastMesh.getAreaTypes().end()); std::vector<unsigned char> areas(mesh.getAreaTypes().begin(), mesh.getAreaTypes().end());
rcClearUnwalkableTriangles( rcClearUnwalkableTriangles(
&context, &context,
config.walkableSlopeAngle, config.walkableSlopeAngle,
recastMesh.getVertices().data(), mesh.getVertices().data(),
static_cast<int>(recastMesh.getVerticesCount()), static_cast<int>(mesh.getVerticesCount()),
recastMesh.getIndices().data(), mesh.getIndices().data(),
static_cast<int>(areas.size()), static_cast<int>(areas.size()),
areas.data() areas.data()
); );
return rcRasterizeTriangles( return rcRasterizeTriangles(
&context, &context,
recastMesh.getVertices().data(), mesh.getVertices().data(),
static_cast<int>(recastMesh.getVerticesCount()), static_cast<int>(mesh.getVerticesCount()),
recastMesh.getIndices().data(), mesh.getIndices().data(),
areas.data(), areas.data(),
static_cast<int>(areas.size()), static_cast<int>(areas.size()),
solid, solid,
@ -242,7 +242,7 @@ namespace
bool rasterizeTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, bool rasterizeTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
const rcConfig& config, const Settings& settings, rcHeightfield& solid) const rcConfig& config, const Settings& settings, rcHeightfield& solid)
{ {
if (!rasterizeSolidObjectsTriangles(context, recastMesh, config, solid)) if (!rasterizeTriangles(context, recastMesh.getMesh(), config, solid))
return false; return false;
rasterizeWaterTriangles(context, agentHalfExtents, recastMesh, settings, config, solid); rasterizeWaterTriangles(context, agentHalfExtents, recastMesh, settings, config, solid);

View file

@ -42,7 +42,7 @@ namespace DetourNavigator
while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize) while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize)
removeLeastRecentlyUsed(); removeLeastRecentlyUsed();
RecastMeshData key {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()}; RecastMeshData key {recastMesh.getMesh(), recastMesh.getWater()};
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(key), itemSize); const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(key), itemSize);
const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, std::cref(iterator->mRecastMeshData)), iterator); const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, std::cref(iterator->mRecastMeshData)), iterator);

View file

@ -22,28 +22,26 @@ namespace DetourNavigator
{ {
struct RecastMeshData struct RecastMeshData
{ {
std::vector<int> mIndices; Mesh mMesh;
std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes;
std::vector<RecastMesh::Water> mWater; std::vector<RecastMesh::Water> mWater;
}; };
inline bool operator <(const RecastMeshData& lhs, const RecastMeshData& rhs) inline bool operator <(const RecastMeshData& lhs, const RecastMeshData& rhs)
{ {
return std::tie(lhs.mIndices, lhs.mVertices, lhs.mAreaTypes, lhs.mWater) return std::tie(lhs.mMesh, lhs.mWater)
< std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater); < std::tie(rhs.mMesh, rhs.mWater);
} }
inline bool operator <(const RecastMeshData& lhs, const RecastMesh& rhs) inline bool operator <(const RecastMeshData& lhs, const RecastMesh& rhs)
{ {
return std::tie(lhs.mIndices, lhs.mVertices, lhs.mAreaTypes, lhs.mWater) return std::tie(lhs.mMesh, lhs.mWater)
< std::tie(rhs.getIndices(), rhs.getVertices(), rhs.getAreaTypes(), rhs.getWater()); < std::tie(rhs.getMesh(), rhs.getWater());
} }
inline bool operator <(const RecastMesh& lhs, const RecastMeshData& rhs) inline bool operator <(const RecastMesh& lhs, const RecastMeshData& rhs)
{ {
return std::tie(lhs.getIndices(), lhs.getVertices(), lhs.getAreaTypes(), lhs.getWater()) return std::tie(lhs.getMesh(), lhs.getWater())
< std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater); < std::tie(rhs.mMesh, rhs.mWater);
} }
class NavMeshTilesCache class NavMeshTilesCache

View file

@ -5,23 +5,28 @@
namespace DetourNavigator namespace DetourNavigator
{ {
RecastMesh::RecastMesh(std::size_t generation, std::size_t revision, std::vector<int> indices, std::vector<float> vertices, Mesh::Mesh(std::vector<int>&& indices, std::vector<float>&& vertices, std::vector<AreaType>&& areaTypes)
std::vector<AreaType> areaTypes, std::vector<Water> water) {
if (indices.size() / 3 != areaTypes.size())
throw InvalidArgument("Number of flags doesn't match number of triangles: triangles="
+ std::to_string(indices.size() / 3) + ", areaTypes=" + std::to_string(areaTypes.size()));
indices.shrink_to_fit();
vertices.shrink_to_fit();
areaTypes.shrink_to_fit();
mIndices = std::move(indices);
mVertices = std::move(vertices);
mAreaTypes = std::move(areaTypes);
}
RecastMesh::RecastMesh(std::size_t generation, std::size_t revision, Mesh mesh, std::vector<Water> water)
: mGeneration(generation) : mGeneration(generation)
, mRevision(revision) , mRevision(revision)
, mIndices(std::move(indices)) , mMesh(std::move(mesh))
, mVertices(std::move(vertices))
, mAreaTypes(std::move(areaTypes))
, mWater(std::move(water)) , mWater(std::move(water))
{ {
if (getTrianglesCount() != mAreaTypes.size()) if (mMesh.getVerticesCount() > 0)
throw InvalidArgument("Number of flags doesn't match number of triangles: triangles=" rcCalcBounds(mMesh.getVertices().data(), static_cast<int>(mMesh.getVerticesCount()),
+ std::to_string(getTrianglesCount()) + ", areaTypes=" + std::to_string(mAreaTypes.size())); mBounds.mMin.ptr(), mBounds.mMax.ptr());
if (getVerticesCount())
rcCalcBounds(mVertices.data(), static_cast<int>(getVerticesCount()), mBounds.mMin.ptr(), mBounds.mMax.ptr());
mIndices.shrink_to_fit();
mVertices.shrink_to_fit();
mAreaTypes.shrink_to_fit();
mWater.shrink_to_fit(); mWater.shrink_to_fit();
} }
} }

View file

@ -15,6 +15,36 @@
namespace DetourNavigator namespace DetourNavigator
{ {
class Mesh
{
public:
Mesh(std::vector<int>&& indices, std::vector<float>&& vertices, std::vector<AreaType>&& areaTypes);
const std::vector<int>& getIndices() const noexcept { return mIndices; }
const std::vector<float>& getVertices() const noexcept { return mVertices; }
const std::vector<AreaType>& getAreaTypes() const noexcept { return mAreaTypes; }
std::size_t getVerticesCount() const noexcept { return mVertices.size() / 3; }
std::size_t getTrianglesCount() const noexcept { return mAreaTypes.size(); }
private:
std::vector<int> mIndices;
std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes;
friend inline bool operator<(const Mesh& lhs, const Mesh& rhs) noexcept
{
return std::tie(lhs.mIndices, lhs.mVertices, lhs.mAreaTypes)
< std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes);
}
friend inline std::size_t getSize(const Mesh& value) noexcept
{
return value.mIndices.size() * sizeof(int)
+ value.mVertices.size() * sizeof(float)
+ value.mAreaTypes.size() * sizeof(AreaType);
}
};
class RecastMesh class RecastMesh
{ {
public: public:
@ -24,8 +54,7 @@ namespace DetourNavigator
btTransform mTransform; btTransform mTransform;
}; };
RecastMesh(std::size_t generation, std::size_t revision, std::vector<int> indices, std::vector<float> vertices, RecastMesh(std::size_t generation, std::size_t revision, Mesh mesh, std::vector<Water> water);
std::vector<AreaType> areaTypes, std::vector<Water> water);
std::size_t getGeneration() const std::size_t getGeneration() const
{ {
@ -37,36 +66,13 @@ namespace DetourNavigator
return mRevision; return mRevision;
} }
const std::vector<int>& getIndices() const const Mesh& getMesh() const noexcept { return mMesh; }
{
return mIndices;
}
const std::vector<float>& getVertices() const
{
return mVertices;
}
const std::vector<AreaType>& getAreaTypes() const
{
return mAreaTypes;
}
const std::vector<Water>& getWater() const const std::vector<Water>& getWater() const
{ {
return mWater; return mWater;
} }
std::size_t getVerticesCount() const
{
return mVertices.size() / 3;
}
std::size_t getTrianglesCount() const
{
return mIndices.size() / 3;
}
const Bounds& getBounds() const const Bounds& getBounds() const
{ {
return mBounds; return mBounds;
@ -75,32 +81,25 @@ namespace DetourNavigator
private: private:
std::size_t mGeneration; std::size_t mGeneration;
std::size_t mRevision; std::size_t mRevision;
std::vector<int> mIndices; Mesh mMesh;
std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes;
std::vector<Water> mWater; std::vector<Water> mWater;
Bounds mBounds; Bounds mBounds;
friend inline std::size_t getSize(const RecastMesh& recastMesh) noexcept friend inline bool operator <(const RecastMesh& lhs, const RecastMesh& rhs) noexcept
{ {
const std::size_t indicesSize = recastMesh.mIndices.size() * sizeof(int); return std::tie(lhs.mMesh, lhs.mWater) < std::tie(rhs.mMesh, rhs.mWater);
const std::size_t verticesSize = recastMesh.mVertices.size() * sizeof(float); }
const std::size_t areaTypesSize = recastMesh.mAreaTypes.size() * sizeof(AreaType);
const std::size_t waterSize = recastMesh.mWater.size() * sizeof(RecastMesh::Water); friend inline std::size_t getSize(const RecastMesh& value) noexcept
return indicesSize + verticesSize + areaTypesSize + waterSize; {
return getSize(value.mMesh) + value.mWater.size() * sizeof(RecastMesh::Water);
} }
}; };
inline bool operator<(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs) inline bool operator<(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs) noexcept
{ {
return std::tie(lhs.mCellSize, lhs.mTransform) < std::tie(rhs.mCellSize, rhs.mTransform); return std::tie(lhs.mCellSize, lhs.mTransform) < std::tie(rhs.mCellSize, rhs.mTransform);
} }
inline bool operator <(const RecastMesh& lhs, const RecastMesh& rhs)
{
return std::tie(lhs.getIndices(), lhs.getVertices(), lhs.getAreaTypes(), lhs.getWater())
< std::tie(rhs.getIndices(), rhs.getVertices(), rhs.getAreaTypes(), rhs.getWater());
}
} }
#endif #endif

View file

@ -17,8 +17,8 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <tuple>
#include <array> #include <array>
#include <vector>
namespace DetourNavigator namespace DetourNavigator
{ {
@ -26,40 +26,59 @@ namespace DetourNavigator
namespace namespace
{ {
void optimizeRecastMesh(std::vector<int>& indices, std::vector<float>& vertices) RecastMeshTriangle makeRecastMeshTriangle(const btVector3* vertices, const AreaType areaType, const Settings& settings)
{ {
std::vector<std::tuple<float, float, float>> uniqueVertices; RecastMeshTriangle result;
uniqueVertices.reserve(vertices.size() / 3); result.mAreaType = areaType;
for (std::size_t i = 0; i < 3; ++i)
result.mVertices[i] = toNavMeshCoordinates(settings, Misc::Convert::makeOsgVec3f(vertices[i]));
return result;
}
}
for (std::size_t i = 0, n = vertices.size() / 3; i < n; ++i) Mesh makeMesh(std::vector<RecastMeshTriangle>&& triangles)
uniqueVertices.emplace_back(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]); {
std::vector<osg::Vec3f> uniqueVertices;
uniqueVertices.reserve(3 * triangles.size());
for (const RecastMeshTriangle& v : triangles)
for (const osg::Vec3f& v : v.mVertices)
uniqueVertices.push_back(v);
std::sort(uniqueVertices.begin(), uniqueVertices.end()); std::sort(uniqueVertices.begin(), uniqueVertices.end());
const auto end = std::unique(uniqueVertices.begin(), uniqueVertices.end()); uniqueVertices.erase(std::unique(uniqueVertices.begin(), uniqueVertices.end()), uniqueVertices.end());
uniqueVertices.erase(end, uniqueVertices.end());
if (uniqueVertices.size() == vertices.size() / 3) std::vector<int> indices;
return; indices.reserve(3 * triangles.size());
std::vector<AreaType> areaTypes;
areaTypes.reserve(triangles.size());
for (std::size_t i = 0, n = indices.size(); i < n; ++i) for (const RecastMeshTriangle& v : triangles)
{ {
const auto index = indices[i]; areaTypes.push_back(v.mAreaType);
const auto vertex = std::make_tuple(vertices[index * 3], vertices[index * 3 + 1], vertices[index * 3 + 2]);
const auto it = std::lower_bound(uniqueVertices.begin(), uniqueVertices.end(), vertex); for (const osg::Vec3f& v : v.mVertices)
{
const auto it = std::lower_bound(uniqueVertices.begin(), uniqueVertices.end(), v);
assert(it != uniqueVertices.end()); assert(it != uniqueVertices.end());
assert(*it == vertex); assert(*it == v);
indices[i] = std::distance(uniqueVertices.begin(), it); indices.push_back(static_cast<int>(it - uniqueVertices.begin()));
}
} }
vertices.resize(uniqueVertices.size() * 3); triangles.clear();
for (std::size_t i = 0, n = uniqueVertices.size(); i < n; ++i) std::vector<float> vertices;
vertices.reserve(3 * uniqueVertices.size());
for (const osg::Vec3f& v : uniqueVertices)
{ {
vertices[i * 3] = std::get<0>(uniqueVertices[i]); vertices.push_back(v.x());
vertices[i * 3 + 1] = std::get<1>(uniqueVertices[i]); vertices.push_back(v.y());
vertices[i * 3 + 2] = std::get<2>(uniqueVertices[i]); vertices.push_back(v.z());
}
} }
return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes));
} }
RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds) RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds)
@ -96,37 +115,26 @@ namespace DetourNavigator
void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform, void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
{ {
return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* vertices, int, int)
{ {
for (std::size_t i = 3; i > 0; --i) RecastMeshTriangle triangle = makeRecastMeshTriangle(vertices, areaType, mSettings);
addTriangleVertex(triangle[i - 1]); std::reverse(triangle.mVertices.begin(), triangle.mVertices.end());
mAreaTypes.push_back(areaType); mTriangles.emplace_back(triangle);
})); }));
} }
void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
{ {
return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* vertices, int, int)
{ {
for (std::size_t i = 0; i < 3; ++i) mTriangles.emplace_back(makeRecastMeshTriangle(vertices, areaType, mSettings));
addTriangleVertex(triangle[i]);
mAreaTypes.push_back(areaType);
})); }));
} }
void RecastMeshBuilder::addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType) void RecastMeshBuilder::addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType)
{ {
const auto indexOffset = static_cast<int>(mVertices.size() / 3); constexpr std::array<int, 36> indices {{
for (int vertex = 0, count = shape.getNumVertices(); vertex < count; ++vertex)
{
btVector3 position;
shape.getVertex(vertex, position);
addVertex(transform(position));
}
const std::array<int, 36> indices {{
0, 2, 3, 0, 2, 3,
3, 1, 0, 3, 1, 0,
0, 4, 6, 0, 4, 6,
@ -141,10 +149,17 @@ namespace DetourNavigator
4, 5, 7, 4, 5, 7,
}}; }};
std::transform(indices.begin(), indices.end(), std::back_inserter(mIndices), for (std::size_t i = 0; i < indices.size(); i += 3)
[&] (int index) { return index + indexOffset; }); {
std::array<btVector3, 3> vertices;
std::generate_n(std::back_inserter(mAreaTypes), 12, [=] { return areaType; }); for (std::size_t j = 0; j < 3; ++j)
{
btVector3 position;
shape.getVertex(indices[i + j], position);
vertices[j] = transform(position);
}
mTriangles.emplace_back(makeRecastMeshTriangle(vertices.data(), areaType, mSettings));
}
} }
void RecastMeshBuilder::addWater(const int cellSize, const btTransform& transform) void RecastMeshBuilder::addWater(const int cellSize, const btTransform& transform)
@ -154,9 +169,10 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision) && std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision) &&
{ {
optimizeRecastMesh(mIndices, mVertices); std::sort(mTriangles.begin(), mTriangles.end());
std::sort(mWater.begin(), mWater.end()); std::sort(mWater.begin(), mWater.end());
return std::make_shared<RecastMesh>(generation, revision, std::move(mIndices), std::move(mVertices), std::move(mAreaTypes), std::move(mWater)); Mesh mesh = makeMesh(std::move(mTriangles));
return std::make_shared<RecastMesh>(generation, revision, std::move(mesh), std::move(mWater));
} }
void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform, void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform,
@ -218,18 +234,4 @@ namespace DetourNavigator
shape.processAllTriangles(&wrapper, aabbMin, aabbMax); shape.processAllTriangles(&wrapper, aabbMin, aabbMax);
} }
void RecastMeshBuilder::addTriangleVertex(const btVector3& worldPosition)
{
mIndices.push_back(static_cast<int>(mVertices.size() / 3));
addVertex(worldPosition);
}
void RecastMeshBuilder::addVertex(const btVector3& worldPosition)
{
const auto navMeshPosition = toNavMeshCoordinates(mSettings, Misc::Convert::makeOsgVec3f(worldPosition));
mVertices.push_back(navMeshPosition.x());
mVertices.push_back(navMeshPosition.y());
mVertices.push_back(navMeshPosition.z());
}
} }

View file

@ -4,8 +4,16 @@
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "tilebounds.hpp" #include "tilebounds.hpp"
#include <osg/Vec3f>
#include <LinearMath/btTransform.h> #include <LinearMath/btTransform.h>
#include <array>
#include <functional>
#include <memory>
#include <tuple>
#include <vector>
class btBoxShape; class btBoxShape;
class btCollisionShape; class btCollisionShape;
class btCompoundShape; class btCompoundShape;
@ -17,6 +25,17 @@ namespace DetourNavigator
{ {
struct Settings; struct Settings;
struct RecastMeshTriangle
{
AreaType mAreaType;
std::array<osg::Vec3f, 3> mVertices;
friend inline bool operator<(const RecastMeshTriangle& lhs, const RecastMeshTriangle& rhs)
{
return std::tie(lhs.mAreaType, lhs.mVertices) < std::tie(rhs.mAreaType, rhs.mVertices);
}
};
class RecastMeshBuilder class RecastMeshBuilder
{ {
public: public:
@ -39,19 +58,15 @@ namespace DetourNavigator
private: private:
std::reference_wrapper<const Settings> mSettings; std::reference_wrapper<const Settings> mSettings;
TileBounds mBounds; TileBounds mBounds;
std::vector<int> mIndices; std::vector<RecastMeshTriangle> mTriangles;
std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes;
std::vector<RecastMesh::Water> mWater; std::vector<RecastMesh::Water> mWater;
void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback); void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback);
void addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, btTriangleCallback&& callback); void addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, btTriangleCallback&& callback);
void addTriangleVertex(const btVector3& worldPosition);
void addVertex(const btVector3& worldPosition);
}; };
Mesh makeMesh(std::vector<RecastMeshTriangle>&& triangles);
} }
#endif #endif

View file

@ -16,9 +16,8 @@ namespace DetourNavigator
const auto object = mObjects.lower_bound(id); const auto object = mObjects.lower_bound(id);
if (object != mObjects.end() && object->first == id) if (object != mObjects.end() && object->first == id)
return false; return false;
const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(), mObjects.emplace_hint(object, id,
OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1)); OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1));
mObjects.emplace_hint(object, id, iterator);
++mRevision; ++mRevision;
return true; return true;
} }
@ -30,7 +29,7 @@ namespace DetourNavigator
return false; return false;
const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value() const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value()
? mLastNavMeshReportedChange->mRevision : mRevision; ? mLastNavMeshReportedChange->mRevision : mRevision;
if (!object->second->update(transform, areaType, lastChangeRevision, mTileBounds)) if (!object->second.update(transform, areaType, lastChangeRevision, mTileBounds))
return false; return false;
++mRevision; ++mRevision;
return true; return true;
@ -41,8 +40,7 @@ namespace DetourNavigator
const auto object = mObjects.find(id); const auto object = mObjects.find(id);
if (object == mObjects.end()) if (object == mObjects.end())
return std::nullopt; return std::nullopt;
const RemovedRecastMeshObject result {object->second->getImpl().getShape(), object->second->getImpl().getTransform()}; const RemovedRecastMeshObject result {object->second.getImpl().getShape(), object->second.getImpl().getTransform()};
mObjectsOrder.erase(object->second);
mObjects.erase(object); mObjects.erase(object);
++mRevision; ++mRevision;
return result; return result;
@ -51,12 +49,8 @@ namespace DetourNavigator
bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,
const btTransform& transform) const btTransform& transform)
{ {
const auto iterator = mWaterOrder.emplace(mWaterOrder.end(), Water {cellSize, transform}); if (!mWater.emplace(cellPosition, Water {cellSize, transform}).second)
if (!mWater.emplace(cellPosition, iterator).second)
{
mWaterOrder.erase(iterator);
return false; return false;
}
++mRevision; ++mRevision;
return true; return true;
} }
@ -67,8 +61,7 @@ namespace DetourNavigator
if (water == mWater.end()) if (water == mWater.end())
return std::nullopt; return std::nullopt;
++mRevision; ++mRevision;
const auto result = *water->second; const Water result = water->second;
mWaterOrder.erase(water->second);
mWater.erase(water); mWater.erase(water);
return result; return result;
} }
@ -76,9 +69,9 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> RecastMeshManager::getMesh() std::shared_ptr<RecastMesh> RecastMeshManager::getMesh()
{ {
RecastMeshBuilder builder(mSettings, mTileBounds); RecastMeshBuilder builder(mSettings, mTileBounds);
for (const auto& v : mWaterOrder) for (const auto& [k, v] : mWater)
builder.addWater(v.mCellSize, v.mTransform); builder.addWater(v.mCellSize, v.mTransform);
for (const auto& object : mObjectsOrder) for (const auto& [k, object] : mObjects)
{ {
const RecastMeshObject& v = object.getImpl(); const RecastMeshObject& v = object.getImpl();
builder.addObject(v.getShape(), v.getTransform(), v.getAreaType()); builder.addObject(v.getShape(), v.getTransform(), v.getAreaType());

View file

@ -9,7 +9,6 @@
#include <osg/Vec2i> #include <osg/Vec2i>
#include <list>
#include <map> #include <map>
#include <optional> #include <optional>
#include <memory> #include <memory>
@ -68,10 +67,8 @@ namespace DetourNavigator
std::size_t mRevision = 0; std::size_t mRevision = 0;
std::size_t mGeneration; std::size_t mGeneration;
TileBounds mTileBounds; TileBounds mTileBounds;
std::list<OscillatingRecastMeshObject> mObjectsOrder; std::map<ObjectId, OscillatingRecastMeshObject> mObjects;
std::map<ObjectId, std::list<OscillatingRecastMeshObject>::iterator> mObjects; std::map<osg::Vec2i, Water> mWater;
std::list<Water> mWaterOrder;
std::map<osg::Vec2i, std::list<Water>::iterator> mWater;
std::optional<Report> mLastNavMeshReportedChange; std::optional<Report> mLastNavMeshReportedChange;
std::optional<Report> mLastNavMeshReport; std::optional<Report> mLastNavMeshReport;
}; };

View file

@ -39,10 +39,11 @@ namespace SceneUtil
{ {
const osg::ref_ptr<osg::Group> group(new osg::Group); const osg::ref_ptr<osg::Group> group(new osg::Group);
DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1.0f / settings.mRecastScaleFactor); DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1.0f / settings.mRecastScaleFactor);
const auto normals = calculateNormals(recastMesh.getVertices(), recastMesh.getIndices()); const DetourNavigator::Mesh& mesh = recastMesh.getMesh();
const auto normals = calculateNormals(mesh.getVertices(), mesh.getIndices());
const auto texScale = 1.0f / (settings.mCellSize * 10.0f); const auto texScale = 1.0f / (settings.mCellSize * 10.0f);
duDebugDrawTriMesh(&debugDraw, recastMesh.getVertices().data(), recastMesh.getVerticesCount(), duDebugDrawTriMesh(&debugDraw, mesh.getVertices().data(), mesh.getVerticesCount(),
recastMesh.getIndices().data(), normals.data(), recastMesh.getTrianglesCount(), nullptr, texScale); mesh.getIndices().data(), normals.data(), mesh.getTrianglesCount(), nullptr, texScale);
return group; return group;
} }
} }