1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-16 15:29:55 +00:00

Read navmesh tile data from database

When tile is not found in memory cache try to find it in the database.
This commit is contained in:
elsid 2021-07-09 22:51:42 +02:00
parent 953a4c5550
commit c9b8ba7b46
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
32 changed files with 424 additions and 157 deletions

View file

@ -264,6 +264,7 @@ namespace NavMeshTool
{
it = navMeshInputs.emplace(cell.mCellId.mWorldspace,
std::make_unique<WorldspaceNavMeshInput>(cell.mCellId.mWorldspace, settings.mRecast)).first;
it->second->mTileCachedRecastMeshManager.setWorldspace(cell.mCellId.mWorldspace);
}
return *it->second;
} ();

View file

@ -788,7 +788,7 @@ namespace MWMechanics
MWBase::World* world = MWBase::Environment::get().getWorld();
world->getNavigator()->setUpdatesEnabled(mAI);
if (mAI)
world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3());
world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3());
return mAI;
}

View file

@ -350,8 +350,7 @@ namespace MWWorld
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
mNavigator.removePathgrid(*pathgrid);
const auto player = world->getPlayerPtr();
mNavigator.update(player.getRefData().getPosition().asVec3());
mNavigator.update(world->getPlayerPtr().getRefData().getPosition().asVec3());
MWBase::Environment::get().getMechanicsManager()->drop (cell);
@ -378,6 +377,8 @@ namespace MWWorld
const int cellX = cell->getCell()->getGridX();
const int cellY = cell->getCell()->getGridY();
mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace);
if (cell->getCell()->isExterior())
{
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);
@ -507,10 +508,13 @@ namespace MWWorld
void Scene::playerMoved(const osg::Vec3f &pos)
{
if (mCurrentCell == nullptr)
return;
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
mNavigator.updatePlayerPosition(player.getRefData().getPosition().asVec3());
if (!mCurrentCell || !mCurrentCell->isExterior())
if (!mCurrentCell->isExterior())
return;
osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter);
@ -886,8 +890,11 @@ namespace MWWorld
addObject(ptr, *mPhysics, mRendering, mPagedRefs);
addObject(ptr, *mPhysics, mNavigator);
MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale());
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
mNavigator.update(player.getRefData().getPosition().asVec3());
if (mCurrentCell != nullptr)
{
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
mNavigator.update(player.getRefData().getPosition().asVec3());
}
}
catch (std::exception& e)
{
@ -903,8 +910,11 @@ namespace MWWorld
if (const auto object = mPhysics->getObject(ptr))
{
mNavigator.removeObject(DetourNavigator::ObjectId(object));
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
mNavigator.update(player.getRefData().getPosition().asVec3());
if (mCurrentCell != nullptr)
{
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
mNavigator.update(player.getRefData().getPosition().asVec3());
}
}
else if (mPhysics->getActor(ptr))
{

View file

@ -187,7 +187,7 @@ namespace MWWorld
{
auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
navigatorSettings.mRecast.mSwimHeightScale = mSwimHeightScale;
mNavigator = DetourNavigator::makeNavigator(navigatorSettings);
mNavigator = DetourNavigator::makeNavigator(navigatorSettings, userDataPath);
}
else
{
@ -1524,9 +1524,10 @@ namespace MWWorld
if (const auto object = mPhysics->getObject(door.first))
updateNavigatorObject(*object);
if (mShouldUpdateNavigator)
auto player = getPlayerPtr();
if (mShouldUpdateNavigator && player.getCell() != nullptr)
{
mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3());
mNavigator->update(player.getRefData().getPosition().asVec3());
mShouldUpdateNavigator = false;
}
}

View file

@ -3,6 +3,7 @@
#include <components/detournavigator/navigatorimpl.hpp>
#include <components/detournavigator/exceptions.hpp>
#include <components/detournavigator/navigatorutils.hpp>
#include <components/detournavigator/navmeshdb.hpp>
#include <components/misc/rng.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/esm/loadland.hpp>
@ -37,6 +38,7 @@ namespace
Settings mSettings;
std::unique_ptr<Navigator> mNavigator;
const osg::Vec3f mPlayerPosition;
const std::string mWorldspace;
const osg::Vec3f mAgentHalfExtents;
osg::Vec3f mStart;
osg::Vec3f mEnd;
@ -53,6 +55,7 @@ namespace
DetourNavigatorNavigatorTest()
: mPlayerPosition(256, 256, 0)
, mWorldspace("sys::default")
, mAgentHalfExtents(29, 29, 66)
, mStart(52, 460, 1)
, mEnd(460, 52, 1)
@ -87,7 +90,7 @@ namespace
mSettings.mDetour.mMaxPolys = 4096;
mSettings.mMaxTilesNumber = 512;
mSettings.mMinUpdateInterval = std::chrono::milliseconds(50);
mNavigator.reset(new NavigatorImpl(mSettings));
mNavigator.reset(new NavigatorImpl(mSettings, std::make_unique<NavMeshDb>(":memory:")));
}
};
@ -854,7 +857,7 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, multiple_threads_should_lock_tiles)
{
mSettings.mAsyncNavMeshUpdaterThreads = 2;
mNavigator.reset(new NavigatorImpl(mSettings));
mNavigator.reset(new NavigatorImpl(mSettings, std::make_unique<NavMeshDb>(":memory:")));
const std::array<float, 5 * 5> heightfieldData {{
0, 0, 0, 0, 0,

View file

@ -24,14 +24,6 @@ namespace
using namespace DetourNavigator;
using namespace DetourNavigator::Tests;
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, class Random>
void generateRecastArray(T*& values, int size, Random& random)
{

View file

@ -38,7 +38,7 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_empty_should_return_nullptr)
{
TileCachedRecastMeshManager manager(mSettings);
EXPECT_EQ(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_for_empty_should_return_zero)
@ -75,12 +75,13 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y)
ASSERT_NE(manager.getMesh(TilePosition(x, y)), nullptr);
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles)
@ -113,90 +114,98 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_nullptr_for_unused_tile)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_nullptr_for_unused_tile)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_removed_object_should_return_nullptr_for_all_previously_used_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
manager.removeObject(ObjectId(&boxShape));
EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_not_changed_object_after_update_should_return_recast_mesh_for_same_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_add_object_new_should_return_incremented_value)
@ -235,6 +244,7 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_update_not_changed_object_should_return_same_value)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
@ -273,17 +283,19 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192;
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
for (int x = -1; x < 12; ++x)
for (int y = -1; y < 12; ++y)
ASSERT_NE(manager.getMesh(TilePosition(x, y)), nullptr);
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
@ -292,7 +304,7 @@ namespace
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh(TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0);
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_return_nullopt)
@ -315,18 +327,20 @@ namespace
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192;
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
ASSERT_TRUE(manager.removeWater(cellPosition));
for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh(TilePosition(x, y)), nullptr);
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
@ -336,12 +350,13 @@ namespace
ASSERT_TRUE(manager.removeWater(cellPosition));
for (int x = -6; x < 6; ++x)
for (int y = -6; y < 6; ++y)
ASSERT_EQ(manager.getMesh(TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0);
ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_not_remove_tile_with_water)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192;
const btBoxShape boxShape(btVector3(20, 20, 100));
@ -351,6 +366,19 @@ namespace
ASSERT_TRUE(manager.removeObject(ObjectId(&boxShape)));
for (int x = -1; x < 12; ++x)
for (int y = -1; y < 12; ++y)
ASSERT_NE(manager.getMesh(TilePosition(x, y)), nullptr);
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_new_worldspace_should_remove_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(nullptr, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
manager.setWorldspace("other");
for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y)
ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr);
}
}

