1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-27 03:40:24 +00:00

Merge branch 'fix_recastmesh' into 'master'

Limit number of cached tiles with recast meshes

See merge request OpenMW/openmw!4112
This commit is contained in:
psi29a 2024-05-22 20:38:42 +00:00
commit 2be6116ef6
34 changed files with 440 additions and 248 deletions

View file

@ -106,8 +106,8 @@ namespace NavMeshTool
return DetourNavigator::resolveMeshSource(mDb, source, mNextShapeId); return DetourNavigator::resolveMeshSource(mDb, source, mNextShapeId);
} }
std::optional<NavMeshTileInfo> find(std::string_view worldspace, const TilePosition& tilePosition, std::optional<NavMeshTileInfo> find(
const std::vector<std::byte>& input) override ESM::RefId worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) override
{ {
std::optional<NavMeshTileInfo> result; std::optional<NavMeshTileInfo> result;
std::lock_guard lock(mMutex); std::lock_guard lock(mMutex);
@ -121,7 +121,7 @@ namespace NavMeshTool
return result; return result;
} }
void ignore(std::string_view worldspace, const TilePosition& tilePosition) override void ignore(ESM::RefId worldspace, const TilePosition& tilePosition) override
{ {
if (mRemoveUnusedTiles) if (mRemoveUnusedTiles)
{ {
@ -131,7 +131,7 @@ namespace NavMeshTool
report(); report();
} }
void identity(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t tileId) override void identity(ESM::RefId worldspace, const TilePosition& tilePosition, std::int64_t tileId) override
{ {
if (mRemoveUnusedTiles) if (mRemoveUnusedTiles)
{ {
@ -142,7 +142,7 @@ namespace NavMeshTool
report(); report();
} }
void insert(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t version, void insert(ESM::RefId worldspace, const TilePosition& tilePosition, std::int64_t version,
const std::vector<std::byte>& input, PreparedNavMeshData& data) override const std::vector<std::byte>& input, PreparedNavMeshData& data) override
{ {
{ {
@ -158,7 +158,7 @@ namespace NavMeshTool
report(); report();
} }
void update(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t tileId, void update(ESM::RefId worldspace, const TilePosition& tilePosition, std::int64_t tileId,
std::int64_t version, PreparedNavMeshData& data) override std::int64_t version, PreparedNavMeshData& data) override
{ {
data.mUserId = static_cast<unsigned>(tileId); data.mUserId = static_cast<unsigned>(tileId);
@ -217,7 +217,7 @@ namespace NavMeshTool
mDb.vacuum(); mDb.vacuum();
} }
void removeTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range) void removeTilesOutsideRange(ESM::RefId worldspace, const TilesPositionsRange& range)
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
mTransaction.commit(); mTransaction.commit();

View file

@ -234,8 +234,8 @@ namespace NavMeshTool
} }
WorldspaceNavMeshInput::WorldspaceNavMeshInput( WorldspaceNavMeshInput::WorldspaceNavMeshInput(
std::string worldspace, const DetourNavigator::RecastSettings& settings) ESM::RefId worldspace, const DetourNavigator::RecastSettings& settings)
: mWorldspace(std::move(worldspace)) : mWorldspace(worldspace)
, mTileCachedRecastMeshManager(settings) , mTileCachedRecastMeshManager(settings)
{ {
mAabb.m_min = btVector3(0, 0, 0); mAabb.m_min = btVector3(0, 0, 0);
@ -248,7 +248,7 @@ namespace NavMeshTool
{ {
Log(Debug::Info) << "Processing " << esmData.mCells.size() << " cells..."; Log(Debug::Info) << "Processing " << esmData.mCells.size() << " cells...";
std::map<std::string_view, std::unique_ptr<WorldspaceNavMeshInput>> navMeshInputs; std::unordered_map<ESM::RefId, std::unique_ptr<WorldspaceNavMeshInput>> navMeshInputs;
WorldspaceData data; WorldspaceData data;
std::size_t objectsCounter = 0; std::size_t objectsCounter = 0;
@ -276,8 +276,7 @@ namespace NavMeshTool
const osg::Vec2i cellPosition(cell.mData.mX, cell.mData.mY); const osg::Vec2i cellPosition(cell.mData.mX, cell.mData.mY);
const std::size_t cellObjectsBegin = data.mObjects.size(); const std::size_t cellObjectsBegin = data.mObjects.size();
const auto cellWorldspace = Misc::StringUtils::lowerCase( const ESM::RefId cellWorldspace = cell.isExterior() ? ESM::Cell::sDefaultWorldspaceId : cell.mId;
(cell.isExterior() ? ESM::Cell::sDefaultWorldspaceId : cell.mId).serializeText());
WorldspaceNavMeshInput& navMeshInput = [&]() -> WorldspaceNavMeshInput& { WorldspaceNavMeshInput& navMeshInput = [&]() -> WorldspaceNavMeshInput& {
auto it = navMeshInputs.find(cellWorldspace); auto it = navMeshInputs.find(cellWorldspace);
if (it == navMeshInputs.end()) if (it == navMeshInputs.end())

View file

@ -48,12 +48,12 @@ namespace NavMeshTool
struct WorldspaceNavMeshInput struct WorldspaceNavMeshInput
{ {
std::string mWorldspace; ESM::RefId mWorldspace;
TileCachedRecastMeshManager mTileCachedRecastMeshManager; TileCachedRecastMeshManager mTileCachedRecastMeshManager;
btAABB mAabb; btAABB mAabb;
bool mAabbInitialized = false; bool mAabbInitialized = false;
explicit WorldspaceNavMeshInput(std::string worldspace, const DetourNavigator::RecastSettings& settings); explicit WorldspaceNavMeshInput(ESM::RefId worldspace, const DetourNavigator::RecastSettings& settings);
}; };
class BulletObject class BulletObject

View file

@ -67,14 +67,22 @@ namespace MWRender
++it; ++it;
} }
for (const auto& tile : tiles) for (const auto& [position, mesh] : tiles)
{ {
if (mGroups.count(tile.first)) const auto it = mGroups.find(position);
continue;
const auto group = SceneUtil::createRecastMeshGroup(*tile.second, settings.mRecast); if (it != mGroups.end())
{
if (it->second.mVersion == mesh->getVersion())
continue;
mRootNode->removeChild(it->second.mValue);
}
const auto group = SceneUtil::createRecastMeshGroup(*mesh, settings.mRecast);
MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(group, "debug"); MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(group, "debug");
group->setNodeMask(Mask_Debug); group->setNodeMask(Mask_Debug);
mGroups.emplace(tile.first, Group{ tile.second->getVersion(), group }); mGroups.insert_or_assign(it, position, Group{ mesh->getVersion(), group });
mRootNode->addChild(group); mRootNode->addChild(group);
} }
} }

View file

@ -6,8 +6,6 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <vector>
namespace osg namespace osg
{ {
class Group; class Group;

View file

@ -614,11 +614,11 @@ namespace MWWorld
void Scene::changeCellGrid(const osg::Vec3f& pos, ESM::ExteriorCellLocation playerCellIndex, bool changeEvent) void Scene::changeCellGrid(const osg::Vec3f& pos, ESM::ExteriorCellLocation playerCellIndex, bool changeEvent)
{ {
mHalfGridSize const int halfGridSize
= isEsm4Ext(playerCellIndex.mWorldspace) ? Constants::ESM4CellGridRadius : Constants::CellGridRadius; = isEsm4Ext(playerCellIndex.mWorldspace) ? Constants::ESM4CellGridRadius : Constants::CellGridRadius;
auto navigatorUpdateGuard = mNavigator.makeUpdateGuard(); auto navigatorUpdateGuard = mNavigator.makeUpdateGuard();
int playerCellX = playerCellIndex.mX; const int playerCellX = playerCellIndex.mX;
int playerCellY = playerCellIndex.mY; const int playerCellY = playerCellIndex.mY;
for (auto iter = mActiveCells.begin(); iter != mActiveCells.end();) for (auto iter = mActiveCells.begin(); iter != mActiveCells.end();)
{ {
@ -627,17 +627,21 @@ namespace MWWorld
{ {
const auto dx = std::abs(playerCellX - cell->getCell()->getGridX()); const auto dx = std::abs(playerCellX - cell->getCell()->getGridX());
const auto dy = std::abs(playerCellY - cell->getCell()->getGridY()); const auto dy = std::abs(playerCellY - cell->getCell()->getGridY());
if (dx > mHalfGridSize || dy > mHalfGridSize) if (dx > halfGridSize || dy > halfGridSize)
unloadCell(cell, navigatorUpdateGuard.get()); unloadCell(cell, navigatorUpdateGuard.get());
} }
else else
unloadCell(cell, navigatorUpdateGuard.get()); unloadCell(cell, navigatorUpdateGuard.get());
} }
mNavigator.setWorldspace(
mWorld.getWorldModel().getExterior(playerCellIndex).getCell()->getWorldSpace().serializeText(),
navigatorUpdateGuard.get());
mNavigator.updateBounds(pos, navigatorUpdateGuard.get());
const DetourNavigator::CellGridBounds cellGridBounds{
.mCenter = osg::Vec2i(playerCellX, playerCellY),
.mHalfSize = halfGridSize,
};
mNavigator.updateBounds(playerCellIndex.mWorldspace, cellGridBounds, pos, navigatorUpdateGuard.get());
mHalfGridSize = halfGridSize;
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
mRendering.setActiveGrid(newGrid); mRendering.setActiveGrid(newGrid);
@ -738,10 +742,18 @@ namespace MWWorld
CellStore& cell = mWorld.getWorldModel().getExterior( CellStore& cell = mWorld.getWorldModel().getExterior(
ESM::ExteriorCellLocation(it->mData.mX, it->mData.mY, ESM::Cell::sDefaultWorldspaceId)); ESM::ExteriorCellLocation(it->mData.mX, it->mData.mY, ESM::Cell::sDefaultWorldspaceId));
mNavigator.setWorldspace(cell.getCell()->getWorldSpace().serializeText(), navigatorUpdateGuard.get());
const osg::Vec3f position const osg::Vec3f position
= osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits; = osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits;
mNavigator.updateBounds(position, navigatorUpdateGuard.get()); const osg::Vec2i cellPosition(it->mData.mX, it->mData.mY);
const DetourNavigator::CellGridBounds cellGridBounds{
.mCenter = osg::Vec2i(it->mData.mX, it->mData.mY),
.mHalfSize = Constants::CellGridRadius,
};
mNavigator.updateBounds(
ESM::Cell::sDefaultWorldspaceId, cellGridBounds, position, navigatorUpdateGuard.get());
loadCell(cell, nullptr, false, position, navigatorUpdateGuard.get()); loadCell(cell, nullptr, false, position, navigatorUpdateGuard.get());
mNavigator.update(position, navigatorUpdateGuard.get()); mNavigator.update(position, navigatorUpdateGuard.get());
@ -795,10 +807,10 @@ namespace MWWorld
+ std::to_string(cells.getIntSize()) + ")..."); + std::to_string(cells.getIntSize()) + ")...");
CellStore& cell = mWorld.getWorldModel().getInterior(it->mName); CellStore& cell = mWorld.getWorldModel().getInterior(it->mName);
mNavigator.setWorldspace(cell.getCell()->getWorldSpace().serializeText(), navigatorUpdateGuard.get());
ESM::Position position; ESM::Position position;
mWorld.findInteriorPosition(it->mName, position); mWorld.findInteriorPosition(it->mName, position);
mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get()); mNavigator.updateBounds(
cell.getCell()->getWorldSpace(), std::nullopt, position.asVec3(), navigatorUpdateGuard.get());
loadCell(cell, nullptr, false, position.asVec3(), navigatorUpdateGuard.get()); loadCell(cell, nullptr, false, position.asVec3(), navigatorUpdateGuard.get());
mNavigator.update(position.asVec3(), navigatorUpdateGuard.get()); mNavigator.update(position.asVec3(), navigatorUpdateGuard.get());
@ -949,8 +961,8 @@ namespace MWWorld
loadingListener->setProgressRange(cell.count()); loadingListener->setProgressRange(cell.count());
mNavigator.setWorldspace(cell.getCell()->getWorldSpace().serializeText(), navigatorUpdateGuard.get()); mNavigator.updateBounds(
mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get()); cell.getCell()->getWorldSpace(), std::nullopt, position.asVec3(), navigatorUpdateGuard.get());
// Load cell. // Load cell.
mPagedRefs.clear(); mPagedRefs.clear();

View file

@ -52,7 +52,7 @@ namespace
OffMeshConnectionsManager mOffMeshConnectionsManager{ mSettings.mRecast }; OffMeshConnectionsManager mOffMeshConnectionsManager{ mSettings.mRecast };
const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } }; const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } };
const TilePosition mPlayerTile{ 0, 0 }; const TilePosition mPlayerTile{ 0, 0 };
const std::string mWorldspace = "sys::default"; const ESM::RefId mWorldspace = ESM::RefId::stringRefId("sys::default");
const btBoxShape mBox{ btVector3(100, 100, 20) }; const btBoxShape mBox{ btVector3(100, 100, 20) };
Loading::Listener mListener; Loading::Listener mListener;
}; };
@ -310,7 +310,7 @@ namespace
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db)); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db));
const TileId nextTileId(dbPtr->getMaxTileId() + 1); const TileId nextTileId(dbPtr->getMaxTileId() + 1);
ASSERT_EQ(dbPtr->insertTile(nextTileId, "worldspace", TilePosition{}, TileVersion{ 1 }, {}, {}), 1); ASSERT_EQ(dbPtr->insertTile(nextTileId, mWorldspace, TilePosition{}, TileVersion{ 1 }, {}, {}), 1);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings); const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const TilePosition tilePosition{ 0, 0 }; const TilePosition tilePosition{ 0, 0 };
@ -385,7 +385,7 @@ namespace
const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) }; const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) };
const std::shared_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItemPtr; const std::shared_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItemPtr;
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem = mNavMeshCacheItemPtr; const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem = mNavMeshCacheItemPtr;
const std::string_view mWorldspace = "worldspace"; const ESM::RefId mWorldspace = ESM::RefId::stringRefId("worldspace");
const TilePosition mChangedTile{ 0, 0 }; const TilePosition mChangedTile{ 0, 0 };
const std::chrono::steady_clock::time_point mProcessTime{}; const std::chrono::steady_clock::time_point mProcessTime{};
const TilePosition mPlayerTile{ 0, 0 }; const TilePosition mPlayerTile{ 0, 0 };
@ -397,20 +397,23 @@ namespace
std::list<Job> jobs; std::list<Job> jobs;
SpatialJobQueue queue; SpatialJobQueue queue;
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, "worldspace1", mChangedTile, const ESM::RefId worldspace1 = ESM::RefId::stringRefId("worldspace1");
ChangeType::remove, mProcessTime)); const ESM::RefId worldspace2 = ESM::RefId::stringRefId("worldspace2");
queue.push(jobs.emplace(jobs.end(), mAgentBounds, mNavMeshCacheItem, "worldspace2", mChangedTile,
ChangeType::update, mProcessTime)); queue.push(jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, worldspace1, mChangedTile, ChangeType::remove, mProcessTime));
queue.push(jobs.emplace(
jobs.end(), mAgentBounds, mNavMeshCacheItem, worldspace2, mChangedTile, ChangeType::update, mProcessTime));
ASSERT_EQ(queue.size(), 2); ASSERT_EQ(queue.size(), 2);
const auto job1 = queue.pop(mChangedTile); const auto job1 = queue.pop(mChangedTile);
ASSERT_TRUE(job1.has_value()); ASSERT_TRUE(job1.has_value());
EXPECT_EQ((*job1)->mWorldspace, "worldspace1"); EXPECT_EQ((*job1)->mWorldspace, worldspace1);
const auto job2 = queue.pop(mChangedTile); const auto job2 = queue.pop(mChangedTile);
ASSERT_TRUE(job2.has_value()); ASSERT_TRUE(job2.has_value());
EXPECT_EQ((*job2)->mWorldspace, "worldspace2"); EXPECT_EQ((*job2)->mWorldspace, worldspace2);
EXPECT_EQ(queue.size(), 0); EXPECT_EQ(queue.size(), 0);
} }

