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 #5457: Realistic diagonal movement
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 #5519: Code Patch tab in launcher
Feature #5524: Resume failed script execution after reload

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

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

@ -21,6 +21,16 @@ namespace
{
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
@ -114,24 +124,40 @@ namespace DetourNavigator
void AsyncNavMeshUpdater::wait(Loading::Listener& listener)
{
if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0)
return;
listener.setLabel("Building navigation mesh");
const std::size_t initialJobsLeft = getTotalJobs();
std::size_t maxProgress = initialJobsLeft + mThreads.size();
listener.setProgressRange(maxProgress);
waitUntilJobsDone(initialJobsLeft, maxProgress, listener);
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
listener.setProgress(maxProgress);
const int minDistanceToPlayer = waitUntilJobsDone(initialJobsLeft, maxProgress, listener);
if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer)
{
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 jobsDone = 0;
std::size_t jobsLeft = 0;
const int maxDistanceToPlayer = mSettings.get().mWaitUntilMinDistanceToPlayer;
const TilePosition playerPosition = *mPlayerTile.lockConst();
int minDistanceToPlayer = 0;
const auto isDone = [&]
{
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);
while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone))
@ -150,6 +176,7 @@ namespace DetourNavigator
listener.increaseProgress(newJobsDone);
}
}
return minDistanceToPlayer;
}
void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const

@ -142,7 +142,7 @@ namespace DetourNavigator
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.mRegionMinSize = ::Settings::Manager::getInt("region min 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.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"));

@ -31,6 +31,7 @@ namespace DetourNavigator
int mRegionMergeSize = 0;
int mRegionMinSize = 0;
int mTileSize = 0;
int mWaitUntilMinDistanceToPlayer = 0;
std::size_t mAsyncNavMeshUpdaterThreads = 0;
std::size_t mMaxNavMeshTilesCacheSize = 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``.
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
*****************

@ -908,6 +908,10 @@ max tiles number = 512
# Min time duration for the same tile update in milliseconds (value >= 0)
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]
# 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