diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 23525dea8d..83e9805f6e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -619,7 +619,7 @@ namespace MWWorld if (changeEvent) mCellChanged = true; - mNavigator.wait(*loadingListener); + mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); } void Scene::testExteriorCells() @@ -848,7 +848,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); - mNavigator.wait(*loadingListener); + mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index cb8ae41523..41671993da 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -127,7 +127,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -177,7 +177,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -209,7 +209,7 @@ namespace mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mPath.clear(); mOut = std::back_inserter(mPath); @@ -262,7 +262,7 @@ namespace mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -296,7 +296,7 @@ namespace mNavigator->updateObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mPath.clear(); mOut = std::back_inserter(mPath); @@ -355,7 +355,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape2), shape2, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -411,7 +411,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), ObjectShapes {shape, &shapeAvoid}, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -459,7 +459,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(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mStart.z() = 300; @@ -507,7 +507,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(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -554,7 +554,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits::max(), -25, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -601,7 +601,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(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -645,15 +645,15 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->removeObject(ObjectId(&shape)); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -699,7 +699,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); Misc::Rng::init(42); @@ -748,7 +748,7 @@ namespace } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -791,7 +791,7 @@ namespace mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto start = std::chrono::steady_clock::now(); for (std::size_t i = 0; i < shapes.size(); ++i) @@ -800,7 +800,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); for (std::size_t i = 0; i < shapes.size(); ++i) { @@ -808,7 +808,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto duration = std::chrono::steady_clock::now() - start; @@ -831,7 +831,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk); @@ -862,7 +862,7 @@ namespace mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape, btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto navMeshes = mNavigator->getNavMeshes(); ASSERT_EQ(navMeshes.size(), 1); @@ -878,7 +878,7 @@ namespace oscillatingBoxShapePosition); mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); } ASSERT_EQ(navMeshes.size(), 1); diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index ff51e39aad..bee4510b42 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -10,7 +10,9 @@ #include +#include #include +#include namespace { @@ -23,12 +25,14 @@ namespace } int getMinDistanceTo(const TilePosition& position, int maxDistance, - const std::map>& tilesPerHalfExtents) + const std::map>& tilesPerHalfExtents, + const std::set>& presentTiles) { int result = maxDistance; for (const auto& [halfExtents, tiles] : tilesPerHalfExtents) for (const TilePosition& tile : tiles) - result = std::min(result, getManhattanDistance(position, tile)); + if (presentTiles.find(std::make_tuple(halfExtents, tile)) == presentTiles.end()) + result = std::min(result, getManhattanDistance(position, tile)); return result; } } @@ -88,13 +92,22 @@ namespace DetourNavigator const SharedNavMeshCacheItem& navMeshCacheItem, const TilePosition& playerTile, const std::map& changedTiles) { - *mPlayerTile.lock() = playerTile; + bool playerTileChanged = false; + { + auto locked = mPlayerTile.lock(); + playerTileChanged = *locked != playerTile; + *locked = playerTile; + } - if (changedTiles.empty()) + if (!playerTileChanged && changedTiles.empty()) return; const std::lock_guard lock(mMutex); + if (playerTileChanged) + for (auto& job : mJobs) + job.mDistanceToPlayer = getManhattanDistance(job.mChangedTile, playerTile); + for (const auto& changedTile : changedTiles) { if (mPushed[agentHalfExtents].insert(changedTile.first).second) @@ -112,17 +125,28 @@ namespace DetourNavigator ? mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] + mSettings.get().mMinUpdateInterval : std::chrono::steady_clock::time_point(); - mJobs.push(std::move(job)); + if (playerTileChanged) + { + mJobs.push_back(std::move(job)); + } + else + { + const auto it = std::upper_bound(mJobs.begin(), mJobs.end(), job); + mJobs.insert(it, std::move(job)); + } } } + if (playerTileChanged) + std::sort(mJobs.begin(), mJobs.end()); + Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs"; if (!mJobs.empty()) mHasJob.notify_all(); } - void AsyncNavMeshUpdater::wait(Loading::Listener& listener) + void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0) return; @@ -130,15 +154,26 @@ namespace DetourNavigator const std::size_t initialJobsLeft = getTotalJobs(); std::size_t maxProgress = initialJobsLeft + mThreads.size(); listener.setProgressRange(maxProgress); - const int minDistanceToPlayer = waitUntilJobsDone(initialJobsLeft, maxProgress, listener); - if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer) + switch (waitConditionType) { - mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); - listener.setProgress(maxProgress); + case WaitConditionType::requiredTilesPresent: + { + const int minDistanceToPlayer = waitUntilJobsDoneForNotPresentTiles(initialJobsLeft, maxProgress, listener); + if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer) + { + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + listener.setProgress(maxProgress); + } + break; + } + case WaitConditionType::allJobsDone: + waitUntilAllJobsDone(); + listener.setProgress(maxProgress); + break; } } - int AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) + int AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) { std::size_t prevJobsLeft = initialJobsLeft; std::size_t jobsDone = 0; @@ -154,9 +189,9 @@ namespace DetourNavigator minDistanceToPlayer = 0; return true; } - minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed); + minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles); for (const auto& [threadId, queue] : mThreadsQueues) - minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed); + minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed, mPresentTiles); return minDistanceToPlayer >= maxDistanceToPlayer; }; std::unique_lock lock(mMutex); @@ -179,6 +214,15 @@ namespace DetourNavigator return minDistanceToPlayer; } + void AsyncNavMeshUpdater::waitUntilAllJobsDone() + { + { + std::unique_lock lock(mMutex); + mDone.wait(lock, [this] { return mJobs.size() + getTotalThreadJobsUnsafe() == 0; }); + } + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + } + void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const { std::size_t jobs = 0; @@ -253,6 +297,11 @@ namespace DetourNavigator navMeshVersion); } + if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost) + mPresentTiles.erase(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); + else if (isSuccess(status) && status != UpdateNavMeshStatus::ignored) + mPresentTiles.insert(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); + const auto finish = std::chrono::steady_clock::now(); writeDebugFiles(job, recastMesh.get()); @@ -283,7 +332,7 @@ namespace DetourNavigator while (true) { const auto hasJob = [&] { - return (!mJobs.empty() && mJobs.top().mProcessTime <= std::chrono::steady_clock::now()) + return (!mJobs.empty() && mJobs.front().mProcessTime <= std::chrono::steady_clock::now()) || !threadQueue.mJobs.empty(); }; @@ -318,11 +367,11 @@ namespace DetourNavigator { const auto now = std::chrono::steady_clock::now(); - if (jobs.top().mProcessTime > now) + if (jobs.front().mProcessTime > now) return {}; - Job job = jobs.top(); - jobs.pop(); + Job job = jobs.front(); + jobs.pop_front(); if (changeLastUpdate && job.mChangeType == ChangeType::update) mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] = now; @@ -376,7 +425,7 @@ namespace DetourNavigator if (mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second) { ++job.mTryNumber; - mJobs.push(std::move(job)); + mJobs.push_back(std::move(job)); mHasJob.notify_all(); } } @@ -385,7 +434,7 @@ namespace DetourNavigator { if (queue.mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second) { - queue.mJobs.push(std::move(job)); + queue.mJobs.push_back(std::move(job)); mHasJob.notify_all(); } } diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index c28d8f21db..e8b2611e97 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -6,6 +6,7 @@ #include "tilecachedrecastmeshmanager.hpp" #include "tileposition.hpp" #include "navmeshtilescache.hpp" +#include "waitconditiontype.hpp" #include @@ -14,9 +15,10 @@ #include #include #include -#include +#include #include #include +#include class dtNavMesh; @@ -60,7 +62,7 @@ namespace DetourNavigator void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem, const TilePosition& playerTile, const std::map& changedTiles); - void wait(Loading::Listener& listener); + void wait(Loading::Listener& listener, WaitConditionType waitConditionType); void reportStats(unsigned int frameNumber, osg::Stats& stats) const; @@ -83,11 +85,11 @@ namespace DetourNavigator friend inline bool operator <(const Job& lhs, const Job& rhs) { - return lhs.getPriority() > rhs.getPriority(); + return lhs.getPriority() < rhs.getPriority(); } }; - using Jobs = std::priority_queue>; + using Jobs = std::deque; using Pushed = std::map>; struct Queue @@ -113,6 +115,7 @@ namespace DetourNavigator NavMeshTilesCache mNavMeshTilesCache; Misc::ScopeGuarded>> mProcessingTiles; std::map> mLastUpdates; + std::set> mPresentTiles; std::map mThreadsQueues; std::vector mThreads; @@ -142,7 +145,9 @@ namespace DetourNavigator void cleanupLastUpdates(); - int waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); + int waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); + + void waitUntilAllJobsDone(); }; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 3ec5e5acc4..8cf4cb80e8 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -7,6 +7,7 @@ #include "objectid.hpp" #include "navmeshcacheitem.hpp" #include "recastmeshtiles.hpp" +#include "waitconditiontype.hpp" namespace ESM { @@ -165,9 +166,10 @@ namespace DetourNavigator virtual void setUpdatesEnabled(bool enabled) = 0; /** - * @brief wait locks thread until all tiles are updated from last update call. + * @brief wait locks thread until tiles are updated from last update call based on passed condition type. + * @param waitConditionType defines when waiting will stop */ - virtual void wait(Loading::Listener& listener) = 0; + virtual void wait(Loading::Listener& listener, WaitConditionType waitConditionType) = 0; /** * @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through. diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 9ae9171a74..7522fe6227 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -151,9 +151,9 @@ namespace DetourNavigator mUpdatesEnabled = enabled; } - void NavigatorImpl::wait(Loading::Listener& listener) + void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { - mNavMeshManager.wait(listener); + mNavMeshManager.wait(listener, waitConditionType); } SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index a53c9608de..3249462616 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -48,7 +48,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool enabled) override; - void wait(Loading::Listener& listener) override; + void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index c21db2bf8d..2c12c45eb5 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -73,7 +73,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool /*enabled*/) override {} - void wait(Loading::Listener& /*listener*/) override {} + void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override { diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 71a4def4f7..8f1aa86d44 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -5,6 +5,7 @@ #include "makenavmesh.hpp" #include "navmeshcacheitem.hpp" #include "settings.hpp" +#include "waitconditiontype.hpp" #include @@ -190,9 +191,9 @@ namespace DetourNavigator " recastMeshManagerRevision=" << lastRevision; } - void NavMeshManager::wait(Loading::Listener& listener) + void NavMeshManager::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { - mAsyncNavMeshUpdater.wait(listener); + mAsyncNavMeshUpdater.wait(listener, waitConditionType); } SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 25e0b763b2..760ddb6b30 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -5,6 +5,7 @@ #include "cachedrecastmeshmanager.hpp" #include "offmeshconnectionsmanager.hpp" #include "recastmeshtiles.hpp" +#include "waitconditiontype.hpp" #include @@ -44,7 +45,7 @@ namespace DetourNavigator void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); - void wait(Loading::Listener& listener); + void wait(Loading::Listener& listener, WaitConditionType waitConditionType); SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const; diff --git a/components/detournavigator/waitconditiontype.hpp b/components/detournavigator/waitconditiontype.hpp new file mode 100644 index 0000000000..06a5901287 --- /dev/null +++ b/components/detournavigator/waitconditiontype.hpp @@ -0,0 +1,13 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H + +namespace DetourNavigator +{ + enum class WaitConditionType + { + requiredTilesPresent, + allJobsDone, + }; +} + +#endif