View file

@ -44,31 +44,21 @@ namespace
struct DetourNavigatorNavigatorTest : Test struct DetourNavigatorNavigatorTest : Test
{ {
Settings mSettings = makeSettings(); Settings mSettings = makeSettings();
std::unique_ptr<Navigator> mNavigator; std::unique_ptr<Navigator> mNavigator = std::make_unique<NavigatorImpl>(
const osg::Vec3f mPlayerPosition; mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max()));
const std::string mWorldspace; const osg::Vec3f mPlayerPosition{ 256, 256, 0 };
const ESM::RefId mWorldspace = ESM::RefId::stringRefId("sys::default");
const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } }; const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } };
osg::Vec3f mStart; osg::Vec3f mStart{ 52, 460, 1 };
osg::Vec3f mEnd; osg::Vec3f mEnd{ 460, 52, 1 };
std::deque<osg::Vec3f> mPath; std::deque<osg::Vec3f> mPath;
std::back_insert_iterator<std::deque<osg::Vec3f>> mOut; std::back_insert_iterator<std::deque<osg::Vec3f>> mOut{ mPath };
AreaCosts mAreaCosts; AreaCosts mAreaCosts;
Loading::Listener mListener; Loading::Listener mListener;
const osg::Vec2i mCellPosition{ 0, 0 }; const osg::Vec2i mCellPosition{ 0, 0 };
const float mEndTolerance = 0; const float mEndTolerance = 0;
const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) }; const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) };
const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f }; const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f };
DetourNavigatorNavigatorTest()
: mPlayerPosition(256, 256, 0)
, mWorldspace("sys::default")
, mStart(52, 460, 1)
, mEnd(460, 52, 1)
, mOut(mPath)
{
mNavigator.reset(new NavigatorImpl(
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
}
}; };
constexpr std::array<float, 5 * 5> defaultHeightfieldData{ { constexpr std::array<float, 5 * 5> defaultHeightfieldData{ {
@ -869,6 +859,17 @@ namespace
EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version); EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version);
} }
std::pair<TilePosition, TilePosition> getMinMax(const RecastMeshTiles& tiles)
{
const auto lessByX = [](const auto& l, const auto& r) { return l.first.x() < r.first.x(); };
const auto lessByY = [](const auto& l, const auto& r) { return l.first.y() < r.first.y(); };
const auto [minX, maxX] = std::ranges::minmax_element(tiles, lessByX);
const auto [minY, maxY] = std::ranges::minmax_element(tiles, lessByY);
return { TilePosition(minX->first.x(), minY->first.y()), TilePosition(maxX->first.x(), maxY->first.y()) };
}
TEST_F(DetourNavigatorNavigatorTest, update_for_very_big_object_should_be_limited) TEST_F(DetourNavigatorNavigatorTest, update_for_very_big_object_should_be_limited)
{ {
const float size = static_cast<float>((1 << 22) - 1); const float size = static_cast<float>((1 << 22) - 1);
@ -877,8 +878,10 @@ namespace
.mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } }, .mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } },
.mScale = 1.0f, .mScale = 1.0f,
}; };
const std::optional<CellGridBounds> cellGridBounds = std::nullopt;
const osg::Vec3f playerPosition(32, 1024, 0);
mNavigator->updateBounds(mPlayerPosition, nullptr); mNavigator->updateBounds(mWorldspace, cellGridBounds, playerPosition, nullptr);
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addObject(ObjectId(&bigBox.shape()), ObjectShapes(bigBox.instance(), objectTransform), mNavigator->addObject(ObjectId(&bigBox.shape()), ObjectShapes(bigBox.instance(), objectTransform),
btTransform::getIdentity(), nullptr); btTransform::getIdentity(), nullptr);
@ -888,7 +891,8 @@ namespace
std::mutex mutex; std::mutex mutex;
std::thread thread([&] { std::thread thread([&] {
mNavigator->update(mPlayerPosition, nullptr); auto guard = mNavigator->makeUpdateGuard();
mNavigator->update(playerPosition, guard.get());
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
updated = true; updated = true;
updateFinished.notify_all(); updateFinished.notify_all();
@ -896,7 +900,7 @@ namespace
{ {
std::unique_lock lock(mutex); std::unique_lock lock(mutex);
updateFinished.wait_for(lock, std::chrono::seconds(3), [&] { return updated; }); updateFinished.wait_for(lock, std::chrono::seconds(10), [&] { return updated; });
ASSERT_TRUE(updated); ASSERT_TRUE(updated);
} }
@ -904,14 +908,69 @@ namespace
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_EQ(mNavigator->getRecastMeshTiles().size(), 509); const auto recastMeshTiles = mNavigator->getRecastMeshTiles();
ASSERT_EQ(recastMeshTiles.size(), 1033);
EXPECT_EQ(getMinMax(recastMeshTiles), std::pair(TilePosition(-18, -17), TilePosition(18, 19)));
const auto navMesh = mNavigator->getNavMesh(mAgentBounds); const auto navMesh = mNavigator->getNavMesh(mAgentBounds);
ASSERT_NE(navMesh, nullptr); ASSERT_NE(navMesh, nullptr);
std::size_t usedNavMeshTiles = 0; std::size_t usedNavMeshTiles = 0;
navMesh->lockConst()->forEachUsedTile([&](const auto&...) { ++usedNavMeshTiles; }); navMesh->lockConst()->forEachUsedTile([&](const auto&...) { ++usedNavMeshTiles; });
EXPECT_EQ(usedNavMeshTiles, 509); EXPECT_EQ(usedNavMeshTiles, 1024);
}
TEST_F(DetourNavigatorNavigatorTest, update_should_be_limited_by_cell_grid_bounds)
{
const float size = static_cast<float>((1 << 22) - 1);
CollisionShapeInstance bigBox(std::make_unique<btBoxShape>(btVector3(size, size, 1)));
const ObjectTransform objectTransform{
.mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } },
.mScale = 1.0f,
};
const CellGridBounds cellGridBounds{
.mCenter = osg::Vec2i(0, 0),
.mHalfSize = 1,
};
const osg::Vec3f playerPosition(32, 1024, 0);
mNavigator->updateBounds(mWorldspace, cellGridBounds, playerPosition, nullptr);
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addObject(ObjectId(&bigBox.shape()), ObjectShapes(bigBox.instance(), objectTransform),
btTransform::getIdentity(), nullptr);
bool updated = false;
std::condition_variable updateFinished;
std::mutex mutex;
std::thread thread([&] {
auto guard = mNavigator->makeUpdateGuard();
mNavigator->update(playerPosition, guard.get());
std::lock_guard lock(mutex);
updated = true;
updateFinished.notify_all();
});
{
std::unique_lock lock(mutex);
updateFinished.wait_for(lock, std::chrono::seconds(10), [&] { return updated; });
ASSERT_TRUE(updated);
}
thread.join();
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
const auto recastMeshTiles = mNavigator->getRecastMeshTiles();
ASSERT_EQ(recastMeshTiles.size(), 854);
EXPECT_EQ(getMinMax(recastMeshTiles), std::pair(TilePosition(-12, -12), TilePosition(18, 19)));
const auto navMesh = mNavigator->getNavMesh(mAgentBounds);
ASSERT_NE(navMesh, nullptr);
std::size_t usedNavMeshTiles = 0;
navMesh->lockConst()->forEachUsedTile([&](const auto&...) { ++usedNavMeshTiles; });
EXPECT_EQ(usedNavMeshTiles, 854);
} }
struct DetourNavigatorNavigatorNotSupportedAgentBoundsTest : TestWithParam<AgentBounds> struct DetourNavigatorNavigatorNotSupportedAgentBoundsTest : TestWithParam<AgentBounds>