View file

@ -207,6 +207,7 @@ add_component_dir(detournavigator
navmeshdb
serialization
navmeshdbutils
recast
)
add_component_dir(loadinglistener

View file

@ -76,10 +76,11 @@ namespace
namespace DetourNavigator
{
Job::Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::chrono::steady_clock::time_point processTime)
: mAgentHalfExtents(agentHalfExtents)
, mNavMeshCacheItem(std::move(navMeshCacheItem))
, mWorldspace(worldspace)
, mChangedTile(changedTile)
, mProcessTime(processTime)
, mChangeType(changeType)
@ -89,10 +90,11 @@ namespace DetourNavigator
}
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
OffMeshConnectionsManager& offMeshConnectionsManager)
OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings)
, mRecastMeshManager(recastMeshManager)
, mOffMeshConnectionsManager(offMeshConnectionsManager)
, mDb(std::move(db))
, mShouldStop()
, mNavMeshTilesCache(settings.mMaxNavMeshTilesCacheSize)
{
@ -111,8 +113,8 @@ namespace DetourNavigator
thread.join();
}
void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents,
const SharedNavMeshCacheItem& navMeshCacheItem, const TilePosition& playerTile,
void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& navMeshCacheItem,
const TilePosition& playerTile, std::string_view worldspace,
const std::map<TilePosition, ChangeType>& changedTiles)
{
bool playerTileChanged = false;
@ -147,8 +149,8 @@ namespace DetourNavigator
? mLastUpdates[std::tie(agentHalfExtents, changedTile)] + mSettings.get().mMinUpdateInterval
: std::chrono::steady_clock::time_point();
const JobIt it = mJobs.emplace(mJobs.end(), agentHalfExtents, navMeshCacheItem, changedTile,
changeType, getManhattanDistance(changedTile, playerTile), processTime);
const JobIt it = mJobs.emplace(mJobs.end(), agentHalfExtents, navMeshCacheItem, worldspace,
changedTile, changeType, getManhattanDistance(changedTile, playerTile), processTime);
if (playerTileChanged)
mWaiting.push_back(it);
@ -302,12 +304,13 @@ namespace DetourNavigator
if (!navMeshCacheItem)
return true;
const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile);
const auto recastMesh = mRecastMeshManager.get().getMesh(job.mWorldspace, job.mChangedTile);
const auto playerTile = *mPlayerTile.lockConst();
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,
offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache, getUpdateType(job.mChangeType));
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mWorldspace, job.mChangedTile,
playerTile, offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache,
getUpdateType(job.mChangeType), mDb, mNextShapeId);
if (recastMesh != nullptr)
{

View file

@ -7,6 +7,7 @@
#include "tileposition.hpp"
#include "navmeshtilescache.hpp"
#include "waitconditiontype.hpp"
#include "navmeshdb.hpp"
#include <osg/Vec3f>
@ -57,6 +58,7 @@ namespace DetourNavigator
{
const osg::Vec3f mAgentHalfExtents;
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
const std::string mWorldspace;
const TilePosition mChangedTile;
const std::chrono::steady_clock::time_point mProcessTime;
unsigned mTryNumber = 0;
@ -65,7 +67,7 @@ namespace DetourNavigator
const int mDistanceToOrigin;
Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::string_view worldspace, const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
std::chrono::steady_clock::time_point processTime);
};
@ -75,11 +77,12 @@ namespace DetourNavigator
{
public:
AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
OffMeshConnectionsManager& offMeshConnectionsManager);
OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db);
~AsyncNavMeshUpdater();
void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem,
const TilePosition& playerTile, const std::map<TilePosition, ChangeType>& changedTiles);
const TilePosition& playerTile, std::string_view worldspace,
const std::map<TilePosition, ChangeType>& changedTiles);
void wait(Loading::Listener& listener, WaitConditionType waitConditionType);
@ -89,6 +92,8 @@ namespace DetourNavigator
std::reference_wrapper<const Settings> mSettings;
std::reference_wrapper<TileCachedRecastMeshManager> mRecastMeshManager;
std::reference_wrapper<OffMeshConnectionsManager> mOffMeshConnectionsManager;
Misc::ScopeGuarded<std::unique_ptr<NavMeshDb>> mDb;
ShapeId mNextShapeId {1};
std::atomic_bool mShouldStop;
mutable std::mutex mMutex;
std::condition_variable mHasJob;

