1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-22 12:39:59 +00:00

Merge branch 'navmesh_cache_without_off_mesh' into 'master'

Do not use off mesh connections as a part of navmesh cache key

See merge request OpenMW/openmw!1016
This commit is contained in:
psi29a 2021-07-16 08:40:56 +00:00
commit 22068eed6b
17 changed files with 627 additions and 488 deletions

View File

@ -15,13 +15,12 @@ namespace
osg::Vec3f mAgentHalfExtents;
TilePosition mTilePosition;
RecastMesh mRecastMesh;
std::vector<OffMeshConnection> mOffMeshConnections;
};
struct Item
{
Key mKey;
NavMeshData mValue;
PreparedNavMeshData mValue;
};
template <typename Random>
@ -88,17 +87,6 @@ namespace
});
}
template <typename OutputIterator, typename Random>
void generateOffMeshConnection(OutputIterator out, std::size_t count, Random& random)
{
std::uniform_real_distribution<btScalar> distribution(0.0, 1.0);
std::generate_n(out, count, [&] {
const osg::Vec3f start(distribution(random), distribution(random), distribution(random));
const osg::Vec3f end(distribution(random), distribution(random), distribution(random));
return OffMeshConnection {start, end, generateAreaType(random)};
});
}
template <class Random>
Key generateKey(std::size_t triangles, Random& random)
{
@ -116,9 +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<OffMeshConnection> offMeshConnections;
generateOffMeshConnection(std::back_inserter(offMeshConnections), 300, random);
return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)};
return Key {agentHalfExtents, tilePosition, std::move(recastMesh)};
}
constexpr std::size_t trianglesPerTile = 310;
@ -137,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<PreparedNavMeshData>());
*out++ = std::move(key);
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
if (size >= newSize)
@ -159,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);
}
}
@ -187,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<PreparedNavMeshData>());
benchmark::DoNotOptimize(result);
}
}

View File