View file

@ -8,7 +8,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <limits> #include <limits>
#include <numeric>
#include <random> #include <random>
namespace namespace
@ -19,7 +18,7 @@ namespace
struct Tile struct Tile
{ {
std::string mWorldspace; ESM::RefId mWorldspace;
TilePosition mTilePosition; TilePosition mTilePosition;
std::vector<std::byte> mInput; std::vector<std::byte> mInput;
std::vector<std::byte> mData; std::vector<std::byte> mData;
@ -39,12 +38,12 @@ namespace
Tile insertTile(TileId tileId, TileVersion version) Tile insertTile(TileId tileId, TileVersion version)
{ {
std::string worldspace = "sys::default"; const ESM::RefId worldspace = ESM::RefId::stringRefId("sys::default");
const TilePosition tilePosition{ 3, 4 }; const TilePosition tilePosition{ 3, 4 };
std::vector<std::byte> input = generateData(); std::vector<std::byte> input = generateData();
std::vector<std::byte> data = generateData(); std::vector<std::byte> data = generateData();
EXPECT_EQ(mDb.insertTile(tileId, worldspace, tilePosition, version, input, data), 1); EXPECT_EQ(mDb.insertTile(tileId, worldspace, tilePosition, version, input, data), 1);
return { std::move(worldspace), tilePosition, std::move(input), std::move(data) }; return { worldspace, tilePosition, std::move(input), std::move(data) };
} }
}; };
@ -89,7 +88,7 @@ namespace
{ {
const TileId tileId{ 53 }; const TileId tileId{ 53 };
const TileVersion version{ 1 }; const TileVersion version{ 1 };
const std::string worldspace = "sys::default"; const ESM::RefId worldspace = ESM::RefId::stringRefId("sys::default");
const TilePosition tilePosition{ 3, 4 }; const TilePosition tilePosition{ 3, 4 };
const std::vector<std::byte> input = generateData(); const std::vector<std::byte> input = generateData();
const std::vector<std::byte> data = generateData(); const std::vector<std::byte> data = generateData();
@ -101,7 +100,7 @@ namespace
{ {
const TileId tileId{ 53 }; const TileId tileId{ 53 };
const TileVersion version{ 1 }; const TileVersion version{ 1 };
const std::string worldspace = "sys::default"; const ESM::RefId worldspace = ESM::RefId::stringRefId("sys::default");
const TilePosition tilePosition{ 3, 4 }; const TilePosition tilePosition{ 3, 4 };
const std::vector<std::byte> input = generateData(); const std::vector<std::byte> input = generateData();
const std::vector<std::byte> data = generateData(); const std::vector<std::byte> data = generateData();
@ -113,7 +112,7 @@ namespace
TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_at_should_remove_all_tiles_with_given_worldspace_and_position) TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_at_should_remove_all_tiles_with_given_worldspace_and_position)
{ {
const TileVersion version{ 1 }; const TileVersion version{ 1 };
const std::string worldspace = "sys::default"; const ESM::RefId worldspace = ESM::RefId::stringRefId("sys::default");
const TilePosition tilePosition{ 3, 4 }; const TilePosition tilePosition{ 3, 4 };
const std::vector<std::byte> input1 = generateData(); const std::vector<std::byte> input1 = generateData();
const std::vector<std::byte> input2 = generateData(); const std::vector<std::byte> input2 = generateData();
@ -130,7 +129,7 @@ namespace
const TileId leftTileId{ 53 }; const TileId leftTileId{ 53 };
const TileId removedTileId{ 54 }; const TileId removedTileId{ 54 };
const TileVersion version{ 1 }; const TileVersion version{ 1 };
const std::string worldspace = "sys::default"; const ESM::RefId worldspace = ESM::RefId::stringRefId("sys::default");
const TilePosition tilePosition{ 3, 4 }; const TilePosition tilePosition{ 3, 4 };
const std::vector<std::byte> leftInput = generateData(); const std::vector<std::byte> leftInput = generateData();
const std::vector<std::byte> removedInput = generateData(); const std::vector<std::byte> removedInput = generateData();
@ -148,7 +147,7 @@ namespace
{ {
TileId tileId{ 1 }; TileId tileId{ 1 };
const TileVersion version{ 1 }; const TileVersion version{ 1 };
const std::string worldspace = "sys::default"; const ESM::RefId worldspace = ESM::RefId::stringRefId("sys::default");
const std::vector<std::byte> input = generateData(); const std::vector<std::byte> input = generateData();
const std::vector<std::byte> data = generateData(); const std::vector<std::byte> data = generateData();
for (int x = -2; x <= 2; ++x) for (int x = -2; x <= 2; ++x)

View file

@ -39,7 +39,7 @@ namespace DetourNavigator
result.mDetour.mMaxPolygonPathSize = 1024; result.mDetour.mMaxPolygonPathSize = 1024;
result.mDetour.mMaxSmoothPathSize = 1024; result.mDetour.mMaxSmoothPathSize = 1024;
result.mDetour.mMaxPolys = 4096; result.mDetour.mMaxPolys = 4096;
result.mMaxTilesNumber = 512; result.mMaxTilesNumber = 1024;
result.mMinUpdateInterval = std::chrono::milliseconds(50); result.mMinUpdateInterval = std::chrono::milliseconds(50);
result.mWriteToNavMeshDb = true; result.mWriteToNavMeshDb = true;
return result; return result;

View file

@ -21,6 +21,7 @@ namespace
const ObjectTransform mObjectTransform{ ESM::Position{ { 0, 0, 0 }, { 0, 0, 0 } }, 0.0f }; const ObjectTransform mObjectTransform{ ESM::Position{ { 0, 0, 0 }, { 0, 0, 0 } }, 0.0f };
const osg::ref_ptr<const Resource::BulletShape> mShape = new Resource::BulletShape; const osg::ref_ptr<const Resource::BulletShape> mShape = new Resource::BulletShape;
const osg::ref_ptr<const Resource::BulletShapeInstance> mInstance = new Resource::BulletShapeInstance(mShape); const osg::ref_ptr<const Resource::BulletShapeInstance> mInstance = new Resource::BulletShapeInstance(mShape);
const ESM::RefId mWorldspace = ESM::RefId::stringRefId("worldspace");
DetourNavigatorTileCachedRecastMeshManagerTest() DetourNavigatorTileCachedRecastMeshManagerTest()
{ {
@ -34,7 +35,7 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_empty_should_return_nullptr) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_empty_should_return_nullptr)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(0, 0)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_for_empty_should_return_zero) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_for_empty_should_return_zero)
@ -65,14 +66,14 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject( ASSERT_TRUE(manager.addObject(
ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
for (int x = -1; x < 1; ++x) for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y) for (int y = -1; y < 1; ++y)
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_NE(manager.getMesh(mWorldspace, TilePosition(x, y)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_return_add_changed_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_return_add_changed_tiles)
@ -145,25 +146,25 @@ namespace
get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile) get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, 0)), nullptr);
} }
TEST_F( TEST_F(
DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_nullptr_for_unused_tile) DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_nullptr_for_unused_tile)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(1, 0)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest,
@ -172,10 +173,10 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const TilesPositionsRange range{ const TilesPositionsRange range{
.mBegin = TilePosition(-1, -1), .mBegin = TilePosition(-1, -1),
.mEnd = TilePosition(1, 1), .mEnd = TilePosition(2, 2),
}; };
manager.setRange(range, nullptr); manager.setRange(range, nullptr);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform( const btTransform transform(
@ -183,23 +184,23 @@ namespace
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(1, -1)), nullptr);
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, 0)), nullptr);
} }
TEST_F( TEST_F(
DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_nullptr_for_unused_tile) DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_nullptr_for_unused_tile)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform( const btTransform transform(
@ -207,48 +208,48 @@ namespace
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(-1, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(1, -1)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest,
get_mesh_for_removed_object_should_return_nullptr_for_all_previously_used_tiles) get_mesh_for_removed_object_should_return_nullptr_for_all_previously_used_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
manager.removeObject(ObjectId(&boxShape), nullptr); manager.removeObject(ObjectId(&boxShape), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(-1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(0, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_EQ(manager.getMesh(mWorldspace, TilePosition(0, 0)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest,
get_mesh_for_not_changed_object_after_update_should_return_recast_mesh_for_same_tiles) get_mesh_for_not_changed_object_after_update_should_return_recast_mesh_for_same_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(mWorldspace, TilePosition(0, 0)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest,
@ -293,7 +294,7 @@ namespace
get_revision_after_update_not_changed_object_should_return_same_value) get_revision_after_update_not_changed_object_should_return_same_value)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
@ -339,19 +340,19 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
manager.addWater(cellPosition, cellSize, 0.0f, nullptr); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
for (int x = -1; x < 12; ++x) for (int x = -1; x < 12; ++x)
for (int y = -1; y < 12; ++y) for (int y = -1; y < 12; ++y)
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_NE(manager.getMesh(mWorldspace, TilePosition(x, y)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject( ASSERT_TRUE(manager.addObject(
@ -361,7 +362,7 @@ namespace
manager.addWater(cellPosition, cellSize, 0.0f, nullptr); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
for (int x = -6; x < 6; ++x) for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y) for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, ASSERT_EQ(manager.getMesh(mWorldspace, TilePosition(x, y)) != nullptr,
-1 <= x && x <= 0 && -1 <= y && y <= 0); -1 <= x && x <= 0 && -1 <= y && y <= 0);
} }
@ -390,20 +391,20 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
manager.addWater(cellPosition, cellSize, 0.0f, nullptr); manager.addWater(cellPosition, cellSize, 0.0f, nullptr);
manager.removeWater(cellPosition, nullptr); manager.removeWater(cellPosition, nullptr);
for (int x = -6; x < 6; ++x) for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y) for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_EQ(manager.getMesh(mWorldspace, TilePosition(x, y)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject( ASSERT_TRUE(manager.addObject(
@ -414,14 +415,14 @@ namespace
manager.removeWater(cellPosition, nullptr); manager.removeWater(cellPosition, nullptr);
for (int x = -6; x < 6; ++x) for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y) for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, ASSERT_EQ(manager.getMesh(mWorldspace, TilePosition(x, y)) != nullptr,
-1 <= x && x <= 0 && -1 <= y && y <= 0); -1 <= x && x <= 0 && -1 <= y && y <= 0);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_not_remove_tile_with_water) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_not_remove_tile_with_water)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
@ -432,24 +433,25 @@ namespace
manager.removeObject(ObjectId(&boxShape), nullptr); manager.removeObject(ObjectId(&boxShape), nullptr);
for (int x = -1; x < 12; ++x) for (int x = -1; x < 12; ++x)
for (int y = -1; y < 12; ++y) for (int y = -1; y < 12; ++y)
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_NE(manager.getMesh(mWorldspace, TilePosition(x, y)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_new_worldspace_should_remove_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_new_worldspace_should_remove_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace", nullptr); manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(nullptr, boxShape, mObjectTransform); const CollisionShape shape(nullptr, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject( ASSERT_TRUE(manager.addObject(
ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr));
manager.setWorldspace("other", nullptr); const ESM::RefId otherWorldspace(ESM::FormId::fromUint32(0x1));
manager.setWorldspace(ESM::FormId::fromUint32(0x1), nullptr);
for (int x = -1; x < 1; ++x) for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y) for (int y = -1; y < 1; ++y)
ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr); ASSERT_EQ(manager.getMesh(otherWorldspace, TilePosition(x, y)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_bounds_should_add_changed_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_range_should_add_changed_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
@ -470,4 +472,35 @@ namespace
ElementsAre( ElementsAre(
std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(0, 0), ChangeType::remove))); std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(0, 0), ChangeType::remove)));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_range_should_remove_cached_recast_meshes_outside_range)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace(mWorldspace, nullptr);
const btBoxShape boxShape(btVector3(100, 100, 20));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
const TilesPositionsRange range1{
.mBegin = TilePosition(0, 0),
.mEnd = TilePosition(1, 1),
};
manager.setRange(range1, nullptr);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr);
const TilePosition tilePosition(0, 0);
ASSERT_EQ(manager.getCachedMesh(mWorldspace, tilePosition), nullptr);
ASSERT_NE(manager.getMesh(mWorldspace, tilePosition), nullptr);
ASSERT_NE(manager.getCachedMesh(mWorldspace, tilePosition), nullptr);
const TilesPositionsRange range2{
.mBegin = TilePosition(-1, -1),
.mEnd = TilePosition(0, 0),
};
manager.takeChangedTiles(nullptr);
manager.setRange(range2, nullptr);
ASSERT_EQ(manager.getCachedMesh(mWorldspace, tilePosition), nullptr);
}
} }