View file

@ -61,7 +61,7 @@ namespace DetourNavigator
{
Ignore ignore {consumer};
const std::shared_ptr<RecastMesh> recastMesh = mRecastMeshProvider.getMesh(mTilePosition);
const std::shared_ptr<RecastMesh> recastMesh = mRecastMeshProvider.getMesh(mWorldspace, mTilePosition);
if (recastMesh == nullptr || isEmpty(*recastMesh))
return;

View file

@ -10,9 +10,15 @@
#include "preparednavmeshdata.hpp"
#include "navmeshdata.hpp"
#include "recastmeshbuilder.hpp"
#include "navmeshdb.hpp"
#include "serialization.hpp"
#include "dbrefgeometryobject.hpp"
#include "navmeshdbutils.hpp"
#include <components/misc/convert.hpp>
#include <components/bullethelpers/processtrianglecallback.hpp>
#include <components/misc/convert.hpp>
#include <components/misc/guarded.hpp>
#include <DetourNavMesh.h>
#include <DetourNavMeshBuilder.h>
@ -540,9 +546,10 @@ namespace DetourNavigator
}
UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh,
const TilePosition& changedTile, const TilePosition& playerTile,
const std::string& worldspace, const TilePosition& changedTile, const TilePosition& playerTile,
const std::vector<OffMeshConnection>& offMeshConnections, const Settings& settings,
const SharedNavMeshCacheItem& navMeshCacheItem, NavMeshTilesCache& navMeshTilesCache, UpdateType updateType)
const SharedNavMeshCacheItem& navMeshCacheItem, NavMeshTilesCache& navMeshTilesCache, UpdateType updateType,
Misc::ScopeGuarded<std::unique_ptr<NavMeshDb>>& db, ShapeId& nextShapeId)
{
Log(Debug::Debug) << std::fixed << std::setprecision(2) <<
"Update NavMesh with multiple tiles:" <<
@ -578,7 +585,24 @@ namespace DetourNavigator
if (!cachedNavMeshData)
{
auto prepared = prepareNavMeshTileData(*recastMesh, changedTile, agentHalfExtents, settings.mRecast);
std::optional<TileData> stored;
if (const auto dbLocked = db.lock(); *dbLocked != nullptr)
{
const std::vector<DbRefGeometryObject> objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(),
[&] (const MeshSource& v) { return resolveMeshSource(**dbLocked, v, nextShapeId); });
stored = (*dbLocked)->getTileData(worldspace, changedTile, serialize(settings.mRecast, *recastMesh, objects));
}
std::unique_ptr<PreparedNavMeshData> prepared;
if (stored.has_value() && stored->mVersion == settings.mNavMeshVersion)
{
prepared = std::make_unique<PreparedNavMeshData>();
if (!deserialize(stored->mData, *prepared))
prepared = nullptr;
}
if (prepared == nullptr)
prepared = prepareNavMeshTileData(*recastMesh, changedTile, agentHalfExtents, settings.mRecast);
if (prepared == nullptr)
{

View file

@ -7,6 +7,9 @@
#include "sharednavmesh.hpp"
#include "navmeshtilescache.hpp"
#include "offmeshconnection.hpp"
#include "navmeshdb.hpp"
#include <components/misc/guarded.hpp>
#include <osg/Vec3f>
@ -63,9 +66,10 @@ namespace DetourNavigator
};
UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh,
const TilePosition& changedTile, const TilePosition& playerTile,
const std::string& worldspace, const TilePosition& changedTile, const TilePosition& playerTile,
const std::vector<OffMeshConnection>& offMeshConnections, const Settings& settings,
const SharedNavMeshCacheItem& navMeshCacheItem, NavMeshTilesCache& navMeshTilesCache, UpdateType updateType);
const SharedNavMeshCacheItem& navMeshCacheItem, NavMeshTilesCache& navMeshTilesCache, UpdateType updateType,
Misc::ScopeGuarded<std::unique_ptr<NavMeshDb>>& db, ShapeId& nextShapeId);
}
#endif

