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:
commit
22068eed6b
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -193,6 +193,8 @@ add_component_dir(detournavigator
|
||||
navmeshtileview
|
||||
oscillatingrecastmeshobject
|
||||
offmeshconnectionsmanager
|
||||
preparednavmeshdata
|
||||
navmeshcacheitem
|
||||
)
|
||||
|
||||
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
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 "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>;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include <DetourAlloc.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace DetourNavigator
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
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<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)
|
||||
|
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…
x
Reference in New Issue
Block a user