View file

@ -91,7 +91,7 @@ namespace DetourNavigator
} }
Job::Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem, Job::Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, ESM::RefId worldspace, const TilePosition& changedTile, ChangeType changeType,
std::chrono::steady_clock::time_point processTime) std::chrono::steady_clock::time_point processTime)
: mId(getNextJobId()) : mId(getNextJobId())
, mAgentBounds(agentBounds) , mAgentBounds(agentBounds)
@ -261,8 +261,7 @@ namespace DetourNavigator
} }
void AsyncNavMeshUpdater::post(const AgentBounds& agentBounds, const SharedNavMeshCacheItem& navMeshCacheItem, void AsyncNavMeshUpdater::post(const AgentBounds& agentBounds, const SharedNavMeshCacheItem& navMeshCacheItem,
const TilePosition& playerTile, std::string_view worldspace, const TilePosition& playerTile, ESM::RefId worldspace, const std::map<TilePosition, ChangeType>& changedTiles)
const std::map<TilePosition, ChangeType>& changedTiles)
{ {
bool playerTileChanged = false; bool playerTileChanged = false;
{ {

View file

@ -50,7 +50,7 @@ namespace DetourNavigator
const std::size_t mId; const std::size_t mId;
const AgentBounds mAgentBounds; const AgentBounds mAgentBounds;
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem; const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
const std::string mWorldspace; const ESM::RefId mWorldspace;
const TilePosition mChangedTile; const TilePosition mChangedTile;
std::chrono::steady_clock::time_point mProcessTime; std::chrono::steady_clock::time_point mProcessTime;
ChangeType mChangeType; ChangeType mChangeType;
@ -61,7 +61,7 @@ namespace DetourNavigator
std::unique_ptr<PreparedNavMeshData> mGeneratedNavMeshData; std::unique_ptr<PreparedNavMeshData> mGeneratedNavMeshData;
Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem, Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, ESM::RefId worldspace, const TilePosition& changedTile, ChangeType changeType,
std::chrono::steady_clock::time_point processTime); std::chrono::steady_clock::time_point processTime);
}; };
@ -199,7 +199,7 @@ namespace DetourNavigator
~AsyncNavMeshUpdater(); ~AsyncNavMeshUpdater();
void post(const AgentBounds& agentBounds, const SharedNavMeshCacheItem& navMeshCacheItem, void post(const AgentBounds& agentBounds, const SharedNavMeshCacheItem& navMeshCacheItem,
const TilePosition& playerTile, std::string_view worldspace, const TilePosition& playerTile, ESM::RefId worldspace,
const std::map<TilePosition, ChangeType>& changedTiles); const std::map<TilePosition, ChangeType>& changedTiles);
void wait(WaitConditionType waitConditionType, Loading::Listener* listener); void wait(WaitConditionType waitConditionType, Loading::Listener* listener);