View file

@ -5,10 +5,15 @@
namespace DetourNavigator
{
std::unique_ptr<Navigator> makeNavigator(const Settings& settings)
std::unique_ptr<Navigator> makeNavigator(const Settings& settings, const std::string& userDataPath)
{
DetourNavigator::RecastGlobalAllocator::init();
return std::make_unique<NavigatorImpl>(settings);
std::unique_ptr<NavMeshDb> db;
if (settings.mEnableNavMeshDiskCache)
db = std::make_unique<NavMeshDb>(userDataPath + "/navmesh.db");
return std::make_unique<NavigatorImpl>(settings, std::move(db));
}
std::unique_ptr<Navigator> makeNavigatorStub()

View file

@ -10,6 +10,8 @@
#include <components/resource/bulletshape.hpp>
#include <string_view>
namespace ESM
{
struct Cell;
@ -75,6 +77,12 @@ namespace DetourNavigator
*/
virtual void removeAgent(const osg::Vec3f& agentHalfExtents) = 0;
/**
* @brief setWorldspace should be called before adding object from new worldspace
* @param worldspace
*/
virtual void setWorldspace(std::string_view worldspace) = 0;
/**
* @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes
* @param id is used to distinguish different objects
@ -188,7 +196,7 @@ namespace DetourNavigator
virtual float getMaxNavmeshAreaRealRadius() const = 0;
};
std::unique_ptr<Navigator> makeNavigator(const Settings& settings);
std::unique_ptr<Navigator> makeNavigator(const Settings& settings, const std::string& userDataPath);
std::unique_ptr<Navigator> makeNavigatorStub();
}

View file

@ -8,9 +8,9 @@
namespace DetourNavigator
{
NavigatorImpl::NavigatorImpl(const Settings& settings)
NavigatorImpl::NavigatorImpl(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings)
, mNavMeshManager(mSettings)
, mNavMeshManager(mSettings, std::move(db))
, mUpdatesEnabled(true)
{
}
@ -32,6 +32,11 @@ namespace DetourNavigator
--it->second;
}
void NavigatorImpl::setWorldspace(std::string_view worldspace)
{
mNavMeshManager.setWorldspace(worldspace);
}
bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
{
const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);

View file

@ -5,6 +5,7 @@
#include "navmeshmanager.hpp"
#include <set>
#include <memory>
namespace DetourNavigator
{
@ -15,12 +16,14 @@ namespace DetourNavigator
* @brief Navigator constructor initializes all internal data. Constructed object is ready to build a scene.
* @param settings allows to customize navigator work. Constructor is only place to set navigator settings.
*/
explicit NavigatorImpl(const Settings& settings);
explicit NavigatorImpl(const Settings& settings, std::unique_ptr<NavMeshDb>&& db);
void addAgent(const osg::Vec3f& agentHalfExtents) override;
void removeAgent(const osg::Vec3f& agentHalfExtents) override;
void setWorldspace(std::string_view worldspace) override;
bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;

View file

@ -19,6 +19,8 @@ namespace DetourNavigator
void removeAgent(const osg::Vec3f& /*agentHalfExtents*/) override {}
void setWorldspace(std::string_view /*worldspace*/) override {}
bool addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override
{
return false;

View file

@ -41,13 +41,23 @@ namespace
namespace DetourNavigator
{
NavMeshManager::NavMeshManager(const Settings& settings)
NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings)
, mRecastMeshManager(mSettings.mRecast)
, mOffMeshConnectionsManager(mSettings.mRecast)
, mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager)
, mRecastMeshManager(settings.mRecast)
, mOffMeshConnectionsManager(settings.mRecast)
, mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db))
{}
void NavMeshManager::setWorldspace(std::string_view worldspace)
{
if (worldspace == mWorldspace)
return;
mRecastMeshManager.setWorldspace(worldspace);
for (auto& [agent, cache] : mCache)
cache = std::make_shared<GuardedNavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter);
mWorldspace = worldspace;
}
bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType)
{
@ -208,7 +218,7 @@ namespace DetourNavigator
recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0});
});
}
mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost);
mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, mRecastMeshManager.getWorldspace(), tilesToPost);
if (changedTiles != mChangedTiles.end())
changedTiles->second.clear();
Log(Debug::Debug) << "Cache update posted for agent=" << agentHalfExtents <<
@ -241,9 +251,10 @@ namespace DetourNavigator
std::vector<TilePosition> tiles;
mRecastMeshManager.forEachTile(
[&tiles] (const TilePosition& tile, const CachedRecastMeshManager&) { tiles.push_back(tile); });
const std::string worldspace = mRecastMeshManager.getWorldspace();
RecastMeshTiles result;
for (const TilePosition& tile : tiles)
if (auto mesh = mRecastMeshManager.getCachedMesh(tile))
if (auto mesh = mRecastMeshManager.getCachedMesh(worldspace, tile))
result.emplace(tile, std::move(mesh));
return result;
}

