From beeb882ea88938c3ae98784b841e9935636147d7 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 14 Jul 2021 00:03:10 +0200 Subject: [PATCH] Do not use off mesh connections as a part of navmesh cache key To reduce cache size and make it more flexible. Adding off mesh connections to the navmesh is the last step of navmesh generation and it's very fast comparing to other steps (microseconds vs milliseconds). Having less cache size makes get and set operations almost 2x times faster that also have an order of microseconds. So in total there is no performance impact. --- .../detournavigator/navmeshtilescache.cpp | 14 +- .../detournavigator/navmeshtilescache.cpp | 366 +++++++++++------- components/CMakeLists.txt | 2 + components/detournavigator/makenavmesh.cpp | 133 +++---- components/detournavigator/makenavmesh.hpp | 11 + .../detournavigator/navmeshcacheitem.cpp | 82 ++++ .../detournavigator/navmeshcacheitem.hpp | 102 +---- components/detournavigator/navmeshdata.hpp | 1 - .../detournavigator/navmeshtilescache.cpp | 37 +- .../detournavigator/navmeshtilescache.hpp | 94 +---- .../detournavigator/navmeshtileview.cpp | 45 +-- .../detournavigator/offmeshconnection.hpp | 1 + .../detournavigator/preparednavmeshdata.cpp | 52 +++ .../detournavigator/preparednavmeshdata.hpp | 48 +++ .../preparednavmeshdatatuple.hpp | 51 +++ components/detournavigator/recastmesh.hpp | 9 + components/detournavigator/ref.hpp | 55 +++ 17 files changed, 627 insertions(+), 476 deletions(-) create mode 100644 components/detournavigator/navmeshcacheitem.cpp create mode 100644 components/detournavigator/preparednavmeshdata.cpp create mode 100644 components/detournavigator/preparednavmeshdata.hpp create mode 100644 components/detournavigator/preparednavmeshdatatuple.hpp create mode 100644 components/detournavigator/ref.hpp diff --git a/apps/benchmarks/detournavigator/navmeshtilescache.cpp b/apps/benchmarks/detournavigator/navmeshtilescache.cpp index dc3b31ce41..487562f404 100644 --- a/apps/benchmarks/detournavigator/navmeshtilescache.cpp +++ b/apps/benchmarks/detournavigator/navmeshtilescache.cpp @@ -15,13 +15,12 @@ namespace osg::Vec3f mAgentHalfExtents; TilePosition mTilePosition; RecastMesh mRecastMesh; - std::vector mOffMeshConnections; }; struct Item { Key mKey; - NavMeshData mValue; + PreparedNavMeshData mValue; }; template @@ -105,8 +104,7 @@ namespace generateWater(std::back_inserter(water), 2, random); RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices), std::move(areaTypes), std::move(water)); - std::vector offMeshConnections; - return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)}; + return Key {agentHalfExtents, tilePosition, std::move(recastMesh)}; } constexpr std::size_t trianglesPerTile = 310; @@ -125,7 +123,8 @@ namespace while (true) { Key key = generateKey(trianglesPerTile, random); - cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData()); + cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, + std::make_unique()); *out++ = std::move(key); const std::size_t newSize = cache.getStats().mNavMeshCacheSize; if (size >= newSize) @@ -147,7 +146,7 @@ namespace while (state.KeepRunning()) { const auto& key = keys[n++ % keys.size()]; - const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections); + const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh); benchmark::DoNotOptimize(result); } } @@ -175,7 +174,8 @@ namespace while (state.KeepRunning()) { const auto& key = keys[n++ % keys.size()]; - const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData()); + const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, + std::make_unique()); benchmark::DoNotOptimize(result); } } diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index 447a5b44e4..17c1b955cf 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -3,24 +3,131 @@ #include #include #include +#include +#include +#include + +#include #include #include -namespace DetourNavigator -{ - static inline bool operator ==(const NavMeshDataRef& lhs, const NavMeshDataRef& rhs) - { - return std::make_pair(lhs.mValue, lhs.mSize) == std::make_pair(rhs.mValue, rhs.mSize); - } -} +#include +#include namespace { using namespace testing; using namespace DetourNavigator; + void* permRecastAlloc(int size) + { + void* result = rcAlloc(static_cast(size), RC_ALLOC_PERM); + if (result == nullptr) + throw std::bad_alloc(); + return result; + } + + template + void generate(T*& values, int size) + { + values = static_cast(permRecastAlloc(size * sizeof(T))); + std::generate_n(values, static_cast(size), [] { return static_cast(std::rand()); }); + } + + void generate(rcPolyMesh& value, int size) + { + value.nverts = size; + value.maxpolys = size; + value.nvp = size; + value.npolys = size; + rcVcopy(value.bmin, osg::Vec3f(-1, -2, -3).ptr()); + rcVcopy(value.bmax, osg::Vec3f(3, 2, 1).ptr()); + value.cs = 1.0f / (std::rand() % 999 + 1); + value.ch = 1.0f / (std::rand() % 999 + 1); + value.borderSize = std::rand(); + value.maxEdgeError = 1.0f / (std::rand() % 999 + 1); + generate(value.verts, 3 * value.nverts); + generate(value.polys, value.maxpolys * 2 * value.nvp); + generate(value.regs, value.maxpolys); + generate(value.flags, value.maxpolys); + generate(value.areas, value.maxpolys); + } + + void generate(rcPolyMeshDetail& value, int size) + { + value.nmeshes = size; + value.nverts = size; + value.ntris = size; + generate(value.meshes, 4 * value.nmeshes); + generate(value.verts, 3 * value.nverts); + generate(value.tris, 4 * value.ntris); + } + + void generate(PreparedNavMeshData& value, int size) + { + value.mUserId = std::rand(); + value.mCellHeight = 1.0f / (std::rand() % 999 + 1); + value.mCellSize = 1.0f / (std::rand() % 999 + 1); + generate(value.mPolyMesh, size); + generate(value.mPolyMeshDetail, size); + } + + std::unique_ptr makePeparedNavMeshData(int size) + { + auto result = std::make_unique(); + generate(*result, size); + return result; + } + + template + void clone(const T* src, T*& dst, int size) + { + dst = static_cast(permRecastAlloc(size * sizeof(T))); + std::memcpy(dst, src, static_cast(size) * sizeof(T)); + } + + void clone(const rcPolyMesh& src, rcPolyMesh& dst) + { + dst.nverts = src.nverts; + dst.npolys = src.npolys; + dst.maxpolys = src.maxpolys; + dst.nvp = src.nvp; + rcVcopy(dst.bmin, src.bmin); + rcVcopy(dst.bmax, src.bmax); + dst.cs = src.cs; + dst.ch = src.ch; + dst.borderSize = src.borderSize; + dst.maxEdgeError = src.maxEdgeError; + clone(src.verts, dst.verts, 3 * dst.nverts); + clone(src.polys, dst.polys, dst.maxpolys * 2 * dst.nvp); + clone(src.regs, dst.regs, dst.maxpolys); + clone(src.flags, dst.flags, dst.maxpolys); + clone(src.areas, dst.areas, dst.maxpolys); + } + + void clone(const rcPolyMeshDetail& src, rcPolyMeshDetail& dst) + { + dst.nmeshes = src.nmeshes; + dst.nverts = src.nverts; + dst.ntris = src.ntris; + clone(src.meshes, dst.meshes, 4 * dst.nmeshes); + clone(src.verts, dst.verts, 3 * dst.nverts); + clone(src.tris, dst.tris, 4 * dst.ntris); + } + + std::unique_ptr clone(const PreparedNavMeshData& value) + { + auto result = std::make_unique(); + result->mUserId = value.mUserId; + result->mCellHeight = value.mCellHeight; + result->mCellSize = value.mCellSize; + clone(value.mPolyMesh, result->mPolyMesh); + clone(value.mPolyMeshDetail, result->mPolyMeshDetail); + return result; + } + struct DetourNavigatorNavMeshTilesCacheTest : Test { const osg::Vec3f mAgentHalfExtents {1, 2, 3}; @@ -32,17 +139,11 @@ namespace const std::vector mAreaTypes {1, AreaType_ground}; const std::vector mWater {}; const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater}; - const std::vector mOffMeshConnections {}; - unsigned char* const mData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData mNavMeshData {mData, 1}; - - const size_t cRecastMeshKeySize = mRecastMesh.getIndices().size() * sizeof(int) - + mRecastMesh.getVertices().size() * sizeof(float) - + mRecastMesh.getAreaTypes().size() * sizeof(AreaType) - + mRecastMesh.getWater().size() * sizeof(RecastMesh::Water) - + mOffMeshConnections.size() * sizeof(OffMeshConnection); + std::unique_ptr mPreparedNavMeshData {makePeparedNavMeshData(3)}; - const size_t cRecastMeshWithWaterKeySize = cRecastMeshKeySize + sizeof(RecastMesh::Water); + const std::size_t mRecastMeshSize = sizeof(mRecastMesh) + getSize(mRecastMesh); + const std::size_t mRecastMeshWithWaterSize = mRecastMeshSize + sizeof(RecastMesh::Water); + const std::size_t mPreparedNavMeshDataSize = sizeof(*mPreparedNavMeshData) + getSize(*mPreparedNavMeshData); }; TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_empty_cache_should_return_empty_value) @@ -50,7 +151,7 @@ namespace const std::size_t maxSize = 0; NavMeshTilesCache cache(maxSize); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_for_not_enought_cache_size_should_return_empty_value) @@ -58,51 +159,46 @@ namespace const std::size_t maxSize = 0; NavMeshTilesCache cache(maxSize); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData))); - EXPECT_NE(mNavMeshData.mValue, nullptr); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData))); + EXPECT_NE(mPreparedNavMeshData, nullptr); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_return_cached_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); + const auto copy = clone(*mPreparedNavMeshData); + ASSERT_EQ(*mPreparedNavMeshData, *copy); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + EXPECT_EQ(result.get(), *copy); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_existing_element_should_return_cached_element) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto copy = clone(*mPreparedNavMeshData); + const auto sameCopy = clone(*mPreparedNavMeshData); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_EQ(mNavMeshData.mValue, nullptr); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(anotherNavMeshData)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_EQ(mPreparedNavMeshData, nullptr); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(copy)); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + EXPECT_EQ(result.get(), *sameCopy); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_should_return_cached_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); + const auto copy = clone(*mPreparedNavMeshData); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + EXPECT_EQ(result.get(), *copy); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_agent_half_extents_should_return_empty_value) @@ -111,8 +207,8 @@ namespace NavMeshTilesCache cache(maxSize); const osg::Vec3f unexsistentAgentHalfExtents {1, 1, 1}; - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value) @@ -121,8 +217,8 @@ namespace NavMeshTilesCache cache(maxSize); const TilePosition unexistentTilePosition {1, 1}; - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_recast_mesh_should_return_empty_value) @@ -132,217 +228,189 @@ namespace const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); + const auto copy = clone(*anotherPreparedNavMeshData); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, + std::move(anotherPreparedNavMeshData)); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {anotherData, 1})); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_EQ(result.get(), *copy); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); - const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); + const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, + std::move(anotherPreparedNavMeshData))); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_set_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); + const auto copy = clone(*mPreparedNavMeshData); const std::vector leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, leastRecentlySetWater}; - const auto leastRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1}; + auto leastRecentlySetData = makePeparedNavMeshData(3); const std::vector mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mostRecentlySetWater}; - const auto mostRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1}; + auto mostRecentlySetData = makePeparedNavMeshData(3); - ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections, - std::move(leastRecentlySetNavMeshData))); - ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections, - std::move(mostRecentlySetNavMeshData))); + ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, + std::move(leastRecentlySetData))); + ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, + std::move(mostRecentlySetData))); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); + EXPECT_EQ(result.get(), *copy); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections)); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh)); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, leastRecentlyUsedWater}; - const auto leastRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1}; + auto leastRecentlyUsedData = makePeparedNavMeshData(3); + const auto leastRecentlyUsedCopy = clone(*leastRecentlyUsedData); const std::vector mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mostRecentlyUsedWater}; - const auto mostRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1}; + auto mostRecentlyUsedData = makePeparedNavMeshData(3); + const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData); - cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections, - std::move(leastRecentlyUsedNavMeshData)); - cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections, - std::move(mostRecentlyUsedNavMeshData)); + cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, std::move(leastRecentlyUsedData)); + cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, std::move(mostRecentlyUsedData)); { - const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections); + const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh); ASSERT_TRUE(value); - ASSERT_EQ(value.get(), (NavMeshDataRef {leastRecentlyUsedData, 1})); + ASSERT_EQ(value.get(), *leastRecentlyUsedCopy); } { - const auto value = cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections); + const auto value = cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh); ASSERT_TRUE(value); - ASSERT_EQ(value.get(), (NavMeshDataRef {mostRecentlyUsedData, 1})); + ASSERT_EQ(value.get(), *mostRecentlyUsedCopy); } - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + const auto copy = clone(*mPreparedNavMeshData); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); + EXPECT_EQ(result.get(), *copy); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections)); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh)); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_cache_max_size) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); - NavMeshData tooLargeNavMeshData {tooLargeData, 2}; + auto tooLargeData = makePeparedNavMeshData(10); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections, - std::move(tooLargeNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, std::move(tooLargeData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_size_of_unused_items) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize1 = cRecastMeshKeySize; - const std::size_t navMeshKeySize2 = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * navMeshDataSize + navMeshKeySize1 + navMeshKeySize2; + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); const std::vector anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherData = makePeparedNavMeshData(3); const std::vector tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, tooLargeWater}; - const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); - NavMeshData tooLargeNavMeshData {tooLargeData, 2}; + auto tooLargeData = makePeparedNavMeshData(10); - const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); + const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); ASSERT_TRUE(value); - ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections, - std::move(tooLargeNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections)); + ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, + std::move(anotherData))); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, + std::move(tooLargeData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_used_after_set_then_used_by_get_item_should_left_this_item_available) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherData = makePeparedNavMeshData(3); - const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); + const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); ASSERT_TRUE(firstCopy); { - const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(secondCopy); } - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherData = makePeparedNavMeshData(3); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(firstCopy); { - const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(secondCopy); } - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 9d72320ca9..678a4ea02f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -193,6 +193,8 @@ add_component_dir(detournavigator navmeshtileview oscillatingrecastmeshobject offmeshconnectionsmanager + preparednavmeshdata + navmeshcacheitem ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 422fdffb1d..507ca078e9 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -7,6 +7,8 @@ #include "sharednavmesh.hpp" #include "flags.hpp" #include "navmeshtilescache.hpp" +#include "preparednavmeshdata.hpp" +#include "navmeshdata.hpp" #include @@ -26,25 +28,6 @@ namespace { using namespace DetourNavigator; - void initPolyMeshDetail(rcPolyMeshDetail& value) - { - value.meshes = nullptr; - value.verts = nullptr; - value.tris = nullptr; - } - - struct PolyMeshDetailStackDeleter - { - void operator ()(rcPolyMeshDetail* value) const - { - rcFree(value->meshes); - rcFree(value->verts); - rcFree(value->tris); - } - }; - - using PolyMeshDetailStackPtr = std::unique_ptr; - struct WaterBounds { osg::Vec3f mMin; @@ -363,10 +346,25 @@ namespace return true; } - NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, - const std::vector& offMeshConnections, const TilePosition& tile, - const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings) + template + unsigned long getMinValuableBitsNumber(const T value) + { + unsigned long power = 0; + while (power < sizeof(T) * 8 && (static_cast(1) << power) < value) + ++power; + return power; + } +} + +namespace DetourNavigator +{ + std::unique_ptr prepareNavMeshTileData(const RecastMesh& recastMesh, + const TilePosition& tile, const Bounds& bounds, const osg::Vec3f& agentHalfExtents, const Settings& settings) { + const TileBounds tileBounds = makeTileBounds(settings, tile); + const osg::Vec3f boundsMin(tileBounds.mMin.x(), bounds.mMin.y() - 1, tileBounds.mMin.y()); + const osg::Vec3f boundsMax(tileBounds.mMax.x(), bounds.mMax.y() + 1, tileBounds.mMax.y()); + rcContext context; const auto config = makeConfig(agentHalfExtents, boundsMin, boundsMax, settings); @@ -374,19 +372,27 @@ namespace createHeightfield(context, solid, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch); if (!rasterizeTriangles(context, agentHalfExtents, recastMesh, config, settings, solid)) - return NavMeshData(); + return nullptr; rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid); rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid); rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid); - rcPolyMesh polyMesh; - rcPolyMeshDetail polyMeshDetail; - initPolyMeshDetail(polyMeshDetail); - const PolyMeshDetailStackPtr polyMeshDetailPtr(&polyMeshDetail); - if (!fillPolyMesh(context, config, solid, polyMesh, polyMeshDetail)) - return NavMeshData(); + std::unique_ptr result = std::make_unique(); + if (!fillPolyMesh(context, config, solid, result->mPolyMesh, result->mPolyMeshDetail)) + return nullptr; + + result->mCellSize = config.cs; + result->mCellHeight = config.ch; + + return result; + } + + NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data, + const std::vector& offMeshConnections, const osg::Vec3f& agentHalfExtents, + const TilePosition& tile, const Settings& settings) + { const auto offMeshConVerts = getOffMeshVerts(offMeshConnections); const std::vector offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents)); const std::vector offMeshConDir(offMeshConnections.size(), 0); @@ -394,18 +400,18 @@ namespace const std::vector offMeshConFlags = getOffMeshFlags(offMeshConnections); dtNavMeshCreateParams params; - params.verts = polyMesh.verts; - params.vertCount = polyMesh.nverts; - params.polys = polyMesh.polys; - params.polyAreas = polyMesh.areas; - params.polyFlags = polyMesh.flags; - params.polyCount = polyMesh.npolys; - params.nvp = polyMesh.nvp; - params.detailMeshes = polyMeshDetail.meshes; - params.detailVerts = polyMeshDetail.verts; - params.detailVertsCount = polyMeshDetail.nverts; - params.detailTris = polyMeshDetail.tris; - params.detailTriCount = polyMeshDetail.ntris; + params.verts = data.mPolyMesh.verts; + params.vertCount = data.mPolyMesh.nverts; + params.polys = data.mPolyMesh.polys; + params.polyAreas = data.mPolyMesh.areas; + params.polyFlags = data.mPolyMesh.flags; + params.polyCount = data.mPolyMesh.npolys; + params.nvp = data.mPolyMesh.nvp; + params.detailMeshes = data.mPolyMeshDetail.meshes; + params.detailVerts = data.mPolyMeshDetail.verts; + params.detailVertsCount = data.mPolyMeshDetail.nverts; + params.detailTris = data.mPolyMeshDetail.tris; + params.detailTriCount = data.mPolyMeshDetail.ntris; params.offMeshConVerts = offMeshConVerts.data(); params.offMeshConRad = offMeshConRad.data(); params.offMeshConDir = offMeshConDir.data(); @@ -416,12 +422,12 @@ namespace params.walkableHeight = getHeight(settings, agentHalfExtents); params.walkableRadius = getRadius(settings, agentHalfExtents); params.walkableClimb = getMaxClimb(settings); - rcVcopy(params.bmin, polyMesh.bmin); - rcVcopy(params.bmax, polyMesh.bmax); - params.cs = config.cs; - params.ch = config.ch; + rcVcopy(params.bmin, data.mPolyMesh.bmin); + rcVcopy(params.bmax, data.mPolyMesh.bmax); + params.cs = data.mCellSize; + params.ch = data.mCellHeight; params.buildBvTree = true; - params.userId = 0; + params.userId = data.mUserId; params.tileX = tile.x(); params.tileY = tile.y(); params.tileLayer = 0; @@ -436,20 +442,6 @@ namespace return NavMeshData(navMeshData, navMeshDataSize); } - - - template - unsigned long getMinValuableBitsNumber(const T value) - { - unsigned long power = 0; - while (power < sizeof(T) * 8 && (static_cast(1) << power) < value) - ++power; - return power; - } -} - -namespace DetourNavigator -{ NavMeshPtr makeEmptyNavMesh(const Settings& settings) { // Max tiles and max polys affect how the tile IDs are caculated. @@ -522,35 +514,32 @@ namespace DetourNavigator return navMeshCacheItem->lock()->removeTile(changedTile); } - auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections); + auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh); bool cached = static_cast(cachedNavMeshData); if (!cachedNavMeshData) { - const auto tileBounds = makeTileBounds(settings, changedTile); - const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), recastMeshBounds.mMin.y() - 1, tileBounds.mMin.y()); - const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), recastMeshBounds.mMax.y() + 1, tileBounds.mMax.y()); - - auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, changedTile, - tileBorderMin, tileBorderMax, settings); + auto prepared = prepareNavMeshTileData(*recastMesh, changedTile, recastMeshBounds, + agentHalfExtents, settings); - if (!navMeshData.mValue) + if (prepared == nullptr) { Log(Debug::Debug) << "Ignore add tile: NavMeshData is null"; return navMeshCacheItem->lock()->removeTile(changedTile); } - cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh, - offMeshConnections, std::move(navMeshData)); + cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh, std::move(prepared)); if (!cachedNavMeshData) { Log(Debug::Debug) << "Navigator cache overflow"; - return navMeshCacheItem->lock()->updateTile(changedTile, std::move(navMeshData)); + return navMeshCacheItem->lock()->updateTile(changedTile, NavMeshTilesCache::Value(), + makeNavMeshTileData(*prepared, offMeshConnections, agentHalfExtents, changedTile, settings)); } } - const auto updateStatus = navMeshCacheItem->lock()->updateTile(changedTile, std::move(cachedNavMeshData)); + const auto updateStatus = navMeshCacheItem->lock()->updateTile(changedTile, std::move(cachedNavMeshData), + makeNavMeshTileData(cachedNavMeshData.get(), offMeshConnections, agentHalfExtents, changedTile, settings)); return UpdateNavMeshStatusBuilder(updateStatus).cached(cached).getResult(); } diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 95720634cd..3e07341106 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -6,10 +6,12 @@ #include "tileposition.hpp" #include "sharednavmesh.hpp" #include "navmeshtilescache.hpp" +#include "offmeshconnection.hpp" #include #include +#include class dtNavMesh; @@ -17,6 +19,8 @@ namespace DetourNavigator { class RecastMesh; struct Settings; + struct PreparedNavMeshData; + struct NavMeshData; inline float getLength(const osg::Vec2i& value) { @@ -34,6 +38,13 @@ namespace DetourNavigator return expectedTilesCount <= maxTiles; } + std::unique_ptr prepareNavMeshTileData(const RecastMesh& recastMesh, const TilePosition& tile, + const Bounds& bounds, const osg::Vec3f& agentHalfExtents, const Settings& settings); + + NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data, + const std::vector& offMeshConnections, const osg::Vec3f& agentHalfExtents, + const TilePosition& tile, const Settings& settings); + NavMeshPtr makeEmptyNavMesh(const Settings& settings); UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, diff --git a/components/detournavigator/navmeshcacheitem.cpp b/components/detournavigator/navmeshcacheitem.cpp new file mode 100644 index 0000000000..ee6f3308d0 --- /dev/null +++ b/components/detournavigator/navmeshcacheitem.cpp @@ -0,0 +1,82 @@ +#include "tileposition.hpp" +#include "navmeshtilescache.hpp" +#include "dtstatus.hpp" +#include "navmeshtileview.hpp" +#include "navmeshcacheitem.hpp" +#include "navmeshdata.hpp" + +#include + +#include + +namespace +{ + using DetourNavigator::TilePosition; + + const dtMeshTile* getTile(const dtNavMesh& navMesh, const TilePosition& position) + { + const int layer = 0; + return navMesh.getTileAt(position.x(), position.y(), layer); + } + + bool removeTile(dtNavMesh& navMesh, const TilePosition& position) + { + const int layer = 0; + const auto tileRef = navMesh.getTileRefAt(position.x(), position.y(), layer); + if (tileRef == 0) + return false; + unsigned char** const data = nullptr; + int* const dataSize = nullptr; + return dtStatusSucceed(navMesh.removeTile(tileRef, data, dataSize)); + } + + dtStatus addTile(dtNavMesh& navMesh, unsigned char* data, int size) + { + const int doNotTransferOwnership = 0; + const dtTileRef lastRef = 0; + dtTileRef* const result = nullptr; + return navMesh.addTile(data, size, doNotTransferOwnership, lastRef, result); + } +} + +namespace DetourNavigator +{ + UpdateNavMeshStatus NavMeshCacheItem::updateTile(const TilePosition& position, NavMeshTilesCache::Value&& cached, + NavMeshData&& navMeshData) + { + const dtMeshTile* currentTile = ::getTile(*mImpl, position); + if (currentTile != nullptr + && asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(navMeshData.mValue.get())) + { + return UpdateNavMeshStatus::ignored; + } + const auto removed = ::removeTile(*mImpl, position); + const auto addStatus = addTile(*mImpl, navMeshData.mValue.get(), navMeshData.mSize); + if (dtStatusSucceed(addStatus)) + { + mUsedTiles[position] = std::make_pair(std::move(cached), std::move(navMeshData)); + ++mNavMeshRevision; + return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); + } + else + { + if (removed) + { + mUsedTiles.erase(position); + ++mNavMeshRevision; + } + return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); + } + } + + UpdateNavMeshStatus NavMeshCacheItem::removeTile(const TilePosition& position) + { + const auto removed = ::removeTile(*mImpl, position); + if (removed) + { + mUsedTiles.erase(position); + ++mNavMeshRevision; + } + return UpdateNavMeshStatusBuilder().removed(removed).getResult(); + } +} diff --git a/components/detournavigator/navmeshcacheitem.hpp b/components/detournavigator/navmeshcacheitem.hpp index a9a4ebee62..5558f4e1f5 100644 --- a/components/detournavigator/navmeshcacheitem.hpp +++ b/components/detournavigator/navmeshcacheitem.hpp @@ -5,14 +5,14 @@ #include "tileposition.hpp" #include "navmeshtilescache.hpp" #include "dtstatus.hpp" -#include "navmeshtileview.hpp" +#include "navmeshdata.hpp" #include -#include - #include +struct dtMeshTile; + namespace DetourNavigator { enum class UpdateNavMeshStatus : unsigned @@ -96,26 +96,6 @@ namespace DetourNavigator } }; - inline unsigned char* getRawData(NavMeshData& navMeshData) - { - return navMeshData.mValue.get(); - } - - inline unsigned char* getRawData(NavMeshTilesCache::Value& cachedNavMeshData) - { - return cachedNavMeshData.get().mValue; - } - - inline int getSize(const NavMeshData& navMeshData) - { - return navMeshData.mSize; - } - - inline int getSize(const NavMeshTilesCache::Value& cachedNavMeshData) - { - return cachedNavMeshData.get().mSize; - } - class NavMeshCacheItem { public: @@ -139,86 +119,16 @@ namespace DetourNavigator return mNavMeshRevision; } - template - UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData) - { - const dtMeshTile* currentTile = getTile(position); - if (currentTile != nullptr - && asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(getRawData(navMeshData))) - { - return UpdateNavMeshStatus::ignored; - } - const auto removed = removeTileImpl(position); - const auto addStatus = addTileImpl(getRawData(navMeshData), getSize(navMeshData)); - if (dtStatusSucceed(addStatus)) - { - setUsedTile(position, std::forward(navMeshData)); - return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); - } - else - { - if (removed) - removeUsedTile(position); - return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); - } - } + UpdateNavMeshStatus updateTile(const TilePosition& position, NavMeshTilesCache::Value&& cached, + NavMeshData&& navMeshData); - UpdateNavMeshStatus removeTile(const TilePosition& position) - { - const auto removed = removeTileImpl(position); - if (removed) - removeUsedTile(position); - return UpdateNavMeshStatusBuilder().removed(removed).getResult(); - } + UpdateNavMeshStatus removeTile(const TilePosition& position); private: NavMeshPtr mImpl; std::size_t mGeneration; std::size_t mNavMeshRevision; std::map> mUsedTiles; - - void setUsedTile(const TilePosition& tilePosition, NavMeshTilesCache::Value value) - { - mUsedTiles[tilePosition] = std::make_pair(std::move(value), NavMeshData()); - ++mNavMeshRevision; - } - - void setUsedTile(const TilePosition& tilePosition, NavMeshData value) - { - mUsedTiles[tilePosition] = std::make_pair(NavMeshTilesCache::Value(), std::move(value)); - ++mNavMeshRevision; - } - - void removeUsedTile(const TilePosition& tilePosition) - { - mUsedTiles.erase(tilePosition); - ++mNavMeshRevision; - } - - dtStatus addTileImpl(unsigned char* data, int size) - { - const int doNotTransferOwnership = 0; - const dtTileRef lastRef = 0; - dtTileRef* const result = nullptr; - return mImpl->addTile(data, size, doNotTransferOwnership, lastRef, result); - } - - bool removeTileImpl(const TilePosition& position) - { - const int layer = 0; - const auto tileRef = mImpl->getTileRefAt(position.x(), position.y(), layer); - if (tileRef == 0) - return false; - unsigned char** const data = nullptr; - int* const dataSize = nullptr; - return dtStatusSucceed(mImpl->removeTile(tileRef, data, dataSize)); - } - - const dtMeshTile* getTile(const TilePosition& position) const - { - const int layer = 0; - return mImpl->getTileAt(position.x(), position.y(), layer); - } }; using GuardedNavMeshCacheItem = Misc::ScopeGuarded; diff --git a/components/detournavigator/navmeshdata.hpp b/components/detournavigator/navmeshdata.hpp index 8ce79614b3..501e971cba 100644 --- a/components/detournavigator/navmeshdata.hpp +++ b/components/detournavigator/navmeshdata.hpp @@ -3,7 +3,6 @@ #include -#include #include namespace DetourNavigator diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index ccc41a666d..7c29f734ea 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -6,32 +6,18 @@ namespace DetourNavigator { - namespace - { - inline std::size_t getSize(const RecastMesh& recastMesh, - const std::vector& offMeshConnections) - { - const std::size_t indicesSize = recastMesh.getIndices().size() * sizeof(int); - const std::size_t verticesSize = recastMesh.getVertices().size() * sizeof(float); - const std::size_t areaTypesSize = recastMesh.getAreaTypes().size() * sizeof(AreaType); - const std::size_t waterSize = recastMesh.getWater().size() * sizeof(RecastMesh::Water); - const std::size_t offMeshConnectionsSize = offMeshConnections.size() * sizeof(OffMeshConnection); - return indicesSize + verticesSize + areaTypesSize + waterSize + offMeshConnectionsSize; - } - } - NavMeshTilesCache::NavMeshTilesCache(const std::size_t maxNavMeshDataSize) : mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0), mHitCount(0), mGetCount(0) {} NavMeshTilesCache::Value NavMeshTilesCache::get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections) + const RecastMesh& recastMesh) { const std::lock_guard lock(mMutex); ++mGetCount; - const auto tile = mValues.find(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyView(recastMesh, offMeshConnections))); + const auto tile = mValues.find(std::make_tuple(agentHalfExtents, changedTile, recastMesh)); if (tile == mValues.end()) return Value(); @@ -43,10 +29,10 @@ namespace DetourNavigator } NavMeshTilesCache::Value NavMeshTilesCache::set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections, - NavMeshData&& value) + const RecastMesh& recastMesh, std::unique_ptr&& value) { - const auto itemSize = static_cast(value.mSize) + getSize(recastMesh, offMeshConnections); + const auto itemSize = sizeof(RecastMesh) + getSize(recastMesh) + + (value == nullptr ? 0 : sizeof(PreparedNavMeshData) + getSize(*value)); const std::lock_guard lock(mMutex); @@ -56,13 +42,10 @@ namespace DetourNavigator while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize) removeLeastRecentlyUsed(); - NavMeshKey navMeshKey { - RecastMeshData {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()}, - offMeshConnections - }; + RecastMeshData key {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()}; - const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey), itemSize); - const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyRef(iterator->mNavMeshKey)), iterator); + 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); if (!emplaced.second) { @@ -73,7 +56,7 @@ namespace DetourNavigator return Value(*this, emplaced.first->second); } - iterator->mNavMeshData = std::move(value); + iterator->mPreparedNavMeshData = std::move(value); ++iterator->mUseCount; mUsedNavMeshDataSize += itemSize; mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator); @@ -108,7 +91,7 @@ namespace DetourNavigator { const auto& item = mFreeItems.back(); - const auto value = mValues.find(std::make_tuple(item.mAgentHalfExtents, item.mChangedTile, NavMeshKeyRef(item.mNavMeshKey))); + const auto value = mValues.find(std::make_tuple(item.mAgentHalfExtents, item.mChangedTile, std::cref(item.mRecastMeshData))); if (value == mValues.end()) return; diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index afa53beba0..37c0be7211 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -1,8 +1,7 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H -#include "offmeshconnection.hpp" -#include "navmeshdata.hpp" +#include "preparednavmeshdata.hpp" #include "recastmesh.hpp" #include "tileposition.hpp" @@ -21,12 +20,6 @@ namespace osg namespace DetourNavigator { - struct NavMeshDataRef - { - unsigned char* mValue; - int mSize; - }; - struct RecastMeshData { std::vector mIndices; @@ -53,71 +46,6 @@ namespace DetourNavigator < std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater); } - struct NavMeshKey - { - RecastMeshData mRecastMesh; - std::vector mOffMeshConnections; - }; - - inline bool operator <(const NavMeshKey& lhs, const NavMeshKey& rhs) - { - return std::tie(lhs.mRecastMesh, lhs.mOffMeshConnections) - < std::tie(rhs.mRecastMesh, rhs.mOffMeshConnections); - } - - struct NavMeshKeyRef - { - std::reference_wrapper mRef; - - explicit NavMeshKeyRef(const NavMeshKey& ref) : mRef(ref) {} - }; - - inline bool operator <(const NavMeshKeyRef& lhs, const NavMeshKeyRef& rhs) - { - return lhs.mRef.get() < rhs.mRef.get(); - } - - struct NavMeshKeyView - { - std::reference_wrapper mRecastMesh; - std::reference_wrapper> mOffMeshConnections; - - NavMeshKeyView(const RecastMesh& recastMesh, const std::vector& offMeshConnections) - : mRecastMesh(recastMesh), mOffMeshConnections(offMeshConnections) {} - }; - - inline bool operator <(const NavMeshKeyView& lhs, const NavMeshKey& rhs) - { - return std::tie(lhs.mRecastMesh.get(), lhs.mOffMeshConnections.get()) - < std::tie(rhs.mRecastMesh, rhs.mOffMeshConnections); - } - - inline bool operator <(const NavMeshKey& lhs, const NavMeshKeyView& rhs) - { - return std::tie(lhs.mRecastMesh, lhs.mOffMeshConnections) - < std::tie(rhs.mRecastMesh.get(), rhs.mOffMeshConnections.get()); - } - - template - inline bool operator <(const NavMeshKeyRef& lhs, const R& rhs) - { - return lhs.mRef.get() < rhs; - } - - template - inline bool operator <(const L& lhs, const NavMeshKeyRef& rhs) - { - return lhs < rhs.mRef.get(); - } - - template - inline bool operator <(const std::tuple& lhs, const std::tuple& rhs) - { - const auto left = std::tie(std::get<0>(lhs), std::get<1>(lhs)); - const auto right = std::tie(std::get<0>(rhs), std::get<1>(rhs)); - return std::tie(left, std::get<2>(lhs)) < std::tie(right, std::get<2>(rhs)); - } - class NavMeshTilesCache { public: @@ -126,15 +54,16 @@ namespace DetourNavigator std::atomic mUseCount; osg::Vec3f mAgentHalfExtents; TilePosition mChangedTile; - NavMeshKey mNavMeshKey; - NavMeshData mNavMeshData; + RecastMeshData mRecastMeshData; + std::unique_ptr mPreparedNavMeshData; std::size_t mSize; - Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, NavMeshKey&& navMeshKey, std::size_t size) + Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, + RecastMeshData&& recastMeshData, std::size_t size) : mUseCount(0) , mAgentHalfExtents(agentHalfExtents) , mChangedTile(changedTile) - , mNavMeshKey(navMeshKey) + , mRecastMeshData(std::move(recastMeshData)) , mSize(size) {} }; @@ -181,9 +110,9 @@ namespace DetourNavigator return *this; } - NavMeshDataRef get() const + const PreparedNavMeshData& get() const { - return NavMeshDataRef {mIterator->mNavMeshData.mValue.get(), mIterator->mNavMeshData.mSize}; + return *mIterator->mPreparedNavMeshData; } operator bool() const @@ -208,11 +137,10 @@ namespace DetourNavigator NavMeshTilesCache(const std::size_t maxNavMeshDataSize); Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections); + const RecastMesh& recastMesh); Value set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections, - NavMeshData&& value); + const RecastMesh& recastMesh, std::unique_ptr&& value); Stats getStats() const; @@ -227,7 +155,7 @@ namespace DetourNavigator std::size_t mGetCount; std::list mBusyItems; std::list mFreeItems; - std::map, ItemIterator, std::less<>> mValues; + std::map>, ItemIterator, std::less<>> mValues; void removeLeastRecentlyUsed(); diff --git a/components/detournavigator/navmeshtileview.cpp b/components/detournavigator/navmeshtileview.cpp index 96f07c6b1c..336cd1ba84 100644 --- a/components/detournavigator/navmeshtileview.cpp +++ b/components/detournavigator/navmeshtileview.cpp @@ -1,4 +1,5 @@ #include "navmeshtileview.hpp" +#include "ref.hpp" #include #include @@ -10,47 +11,9 @@ namespace { - template - struct Ref - { - T& mRef; - - explicit Ref(T& ref) : mRef(ref) {} - - friend bool operator==(const Ref& lhs, const Ref& rhs) - { - return lhs.mRef == rhs.mRef; - } - }; - - template - struct ArrayRef - { - T (&mRef)[size]; - - explicit ArrayRef(T (&ref)[size]) : mRef(ref) {} - - friend bool operator==(const ArrayRef& lhs, const ArrayRef& rhs) - { - return std::equal(std::begin(lhs.mRef), std::end(lhs.mRef), std::begin(rhs.mRef)); - } - }; - - template - struct Span - { - T* mBegin; - T* mEnd; - - explicit Span(T* data, int size) : mBegin(data), mEnd(data + static_cast(size)) {} - - friend bool operator==(const Span& lhs, const Span& rhs) - { - // size is already equal if headers are equal - assert((lhs.mEnd - lhs.mBegin) == (rhs.mEnd - rhs.mBegin)); - return std::equal(lhs.mBegin, lhs.mEnd, rhs.mBegin); - } - }; + using DetourNavigator::ArrayRef; + using DetourNavigator::Ref; + using DetourNavigator::Span; auto makeTuple(const dtMeshHeader& v) { diff --git a/components/detournavigator/offmeshconnection.hpp b/components/detournavigator/offmeshconnection.hpp index 01bae02732..9312bc6c48 100644 --- a/components/detournavigator/offmeshconnection.hpp +++ b/components/detournavigator/offmeshconnection.hpp @@ -5,6 +5,7 @@ #include +#include #include namespace DetourNavigator diff --git a/components/detournavigator/preparednavmeshdata.cpp b/components/detournavigator/preparednavmeshdata.cpp new file mode 100644 index 0000000000..3fea46b26c --- /dev/null +++ b/components/detournavigator/preparednavmeshdata.cpp @@ -0,0 +1,52 @@ +#include "preparednavmeshdata.hpp" +#include "preparednavmeshdatatuple.hpp" + +#include +#include + +namespace +{ + using namespace DetourNavigator; + + void initPolyMeshDetail(rcPolyMeshDetail& value) noexcept + { + value.meshes = nullptr; + value.verts = nullptr; + value.tris = nullptr; + value.nmeshes = 0; + value.nverts = 0; + value.ntris = 0; + } + + void freePolyMeshDetail(rcPolyMeshDetail& value) noexcept + { + rcFree(value.meshes); + rcFree(value.verts); + rcFree(value.tris); + } +} + +template +inline constexpr auto operator==(const T& lhs, const T& rhs) noexcept + -> std::enable_if_t, void>, bool> +{ + return makeTuple(lhs) == makeTuple(rhs); +} + +namespace DetourNavigator +{ + PreparedNavMeshData::PreparedNavMeshData() noexcept + { + initPolyMeshDetail(mPolyMeshDetail); + } + + PreparedNavMeshData::~PreparedNavMeshData() noexcept + { + freePolyMeshDetail(mPolyMeshDetail); + } + + bool operator==(const PreparedNavMeshData& lhs, const PreparedNavMeshData& rhs) noexcept + { + return makeTuple(lhs) == makeTuple(rhs); + } +} diff --git a/components/detournavigator/preparednavmeshdata.hpp b/components/detournavigator/preparednavmeshdata.hpp new file mode 100644 index 0000000000..4a92265231 --- /dev/null +++ b/components/detournavigator/preparednavmeshdata.hpp @@ -0,0 +1,48 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATA_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATA_H + +#include + +#include + +namespace DetourNavigator +{ + struct PreparedNavMeshData + { + unsigned int mUserId = 0; + float mCellSize = 0; + float mCellHeight = 0; + rcPolyMesh mPolyMesh; + rcPolyMeshDetail mPolyMeshDetail; + + PreparedNavMeshData() noexcept; + PreparedNavMeshData(const PreparedNavMeshData&) = delete; + + ~PreparedNavMeshData() noexcept; + + friend bool operator==(const PreparedNavMeshData& lhs, const PreparedNavMeshData& rhs) noexcept; + }; + + inline constexpr std::size_t getSize(const rcPolyMesh& value) noexcept + { + return static_cast(3 * value.nverts) * sizeof(*value.verts) + + static_cast(value.maxpolys * 2 * value.nvp) * sizeof(*value.polys) + + static_cast(value.maxpolys) * sizeof(*value.regs) + + static_cast(value.maxpolys) * sizeof(*value.flags) + + static_cast(value.maxpolys) * sizeof(*value.areas); + } + + inline constexpr std::size_t getSize(const rcPolyMeshDetail& value) noexcept + { + return static_cast(4 * value.nmeshes) * sizeof(*value.meshes) + + static_cast(4 * value.ntris) * sizeof(*value.tris) + + static_cast(3 * value.nverts) * sizeof(*value.verts); + } + + inline constexpr std::size_t getSize(const PreparedNavMeshData& value) noexcept + { + return getSize(value.mPolyMesh) + getSize(value.mPolyMeshDetail); + } +} + +#endif diff --git a/components/detournavigator/preparednavmeshdatatuple.hpp b/components/detournavigator/preparednavmeshdatatuple.hpp new file mode 100644 index 0000000000..5b5a72f629 --- /dev/null +++ b/components/detournavigator/preparednavmeshdatatuple.hpp @@ -0,0 +1,51 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATATUPLE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATATUPLE_H + +#include "preparednavmeshdata.hpp" +#include "ref.hpp" + +#include + +#include + +namespace DetourNavigator +{ + constexpr auto makeTuple(const rcPolyMesh& v) noexcept + { + return std::tuple( + Span(v.verts, 3 * v.nverts), + Span(v.polys, v.maxpolys * 2 * v.nvp), + Span(v.regs, v.maxpolys), + Span(v.flags, v.maxpolys), + Span(v.areas, v.maxpolys), + ArrayRef(v.bmin), + ArrayRef(v.bmax), + v.cs, + v.ch, + v.borderSize, + v.maxEdgeError + ); + } + + constexpr auto makeTuple(const rcPolyMeshDetail& v) noexcept + { + return std::tuple( + Span(v.meshes, 4 * v.nmeshes), + Span(v.verts, 3 * v.nverts), + Span(v.tris, 4 * v.ntris) + ); + } + + constexpr auto makeTuple(const PreparedNavMeshData& v) noexcept + { + return std::tuple( + v.mUserId, + v.mCellHeight, + v.mCellSize, + Ref(v.mPolyMesh), + Ref(v.mPolyMeshDetail) + ); + } +} + +#endif diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 0e6bc49204..7f5758a85e 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -80,6 +80,15 @@ namespace DetourNavigator std::vector mAreaTypes; std::vector mWater; Bounds mBounds; + + friend inline std::size_t getSize(const RecastMesh& recastMesh) noexcept + { + const std::size_t indicesSize = recastMesh.mIndices.size() * sizeof(int); + 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); + return indicesSize + verticesSize + areaTypesSize + waterSize; + } }; inline bool operator<(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs) diff --git a/components/detournavigator/ref.hpp b/components/detournavigator/ref.hpp new file mode 100644 index 0000000000..d800bde8f8 --- /dev/null +++ b/components/detournavigator/ref.hpp @@ -0,0 +1,55 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_REF_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_REF_H + +#include +#include + +namespace DetourNavigator +{ + template + struct Ref + { + T& mRef; + + constexpr explicit Ref(T& ref) noexcept : mRef(ref) {} + + friend bool operator==(const Ref& lhs, const Ref& rhs) + { + return lhs.mRef == rhs.mRef; + } + }; + + template + struct ArrayRef + { + T (&mRef)[size]; + + constexpr explicit ArrayRef(T (&ref)[size]) noexcept : mRef(ref) {} + + friend bool operator==(const ArrayRef& lhs, const ArrayRef& rhs) + { + return std::equal(std::begin(lhs.mRef), std::end(lhs.mRef), std::begin(rhs.mRef)); + } + }; + + template + struct Span + { + T* mBegin; + T* mEnd; + + constexpr explicit Span(T* data, int size) noexcept + : mBegin(data) + , mEnd(data + static_cast(size)) + {} + + friend bool operator==(const Span& lhs, const Span& rhs) + { + // size is already equal if headers are equal + assert((lhs.mEnd - lhs.mBegin) == (rhs.mEnd - rhs.mBegin)); + return std::equal(lhs.mBegin, lhs.mEnd, rhs.mBegin); + } + }; +} + +#endif