1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-06 07:45:36 +00:00

Wait until navmesh is generated for interior cells

Add special loading progress bar.

It should be fast enough to not keep loading screen for noticably long but
will provide better pathfinding for actors inside interior cells.
This commit is contained in:
elsid 2021-05-05 18:13:17 +02:00
parent 3618eabaf0
commit f169f8e6f0
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
11 changed files with 99 additions and 38 deletions

View file

@ -847,6 +847,8 @@ namespace MWWorld
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
mNavigator.wait(*loadingListener);
}
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)

View file

@ -3,6 +3,7 @@
#include <components/detournavigator/navigatorimpl.hpp>
#include <components/detournavigator/exceptions.hpp>
#include <components/misc/rng.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <BulletCollision/CollisionShapes/btBoxShape.h>
@ -35,6 +36,7 @@ namespace
std::back_insert_iterator<std::deque<osg::Vec3f>> mOut;
float mStepSize;
AreaCosts mAreaCosts;
Loading::Listener mListener;
DetourNavigatorNavigatorTest()
: mPlayerPosition(0, 0, 0)
@ -124,7 +126,7 @@ namespace
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
@ -174,7 +176,7 @@ namespace
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
@ -206,7 +208,7 @@ namespace
mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mPath.clear();
mOut = std::back_inserter(mPath);
@ -259,7 +261,7 @@ namespace
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
@ -293,7 +295,7 @@ namespace
mNavigator->updateObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mPath.clear();
mOut = std::back_inserter(mPath);
@ -352,7 +354,7 @@ namespace
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape2), shape2, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
@ -408,7 +410,7 @@ namespace
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), ObjectShapes {shape, &shapeAvoid}, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
@ -456,7 +458,7 @@ namespace
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mStart.x() = 0;
mStart.z() = 300;
@ -504,7 +506,7 @@ namespace
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mStart.x() = 0;
mEnd.x() = 0;
@ -551,7 +553,7 @@ namespace
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits<int>::max(), -25, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mStart.x() = 0;
mEnd.x() = 0;
@ -598,7 +600,7 @@ namespace
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mStart.x() = 0;
mEnd.x() = 0;
@ -642,15 +644,15 @@ namespace
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mNavigator->removeObject(ObjectId(&shape));
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
@ -696,7 +698,7 @@ namespace
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
Misc::Rng::init(42);
@ -745,7 +747,7 @@ namespace
}
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success);
@ -788,7 +790,7 @@ namespace
mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform);
}
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
const auto start = std::chrono::steady_clock::now();
for (std::size_t i = 0; i < shapes.size(); ++i)
@ -797,7 +799,7 @@ namespace
mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform);
}
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
for (std::size_t i = 0; i < shapes.size(); ++i)
{
@ -805,7 +807,7 @@ namespace
mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform);
}
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
const auto duration = std::chrono::steady_clock::now() - start;
@ -828,7 +830,7 @@ namespace
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk);
@ -859,7 +861,7 @@ namespace
mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape,
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200)));
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
const auto navMeshes = mNavigator->getNavMeshes();
ASSERT_EQ(navMeshes.size(), 1);
@ -875,7 +877,7 @@ namespace
oscillatingBoxShapePosition);
mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform);
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mNavigator->wait(mListener);
}
ASSERT_EQ(navMeshes.size(), 1);

View file