View file

@ -0,0 +1,15 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CELLGRIDBOUNDS_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CELLGRIDBOUNDS_H
#include <osg/Vec2i>
namespace DetourNavigator
{
struct CellGridBounds
{
osg::Vec2i mCenter;
int mHalfSize;
};
}
#endif

View file

@ -22,7 +22,7 @@ namespace DetourNavigator
{ {
struct Ignore struct Ignore
{ {
std::string_view mWorldspace; ESM::RefId mWorldspace;
const TilePosition& mTilePosition; const TilePosition& mTilePosition;
std::shared_ptr<NavMeshTileConsumer> mConsumer; std::shared_ptr<NavMeshTileConsumer> mConsumer;
@ -34,10 +34,10 @@ namespace DetourNavigator
}; };
} }
GenerateNavMeshTile::GenerateNavMeshTile(std::string worldspace, const TilePosition& tilePosition, GenerateNavMeshTile::GenerateNavMeshTile(ESM::RefId worldspace, const TilePosition& tilePosition,
RecastMeshProvider recastMeshProvider, const AgentBounds& agentBounds, RecastMeshProvider recastMeshProvider, const AgentBounds& agentBounds,
const DetourNavigator::Settings& settings, std::weak_ptr<NavMeshTileConsumer> consumer) const DetourNavigator::Settings& settings, std::weak_ptr<NavMeshTileConsumer> consumer)
: mWorldspace(std::move(worldspace)) : mWorldspace(worldspace)
, mTilePosition(tilePosition) , mTilePosition(tilePosition)
, mRecastMeshProvider(recastMeshProvider) , mRecastMeshProvider(recastMeshProvider)
, mAgentBounds(agentBounds) , mAgentBounds(agentBounds)

View file

@ -35,18 +35,18 @@ namespace DetourNavigator
virtual std::int64_t resolveMeshSource(const MeshSource& source) = 0; virtual std::int64_t resolveMeshSource(const MeshSource& source) = 0;
virtual std::optional<NavMeshTileInfo> find( virtual std::optional<NavMeshTileInfo> find(
std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) ESM::RefId worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input)
= 0; = 0;
virtual void ignore(std::string_view worldspace, const TilePosition& tilePosition) = 0; virtual void ignore(ESM::RefId worldspace, const TilePosition& tilePosition) = 0;
virtual void identity(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t tileId) = 0; virtual void identity(ESM::RefId worldspace, const TilePosition& tilePosition, std::int64_t tileId) = 0;
virtual void insert(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t version, virtual void insert(ESM::RefId worldspace, const TilePosition& tilePosition, std::int64_t version,
const std::vector<std::byte>& input, PreparedNavMeshData& data) const std::vector<std::byte>& input, PreparedNavMeshData& data)
= 0; = 0;
virtual void update(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t tileId, virtual void update(ESM::RefId worldspace, const TilePosition& tilePosition, std::int64_t tileId,
std::int64_t version, PreparedNavMeshData& data) std::int64_t version, PreparedNavMeshData& data)
= 0; = 0;
@ -56,14 +56,14 @@ namespace DetourNavigator
class GenerateNavMeshTile final : public SceneUtil::WorkItem class GenerateNavMeshTile final : public SceneUtil::WorkItem
{ {
public: public:
GenerateNavMeshTile(std::string worldspace, const TilePosition& tilePosition, GenerateNavMeshTile(ESM::RefId worldspace, const TilePosition& tilePosition,
RecastMeshProvider recastMeshProvider, const AgentBounds& agentBounds, const Settings& settings, RecastMeshProvider recastMeshProvider, const AgentBounds& agentBounds, const Settings& settings,
std::weak_ptr<NavMeshTileConsumer> consumer); std::weak_ptr<NavMeshTileConsumer> consumer);
void doWork() final; void doWork() final;
private: private:
const std::string mWorldspace; const ESM::RefId mWorldspace;
const TilePosition mTilePosition; const TilePosition mTilePosition;
const RecastMeshProvider mRecastMeshProvider; const RecastMeshProvider mRecastMeshProvider;
const AgentBounds mAgentBounds; const AgentBounds mAgentBounds;

View file

@ -520,9 +520,8 @@ namespace DetourNavigator
} }
} }
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, ESM::RefId worldspace,
std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds, const TilePosition& tilePosition, const AgentBounds& agentBounds, const RecastSettings& settings)
const RecastSettings& settings)
{ {
RecastContext context(worldspace, tilePosition, agentBounds); RecastContext context(worldspace, tilePosition, agentBounds);

View file

@ -4,6 +4,8 @@
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
#include <components/esm/refid.hpp>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -41,9 +43,8 @@ namespace DetourNavigator
&& recastMesh.getHeightfields().empty() && recastMesh.getFlatHeightfields().empty(); && recastMesh.getHeightfields().empty() && recastMesh.getFlatHeightfields().empty();
} }
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, ESM::RefId worldspace,
std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds, const TilePosition& tilePosition, const AgentBounds& agentBounds, const RecastSettings& settings);
const RecastSettings& settings);
NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data, NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data,
const std::vector<OffMeshConnection>& offMeshConnections, const AgentBounds& agentBounds, const std::vector<OffMeshConnection>& offMeshConnections, const AgentBounds& agentBounds,

View file

@ -3,7 +3,9 @@
#include <cassert> #include <cassert>
#include <filesystem> #include <filesystem>
#include <optional>
#include "cellgridbounds.hpp"
#include "heightfieldshape.hpp" #include "heightfieldshape.hpp"
#include "objectid.hpp" #include "objectid.hpp"
#include "objecttransform.hpp" #include "objecttransform.hpp"
@ -12,6 +14,7 @@
#include "updateguard.hpp" #include "updateguard.hpp"
#include "waitconditiontype.hpp" #include "waitconditiontype.hpp"
#include <components/esm/refid.hpp>
#include <components/resource/bulletshape.hpp> #include <components/resource/bulletshape.hpp>
namespace ESM namespace ESM
@ -87,17 +90,10 @@ namespace DetourNavigator
*/ */
virtual void removeAgent(const AgentBounds& agentBounds) = 0; virtual void removeAgent(const AgentBounds& agentBounds) = 0;
/** // Updates bounds for recast mesh and navmesh tiles, removes tiles outside the range.
* @brief setWorldspace should be called before adding object from new worldspace virtual void updateBounds(ESM::RefId worldspace, const std::optional<CellGridBounds>& cellGridBounds,
* @param worldspace const osg::Vec3f& playerPosition, const UpdateGuard* guard)
*/ = 0;
virtual void setWorldspace(std::string_view worldspace, const UpdateGuard* guard) = 0;
/**
* @brief updateBounds should be called before adding object from loading cell
* @param playerPosition corresponds to the bounds center
*/
virtual void updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard) = 0;
/** /**
* @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes * @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes

View file

@ -33,14 +33,10 @@ namespace DetourNavigator
--it->second; --it->second;
} }
void NavigatorImpl::setWorldspace(std::string_view worldspace, const UpdateGuard* guard) void NavigatorImpl::updateBounds(ESM::RefId worldspace, const std::optional<CellGridBounds>& cellGridBounds,
const osg::Vec3f& playerPosition, const UpdateGuard* guard)
{ {
mNavMeshManager.setWorldspace(worldspace, guard); mNavMeshManager.updateBounds(worldspace, cellGridBounds, playerPosition, guard);
}
void NavigatorImpl::updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard)
{
mNavMeshManager.updateBounds(playerPosition, guard);
} }
void NavigatorImpl::addObject( void NavigatorImpl::addObject(

View file

@ -8,7 +8,6 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string_view>
#include <unordered_map> #include <unordered_map>
namespace DetourNavigator namespace DetourNavigator
@ -28,9 +27,8 @@ namespace DetourNavigator
void removeAgent(const AgentBounds& agentBounds) override; void removeAgent(const AgentBounds& agentBounds) override;
void setWorldspace(std::string_view worldspace, const UpdateGuard* guard) override; void updateBounds(ESM::RefId worldspace, const std::optional<CellGridBounds>& cellGridBounds,
const osg::Vec3f& playerPosition, const UpdateGuard* guard) override;
void updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard) override;
void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform,
const UpdateGuard* guard) override; const UpdateGuard* guard) override;

View file

@ -24,7 +24,10 @@ namespace DetourNavigator
void removeAgent(const AgentBounds& /*agentBounds*/) override {} void removeAgent(const AgentBounds& /*agentBounds*/) override {}
void setWorldspace(std::string_view /*worldspace*/, const UpdateGuard* /*guard*/) override {} void updateBounds(ESM::RefId /*worldspace*/, const std::optional<CellGridBounds>& /*cellGridBounds*/,
const osg::Vec3f& /*playerPosition*/, const UpdateGuard* /*guard*/) override
{
}
void addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/, void addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/,
const UpdateGuard* /*guard*/) override const UpdateGuard* /*guard*/) override
@ -68,8 +71,6 @@ namespace DetourNavigator
void update(const osg::Vec3f& /*playerPosition*/, const UpdateGuard* /*guard*/) override {} void update(const osg::Vec3f& /*playerPosition*/, const UpdateGuard* /*guard*/) override {}
void updateBounds(const osg::Vec3f& /*playerPosition*/, const UpdateGuard* /*guard*/) override {}
void wait(WaitConditionType /*waitConditionType*/, Loading::Listener* /*listener*/) override {} void wait(WaitConditionType /*waitConditionType*/, Loading::Listener* /*listener*/) override {}
SharedNavMeshCacheItem getNavMesh(const AgentBounds& /*agentBounds*/) const override SharedNavMeshCacheItem getNavMesh(const AgentBounds& /*agentBounds*/) const override

