Wait until navmesh is generated within given distance around player

Add a setting to change this distance.

To prevent situations when there is not enough navmesh generated and actors
can't find path correctly.
pull/593/head
elsid 4 years ago
parent f169f8e6f0
commit 7a51d0db18
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -141,6 +141,7 @@
Feature #5456: Basic collada animation support Feature #5456: Basic collada animation support
Feature #5457: Realistic diagonal movement Feature #5457: Realistic diagonal movement
Feature #5486: Fixes trainers to choose their training skills based on their base skill points Feature #5486: Fixes trainers to choose their training skills based on their base skill points
Feature #5500: Prepare enough navmesh tiles before scene loading ends
Feature #5511: Add in game option to toggle HRTF support in OpenMW Feature #5511: Add in game option to toggle HRTF support in OpenMW
Feature #5519: Code Patch tab in launcher Feature #5519: Code Patch tab in launcher
Feature #5524: Resume failed script execution after reload Feature #5524: Resume failed script execution after reload

@ -620,6 +620,8 @@ namespace MWWorld
if (changeEvent) if (changeEvent)
mCellChanged = true; mCellChanged = true;
mNavigator.wait(*loadingListener);
} }
void Scene::testExteriorCells() void Scene::testExteriorCells()

@ -66,6 +66,7 @@ namespace
mSettings.mRegionMergeSize = 20; mSettings.mRegionMergeSize = 20;
mSettings.mRegionMinSize = 8; mSettings.mRegionMinSize = 8;
mSettings.mTileSize = 64; mSettings.mTileSize = 64;
mSettings.mWaitUntilMinDistanceToPlayer = std::numeric_limits<int>::max();
mSettings.mAsyncNavMeshUpdaterThreads = 1; mSettings.mAsyncNavMeshUpdaterThreads = 1;
mSettings.mMaxNavMeshTilesCacheSize = 1024 * 1024; mSettings.mMaxNavMeshTilesCacheSize = 1024 * 1024;
mSettings.mMaxPolygonPathSize = 1024; mSettings.mMaxPolygonPathSize = 1024;

@ -21,6 +21,16 @@ namespace
{ {
return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y()); return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y());
} }
int getMinDistanceTo(const TilePosition& position, int maxDistance,
const std::map<osg::Vec3f, std::set<TilePosition>>& tilesPerHalfExtents)
{
int result = maxDistance;
for (const auto& [halfExtents, tiles] : tilesPerHalfExtents)
for (const TilePosition& tile : tiles)
result = std::min(result, getManhattanDistance(position, tile));
return result;
}
} }
namespace DetourNavigator namespace DetourNavigator
@ -114,24 +124,40 @@ namespace DetourNavigator
void AsyncNavMeshUpdater::wait(Loading::Listener& listener) void AsyncNavMeshUpdater::wait(Loading::Listener& listener)
{ {
if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0)
return;
listener.setLabel("Building navigation mesh"); listener.setLabel("Building navigation mesh");
const std::size_t initialJobsLeft = getTotalJobs(); const std::size_t initialJobsLeft = getTotalJobs();
std::size_t maxProgress = initialJobsLeft + mThreads.size(); std::size_t maxProgress = initialJobsLeft + mThreads.size();
listener.setProgressRange(maxProgress); listener.setProgressRange(maxProgress);
waitUntilJobsDone(initialJobsLeft, maxProgress, listener); const int minDistanceToPlayer = waitUntilJobsDone(initialJobsLeft, maxProgress, listener);
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer)
listener.setProgress(maxProgress); {
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
listener.setProgress(maxProgress);
}
} }
void AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) int AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener)
{ {
std::size_t prevJobsLeft = initialJobsLeft; std::size_t prevJobsLeft = initialJobsLeft;
std::size_t jobsDone = 0; std::size_t jobsDone = 0;
std::size_t jobsLeft = 0; std::size_t jobsLeft = 0;
const int maxDistanceToPlayer = mSettings.get().mWaitUntilMinDistanceToPlayer;
const TilePosition playerPosition = *mPlayerTile.lockConst();
int minDistanceToPlayer = 0;
const auto isDone = [&] const auto isDone = [&]
{ {
jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe(); jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe();
return jobsLeft == 0; if (jobsLeft == 0)
{
minDistanceToPlayer = 0;
return true;
}
minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed);
for (const auto& [threadId, queue] : mThreadsQueues)
minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed);
return minDistanceToPlayer >= maxDistanceToPlayer;
}; };
std::unique_lock<std::mutex> lock(mMutex); std::unique_lock<std::mutex> lock(mMutex);
while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone)) while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone))
@ -150,6 +176,7 @@ namespace DetourNavigator
listener.increaseProgress(newJobsDone); listener.increaseProgress(newJobsDone);
} }
} }
return minDistanceToPlayer;
} }
void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const