View file

@ -22,7 +22,9 @@ namespace DetourNavigator
class NavMeshManager
{
public:
NavMeshManager(const Settings& settings);
explicit NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db);
void setWorldspace(std::string_view worldspace);
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType);
@ -62,6 +64,7 @@ namespace DetourNavigator
private:
const Settings& mSettings;
std::string mWorldspace;
TileCachedRecastMeshManager mRecastMeshManager;
OffMeshConnectionsManager mOffMeshConnectionsManager;
AsyncNavMeshUpdater mAsyncNavMeshUpdater;

View file

@ -1,8 +1,8 @@
#include "preparednavmeshdata.hpp"
#include "preparednavmeshdatatuple.hpp"
#include "recast.hpp"
#include <Recast.h>
#include <RecastAlloc.h>
namespace
{
@ -15,13 +15,6 @@ namespace
value.nverts = 0;
value.ntris = 0;
}
void freePolyMeshDetail(rcPolyMeshDetail& value) noexcept
{
rcFree(value.meshes);
rcFree(value.verts);
rcFree(value.tris);
}
}
namespace DetourNavigator

View file

@ -0,0 +1,49 @@
#include "recast.hpp"
#include <Recast.h>
#include <RecastAlloc.h>
#include <cstring>
#include <new>
namespace DetourNavigator
{
void* permRecastAlloc(std::size_t size)
{
void* const result = rcAlloc(size, RC_ALLOC_PERM);
if (result == nullptr)
throw std::bad_alloc();
return result;
}
void permRecastAlloc(rcPolyMesh& value)
{
permRecastAlloc(value.verts, getVertsLength(value));
permRecastAlloc(value.polys, getPolysLength(value));
permRecastAlloc(value.regs, getRegsLength(value));
permRecastAlloc(value.flags, getFlagsLength(value));
permRecastAlloc(value.areas, getAreasLength(value));
}
void permRecastAlloc(rcPolyMeshDetail& value)
{
try
{
permRecastAlloc(value.meshes, getMeshesLength(value));
permRecastAlloc(value.verts, getVertsLength(value));
permRecastAlloc(value.tris, getTrisLength(value));
}
catch (...)
{
freePolyMeshDetail(value);
throw;
}
}
void freePolyMeshDetail(rcPolyMeshDetail& value) noexcept
{
rcFree(value.meshes);
rcFree(value.verts);
rcFree(value.tris);
}
}

View file

@ -4,6 +4,7 @@
#include <Recast.h>
#include <cstddef>
#include <type_traits>
namespace DetourNavigator
{
@ -46,6 +47,21 @@ namespace DetourNavigator
{
return 4 * static_cast<std::size_t>(value.ntris);
}
void* permRecastAlloc(std::size_t size);
template <class T>
inline void permRecastAlloc(T*& values, std::size_t size)
{
static_assert(std::is_arithmetic_v<T>);
values = new (permRecastAlloc(size * sizeof(T))) T[size];
}
void permRecastAlloc(rcPolyMesh& value);
void permRecastAlloc(rcPolyMeshDetail& value);
void freePolyMeshDetail(rcPolyMeshDetail& value) noexcept;
}
#endif

View file

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

View file