View file

@ -200,34 +200,35 @@ namespace DetourNavigator
} }
std::optional<Tile> NavMeshDb::findTile( std::optional<Tile> NavMeshDb::findTile(
std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) ESM::RefId worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input)
{ {
Tile result; Tile result;
auto row = std::tie(result.mTileId, result.mVersion); auto row = std::tie(result.mTileId, result.mVersion);
const std::vector<std::byte> compressedInput = Misc::compress(input); const std::vector<std::byte> compressedInput = Misc::compress(input);
if (&row == request(*mDb, mFindTile, &row, 1, worldspace, tilePosition, compressedInput)) if (&row == request(*mDb, mFindTile, &row, 1, worldspace.serializeText(), tilePosition, compressedInput))
return {}; return {};
return result; return result;
} }
std::optional<TileData> NavMeshDb::getTileData( std::optional<TileData> NavMeshDb::getTileData(
std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) ESM::RefId worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input)
{ {
TileData result; TileData result;
auto row = std::tie(result.mTileId, result.mVersion, result.mData); auto row = std::tie(result.mTileId, result.mVersion, result.mData);
const std::vector<std::byte> compressedInput = Misc::compress(input); const std::vector<std::byte> compressedInput = Misc::compress(input);
if (&row == request(*mDb, mGetTileData, &row, 1, worldspace, tilePosition, compressedInput)) if (&row == request(*mDb, mGetTileData, &row, 1, worldspace.serializeText(), tilePosition, compressedInput))
return {}; return {};
result.mData = Misc::decompress(result.mData); result.mData = Misc::decompress(result.mData);
return result; return result;
} }
int NavMeshDb::insertTile(TileId tileId, std::string_view worldspace, const TilePosition& tilePosition, int NavMeshDb::insertTile(TileId tileId, ESM::RefId worldspace, const TilePosition& tilePosition,
TileVersion version, const std::vector<std::byte>& input, const std::vector<std::byte>& data) TileVersion version, const std::vector<std::byte>& input, const std::vector<std::byte>& data)
{ {
const std::vector<std::byte> compressedInput = Misc::compress(input); const std::vector<std::byte> compressedInput = Misc::compress(input);
const std::vector<std::byte> compressedData = Misc::compress(data); const std::vector<std::byte> compressedData = Misc::compress(data);
return execute(*mDb, mInsertTile, tileId, worldspace, tilePosition, version, compressedInput, compressedData); return execute(*mDb, mInsertTile, tileId, worldspace.serializeText(), tilePosition, version, compressedInput,
compressedData);
} }
int NavMeshDb::updateTile(TileId tileId, TileVersion version, const std::vector<std::byte>& data) int NavMeshDb::updateTile(TileId tileId, TileVersion version, const std::vector<std::byte>& data)
@ -236,20 +237,19 @@ namespace DetourNavigator
return execute(*mDb, mUpdateTile, tileId, version, compressedData); return execute(*mDb, mUpdateTile, tileId, version, compressedData);
} }
int NavMeshDb::deleteTilesAt(std::string_view worldspace, const TilePosition& tilePosition) int NavMeshDb::deleteTilesAt(ESM::RefId worldspace, const TilePosition& tilePosition)
{ {
return execute(*mDb, mDeleteTilesAt, worldspace, tilePosition); return execute(*mDb, mDeleteTilesAt, worldspace.serializeText(), tilePosition);
} }
int NavMeshDb::deleteTilesAtExcept( int NavMeshDb::deleteTilesAtExcept(ESM::RefId worldspace, const TilePosition& tilePosition, TileId excludeTileId)
std::string_view worldspace, const TilePosition& tilePosition, TileId excludeTileId)
{ {
return execute(*mDb, mDeleteTilesAtExcept, worldspace, tilePosition, excludeTileId); return execute(*mDb, mDeleteTilesAtExcept, worldspace.serializeText(), tilePosition, excludeTileId);
} }
int NavMeshDb::deleteTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range) int NavMeshDb::deleteTilesOutsideRange(ESM::RefId worldspace, const TilesPositionsRange& range)
{ {
return execute(*mDb, mDeleteTilesOutsideRange, worldspace, range); return execute(*mDb, mDeleteTilesOutsideRange, worldspace.serializeText(), range);
} }
ShapeId NavMeshDb::getMaxShapeId() ShapeId NavMeshDb::getMaxShapeId()

View file

@ -2,10 +2,10 @@
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHDB_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHDB_H
#include "tileposition.hpp" #include "tileposition.hpp"
#include "tilespositionsrange.hpp"
#include <components/detournavigator/tilespositionsrange.hpp> #include <components/esm/refid.hpp>
#include <components/misc/strongtypedef.hpp> #include <components/misc/strongtypedef.hpp>
#include <components/sqlite3/db.hpp> #include <components/sqlite3/db.hpp>
#include <components/sqlite3/statement.hpp> #include <components/sqlite3/statement.hpp>
#include <components/sqlite3/transaction.hpp> #include <components/sqlite3/transaction.hpp>
@ -13,13 +13,8 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <memory>
#include <optional> #include <optional>
#include <stdexcept>
#include <string_view> #include <string_view>
#include <tuple>
#include <utility>
#include <vector> #include <vector>
struct sqlite3; struct sqlite3;
@ -148,21 +143,21 @@ namespace DetourNavigator
TileId getMaxTileId(); TileId getMaxTileId();
std::optional<Tile> findTile( std::optional<Tile> findTile(
std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input); ESM::RefId worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input);
std::optional<TileData> getTileData( std::optional<TileData> getTileData(
std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input); ESM::RefId worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input);
int insertTile(TileId tileId, std::string_view worldspace, const TilePosition& tilePosition, int insertTile(TileId tileId, ESM::RefId worldspace, const TilePosition& tilePosition, TileVersion version,
TileVersion version, const std::vector<std::byte>& input, const std::vector<std::byte>& data); const std::vector<std::byte>& input, const std::vector<std::byte>& data);
int updateTile(TileId tileId, TileVersion version, const std::vector<std::byte>& data); int updateTile(TileId tileId, TileVersion version, const std::vector<std::byte>& data);
int deleteTilesAt(std::string_view worldspace, const TilePosition& tilePosition); int deleteTilesAt(ESM::RefId worldspace, const TilePosition& tilePosition);
int deleteTilesAtExcept(std::string_view worldspace, const TilePosition& tilePosition, TileId excludeTileId); int deleteTilesAtExcept(ESM::RefId worldspace, const TilePosition& tilePosition, TileId excludeTileId);
int deleteTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range); int deleteTilesOutsideRange(ESM::RefId worldspace, const TilesPositionsRange& range);
ShapeId getMaxShapeId(); ShapeId getMaxShapeId();

View file