@ -3,24 +3,131 @@
#include <components/detournavigator/navmeshtilescache.hpp>
#include <components/detournavigator/exceptions.hpp>
#include <components/detournavigator/recastmesh.hpp>
#include <components/detournavigator/preparednavmeshdata.hpp>
#include <components/detournavigator/ref.hpp>
#include <components/detournavigator/preparednavmeshdatatuple.hpp>
#include <RecastAlloc.h>
#include <LinearMath/btTransform.h>
#include <gtest/gtest.h>
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 <random>
#include <stdexcept>
namespace
{
using namespace testing;
using namespace DetourNavigator;
void* permRecastAlloc(int size)
{
void* result = rcAlloc(static_cast<std::size_t>(size), RC_ALLOC_PERM);
if (result == nullptr)
throw std::bad_alloc();
return result;
}
template <class T>
void generate(T*& values, int size)
{
values = static_cast<T*>(permRecastAlloc(size * sizeof(T)));
std::generate_n(values, static_cast<std::size_t>(size), [] { return static_cast<T>(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<PreparedNavMeshData> makePeparedNavMeshData(int size)
{
auto result = std::make_unique<PreparedNavMeshData>();
generate(*result, size);
return result;
}
template <class T>
void clone(const T* src, T*& dst, int size)
{
dst = static_cast<T*>(permRecastAlloc(size * sizeof(T)));
std::memcpy(dst, src, static_cast<std::size_t>(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<PreparedNavMeshData> clone(const PreparedNavMeshData& value)
{
auto result = std::make_unique<PreparedNavMeshData>();
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<AreaType> mAreaTypes {1, AreaType_ground};
const std::vector<RecastMesh::Water> mWater {};
const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater};
const std::vector<OffMeshConnection> mOffMeshConnections {};
unsigned char* const mData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData mNavMeshData {mData, 1};
std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData {makePeparedNavMeshData(3)};
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);
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<unsigned char*>(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<RecastMesh::Water> 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<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
const auto anotherData = reinterpret_cast<unsigned char*>(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<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
const auto anotherData = reinterpret_cast<unsigned char*>(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<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, leastRecentlySetWater};
const auto leastRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1};
auto leastRecentlySetData = makePeparedNavMeshData(3);
const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, mostRecentlySetWater};
const auto mostRecentlySetData = reinterpret_cast<unsigned char*>(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<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, leastRecentlyUsedWater};
const auto leastRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1};
auto leastRecentlyUsedData = makePeparedNavMeshData(3);
const auto leastRecentlyUsedCopy = clone(*leastRecentlyUsedData);
const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, mostRecentlyUsedWater};
const auto mostRecentlyUsedData = reinterpret_cast<unsigned char*>(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<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
const auto tooLargeData = reinterpret_cast<unsigned char*>(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<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater};
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData anotherNavMeshData {anotherData, 1};
auto anotherData = makePeparedNavMeshData(3);
const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, tooLargeWater};
const auto tooLargeData = reinterpret_cast<unsigned char*>(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<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, water};
const auto anotherData = reinterpret_cast<unsigned char*>(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<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
const auto anotherData = reinterpret_cast<unsigned char*>(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));
}
}

View File

@ -193,6 +193,8 @@ add_component_dir(detournavigator
navmeshtileview
oscillatingrecastmeshobject
offmeshconnectionsmanager
preparednavmeshdata
navmeshcacheitem
)
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui

View File

@ -7,6 +7,8 @@
#include "sharednavmesh.hpp"
#include "flags.hpp"
#include "navmeshtilescache.hpp"
#include "preparednavmeshdata.hpp"
#include "navmeshdata.hpp"
#include <components/misc/convert.hpp>
@ -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<rcPolyMeshDetail, PolyMeshDetailStackDeleter>;
struct WaterBounds
{
osg::Vec3f mMin;
@ -363,10 +346,25 @@ namespace
return true;
}
NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
const std::vector<OffMeshConnection>& offMeshConnections, const TilePosition& tile,
const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings)
template <class T>
unsigned long getMinValuableBitsNumber(const T value)
{
unsigned long power = 0;
while (power < sizeof(T) * 8 && (static_cast<T>(1) << power) < value)
++power;
return power;
}
}
namespace DetourNavigator
{
std::unique_ptr<PreparedNavMeshData> 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<PreparedNavMeshData> result = std::make_unique<PreparedNavMeshData>();
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<OffMeshConnection>& offMeshConnections, const osg::Vec3f& agentHalfExtents,
const TilePosition& tile, const Settings& settings)
{
const auto offMeshConVerts = getOffMeshVerts(offMeshConnections);
const std::vector<float> offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents));
const std::vector<unsigned char> offMeshConDir(offMeshConnections.size(), 0);
@ -394,18 +400,18 @@ namespace
const std::vector<unsigned short> 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 <class T>
unsigned long getMinValuableBitsNumber(const T value)
{
unsigned long power = 0;
while (power < sizeof(T) * 8 && (static_cast<T>(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<bool>(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 prepared = prepareNavMeshTileData(*recastMesh, changedTile, recastMeshBounds,
agentHalfExtents, settings);
auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, changedTile,
tileBorderMin, tileBorderMax, 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();
}

View File

@ -6,10 +6,12 @@
#include "tileposition.hpp"
#include "sharednavmesh.hpp"
#include "navmeshtilescache.hpp"
#include "offmeshconnection.hpp"
#include <osg/Vec3f>
#include <memory>
#include <vector>
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<PreparedNavMeshData> 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<OffMeshConnection>& 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,

View File

@ -0,0 +1,82 @@
#include "tileposition.hpp"
#include "navmeshtilescache.hpp"
#include "dtstatus.hpp"
#include "navmeshtileview.hpp"
#include "navmeshcacheitem.hpp"
#include "navmeshdata.hpp"
#include <components/misc/guarded.hpp>
#include <DetourNavMesh.h>
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();
}
}

View File

@ -5,14 +5,14 @@
#include "tileposition.hpp"
#include "navmeshtilescache.hpp"
#include "dtstatus.hpp"
#include "navmeshtileview.hpp"
#include "navmeshdata.hpp"
#include <components/misc/guarded.hpp>
#include <DetourNavMesh.h>
#include <map>
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 <class T>
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<T>(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<TilePosition, std::pair<NavMeshTilesCache::Value, NavMeshData>> 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<NavMeshCacheItem>;

View File

@ -3,7 +3,6 @@
#include <DetourAlloc.h>
#include <algorithm>
#include <memory>
namespace DetourNavigator

View File

@ -6,32 +6,18 @@
namespace DetourNavigator
{
namespace
{
inline std::size_t getSize(const RecastMesh& recastMesh,
const std::vector<OffMeshConnection>& 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<OffMeshConnection>& offMeshConnections)
const RecastMesh& recastMesh)
{
const std::lock_guard<std::mutex> 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<OffMeshConnection>& offMeshConnections,
NavMeshData&& value)
const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value)
{
const auto itemSize = static_cast<std::size_t>(value.mSize) + getSize(recastMesh, offMeshConnections);
const auto itemSize = sizeof(RecastMesh) + getSize(recastMesh)
+ (value == nullptr ? 0 : sizeof(PreparedNavMeshData) + getSize(*value));
const std::lock_guard<std::mutex> 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;

View File

@ -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<int> mIndices;
@ -53,71 +46,6 @@ namespace DetourNavigator
< std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater);
}
struct NavMeshKey
{
RecastMeshData mRecastMesh;
std::vector<OffMeshConnection> 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<const NavMeshKey> 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<const RecastMesh> mRecastMesh;
std::reference_wrapper<const std::vector<OffMeshConnection>> mOffMeshConnections;
NavMeshKeyView(const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& 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 <class R>
inline bool operator <(const NavMeshKeyRef& lhs, const R& rhs)
{
return lhs.mRef.get() < rhs;
}
template <class L>
inline bool operator <(const L& lhs, const NavMeshKeyRef& rhs)
{
return lhs < rhs.mRef.get();
}
template <class L, class R>
inline bool operator <(const std::tuple<osg::Vec3f, TilePosition, L>& lhs, const std::tuple<osg::Vec3f, TilePosition, R>& 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<std::int64_t> mUseCount;
osg::Vec3f mAgentHalfExtents;
TilePosition mChangedTile;
NavMeshKey mNavMeshKey;
NavMeshData mNavMeshData;
RecastMeshData mRecastMeshData;
std::unique_ptr<PreparedNavMeshData> 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<OffMeshConnection>& offMeshConnections);
const RecastMesh& recastMesh);
Value set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
NavMeshData&& value);
const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value);
Stats getStats() const;
@ -227,7 +155,7 @@ namespace DetourNavigator
std::size_t mGetCount;
std::list<Item> mBusyItems;
std::list<Item> mFreeItems;
std::map<std::tuple<osg::Vec3f, TilePosition, NavMeshKeyRef>, ItemIterator, std::less<>> mValues;
std::map<std::tuple<osg::Vec3f, TilePosition, std::reference_wrapper<const RecastMeshData>>, ItemIterator, std::less<>> mValues;
void removeLeastRecentlyUsed();

View File

@ -1,4 +1,5 @@
#include "navmeshtileview.hpp"
#include "ref.hpp"
#include <DetourCommon.h>
#include <DetourNavMesh.h>
@ -10,47 +11,9 @@
namespace
{
template <typename T>
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 <typename T, std::size_t size>
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 <typename T>
struct Span
{
T* mBegin;
T* mEnd;
explicit Span(T* data, int size) : mBegin(data), mEnd(data + static_cast<std::size_t>(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)
{

View File

@ -5,6 +5,7 @@
#include <osg/Vec3f>
#include <vector>
#include <tuple>
namespace DetourNavigator

View File

@ -0,0 +1,52 @@
#include "preparednavmeshdata.hpp"
#include "preparednavmeshdatatuple.hpp"
#include <Recast.h>
#include <RecastAlloc.h>
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 <class T>
inline constexpr auto operator==(const T& lhs, const T& rhs) noexcept
-> std::enable_if_t<std::is_same_v<std::void_t<decltype(makeTuple(lhs))>, 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);
}
}

View File

@ -0,0 +1,48 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATA_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATA_H
#include <Recast.h>
#include <cstddef>
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<std::size_t>(3 * value.nverts) * sizeof(*value.verts)
+ static_cast<std::size_t>(value.maxpolys * 2 * value.nvp) * sizeof(*value.polys)
+ static_cast<std::size_t>(value.maxpolys) * sizeof(*value.regs)
+ static_cast<std::size_t>(value.maxpolys) * sizeof(*value.flags)
+ static_cast<std::size_t>(value.maxpolys) * sizeof(*value.areas);
}
inline constexpr std::size_t getSize(const rcPolyMeshDetail& value) noexcept
{
return static_cast<std::size_t>(4 * value.nmeshes) * sizeof(*value.meshes)
+ static_cast<std::size_t>(4 * value.ntris) * sizeof(*value.tris)
+ static_cast<std::size_t>(3 * value.nverts) * sizeof(*value.verts);
}
inline constexpr std::size_t getSize(const PreparedNavMeshData& value) noexcept
{
return getSize(value.mPolyMesh) + getSize(value.mPolyMeshDetail);
}
}
#endif

View File

@ -0,0 +1,51 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATATUPLE_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATATUPLE_H
#include "preparednavmeshdata.hpp"
#include "ref.hpp"
#include <Recast.h>
#include <tuple>
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

View File

@ -80,6 +80,15 @@ namespace DetourNavigator
std::vector<AreaType> mAreaTypes;
std::vector<Water> 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)

View File

@ -0,0 +1,55 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_REF_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_REF_H
#include <algorithm>
#include <cassert>
namespace DetourNavigator
{
template <typename T>
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 <typename T, std::size_t size>
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 <typename T>
struct Span
{
T* mBegin;
T* mEnd;
constexpr explicit Span(T* data, int size) noexcept
: mBegin(data)
, mEnd(data + static_cast<std::size_t>(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