@ -2,14 +2,18 @@
#include "dbrefgeometryobject.hpp"
#include "preparednavmeshdata.hpp"
#include "recast.hpp"
#include "recastmesh.hpp"
#include "settings.hpp"
#include <components/serialization/binaryreader.hpp>
#include <components/serialization/binarywriter.hpp>
#include <components/serialization/format.hpp>
#include <components/serialization/sizeaccumulator.hpp>
#include <cstddef>
#include <cstring>
#include <type_traits>
#include <vector>
namespace DetourNavigator
@ -142,8 +146,9 @@ namespace
visitor(*this, dbRefGeometryObjects);
}
template <class Visitor>
auto operator()(Visitor&& visitor, const rcPolyMesh& value) const
template <class Visitor, class T>
auto operator()(Visitor&& visitor, T& value) const
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, rcPolyMesh>>
{
visitor(*this, value.nverts);
visitor(*this, value.npolys);
@ -155,6 +160,19 @@ namespace
visitor(*this, value.ch);
visitor(*this, value.borderSize);
visitor(*this, value.maxEdgeError);
if constexpr (mode == Serialization::Mode::Read)
{
if (value.verts == nullptr)
permRecastAlloc(value.verts, getVertsLength(value));
if (value.polys == nullptr)
permRecastAlloc(value.polys, getPolysLength(value));
if (value.regs == nullptr)
permRecastAlloc(value.regs, getRegsLength(value));
if (value.flags == nullptr)
permRecastAlloc(value.flags, getFlagsLength(value));
if (value.areas == nullptr)
permRecastAlloc(value.areas, getAreasLength(value));
}
visitor(*this, value.verts, getVertsLength(value));
visitor(*this, value.polys, getPolysLength(value));
visitor(*this, value.regs, getRegsLength(value));
@ -162,22 +180,48 @@ namespace
visitor(*this, value.areas, getAreasLength(value));
}
template <class Visitor>
auto operator()(Visitor&& visitor, const rcPolyMeshDetail& value) const
template <class Visitor, class T>
auto operator()(Visitor&& visitor, T& value) const
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, rcPolyMeshDetail>>
{
visitor(*this, value.nmeshes);
if constexpr (mode == Serialization::Mode::Read)
if (value.meshes == nullptr)
permRecastAlloc(value.meshes, getMeshesLength(value));
visitor(*this, value.meshes, getMeshesLength(value));
visitor(*this, value.nverts);
if constexpr (mode == Serialization::Mode::Read)
if (value.verts == nullptr)
permRecastAlloc(value.verts, getVertsLength(value));
visitor(*this, value.verts, getVertsLength(value));
visitor(*this, value.ntris);
if constexpr (mode == Serialization::Mode::Read)
if (value.tris == nullptr)
permRecastAlloc(value.tris, getTrisLength(value));
visitor(*this, value.tris, getTrisLength(value));
}
template <class Visitor>
auto operator()(Visitor&& visitor, const PreparedNavMeshData& value) const
template <class Visitor, class T>
auto operator()(Visitor&& visitor, T& value) const
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, PreparedNavMeshData>>
{
visitor(*this, DetourNavigator::preparedNavMeshDataMagic);
visitor(*this, DetourNavigator::preparedNavMeshDataVersion);
if constexpr (mode == Serialization::Mode::Write)
{
visitor(*this, DetourNavigator::preparedNavMeshDataMagic);
visitor(*this, DetourNavigator::preparedNavMeshDataVersion);
}
else
{
static_assert(mode == Serialization::Mode::Read);
char magic[std::size(DetourNavigator::preparedNavMeshDataMagic)];
visitor(*this, magic);
if (std::memcmp(magic, DetourNavigator::preparedNavMeshDataMagic, sizeof(magic)) != 0)
throw std::runtime_error("Bad PreparedNavMeshData magic");
std::uint32_t version = 0;
visitor(*this, version);
if (version != DetourNavigator::preparedNavMeshDataVersion)
throw std::runtime_error("Bad PreparedNavMeshData version");
}
visitor(*this, value.mUserId);
visitor(*this, value.mCellSize);
visitor(*this, value.mCellHeight);
@ -211,4 +255,18 @@ namespace DetourNavigator
format(Serialization::BinaryWriter(result.data(), result.data() + result.size()), value);
return result;
}
bool deserialize(const std::vector<std::byte>& data, PreparedNavMeshData& value)
{
try
{
constexpr Format<Serialization::Mode::Read> format;
format(Serialization::BinaryReader(data.data(), data.data() + data.size()), value);
return true;
}
catch (const std::exception&)
{
return false;
}
}
}

View file

@ -22,6 +22,8 @@ namespace DetourNavigator
const std::vector<DbRefGeometryObject>& dbRefGeometryObjects);
std::vector<std::byte> serialize(const PreparedNavMeshData& value);
bool deserialize(const std::vector<std::byte>& data, PreparedNavMeshData& value);
}
#endif

View file

@ -62,6 +62,7 @@ namespace DetourNavigator
result.mEnableNavMeshFileNameRevision = ::Settings::Manager::getBool("enable nav mesh file name revision", "Navigator");
result.mMinUpdateInterval = std::chrono::milliseconds(::Settings::Manager::getInt("min update interval ms", "Navigator"));
result.mNavMeshVersion = ::Settings::Manager::getInt("nav mesh version", "Navigator");
result.mEnableNavMeshDiskCache = ::Settings::Manager::getBool("enable nav mesh disk cache", "Navigator");
return result;
}

View file

@ -39,6 +39,7 @@ namespace DetourNavigator
bool mEnableWriteNavMeshToFile = false;
bool mEnableRecastMeshFileNameRevision = false;
bool mEnableNavMeshFileNameRevision = false;
bool mEnableNavMeshDiskCache = false;
RecastSettings mRecast;
DetourSettings mDetour;
int mWaitUntilMinDistanceToPlayer = 0;

View file