@ -1,4 +1,5 @@
#include "navmeshmanager.hpp" #include "navmeshmanager.hpp"
#include "debug.hpp" #include "debug.hpp"
#include "gettilespositions.hpp" #include "gettilespositions.hpp"
#include "makenavmesh.hpp" #include "makenavmesh.hpp"
@ -8,6 +9,7 @@
#include "waitconditiontype.hpp" #include "waitconditiontype.hpp"
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/esm/util.hpp>
#include <osg/io_utils> #include <osg/io_utils>
@ -35,15 +37,46 @@ namespace DetourNavigator
{ {
namespace namespace
{ {
TilesPositionsRange makeRange(const TilePosition& center, int maxTiles) int getMaxRadius(int maxTiles)
{
return static_cast<int>(std::ceil(std::sqrt(static_cast<float>(maxTiles) / osg::PIf) + 1));
}
TilesPositionsRange makeRange(const TilePosition& center, int radius)
{ {
const int radius = static_cast<int>(std::ceil(std::sqrt(static_cast<float>(maxTiles) / osg::PIf) + 1));
return TilesPositionsRange{ return TilesPositionsRange{
.mBegin = center - TilePosition(radius, radius), .mBegin = center - TilePosition(radius, radius),
.mEnd = center + TilePosition(radius + 1, radius + 1), .mEnd = center + TilePosition(radius + 1, radius + 1),
}; };
} }
osg::Vec2f getMinCellGridPosition(const osg::Vec2i& center, int offset, float cellSize)
{
const osg::Vec2i cell = center + osg::Vec2i(offset, offset);
return osg::Vec2f(static_cast<float>(cell.x()) * cellSize, static_cast<float>(cell.y()) * cellSize);
}
TilesPositionsRange makeCellGridRange(
const RecastSettings& settings, ESM::RefId worldspace, const CellGridBounds& bounds)
{
const float floatCellSize = static_cast<float>(ESM::getCellSize(worldspace));
const osg::Vec2f min = getMinCellGridPosition(bounds.mCenter, -bounds.mHalfSize, floatCellSize);
const osg::Vec2f max = getMinCellGridPosition(bounds.mCenter, bounds.mHalfSize + 1, floatCellSize);
return TilesPositionsRange{
.mBegin = getTilePosition(settings, toNavMeshCoordinates(settings, min)),
.mEnd = getTilePosition(settings, toNavMeshCoordinates(settings, max)),
};
}
TilesPositionsRange makeRange(const Settings& settings, ESM::RefId worldspace,
const std::optional<CellGridBounds>& bounds, int radius, const TilePosition& center)
{
TilesPositionsRange result = makeRange(center, radius);
if (bounds.has_value())
result = getIntersection(result, makeCellGridRange(settings.mRecast, worldspace, *bounds));
return result;
}
TilePosition toNavMeshTilePosition(const RecastSettings& settings, const osg::Vec3f& position) TilePosition toNavMeshTilePosition(const RecastSettings& settings, const osg::Vec3f& position)
{ {
return getTilePosition(settings, toNavMeshCoordinates(settings, position)); return getTilePosition(settings, toNavMeshCoordinates(settings, position));
@ -52,27 +85,28 @@ namespace DetourNavigator
NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db) NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings) : mSettings(settings)
, mMaxRadius(getMaxRadius(settings.mMaxTilesNumber))
, mRecastMeshManager(settings.mRecast) , mRecastMeshManager(settings.mRecast)
, mOffMeshConnectionsManager(settings.mRecast) , mOffMeshConnectionsManager(settings.mRecast)
, mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db)) , mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db))
{ {
} }
void NavMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard) void NavMeshManager::updateBounds(ESM::RefId worldspace, const std::optional<CellGridBounds>& cellGridBounds,
const osg::Vec3f& playerPosition, const UpdateGuard* guard)
{ {
if (worldspace == mWorldspace) if (worldspace != mWorldspace)
return; {
mRecastMeshManager.setWorldspace(worldspace, guard); mRecastMeshManager.setWorldspace(worldspace, guard);
for (auto& [agent, cache] : mCache) for (auto& [agent, cache] : mCache)
cache = std::make_shared<GuardedNavMeshCacheItem>(++mGenerationCounter, mSettings); cache = std::make_shared<GuardedNavMeshCacheItem>(++mGenerationCounter, mSettings);
mWorldspace = worldspace; mWorldspace = worldspace;
} }
void NavMeshManager::updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard)
{
const TilePosition playerTile = toNavMeshTilePosition(mSettings.mRecast, playerPosition); const TilePosition playerTile = toNavMeshTilePosition(mSettings.mRecast, playerPosition);
const TilesPositionsRange range = makeRange(playerTile, mSettings.mMaxTilesNumber);
mRecastMeshManager.setRange(range, guard); mRecastMeshManager.setRange(makeRange(mSettings, worldspace, cellGridBounds, mMaxRadius, playerTile), guard);
mCellGridBounds = cellGridBounds;
} }
bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
@ -164,7 +198,7 @@ namespace DetourNavigator
return; return;
mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision(); mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision();
mPlayerTile = playerTile; mPlayerTile = playerTile;
mRecastMeshManager.setRange(makeRange(playerTile, mSettings.mMaxTilesNumber), guard); mRecastMeshManager.setRange(makeRange(mSettings, mWorldspace, mCellGridBounds, mMaxRadius, playerTile), guard);
const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard); const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard);
const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange(); const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange();
for (const auto& [agentBounds, cached] : mCache) for (const auto& [agentBounds, cached] : mCache)
@ -217,7 +251,10 @@ namespace DetourNavigator
Stats NavMeshManager::getStats() const Stats NavMeshManager::getStats() const
{ {
return Stats{ .mUpdater = mAsyncNavMeshUpdater.getStats() }; return Stats{
.mUpdater = mAsyncNavMeshUpdater.getStats(),
.mRecast = mRecastMeshManager.getStats(),
};
} }
RecastMeshTiles NavMeshManager::getRecastMeshTiles() const RecastMeshTiles NavMeshManager::getRecastMeshTiles() const

View file

@ -3,6 +3,7 @@
#include "agentbounds.hpp" #include "agentbounds.hpp"
#include "asyncnavmeshupdater.hpp" #include "asyncnavmeshupdater.hpp"
#include "cellgridbounds.hpp"
#include "heightfieldshape.hpp" #include "heightfieldshape.hpp"
#include "offmeshconnectionsmanager.hpp" #include "offmeshconnectionsmanager.hpp"
#include "recastmeshtiles.hpp" #include "recastmeshtiles.hpp"
@ -24,9 +25,8 @@ namespace DetourNavigator
ScopedUpdateGuard makeUpdateGuard() { return mRecastMeshManager.makeUpdateGuard(); } ScopedUpdateGuard makeUpdateGuard() { return mRecastMeshManager.makeUpdateGuard(); }
void setWorldspace(std::string_view worldspace, const UpdateGuard* guard); void updateBounds(ESM::RefId worldspace, const std::optional<CellGridBounds>& cellGridBounds,
const osg::Vec3f& playerPosition, const UpdateGuard* guard);
void updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard);
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType, const UpdateGuard* guard); const AreaType areaType, const UpdateGuard* guard);
@ -67,7 +67,9 @@ namespace DetourNavigator
private: private:
const Settings& mSettings; const Settings& mSettings;
std::string mWorldspace; const int mMaxRadius;
ESM::RefId mWorldspace;
std::optional<CellGridBounds> mCellGridBounds;
TileCachedRecastMeshManager mRecastMeshManager; TileCachedRecastMeshManager mRecastMeshManager;
OffMeshConnectionsManager mOffMeshConnectionsManager; OffMeshConnectionsManager mOffMeshConnectionsManager;
AsyncNavMeshUpdater mAsyncNavMeshUpdater; AsyncNavMeshUpdater mAsyncNavMeshUpdater;

View file

@ -24,7 +24,7 @@ namespace DetourNavigator
} }
std::string formatPrefix( std::string formatPrefix(
std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds) ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Worldspace: " << worldspace << "; tile position: " << tilePosition.x() << ", " stream << "Worldspace: " << worldspace << "; tile position: " << tilePosition.x() << ", "
@ -34,7 +34,7 @@ namespace DetourNavigator
} }
RecastContext::RecastContext( RecastContext::RecastContext(
std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds) ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds)
: mPrefix(formatPrefix(worldspace, tilePosition, agentBounds)) : mPrefix(formatPrefix(worldspace, tilePosition, agentBounds))
{ {
} }

View file

@ -3,6 +3,8 @@
#include "tileposition.hpp" #include "tileposition.hpp"
#include <components/esm/refid.hpp>
#include <string> #include <string>
#include <Recast.h> #include <Recast.h>
@ -14,8 +16,7 @@ namespace DetourNavigator
class RecastContext final : public rcContext class RecastContext final : public rcContext
{ {
public: public:
explicit RecastContext( explicit RecastContext(ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds);
std::string_view worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds);
const std::string& getPrefix() const { return mPrefix; } const std::string& getPrefix() const { return mPrefix; }

View file

@ -20,7 +20,7 @@ namespace DetourNavigator
{ {
} }
std::shared_ptr<RecastMesh> getMesh(std::string_view worldspace, const TilePosition& tilePosition) const std::shared_ptr<RecastMesh> getMesh(ESM::RefId worldspace, const TilePosition& tilePosition) const
{ {
return mImpl.get().getNewMesh(worldspace, tilePosition); return mImpl.get().getNewMesh(worldspace, tilePosition);
} }

View file

@ -32,11 +32,19 @@ namespace DetourNavigator
out.setAttribute(frameNumber, "NavMesh Cache Get", static_cast<double>(stats.mCache.mGetCount)); out.setAttribute(frameNumber, "NavMesh Cache Get", static_cast<double>(stats.mCache.mGetCount));
out.setAttribute(frameNumber, "NavMesh Cache Hit", static_cast<double>(stats.mCache.mHitCount)); out.setAttribute(frameNumber, "NavMesh Cache Hit", static_cast<double>(stats.mCache.mHitCount));
} }
void reportStats(const TileCachedRecastMeshManagerStats& stats, unsigned int frameNumber, osg::Stats& out)
{
out.setAttribute(frameNumber, "NavMesh Recast Tiles", static_cast<double>(stats.mTiles));
out.setAttribute(frameNumber, "NavMesh Recast Objects", static_cast<double>(stats.mObjects));
out.setAttribute(frameNumber, "NavMesh Recast Heightfields", static_cast<double>(stats.mHeightfields));
out.setAttribute(frameNumber, "NavMesh Recast Water", static_cast<double>(stats.mWater));
}
} }
void reportStats(const Stats& stats, unsigned int frameNumber, osg::Stats& out) void reportStats(const Stats& stats, unsigned int frameNumber, osg::Stats& out)
{ {
if (stats.mUpdater.has_value()) reportStats(stats.mUpdater, frameNumber, out);
reportStats(*stats.mUpdater, frameNumber, out); reportStats(stats.mRecast, frameNumber, out);
} }
} }

