mirror of
https://github.com/OpenMW/openmw.git
synced 2025-04-02 10:06:40 +00:00
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.
This commit is contained in:
parent
6128fcfc82
commit
beeb882ea8
17 changed files with 627 additions and 476 deletions
apps
benchmarks/detournavigator
openmw_test_suite/detournavigator
components
|
@ -15,13 +15,12 @@ namespace
|
||||||
osg::Vec3f mAgentHalfExtents;
|
osg::Vec3f mAgentHalfExtents;
|
||||||
TilePosition mTilePosition;
|
TilePosition mTilePosition;
|
||||||
RecastMesh mRecastMesh;
|
RecastMesh mRecastMesh;
|
||||||
std::vector<OffMeshConnection> mOffMeshConnections;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Item
|
struct Item
|
||||||
{
|
{
|
||||||
Key mKey;
|
Key mKey;
|
||||||
NavMeshData mValue;
|
PreparedNavMeshData mValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Random>
|
template <typename Random>
|
||||||
|
@ -105,8 +104,7 @@ namespace
|
||||||
generateWater(std::back_inserter(water), 2, random);
|
generateWater(std::back_inserter(water), 2, random);
|
||||||
RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices),
|
RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices),
|
||||||
std::move(areaTypes), std::move(water));
|
std::move(areaTypes), std::move(water));
|
||||||
std::vector<OffMeshConnection> offMeshConnections;
|
return Key {agentHalfExtents, tilePosition, std::move(recastMesh)};
|
||||||
return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::size_t trianglesPerTile = 310;
|
constexpr std::size_t trianglesPerTile = 310;
|
||||||
|
@ -125,7 +123,8 @@ namespace
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Key key = generateKey(trianglesPerTile, random);
|
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);
|
*out++ = std::move(key);
|
||||||
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
|
const std::size_t newSize = cache.getStats().mNavMeshCacheSize;
|
||||||
if (size >= newSize)
|
if (size >= newSize)
|
||||||
|
@ -147,7 +146,7 @@ namespace
|
||||||
while (state.KeepRunning())
|
while (state.KeepRunning())
|
||||||
{
|
{
|
||||||
const auto& key = keys[n++ % keys.size()];
|
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);
|
benchmark::DoNotOptimize(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +174,8 @@ namespace
|
||||||
while (state.KeepRunning())
|
while (state.KeepRunning())
|
||||||
{
|
{
|
||||||
const auto& key = keys[n++ % keys.size()];
|
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);
|
benchmark::DoNotOptimize(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,131 @@
|
||||||
#include <components/detournavigator/navmeshtilescache.hpp>
|
#include <components/detournavigator/navmeshtilescache.hpp>
|
||||||
#include <components/detournavigator/exceptions.hpp>
|
#include <components/detournavigator/exceptions.hpp>
|
||||||
#include <components/detournavigator/recastmesh.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 <LinearMath/btTransform.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
namespace DetourNavigator
|
#include <random>
|
||||||
{
|
#include <stdexcept>
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
using namespace DetourNavigator;
|
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
|
struct DetourNavigatorNavMeshTilesCacheTest : Test
|
||||||
{
|
{
|
||||||
const osg::Vec3f mAgentHalfExtents {1, 2, 3};
|
const osg::Vec3f mAgentHalfExtents {1, 2, 3};
|
||||||
|
@ -32,17 +139,11 @@ namespace
|
||||||
const std::vector<AreaType> mAreaTypes {1, AreaType_ground};
|
const std::vector<AreaType> mAreaTypes {1, AreaType_ground};
|
||||||
const std::vector<RecastMesh::Water> mWater {};
|
const std::vector<RecastMesh::Water> mWater {};
|
||||||
const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater};
|
const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater};
|
||||||
const std::vector<OffMeshConnection> mOffMeshConnections {};
|
std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData {makePeparedNavMeshData(3)};
|
||||||
unsigned char* const mData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
|
||||||
NavMeshData mNavMeshData {mData, 1};
|
|
||||||
|
|
||||||
const size_t cRecastMeshKeySize = mRecastMesh.getIndices().size() * sizeof(int)
|
const std::size_t mRecastMeshSize = sizeof(mRecastMesh) + getSize(mRecastMesh);
|
||||||
+ mRecastMesh.getVertices().size() * sizeof(float)
|
const std::size_t mRecastMeshWithWaterSize = mRecastMeshSize + sizeof(RecastMesh::Water);
|
||||||
+ mRecastMesh.getAreaTypes().size() * sizeof(AreaType)
|
const std::size_t mPreparedNavMeshDataSize = sizeof(*mPreparedNavMeshData) + getSize(*mPreparedNavMeshData);
|
||||||
+ mRecastMesh.getWater().size() * sizeof(RecastMesh::Water)
|
|
||||||
+ mOffMeshConnections.size() * sizeof(OffMeshConnection);
|
|
||||||
|
|
||||||
const size_t cRecastMeshWithWaterKeySize = cRecastMeshKeySize + sizeof(RecastMesh::Water);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_empty_cache_should_return_empty_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_empty_cache_should_return_empty_value)
|
||||||
|
@ -50,7 +151,7 @@ namespace
|
||||||
const std::size_t maxSize = 0;
|
const std::size_t maxSize = 0;
|
||||||
NavMeshTilesCache cache(maxSize);
|
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)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_for_not_enought_cache_size_should_return_empty_value)
|
||||||
|
@ -58,51 +159,46 @@ namespace
|
||||||
const std::size_t maxSize = 0;
|
const std::size_t maxSize = 0;
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,
|
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)));
|
||||||
std::move(mNavMeshData)));
|
EXPECT_NE(mPreparedNavMeshData, nullptr);
|
||||||
EXPECT_NE(mNavMeshData.mValue, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_return_cached_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_return_cached_value)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = mRecastMeshSize + mPreparedNavMeshDataSize;
|
||||||
const std::size_t navMeshKeySize = cRecastMeshKeySize;
|
|
||||||
const std::size_t maxSize = navMeshDataSize + navMeshKeySize;
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
const auto copy = clone(*mPreparedNavMeshData);
|
||||||
|
ASSERT_EQ(*mPreparedNavMeshData, *copy);
|
||||||
|
|
||||||
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,
|
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
std::move(mNavMeshData));
|
|
||||||
ASSERT_TRUE(result);
|
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)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_existing_element_should_return_cached_element)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = 2 * (mRecastMeshSize + mPreparedNavMeshDataSize);
|
||||||
const std::size_t navMeshKeySize = cRecastMeshKeySize;
|
|
||||||
const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto copy = clone(*mPreparedNavMeshData);
|
||||||
NavMeshData anotherNavMeshData {anotherData, 1};
|
const auto sameCopy = clone(*mPreparedNavMeshData);
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
EXPECT_EQ(mNavMeshData.mValue, nullptr);
|
EXPECT_EQ(mPreparedNavMeshData, nullptr);
|
||||||
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(anotherNavMeshData));
|
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(copy));
|
||||||
ASSERT_TRUE(result);
|
ASSERT_TRUE(result);
|
||||||
EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));
|
EXPECT_EQ(result.get(), *sameCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_should_return_cached_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_should_return_cached_value)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = mRecastMeshSize + mPreparedNavMeshDataSize;
|
||||||
const std::size_t navMeshKeySize = cRecastMeshKeySize;
|
|
||||||
const std::size_t maxSize = navMeshDataSize + navMeshKeySize;
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
const auto copy = clone(*mPreparedNavMeshData);
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);
|
const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
|
||||||
ASSERT_TRUE(result);
|
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)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_agent_half_extents_should_return_empty_value)
|
||||||
|
@ -111,8 +207,8 @@ namespace
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
const osg::Vec3f unexsistentAgentHalfExtents {1, 1, 1};
|
const osg::Vec3f unexsistentAgentHalfExtents {1, 1, 1};
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));
|
EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value)
|
||||||
|
@ -121,8 +217,8 @@ namespace
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
const TilePosition unexistentTilePosition {1, 1};
|
const TilePosition unexistentTilePosition {1, 1};
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh, mOffMeshConnections));
|
EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_recast_mesh_should_return_empty_value)
|
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 std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections));
|
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize;
|
||||||
const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize;
|
|
||||||
const std::size_t maxSize = navMeshDataSize + navMeshKeySize;
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
||||||
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
|
||||||
NavMeshData anotherNavMeshData {anotherData, 1};
|
const auto copy = clone(*anotherPreparedNavMeshData);
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,
|
const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh,
|
||||||
std::move(anotherNavMeshData));
|
std::move(anotherPreparedNavMeshData));
|
||||||
ASSERT_TRUE(result);
|
ASSERT_TRUE(result);
|
||||||
EXPECT_EQ(result.get(), (NavMeshDataRef {anotherData, 1}));
|
EXPECT_EQ(result.get(), *copy);
|
||||||
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));
|
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize;
|
||||||
const std::size_t navMeshKeySize = cRecastMeshKeySize;
|
|
||||||
const std::size_t maxSize = navMeshDataSize + navMeshKeySize;
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
||||||
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto anotherPreparedNavMeshData = makePeparedNavMeshData(3);
|
||||||
NavMeshData anotherNavMeshData {anotherData, 1};
|
|
||||||
|
|
||||||
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,
|
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
|
||||||
std::move(mNavMeshData));
|
std::move(mPreparedNavMeshData));
|
||||||
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,
|
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh,
|
||||||
std::move(anotherNavMeshData)));
|
std::move(anotherPreparedNavMeshData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_set_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_set_value)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize);
|
||||||
const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize;
|
|
||||||
const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
const auto copy = clone(*mPreparedNavMeshData);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
||||||
mAreaTypes, leastRecentlySetWater};
|
mAreaTypes, leastRecentlySetWater};
|
||||||
const auto leastRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto leastRecentlySetData = makePeparedNavMeshData(3);
|
||||||
NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1};
|
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
|
||||||
const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
||||||
mAreaTypes, mostRecentlySetWater};
|
mAreaTypes, mostRecentlySetWater};
|
||||||
const auto mostRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto mostRecentlySetData = makePeparedNavMeshData(3);
|
||||||
NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1};
|
|
||||||
|
|
||||||
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections,
|
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh,
|
||||||
std::move(leastRecentlySetNavMeshData)));
|
std::move(leastRecentlySetData)));
|
||||||
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections,
|
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh,
|
||||||
std::move(mostRecentlySetNavMeshData)));
|
std::move(mostRecentlySetData)));
|
||||||
|
|
||||||
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,
|
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
|
||||||
std::move(mNavMeshData));
|
std::move(mPreparedNavMeshData));
|
||||||
EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));
|
EXPECT_EQ(result.get(), *copy);
|
||||||
|
|
||||||
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections));
|
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh));
|
||||||
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections));
|
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize);
|
||||||
const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize;
|
|
||||||
const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
||||||
mAreaTypes, leastRecentlyUsedWater};
|
mAreaTypes, leastRecentlyUsedWater};
|
||||||
const auto leastRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto leastRecentlyUsedData = makePeparedNavMeshData(3);
|
||||||
NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1};
|
const auto leastRecentlyUsedCopy = clone(*leastRecentlyUsedData);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
|
||||||
const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
||||||
mAreaTypes, mostRecentlyUsedWater};
|
mAreaTypes, mostRecentlyUsedWater};
|
||||||
const auto mostRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto mostRecentlyUsedData = makePeparedNavMeshData(3);
|
||||||
NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1};
|
const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData);
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections,
|
cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, std::move(leastRecentlyUsedData));
|
||||||
std::move(leastRecentlyUsedNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, std::move(mostRecentlyUsedData));
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections,
|
|
||||||
std::move(mostRecentlyUsedNavMeshData));
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections);
|
const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh);
|
||||||
ASSERT_TRUE(value);
|
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_TRUE(value);
|
||||||
ASSERT_EQ(value.get(), (NavMeshDataRef {mostRecentlyUsedData, 1}));
|
ASSERT_EQ(value.get(), *mostRecentlyUsedCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,
|
const auto copy = clone(*mPreparedNavMeshData);
|
||||||
std::move(mNavMeshData));
|
const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
|
||||||
EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1}));
|
std::move(mPreparedNavMeshData));
|
||||||
|
EXPECT_EQ(result.get(), *copy);
|
||||||
|
|
||||||
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections));
|
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh));
|
||||||
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections));
|
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)
|
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 maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize);
|
||||||
const std::size_t navMeshKeySize = cRecastMeshKeySize;
|
|
||||||
const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize);
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
||||||
const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM));
|
auto tooLargeData = makePeparedNavMeshData(10);
|
||||||
NavMeshData tooLargeNavMeshData {tooLargeData, 2};
|
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections,
|
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, std::move(tooLargeData)));
|
||||||
std::move(tooLargeNavMeshData)));
|
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
|
||||||
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_size_of_unused_items)
|
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 maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize);
|
||||||
const std::size_t navMeshKeySize1 = cRecastMeshKeySize;
|
|
||||||
const std::size_t navMeshKeySize2 = cRecastMeshWithWaterKeySize;
|
|
||||||
const std::size_t maxSize = 2 * navMeshDataSize + navMeshKeySize1 + navMeshKeySize2;
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater};
|
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater};
|
||||||
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto anotherData = makePeparedNavMeshData(3);
|
||||||
NavMeshData anotherNavMeshData {anotherData, 1};
|
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
|
||||||
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
||||||
mAreaTypes, tooLargeWater};
|
mAreaTypes, tooLargeWater};
|
||||||
const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM));
|
auto tooLargeData = makePeparedNavMeshData(10);
|
||||||
NavMeshData tooLargeNavMeshData {tooLargeData, 2};
|
|
||||||
|
|
||||||
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections,
|
const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh,
|
||||||
std::move(mNavMeshData));
|
std::move(mPreparedNavMeshData));
|
||||||
ASSERT_TRUE(value);
|
ASSERT_TRUE(value);
|
||||||
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,
|
ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh,
|
||||||
std::move(anotherNavMeshData)));
|
std::move(anotherData)));
|
||||||
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections,
|
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh,
|
||||||
std::move(tooLargeNavMeshData)));
|
std::move(tooLargeData)));
|
||||||
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));
|
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
|
||||||
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections));
|
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_used_after_set_then_used_by_get_item_should_left_this_item_available)
|
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 maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize;
|
||||||
const std::size_t navMeshKeySize = cRecastMeshKeySize;
|
|
||||||
const std::size_t maxSize = navMeshDataSize + navMeshKeySize;
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices,
|
||||||
mAreaTypes, water};
|
mAreaTypes, water};
|
||||||
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto anotherData = makePeparedNavMeshData(3);
|
||||||
NavMeshData anotherNavMeshData {anotherData, 1};
|
|
||||||
|
|
||||||
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);
|
ASSERT_TRUE(firstCopy);
|
||||||
{
|
{
|
||||||
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);
|
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
|
||||||
ASSERT_TRUE(secondCopy);
|
ASSERT_TRUE(secondCopy);
|
||||||
}
|
}
|
||||||
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,
|
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData)));
|
||||||
std::move(anotherNavMeshData)));
|
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
|
||||||
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available)
|
TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available)
|
||||||
{
|
{
|
||||||
const std::size_t navMeshDataSize = 1;
|
const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize;
|
||||||
const std::size_t navMeshKeySize = cRecastMeshKeySize;
|
|
||||||
const std::size_t maxSize = navMeshDataSize + navMeshKeySize;
|
|
||||||
NavMeshTilesCache cache(maxSize);
|
NavMeshTilesCache cache(maxSize);
|
||||||
|
|
||||||
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
|
||||||
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water};
|
||||||
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
|
auto anotherData = makePeparedNavMeshData(3);
|
||||||
NavMeshData anotherNavMeshData {anotherData, 1};
|
|
||||||
|
|
||||||
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
|
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData));
|
||||||
const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);
|
const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
|
||||||
ASSERT_TRUE(firstCopy);
|
ASSERT_TRUE(firstCopy);
|
||||||
{
|
{
|
||||||
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections);
|
const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh);
|
||||||
ASSERT_TRUE(secondCopy);
|
ASSERT_TRUE(secondCopy);
|
||||||
}
|
}
|
||||||
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections,
|
EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData)));
|
||||||
std::move(anotherNavMeshData)));
|
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh));
|
||||||
EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,6 +193,8 @@ add_component_dir(detournavigator
|
||||||
navmeshtileview
|
navmeshtileview
|
||||||
oscillatingrecastmeshobject
|
oscillatingrecastmeshobject
|
||||||
offmeshconnectionsmanager
|
offmeshconnectionsmanager
|
||||||
|
preparednavmeshdata
|
||||||
|
navmeshcacheitem
|
||||||
)
|
)
|
||||||
|
|
||||||
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "sharednavmesh.hpp"
|
#include "sharednavmesh.hpp"
|
||||||
#include "flags.hpp"
|
#include "flags.hpp"
|
||||||
#include "navmeshtilescache.hpp"
|
#include "navmeshtilescache.hpp"
|
||||||
|
#include "preparednavmeshdata.hpp"
|
||||||
|
#include "navmeshdata.hpp"
|
||||||
|
|
||||||
#include <components/misc/convert.hpp>
|
#include <components/misc/convert.hpp>
|
||||||
|
|
||||||
|
@ -26,25 +28,6 @@ namespace
|
||||||
{
|
{
|
||||||
using namespace DetourNavigator;
|
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
|
struct WaterBounds
|
||||||
{
|
{
|
||||||
osg::Vec3f mMin;
|
osg::Vec3f mMin;
|
||||||
|
@ -363,10 +346,25 @@ namespace
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
|
template <class T>
|
||||||
const std::vector<OffMeshConnection>& offMeshConnections, const TilePosition& tile,
|
unsigned long getMinValuableBitsNumber(const T value)
|
||||||
const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings)
|
|
||||||
{
|
{
|
||||||
|
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;
|
rcContext context;
|
||||||
const auto config = makeConfig(agentHalfExtents, boundsMin, boundsMax, settings);
|
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);
|
createHeightfield(context, solid, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch);
|
||||||
|
|
||||||
if (!rasterizeTriangles(context, agentHalfExtents, recastMesh, config, settings, solid))
|
if (!rasterizeTriangles(context, agentHalfExtents, recastMesh, config, settings, solid))
|
||||||
return NavMeshData();
|
return nullptr;
|
||||||
|
|
||||||
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid);
|
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid);
|
||||||
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid);
|
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid);
|
||||||
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid);
|
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid);
|
||||||
|
|
||||||
rcPolyMesh polyMesh;
|
std::unique_ptr<PreparedNavMeshData> result = std::make_unique<PreparedNavMeshData>();
|
||||||
rcPolyMeshDetail polyMeshDetail;
|
|
||||||
initPolyMeshDetail(polyMeshDetail);
|
|
||||||
const PolyMeshDetailStackPtr polyMeshDetailPtr(&polyMeshDetail);
|
|
||||||
if (!fillPolyMesh(context, config, solid, polyMesh, polyMeshDetail))
|
|
||||||
return NavMeshData();
|
|
||||||
|
|
||||||
|
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 auto offMeshConVerts = getOffMeshVerts(offMeshConnections);
|
||||||
const std::vector<float> offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents));
|
const std::vector<float> offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents));
|
||||||
const std::vector<unsigned char> offMeshConDir(offMeshConnections.size(), 0);
|
const std::vector<unsigned char> offMeshConDir(offMeshConnections.size(), 0);
|
||||||
|
@ -394,18 +400,18 @@ namespace
|
||||||
const std::vector<unsigned short> offMeshConFlags = getOffMeshFlags(offMeshConnections);
|
const std::vector<unsigned short> offMeshConFlags = getOffMeshFlags(offMeshConnections);
|
||||||
|
|
||||||
dtNavMeshCreateParams params;
|
dtNavMeshCreateParams params;
|
||||||
params.verts = polyMesh.verts;
|
params.verts = data.mPolyMesh.verts;
|
||||||
params.vertCount = polyMesh.nverts;
|
params.vertCount = data.mPolyMesh.nverts;
|
||||||
params.polys = polyMesh.polys;
|
params.polys = data.mPolyMesh.polys;
|
||||||
params.polyAreas = polyMesh.areas;
|
params.polyAreas = data.mPolyMesh.areas;
|
||||||
params.polyFlags = polyMesh.flags;
|
params.polyFlags = data.mPolyMesh.flags;
|
||||||
params.polyCount = polyMesh.npolys;
|
params.polyCount = data.mPolyMesh.npolys;
|
||||||
params.nvp = polyMesh.nvp;
|
params.nvp = data.mPolyMesh.nvp;
|
||||||
params.detailMeshes = polyMeshDetail.meshes;
|
params.detailMeshes = data.mPolyMeshDetail.meshes;
|
||||||
params.detailVerts = polyMeshDetail.verts;
|
params.detailVerts = data.mPolyMeshDetail.verts;
|
||||||
params.detailVertsCount = polyMeshDetail.nverts;
|
params.detailVertsCount = data.mPolyMeshDetail.nverts;
|
||||||
params.detailTris = polyMeshDetail.tris;
|
params.detailTris = data.mPolyMeshDetail.tris;
|
||||||
params.detailTriCount = polyMeshDetail.ntris;
|
params.detailTriCount = data.mPolyMeshDetail.ntris;
|
||||||
params.offMeshConVerts = offMeshConVerts.data();
|
params.offMeshConVerts = offMeshConVerts.data();
|
||||||
params.offMeshConRad = offMeshConRad.data();
|
params.offMeshConRad = offMeshConRad.data();
|
||||||
params.offMeshConDir = offMeshConDir.data();
|
params.offMeshConDir = offMeshConDir.data();
|
||||||
|
@ -416,12 +422,12 @@ namespace
|
||||||
params.walkableHeight = getHeight(settings, agentHalfExtents);
|
params.walkableHeight = getHeight(settings, agentHalfExtents);
|
||||||
params.walkableRadius = getRadius(settings, agentHalfExtents);
|
params.walkableRadius = getRadius(settings, agentHalfExtents);
|
||||||
params.walkableClimb = getMaxClimb(settings);
|
params.walkableClimb = getMaxClimb(settings);
|
||||||
rcVcopy(params.bmin, polyMesh.bmin);
|
rcVcopy(params.bmin, data.mPolyMesh.bmin);
|
||||||
rcVcopy(params.bmax, polyMesh.bmax);
|
rcVcopy(params.bmax, data.mPolyMesh.bmax);
|
||||||
params.cs = config.cs;
|
params.cs = data.mCellSize;
|
||||||
params.ch = config.ch;
|
params.ch = data.mCellHeight;
|
||||||
params.buildBvTree = true;
|
params.buildBvTree = true;
|
||||||
params.userId = 0;
|
params.userId = data.mUserId;
|
||||||
params.tileX = tile.x();
|
params.tileX = tile.x();
|
||||||
params.tileY = tile.y();
|
params.tileY = tile.y();
|
||||||
params.tileLayer = 0;
|
params.tileLayer = 0;
|
||||||
|
@ -436,20 +442,6 @@ namespace
|
||||||
return NavMeshData(navMeshData, navMeshDataSize);
|
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)
|
NavMeshPtr makeEmptyNavMesh(const Settings& settings)
|
||||||
{
|
{
|
||||||
// Max tiles and max polys affect how the tile IDs are caculated.
|
// Max tiles and max polys affect how the tile IDs are caculated.
|
||||||
|
@ -522,35 +514,32 @@ namespace DetourNavigator
|
||||||
return navMeshCacheItem->lock()->removeTile(changedTile);
|
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);
|
bool cached = static_cast<bool>(cachedNavMeshData);
|
||||||
|
|
||||||
if (!cachedNavMeshData)
|
if (!cachedNavMeshData)
|
||||||
{
|
{
|
||||||
const auto tileBounds = makeTileBounds(settings, changedTile);
|
auto prepared = prepareNavMeshTileData(*recastMesh, changedTile, recastMeshBounds,
|
||||||
const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), recastMeshBounds.mMin.y() - 1, tileBounds.mMin.y());
|
agentHalfExtents, settings);
|
||||||
const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), recastMeshBounds.mMax.y() + 1, tileBounds.mMax.y());
|
|
||||||
|
|
||||||
auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, changedTile,
|
if (prepared == nullptr)
|
||||||
tileBorderMin, tileBorderMax, settings);
|
|
||||||
|
|
||||||
if (!navMeshData.mValue)
|
|
||||||
{
|
{
|
||||||
Log(Debug::Debug) << "Ignore add tile: NavMeshData is null";
|
Log(Debug::Debug) << "Ignore add tile: NavMeshData is null";
|
||||||
return navMeshCacheItem->lock()->removeTile(changedTile);
|
return navMeshCacheItem->lock()->removeTile(changedTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh,
|
cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh, std::move(prepared));
|
||||||
offMeshConnections, std::move(navMeshData));
|
|
||||||
|
|
||||||
if (!cachedNavMeshData)
|
if (!cachedNavMeshData)
|
||||||
{
|
{
|
||||||
Log(Debug::Debug) << "Navigator cache overflow";
|
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();
|
return UpdateNavMeshStatusBuilder(updateStatus).cached(cached).getResult();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
#include "tileposition.hpp"
|
#include "tileposition.hpp"
|
||||||
#include "sharednavmesh.hpp"
|
#include "sharednavmesh.hpp"
|
||||||
#include "navmeshtilescache.hpp"
|
#include "navmeshtilescache.hpp"
|
||||||
|
#include "offmeshconnection.hpp"
|
||||||
|
|
||||||
#include <osg/Vec3f>
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class dtNavMesh;
|
class dtNavMesh;
|
||||||
|
|
||||||
|
@ -17,6 +19,8 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
class RecastMesh;
|
class RecastMesh;
|
||||||
struct Settings;
|
struct Settings;
|
||||||
|
struct PreparedNavMeshData;
|
||||||
|
struct NavMeshData;
|
||||||
|
|
||||||
inline float getLength(const osg::Vec2i& value)
|
inline float getLength(const osg::Vec2i& value)
|
||||||
{
|
{
|
||||||
|
@ -34,6 +38,13 @@ namespace DetourNavigator
|
||||||
return expectedTilesCount <= maxTiles;
|
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);
|
NavMeshPtr makeEmptyNavMesh(const Settings& settings);
|
||||||
|
|
||||||
UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh,
|
UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh,
|
||||||
|
|
82
components/detournavigator/navmeshcacheitem.cpp
Normal file
82
components/detournavigator/navmeshcacheitem.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,14 +5,14 @@
|
||||||
#include "tileposition.hpp"
|
#include "tileposition.hpp"
|
||||||
#include "navmeshtilescache.hpp"
|
#include "navmeshtilescache.hpp"
|
||||||
#include "dtstatus.hpp"
|
#include "dtstatus.hpp"
|
||||||
#include "navmeshtileview.hpp"
|
#include "navmeshdata.hpp"
|
||||||
|
|
||||||
#include <components/misc/guarded.hpp>
|
#include <components/misc/guarded.hpp>
|
||||||
|
|
||||||
#include <DetourNavMesh.h>
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
struct dtMeshTile;
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
enum class UpdateNavMeshStatus : unsigned
|
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
|
class NavMeshCacheItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -139,86 +119,16 @@ namespace DetourNavigator
|
||||||
return mNavMeshRevision;
|
return mNavMeshRevision;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
UpdateNavMeshStatus updateTile(const TilePosition& position, NavMeshTilesCache::Value&& cached,
|
||||||
UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData)
|
NavMeshData&& 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 removeTile(const TilePosition& position)
|
UpdateNavMeshStatus removeTile(const TilePosition& position);
|
||||||
{
|
|
||||||
const auto removed = removeTileImpl(position);
|
|
||||||
if (removed)
|
|
||||||
removeUsedTile(position);
|
|
||||||
return UpdateNavMeshStatusBuilder().removed(removed).getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NavMeshPtr mImpl;
|
NavMeshPtr mImpl;
|
||||||
std::size_t mGeneration;
|
std::size_t mGeneration;
|
||||||
std::size_t mNavMeshRevision;
|
std::size_t mNavMeshRevision;
|
||||||
std::map<TilePosition, std::pair<NavMeshTilesCache::Value, NavMeshData>> mUsedTiles;
|
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>;
|
using GuardedNavMeshCacheItem = Misc::ScopeGuarded<NavMeshCacheItem>;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include <DetourAlloc.h>
|
#include <DetourAlloc.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
|
|
|
@ -6,32 +6,18 @@
|
||||||
|
|
||||||
namespace DetourNavigator
|
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)
|
NavMeshTilesCache::NavMeshTilesCache(const std::size_t maxNavMeshDataSize)
|
||||||
: mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0),
|
: mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0),
|
||||||
mHitCount(0), mGetCount(0) {}
|
mHitCount(0), mGetCount(0) {}
|
||||||
|
|
||||||
NavMeshTilesCache::Value NavMeshTilesCache::get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
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);
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
++mGetCount;
|
++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())
|
if (tile == mValues.end())
|
||||||
return Value();
|
return Value();
|
||||||
|
|
||||||
|
@ -43,10 +29,10 @@ namespace DetourNavigator
|
||||||
}
|
}
|
||||||
|
|
||||||
NavMeshTilesCache::Value NavMeshTilesCache::set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
NavMeshTilesCache::Value NavMeshTilesCache::set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
||||||
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
|
const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value)
|
||||||
NavMeshData&& 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);
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
@ -56,13 +42,10 @@ namespace DetourNavigator
|
||||||
while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize)
|
while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize)
|
||||||
removeLeastRecentlyUsed();
|
removeLeastRecentlyUsed();
|
||||||
|
|
||||||
NavMeshKey navMeshKey {
|
RecastMeshData key {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()};
|
||||||
RecastMeshData {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()},
|
|
||||||
offMeshConnections
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey), itemSize);
|
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(key), itemSize);
|
||||||
const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyRef(iterator->mNavMeshKey)), iterator);
|
const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, std::cref(iterator->mRecastMeshData)), iterator);
|
||||||
|
|
||||||
if (!emplaced.second)
|
if (!emplaced.second)
|
||||||
{
|
{
|
||||||
|
@ -73,7 +56,7 @@ namespace DetourNavigator
|
||||||
return Value(*this, emplaced.first->second);
|
return Value(*this, emplaced.first->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator->mNavMeshData = std::move(value);
|
iterator->mPreparedNavMeshData = std::move(value);
|
||||||
++iterator->mUseCount;
|
++iterator->mUseCount;
|
||||||
mUsedNavMeshDataSize += itemSize;
|
mUsedNavMeshDataSize += itemSize;
|
||||||
mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator);
|
mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator);
|
||||||
|
@ -108,7 +91,7 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
const auto& item = mFreeItems.back();
|
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())
|
if (value == mValues.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H
|
||||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H
|
||||||
|
|
||||||
#include "offmeshconnection.hpp"
|
#include "preparednavmeshdata.hpp"
|
||||||
#include "navmeshdata.hpp"
|
|
||||||
#include "recastmesh.hpp"
|
#include "recastmesh.hpp"
|
||||||
#include "tileposition.hpp"
|
#include "tileposition.hpp"
|
||||||
|
|
||||||
|
@ -21,12 +20,6 @@ namespace osg
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
struct NavMeshDataRef
|
|
||||||
{
|
|
||||||
unsigned char* mValue;
|
|
||||||
int mSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RecastMeshData
|
struct RecastMeshData
|
||||||
{
|
{
|
||||||
std::vector<int> mIndices;
|
std::vector<int> mIndices;
|
||||||
|
@ -53,71 +46,6 @@ namespace DetourNavigator
|
||||||
< std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater);
|
< 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
|
class NavMeshTilesCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -126,15 +54,16 @@ namespace DetourNavigator
|
||||||
std::atomic<std::int64_t> mUseCount;
|
std::atomic<std::int64_t> mUseCount;
|
||||||
osg::Vec3f mAgentHalfExtents;
|
osg::Vec3f mAgentHalfExtents;
|
||||||
TilePosition mChangedTile;
|
TilePosition mChangedTile;
|
||||||
NavMeshKey mNavMeshKey;
|
RecastMeshData mRecastMeshData;
|
||||||
NavMeshData mNavMeshData;
|
std::unique_ptr<PreparedNavMeshData> mPreparedNavMeshData;
|
||||||
std::size_t mSize;
|
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)
|
: mUseCount(0)
|
||||||
, mAgentHalfExtents(agentHalfExtents)
|
, mAgentHalfExtents(agentHalfExtents)
|
||||||
, mChangedTile(changedTile)
|
, mChangedTile(changedTile)
|
||||||
, mNavMeshKey(navMeshKey)
|
, mRecastMeshData(std::move(recastMeshData))
|
||||||
, mSize(size)
|
, mSize(size)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
@ -181,9 +110,9 @@ namespace DetourNavigator
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
NavMeshDataRef get() const
|
const PreparedNavMeshData& get() const
|
||||||
{
|
{
|
||||||
return NavMeshDataRef {mIterator->mNavMeshData.mValue.get(), mIterator->mNavMeshData.mSize};
|
return *mIterator->mPreparedNavMeshData;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const
|
operator bool() const
|
||||||
|
@ -208,11 +137,10 @@ namespace DetourNavigator
|
||||||
NavMeshTilesCache(const std::size_t maxNavMeshDataSize);
|
NavMeshTilesCache(const std::size_t maxNavMeshDataSize);
|
||||||
|
|
||||||
Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
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,
|
Value set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
||||||
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
|
const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData>&& value);
|
||||||
NavMeshData&& value);
|
|
||||||
|
|
||||||
Stats getStats() const;
|
Stats getStats() const;
|
||||||
|
|
||||||
|
@ -227,7 +155,7 @@ namespace DetourNavigator
|
||||||
std::size_t mGetCount;
|
std::size_t mGetCount;
|
||||||
std::list<Item> mBusyItems;
|
std::list<Item> mBusyItems;
|
||||||
std::list<Item> mFreeItems;
|
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();
|
void removeLeastRecentlyUsed();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "navmeshtileview.hpp"
|
#include "navmeshtileview.hpp"
|
||||||
|
#include "ref.hpp"
|
||||||
|
|
||||||
#include <DetourCommon.h>
|
#include <DetourCommon.h>
|
||||||
#include <DetourNavMesh.h>
|
#include <DetourNavMesh.h>
|
||||||
|
@ -10,47 +11,9 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template <typename T>
|
using DetourNavigator::ArrayRef;
|
||||||
struct Ref
|
using DetourNavigator::Ref;
|
||||||
{
|
using DetourNavigator::Span;
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto makeTuple(const dtMeshHeader& v)
|
auto makeTuple(const dtMeshHeader& v)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <osg/Vec3f>
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
|
|
52
components/detournavigator/preparednavmeshdata.cpp
Normal file
52
components/detournavigator/preparednavmeshdata.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
48
components/detournavigator/preparednavmeshdata.hpp
Normal file
48
components/detournavigator/preparednavmeshdata.hpp
Normal 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
|
51
components/detournavigator/preparednavmeshdatatuple.hpp
Normal file
51
components/detournavigator/preparednavmeshdatatuple.hpp
Normal 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
|
|
@ -80,6 +80,15 @@ namespace DetourNavigator
|
||||||
std::vector<AreaType> mAreaTypes;
|
std::vector<AreaType> mAreaTypes;
|
||||||
std::vector<Water> mWater;
|
std::vector<Water> mWater;
|
||||||
Bounds mBounds;
|
Bounds mBounds;
|
||||||
|
|
||||||
|
friend inline std::size_t getSize(const RecastMesh& recastMesh) noexcept
|
||||||
|
{
|
||||||
|
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)
|
inline bool operator<(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs)
|
||||||
|
|
55
components/detournavigator/ref.hpp
Normal file
55
components/detournavigator/ref.hpp
Normal 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
|
Loading…
Reference in a new issue