@ -142,7 +142,7 @@ namespace DetourNavigator
void cleanupLastUpdates(); void cleanupLastUpdates();
void waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); int waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener);
}; };
} }

@ -29,6 +29,7 @@ namespace DetourNavigator
navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt("region merge size", "Navigator"); navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt("region merge size", "Navigator");
navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt("region min size", "Navigator"); navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt("region min size", "Navigator");
navigatorSettings.mTileSize = ::Settings::Manager::getInt("tile size", "Navigator"); navigatorSettings.mTileSize = ::Settings::Manager::getInt("tile size", "Navigator");
navigatorSettings.mWaitUntilMinDistanceToPlayer = ::Settings::Manager::getInt("wait until min distance to player", "Navigator");
navigatorSettings.mAsyncNavMeshUpdaterThreads = static_cast<std::size_t>(::Settings::Manager::getInt("async nav mesh updater threads", "Navigator")); navigatorSettings.mAsyncNavMeshUpdaterThreads = static_cast<std::size_t>(::Settings::Manager::getInt("async nav mesh updater threads", "Navigator"));
navigatorSettings.mMaxNavMeshTilesCacheSize = static_cast<std::size_t>(::Settings::Manager::getInt("max nav mesh tiles cache size", "Navigator")); navigatorSettings.mMaxNavMeshTilesCacheSize = static_cast<std::size_t>(::Settings::Manager::getInt("max nav mesh tiles cache size", "Navigator"));
navigatorSettings.mMaxPolygonPathSize = static_cast<std::size_t>(::Settings::Manager::getInt("max polygon path size", "Navigator")); navigatorSettings.mMaxPolygonPathSize = static_cast<std::size_t>(::Settings::Manager::getInt("max polygon path size", "Navigator"));

@ -31,6 +31,7 @@ namespace DetourNavigator
int mRegionMergeSize = 0; int mRegionMergeSize = 0;
int mRegionMinSize = 0; int mRegionMinSize = 0;
int mTileSize = 0; int mTileSize = 0;
int mWaitUntilMinDistanceToPlayer = 0;
std::size_t mAsyncNavMeshUpdaterThreads = 0; std::size_t mAsyncNavMeshUpdaterThreads = 0;
std::size_t mMaxNavMeshTilesCacheSize = 0; std::size_t mMaxNavMeshTilesCacheSize = 0;
std::size_t mMaxPolygonPathSize = 0; std::size_t mMaxPolygonPathSize = 0;

@ -42,6 +42,18 @@ Increasing this value may decrease performance.
This condition is always true: ``max tiles number * max polygons per tile <= 4194304``. This condition is always true: ``max tiles number * max polygons per tile <= 4194304``.
It's a limitation of `Recastnavigation <https://github.com/recastnavigation/recastnavigation>`_ library. It's a limitation of `Recastnavigation <https://github.com/recastnavigation/recastnavigation>`_ library.
wait until min distance to player
------------------------------
:Type: integer
:Range: >= 0
:Default: 5
Distance in navmesh tiles around the player to keep loading screen until navigation mesh is generated.
Allows to complete cell loading only when minimal navigation mesh area is generated to correctly find path for actors
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.
Advanced settings Advanced settings
***************** *****************

@ -908,6 +908,10 @@ max tiles number = 512
# Min time duration for the same tile update in milliseconds (value >= 0) # Min time duration for the same tile update in milliseconds (value >= 0)
min update interval ms = 250 min update interval ms = 250
# Keep loading screen until navmesh is generated around the player for all tiles within manhattan distance (value >= 0).
# Distance is measured in the number of tiles and can be only an integer value.
wait until min distance to player = 5
[Shadows] [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. # Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true.

Loading…
Cancel
Save