View file

@ -50,9 +50,18 @@ namespace DetourNavigator
NavMeshTilesCacheStats mCache; NavMeshTilesCacheStats mCache;
}; };
struct TileCachedRecastMeshManagerStats
{
std::size_t mTiles = 0;
std::size_t mObjects = 0;
std::size_t mHeightfields = 0;
std::size_t mWater = 0;
};
struct Stats struct Stats
{ {
std::optional<AsyncNavMeshUpdaterStats> mUpdater; AsyncNavMeshUpdaterStats mUpdater;
TileCachedRecastMeshManagerStats mRecast;
}; };
void reportStats(const Stats& stats, unsigned int frameNumber, osg::Stats& out); void reportStats(const Stats& stats, unsigned int frameNumber, osg::Stats& out);

View file

@ -1,8 +1,10 @@
#include "tilecachedrecastmeshmanager.hpp" #include "tilecachedrecastmeshmanager.hpp"
#include "changetype.hpp" #include "changetype.hpp"
#include "gettilespositions.hpp" #include "gettilespositions.hpp"
#include "recastmeshbuilder.hpp" #include "recastmeshbuilder.hpp"
#include "settingsutils.hpp" #include "settingsutils.hpp"
#include "stats.hpp"
#include "updateguard.hpp" #include "updateguard.hpp"
#include <components/bullethelpers/aabb.hpp> #include <components/bullethelpers/aabb.hpp>
@ -11,7 +13,6 @@
#include <boost/geometry/geometry.hpp> #include <boost/geometry/geometry.hpp>
#include <limits> #include <limits>
#include <vector>
namespace DetourNavigator namespace DetourNavigator
{ {
@ -99,13 +100,17 @@ namespace DetourNavigator
} }
}); });
} }
getTilesPositions(mRange, [&](const TilePosition& v) {
if (!isInTilesPositionsRange(range, v))
mCache.erase(v);
});
} }
const MaybeLockGuard lock(mMutex, guard);
if (changed) if (changed)
{
const MaybeLockGuard lock(mMutex, guard);
++mRevision; ++mRevision;
}
mRange = range; mRange = range;
} }
@ -136,7 +141,7 @@ namespace DetourNavigator
return {}; return {};
} }
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard) void TileCachedRecastMeshManager::setWorldspace(ESM::RefId worldspace, const UpdateGuard* guard)
{ {
const MaybeLockGuard lock(mMutex, guard); const MaybeLockGuard lock(mMutex, guard);
if (mWorldspace == worldspace) if (mWorldspace == worldspace)
@ -345,12 +350,14 @@ namespace DetourNavigator
} }
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh( std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(
std::string_view worldspace, const TilePosition& tilePosition) ESM::RefId worldspace, const TilePosition& tilePosition)
{ {
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
if (mWorldspace != worldspace) if (mWorldspace != worldspace)
return nullptr; return nullptr;
if (!isInTilesPositionsRange(mRange, tilePosition))
return nullptr;
const auto it = mCache.find(tilePosition); const auto it = mCache.find(tilePosition);
if (it != mCache.end() && it->second.mRecastMesh->getVersion() == it->second.mVersion) if (it != mCache.end() && it->second.mRecastMesh->getVersion() == it->second.mVersion)
return it->second.mRecastMesh; return it->second.mRecastMesh;
@ -369,11 +376,13 @@ namespace DetourNavigator
} }
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getCachedMesh( std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getCachedMesh(
std::string_view worldspace, const TilePosition& tilePosition) const ESM::RefId worldspace, const TilePosition& tilePosition) const
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
if (mWorldspace != worldspace) if (mWorldspace != worldspace)
return nullptr; return nullptr;
if (!isInTilesPositionsRange(mRange, tilePosition))
return nullptr;
const auto it = mCache.find(tilePosition); const auto it = mCache.find(tilePosition);
if (it == mCache.end()) if (it == mCache.end())
return nullptr; return nullptr;
@ -381,7 +390,7 @@ namespace DetourNavigator
} }
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getNewMesh( std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getNewMesh(
std::string_view worldspace, const TilePosition& tilePosition) const ESM::RefId worldspace, const TilePosition& tilePosition) const
{ {
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
@ -429,6 +438,17 @@ namespace DetourNavigator
return std::move(mChangedTiles); return std::move(mChangedTiles);
} }
TileCachedRecastMeshManagerStats TileCachedRecastMeshManager::getStats() const
{
const std::lock_guard lock(mMutex);
return TileCachedRecastMeshManagerStats{
.mTiles = mCache.size(),
.mObjects = mObjects.size(),
.mHeightfields = mHeightfields.size(),
.mWater = mWater.size(),
};
}
TileCachedRecastMeshManager::IndexPoint TileCachedRecastMeshManager::makeIndexPoint( TileCachedRecastMeshManager::IndexPoint TileCachedRecastMeshManager::makeIndexPoint(
const TilePosition& tilePosition) const TilePosition& tilePosition)
{ {

View file

@ -25,14 +25,12 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <string>
#include <string_view>
#include <unordered_map> #include <unordered_map>
#include <vector>
namespace DetourNavigator namespace DetourNavigator
{ {
class RecastMesh; class RecastMesh;
struct TileCachedRecastMeshManagerStats;
class TileCachedRecastMeshManager class TileCachedRecastMeshManager
{ {
@ -49,7 +47,7 @@ namespace DetourNavigator
TilesPositionsRange getLimitedObjectsRange() const; TilesPositionsRange getLimitedObjectsRange() const;
void setWorldspace(std::string_view worldspace, const UpdateGuard* guard); void setWorldspace(ESM::RefId worldspace, const UpdateGuard* guard);
bool addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType, bool addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType,
const UpdateGuard* guard); const UpdateGuard* guard);
@ -67,11 +65,11 @@ namespace DetourNavigator
void removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard); void removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard);
std::shared_ptr<RecastMesh> getMesh(std::string_view worldspace, const TilePosition& tilePosition); std::shared_ptr<RecastMesh> getMesh(ESM::RefId worldspace, const TilePosition& tilePosition);
std::shared_ptr<RecastMesh> getCachedMesh(std::string_view worldspace, const TilePosition& tilePosition) const; std::shared_ptr<RecastMesh> getCachedMesh(ESM::RefId worldspace, const TilePosition& tilePosition) const;
std::shared_ptr<RecastMesh> getNewMesh(std::string_view worldspace, const TilePosition& tilePosition) const; std::shared_ptr<RecastMesh> getNewMesh(ESM::RefId worldspace, const TilePosition& tilePosition) const;
std::size_t getRevision() const { return mRevision; } std::size_t getRevision() const { return mRevision; }
@ -81,6 +79,8 @@ namespace DetourNavigator
std::map<osg::Vec2i, ChangeType> takeChangedTiles(const UpdateGuard* guard); std::map<osg::Vec2i, ChangeType> takeChangedTiles(const UpdateGuard* guard);
TileCachedRecastMeshManagerStats getStats() const;
private: private:
struct Report struct Report
{ {
@ -128,7 +128,7 @@ namespace DetourNavigator
const RecastSettings& mSettings; const RecastSettings& mSettings;
TilesPositionsRange mRange; TilesPositionsRange mRange;
std::string mWorldspace; ESM::RefId mWorldspace;
std::unordered_map<ObjectId, std::unique_ptr<ObjectData>> mObjects; std::unordered_map<ObjectId, std::unique_ptr<ObjectData>> mObjects;
boost::geometry::index::rtree<ObjectIndexValue, boost::geometry::index::quadratic<16>> mObjectIndex; boost::geometry::index::rtree<ObjectIndexValue, boost::geometry::index::quadratic<16>> mObjectIndex;
std::map<osg::Vec2i, WaterData> mWater; std::map<osg::Vec2i, WaterData> mWater;

View file

@ -119,6 +119,10 @@ namespace Resource
"NavMesh CachedTiles", "NavMesh CachedTiles",
"NavMesh Cache Get", "NavMesh Cache Get",
"NavMesh Cache Hit", "NavMesh Cache Hit",
"NavMesh Recast Tiles",
"NavMesh Recast Objects",
"NavMesh Recast Heightfields",
"NavMesh Recast Water",
}; };
std::vector<std::string> statNames; std::vector<std::string> statNames;