@ -14,15 +14,30 @@ namespace DetourNavigator
: mSettings(settings)
{}
std::string TileCachedRecastMeshManager::getWorldspace() const
{
const std::lock_guard lock(mMutex);
return mWorldspace;
}
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace)
{
const std::lock_guard lock(mMutex);
if (mWorldspace == worldspace)
return;
mTiles.clear();
mWorldspace = worldspace;
}
bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,
const btTransform& transform, const AreaType areaType)
{
std::vector<TilePosition> tilesPositions;
{
auto tiles = mTiles.lock();
const std::lock_guard lock(mMutex);
getTilesPositions(shape.getShape(), transform, mSettings, [&] (const TilePosition& tilePosition)
{
if (addTile(id, shape, transform, areaType, tilePosition, tiles.get()))
if (addTile(id, shape, transform, areaType, tilePosition, mTiles))
tilesPositions.push_back(tilePosition);
});
}
@ -41,10 +56,10 @@ namespace DetourNavigator
return std::nullopt;
std::optional<RemovedRecastMeshObject> result;
{
auto tiles = mTiles.lock();
const std::lock_guard lock(mMutex);
for (const auto& tilePosition : object->second)
{
const auto removed = removeTile(id, tilePosition, tiles.get());
const auto removed = removeTile(id, tilePosition, mTiles);
if (removed && !result)
result = removed;
}
@ -62,8 +77,8 @@ namespace DetourNavigator
if (cellSize == std::numeric_limits<int>::max())
{
const auto tiles = mTiles.lock();
for (auto& tile : *tiles)
const std::lock_guard lock(mMutex);
for (auto& tile : mTiles)
{
if (tile.second->addWater(cellPosition, cellSize, level))
{
@ -77,13 +92,13 @@ namespace DetourNavigator
const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level));
getTilesPositions(cellSize, shift, mSettings, [&] (const TilePosition& tilePosition)
{
const auto tiles = mTiles.lock();
auto tile = tiles->find(tilePosition);
if (tile == tiles->end())
const std::lock_guard lock(mMutex);
auto tile = mTiles.find(tilePosition);
if (tile == mTiles.end())
{
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
tile = tiles->emplace(tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration)).first;
tile = mTiles.emplace_hint(tile, tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
}
if (tile->second->addWater(cellPosition, cellSize, level))
{
@ -107,14 +122,14 @@ namespace DetourNavigator
std::optional<Water> result;
for (const auto& tilePosition : object->second)
{
const auto tiles = mTiles.lock();
const auto tile = tiles->find(tilePosition);
if (tile == tiles->end())
const std::lock_guard lock(mMutex);
const auto tile = mTiles.find(tilePosition);
if (tile == mTiles.end())
continue;
const auto tileResult = tile->second->removeWater(cellPosition);
if (tile->second->isEmpty())
{
tiles->erase(tile);
mTiles.erase(tile);
++mTilesGeneration;
}
if (tileResult && !result)
@ -135,13 +150,13 @@ namespace DetourNavigator
getTilesPositions(cellSize, shift, mSettings, [&] (const TilePosition& tilePosition)
{
const auto tiles = mTiles.lock();
auto tile = tiles->find(tilePosition);
if (tile == tiles->end())
const std::lock_guard lock(mMutex);
auto tile = mTiles.find(tilePosition);
if (tile == mTiles.end())
{
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
tile = tiles->emplace(tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration)).first;
tile = mTiles.emplace_hint(tile, tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
}
if (tile->second->addHeightfield(cellPosition, cellSize, shape))
{
@ -164,14 +179,14 @@ namespace DetourNavigator
std::optional<SizedHeightfieldShape> result;
for (const auto& tilePosition : object->second)
{
const auto tiles = mTiles.lock();
const auto tile = tiles->find(tilePosition);
if (tile == tiles->end())
const std::lock_guard lock(mMutex);
const auto tile = mTiles.find(tilePosition);
if (tile == mTiles.end())
continue;
const auto tileResult = tile->second->removeHeightfield(cellPosition);
if (tile->second->isEmpty())
{
tiles->erase(tile);
mTiles.erase(tile);
++mTilesGeneration;
}
if (tileResult && !result)
@ -182,23 +197,23 @@ namespace DetourNavigator
return result;
}
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition) const
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(std::string_view worldspace, const TilePosition& tilePosition) const
{
if (const auto manager = getManager(tilePosition))
if (const auto manager = getManager(worldspace, tilePosition))
return manager->getMesh();
return nullptr;
}
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getCachedMesh(const TilePosition& tilePosition) const
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getCachedMesh(std::string_view worldspace, const TilePosition& tilePosition) const
{
if (const auto manager = getManager(tilePosition))
if (const auto manager = getManager(worldspace, tilePosition))
return manager->getCachedMesh();
return nullptr;
}
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getNewMesh(const TilePosition& tilePosition) const
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getNewMesh(std::string_view worldspace, const TilePosition& tilePosition) const
{
if (const auto manager = getManager(tilePosition))
if (const auto manager = getManager(worldspace, tilePosition))
return manager->getNewMesh();
return nullptr;
}
@ -210,9 +225,9 @@ namespace DetourNavigator
void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const
{
const auto tiles = mTiles.lockConst();
const auto it = tiles->find(tilePosition);
if (it == tiles->end())
const std::lock_guard lock(mMutex);
const auto it = mTiles.find(tilePosition);
if (it == mTiles.end())
return;
it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion);
}
@ -225,8 +240,8 @@ namespace DetourNavigator
if (tile == tiles.end())
{
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
tile = tiles.emplace(tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration)).first;
tile = tiles.emplace_hint(tile, tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
}
return tile->second->addObject(id, shape, transform, areaType);
}
@ -253,11 +268,14 @@ namespace DetourNavigator
return tileResult;
}
std::shared_ptr<CachedRecastMeshManager> TileCachedRecastMeshManager::getManager(const TilePosition& tilePosition) const
std::shared_ptr<CachedRecastMeshManager> TileCachedRecastMeshManager::getManager(std::string_view worldspace,
const TilePosition& tilePosition) const
{
const auto tiles = mTiles.lockConst();
const auto it = tiles->find(tilePosition);
if (it == tiles->end())
const std::lock_guard lock(mMutex);
if (mWorldspace != worldspace)
return nullptr;
const auto it = mTiles.find(tilePosition);
if (it == mTiles.end())
return nullptr;
return it->second;
}

View file

@ -8,8 +8,6 @@
#include "version.hpp"
#include "heightfieldshape.hpp"
#include <components/misc/guarded.hpp>
#include <algorithm>
#include <map>
#include <mutex>
@ -22,6 +20,10 @@ namespace DetourNavigator
public:
explicit TileCachedRecastMeshManager(const RecastSettings& settings);
std::string getWorldspace() const;
void setWorldspace(std::string_view worldspace);
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType);
@ -36,19 +38,19 @@ namespace DetourNavigator
bool changed = false;
std::vector<TilePosition> newTiles;
{
auto tiles = mTiles.lock();
const std::lock_guard lock(mMutex);
const auto onTilePosition = [&] (const TilePosition& tilePosition)
{
if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition))
{
newTiles.push_back(tilePosition);
if (updateTile(id, transform, areaType, tilePosition, tiles.get()))
if (updateTile(id, transform, areaType, tilePosition, mTiles))
{
onChangedTile(tilePosition);
changed = true;
}
}
else if (addTile(id, shape, transform, areaType, tilePosition, tiles.get()))
else if (addTile(id, shape, transform, areaType, tilePosition, mTiles))
{
newTiles.push_back(tilePosition);
onChangedTile(tilePosition);
@ -59,7 +61,7 @@ namespace DetourNavigator
std::sort(newTiles.begin(), newTiles.end());
for (const auto& tile : currentTiles)
{
if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, tiles.get()))
if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, mTiles))
{
onChangedTile(tile);
changed = true;
@ -84,16 +86,17 @@ namespace DetourNavigator
std::optional<SizedHeightfieldShape> removeHeightfield(const osg::Vec2i& cellPosition);
std::shared_ptr<RecastMesh> getMesh(const TilePosition& tilePosition) const;
std::shared_ptr<RecastMesh> getMesh(std::string_view worldspace, const TilePosition& tilePosition) const;
std::shared_ptr<RecastMesh> getCachedMesh(const TilePosition& tilePosition) const;
std::shared_ptr<RecastMesh> getCachedMesh(std::string_view worldspace, const TilePosition& tilePosition) const;
std::shared_ptr<RecastMesh> getNewMesh(const TilePosition& tilePosition) const;
std::shared_ptr<RecastMesh> getNewMesh(std::string_view worldspace, const TilePosition& tilePosition) const;
template <class Function>
void forEachTile(Function&& function) const
{
for (auto& [tilePosition, recastMeshManager] : *mTiles.lockConst())
const std::lock_guard lock(mMutex);
for (auto& [tilePosition, recastMeshManager] : mTiles)
function(tilePosition, *recastMeshManager);
}
@ -105,7 +108,9 @@ namespace DetourNavigator
using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>;
const RecastSettings& mSettings;
Misc::ScopeGuarded<TilesMap> mTiles;
mutable std::mutex mMutex;
std::string mWorldspace;
TilesMap mTiles;
std::unordered_map<ObjectId, std::vector<TilePosition>> mObjectsTilesPositions;
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
std::map<osg::Vec2i, std::vector<TilePosition>> mHeightfieldTilesPositions;
@ -121,7 +126,8 @@ namespace DetourNavigator
std::optional<RemovedRecastMeshObject> removeTile(const ObjectId id, const TilePosition& tilePosition,
TilesMap& tiles);
inline std::shared_ptr<CachedRecastMeshManager> getManager(const TilePosition& tilePosition) const;
inline std::shared_ptr<CachedRecastMeshManager> getManager(std::string_view worldspace,
const TilePosition& tilePosition) const;
};
}

View file

@ -54,6 +54,17 @@ Allows to complete cell loading only when minimal navigation mesh area is genera
nearby the player. Increasing this value will keep loading screen longer but will slightly increase nav mesh generation
speed on systems bound by CPU. Zero means no waiting.
enable nav mesh disk cache
--------------------------
:Type: boolean
:Range: True/False
:Default: True
If true navmesh cache stored on disk will be used in addition to memory cache.
If navmesh tile is not present in memory cache, it will be looked up in the disk cache.
If it's not found there it will be generated.
Advanced settings
*****************

View file

@ -934,6 +934,9 @@ wait until min distance to player = 5
# Should be increased each time there is a difference between output of makeNavMeshTileData function for the same input.
nav mesh version = 1
# Use navigation mesh cache stored on disk (true, false)
enable nav mesh disk cache = true
[Shadows]
# Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true.