@ -6,6 +6,7 @@
#include <components/debug/debuglog.hpp>
#include <components/misc/thread.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <osg/Stats>
@ -111,13 +112,44 @@ namespace DetourNavigator
mHasJob.notify_all();
}
void AsyncNavMeshUpdater::wait()
void AsyncNavMeshUpdater::wait(Loading::Listener& listener)
{
{
std::unique_lock<std::mutex> lock(mMutex);
mDone.wait(lock, [&] { return mJobs.empty() && getTotalThreadJobsUnsafe() == 0; });
}
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);
}
void 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 auto isDone = [&]
{
jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe();
return jobsLeft == 0;
};
std::unique_lock<std::mutex> lock(mMutex);
while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone))
{
if (maxProgress < jobsLeft)
{
maxProgress = jobsLeft + mThreads.size();
listener.setProgressRange(maxProgress);
listener.setProgress(jobsDone);
}
else if (jobsLeft < prevJobsLeft)
{
const std::size_t newJobsDone = prevJobsLeft - jobsLeft;
jobsDone += newJobsDone;
prevJobsLeft = jobsLeft;
listener.increaseProgress(newJobsDone);
}
}
}
void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const
@ -381,6 +413,12 @@ namespace DetourNavigator
mProcessed.notify_all();
}
std::size_t AsyncNavMeshUpdater::getTotalJobs() const
{
const std::scoped_lock lock(mMutex);
return mJobs.size() + getTotalThreadJobsUnsafe();
}
std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const
{
return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0),

View file

@ -20,6 +20,11 @@
class dtNavMesh;
namespace Loading
{
class Listener;
}
namespace DetourNavigator
{
enum class ChangeType
@ -55,7 +60,7 @@ namespace DetourNavigator
void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem,
const TilePosition& playerTile, const std::map<TilePosition, ChangeType>& changedTiles);
void wait();
void wait(Loading::Listener& listener);
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
@ -131,9 +136,13 @@ namespace DetourNavigator
void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile);
inline std::size_t getTotalJobs() const;
inline std::size_t getTotalThreadJobsUnsafe() const;
void cleanupLastUpdates();
void waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener);
};
}

View file

@ -14,6 +14,11 @@ namespace ESM
struct Pathgrid;
}
namespace Loading
{
class Listener;
}
namespace DetourNavigator
{
struct ObjectShapes
@ -162,7 +167,7 @@ namespace DetourNavigator
/**
* @brief wait locks thread until all tiles are updated from last update call.
*/
virtual void wait() = 0;
virtual void wait(Loading::Listener& listener) = 0;
/**
* @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through.

View file

@ -153,9 +153,9 @@ namespace DetourNavigator
mUpdatesEnabled = enabled;
}
void NavigatorImpl::wait()
void NavigatorImpl::wait(Loading::Listener& listener)
{
mNavMeshManager.wait();
mNavMeshManager.wait(listener);
}
SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const

View file

@ -48,7 +48,7 @@ namespace DetourNavigator
void setUpdatesEnabled(bool enabled) override;
void wait() override;
void wait(Loading::Listener& listener) override;
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override;

View file

@ -3,6 +3,11 @@
#include "navigator.hpp"
namespace Loading
{
class Listener;
}
namespace DetourNavigator
{
class NavigatorStub final : public Navigator
@ -68,7 +73,7 @@ namespace DetourNavigator
void setUpdatesEnabled(bool /*enabled*/) override {}
void wait() override {}
void wait(Loading::Listener& /*listener*/) override {}
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override
{

View file

@ -188,9 +188,9 @@ namespace DetourNavigator
" recastMeshManagerRevision=" << lastRevision;
}
void NavMeshManager::wait()
void NavMeshManager::wait(Loading::Listener& listener)
{
mAsyncNavMeshUpdater.wait();
mAsyncNavMeshUpdater.wait(listener);
}
SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const

View file

@ -45,7 +45,7 @@ namespace DetourNavigator
void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents);
void wait();
void wait(Loading::Listener& listener);
SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const;

View file

@ -75,7 +75,7 @@ namespace Misc
return Locked<T>(mMutex, mValue);
}
Locked<const T> lockConst()
Locked<const T> lockConst() const
{
return Locked<const T>(mMutex, mValue);
}
@ -88,7 +88,7 @@ namespace Misc
}
private:
std::mutex mMutex;
mutable std::mutex mMutex;
T mValue;
};
}