Merge branch 'optimize_navigator_update' into 'master'

Optimize navigator per frame update

See merge request OpenMW/openmw!2308
crashfix_debugdraw
psi29a 2 years ago
commit 2a2268ea8b

@ -7,8 +7,6 @@
#include <components/esm3/esmwriter.hpp>
#include <components/esm3/stolenitems.hpp>
#include <components/detournavigator/navigator.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwworld/esmstore.hpp"
@ -813,12 +811,6 @@ namespace MWMechanics
bool MechanicsManager::toggleAI()
{
mAI = !mAI;
MWBase::World* world = MWBase::Environment::get().getWorld();
world->getNavigator()->setUpdatesEnabled(mAI);
if (mAI)
world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3());
return mAI;
}

@ -365,8 +365,6 @@ namespace MWWorld
if (const auto pathgrid = mWorld.getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
mNavigator.removePathgrid(*pathgrid);
mNavigator.update(mWorld.getPlayerPtr().getRefData().getPosition().asVec3());
MWBase::Environment::get().getMechanicsManager()->drop (cell);
mRendering.removeCell(cell);
@ -514,12 +512,7 @@ namespace MWWorld
void Scene::playerMoved(const osg::Vec3f &pos)
{
if (mCurrentCell == nullptr)
return;
mNavigator.updatePlayerPosition(pos);
if (!mCurrentCell->isExterior())
if (!mCurrentCell || !mCurrentCell->isExterior())
return;
osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter);
@ -619,7 +612,7 @@ namespace MWWorld
if (changeEvent)
mCellChanged = true;
mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent);
mCellLoaded = true;
}
void Scene::addPostponedPhysicsObjects()
@ -868,13 +861,13 @@ namespace MWWorld
if (changeEvent)
mCellChanged = true;
mCellLoaded = true;
if (useFading)
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent);
MWBase::Environment::get().getWorld()->getPostProcessor()->setExteriorFlag(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx);
}
@ -921,11 +914,6 @@ namespace MWWorld
addObject(ptr, mWorld, mPagedRefs, *mPhysics, mRendering);
addObject(ptr, mWorld, *mPhysics, mNavigator);
mWorld.scaleObject(ptr, ptr.getCellRef().getScale());
if (mCurrentCell != nullptr)
{
const auto player = mWorld.getPlayerPtr();
mNavigator.update(player.getRefData().getPosition().asVec3());
}
}
catch (std::exception& e)
{
@ -945,11 +933,6 @@ namespace MWWorld
if (const auto object = mPhysics->getObject(ptr))
{
mNavigator.removeObject(DetourNavigator::ObjectId(object));
if (mCurrentCell != nullptr)
{
const auto player = mWorld.getPlayerPtr();
mNavigator.update(player.getRefData().getPosition().asVec3());
}
}
else if (mPhysics->getActor(ptr))
{

@ -85,6 +85,7 @@ namespace MWWorld
CellStore* mCurrentCell; // the cell the player is in
CellStoreCollection mActiveCells;
bool mCellChanged;
bool mCellLoaded = false;
MWWorld::World& mWorld;
MWPhysics::PhysicsSystem *mPhysics;
MWRender::RenderingManager& mRendering;
@ -152,6 +153,10 @@ namespace MWWorld
bool hasCellChanged() const;
///< Has the set of active cells changed, since the last frame?
bool hasCellLoaded() const { return mCellLoaded; }
void resetCellLoaded() { mCellLoaded = false; }
void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true);
///< Move to interior cell.
/// @param changeEvent Set cellChanged flag?

@ -154,7 +154,6 @@ namespace MWWorld
mUserDataPath(userDataPath),
mDefaultHalfExtents(Settings::Manager::getVector3("default actor pathfind half extents", "Game")),
mDefaultActorCollisionShapeType(DetourNavigator::toCollisionShapeType(Settings::Manager::getInt("actor collision shape type", "Game"))),
mShouldUpdateNavigator(false),
mActivationDistanceOverride (activationDistanceOverride),
mStartCell(startCell), mDistanceToFacedObject(-1.f), mTeleportEnabled(true),
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
@ -1536,12 +1535,7 @@ namespace MWWorld
if (const auto object = mPhysics->getObject(door.first))
updateNavigatorObject(*object);
auto player = getPlayerPtr();
if (mShouldUpdateNavigator && player.getCell() != nullptr)
{
mNavigator->update(player.getRefData().getPosition().asVec3());
mShouldUpdateNavigator = false;
}
mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3());
}
void World::updateNavigatorObject(const MWPhysics::Object& object)
@ -1549,8 +1543,7 @@ namespace MWWorld
const MWWorld::Ptr ptr = object.getPtr();
const DetourNavigator::ObjectShapes shapes(object.getShapeInstance(),
DetourNavigator::ObjectTransform {ptr.getRefData().getPosition(), ptr.getCellRef().getScale()});
mShouldUpdateNavigator = mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform())
|| mShouldUpdateNavigator;
mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform());
}
const MWPhysics::RayCastingInterface* World::getRayCasting() const
@ -1821,10 +1814,7 @@ namespace MWWorld
updateWeather(duration, paused);
if (!paused)
{
updateNavigator();
}
updateNavigator();
mPlayer->update();
@ -1842,6 +1832,13 @@ namespace MWWorld
mSpellPreloadTimer = 0.1f;
preloadSpells();
}
if (mWorldScene->hasCellLoaded())
{
mNavigator->wait(*MWBase::Environment::get().getWindowManager()->getLoadingScreen(),
DetourNavigator::WaitConditionType::requiredTilesPresent);
mWorldScene->resetCellLoaded();
}
}
void World::updatePhysics (float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)

@ -113,7 +113,6 @@ namespace MWWorld
osg::Vec3f mDefaultHalfExtents;
DetourNavigator::CollisionShapeType mDefaultActorCollisionShapeType;
bool mShouldUpdateNavigator;
int mActivationDistanceOverride;

@ -439,8 +439,17 @@ namespace
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
mNavigator->addAgent(mAgentBounds);
EXPECT_TRUE(mNavigator->addHeightfield(mCellPosition, cellSize1, surface1));
EXPECT_FALSE(mNavigator->addHeightfield(mCellPosition, cellSize2, surface2));
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1);
mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone);
const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion();
mNavigator->addHeightfield(mCellPosition, cellSize2, surface2);
mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version);
}
TEST_F(DetourNavigatorNavigatorTest, path_should_be_around_avoid_shape)
@ -1142,7 +1151,16 @@ namespace
const float level2 = 2;
mNavigator->addAgent(mAgentBounds);
EXPECT_TRUE(mNavigator->addWater(mCellPosition, cellSize1, level1));
EXPECT_FALSE(mNavigator->addWater(mCellPosition, cellSize2, level2));
mNavigator->addWater(mCellPosition, cellSize1, level1);
mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone);
const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion();
mNavigator->addWater(mCellPosition, cellSize2, level2);
mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone);
EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version);
}
}

@ -216,27 +216,13 @@ namespace DetourNavigator
void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType)
{
if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0)
return;
listener.setLabel("#{Navigation:BuildingNavigationMesh}");
const std::size_t initialJobsLeft = getTotalJobs();
std::size_t maxProgress = initialJobsLeft + mThreads.size();
listener.setProgressRange(maxProgress);
switch (waitConditionType)
{
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);
}
waitUntilJobsDoneForNotPresentTiles(listener);
break;
}
case WaitConditionType::allJobsDone:
waitUntilAllJobsDone();
listener.setProgress(maxProgress);
break;
}
}
@ -255,8 +241,10 @@ namespace DetourNavigator
thread.join();
}
int AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener)
void AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(Loading::Listener& listener)
{
const std::size_t initialJobsLeft = getTotalJobs();
std::size_t maxProgress = initialJobsLeft + mThreads.size();
std::size_t prevJobsLeft = initialJobsLeft;
std::size_t jobsDone = 0;
std::size_t jobsLeft = 0;
@ -275,7 +263,13 @@ namespace DetourNavigator
return minDistanceToPlayer >= maxDistanceToPlayer;
};
std::unique_lock<std::mutex> lock(mMutex);
while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone))
if (getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles) >= maxDistanceToPlayer
|| (mJobs.empty() && mProcessingTiles.lockConst()->empty()))
return;
Loading::ScopedLoad load(&listener);
listener.setLabel("#{Navigation:BuildingNavigationMesh}");
listener.setProgressRange(maxProgress);
while (!mDone.wait_for(lock, std::chrono::milliseconds(20), isDone))
{
if (maxProgress < jobsLeft)
{
@ -291,14 +285,19 @@ namespace DetourNavigator
listener.increaseProgress(newJobsDone);
}
}
return minDistanceToPlayer;
lock.unlock();
if (minDistanceToPlayer < maxDistanceToPlayer)
{
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
listener.setProgress(maxProgress);
}
}
void AsyncNavMeshUpdater::waitUntilAllJobsDone()
{
{
std::unique_lock<std::mutex> lock(mMutex);
mDone.wait(lock, [this] { return mJobs.size() == 0; });
mDone.wait(lock, [this] { return mJobs.empty(); });
}
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
}

@ -223,9 +223,9 @@ namespace DetourNavigator
void cleanupLastUpdates();
int waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener);
inline void waitUntilJobsDoneForNotPresentTiles(Loading::Listener& listener);
void waitUntilAllJobsDone();
inline void waitUntilAllJobsDone();
};
void reportStats(const AsyncNavMeshUpdater::Stats& stats, unsigned int frameNumber, osg::Stats& out);

@ -3,6 +3,7 @@
#include "recastmesh.hpp"
#include "settings.hpp"
#include "settingsutils.hpp"
#include "version.hpp"
#include <components/bullethelpers/operators.hpp>
@ -191,6 +192,11 @@ namespace DetourNavigator
return stream << "ChangeType::" << static_cast<int>(value);
}
std::ostream& operator<<(std::ostream& stream, const Version& value)
{
return stream << "Version {" << value.mGeneration << ", " << value.mRevision << "}";
}
void writeToFile(const RecastMesh& recastMesh, const std::string& pathPrefix,
const std::string& revision, const RecastSettings& settings)
{

@ -18,6 +18,8 @@ class dtNavMesh;
namespace DetourNavigator
{
struct Version;
std::ostream& operator<<(std::ostream& stream, const TileBounds& value);
std::ostream& operator<<(std::ostream& stream, Status value);
@ -54,6 +56,8 @@ namespace DetourNavigator
std::ostream& operator<<(std::ostream& stream, ChangeType value);
std::ostream& operator<<(std::ostream& stream, const Version& value);
class RecastMesh;
struct RecastSettings;

@ -100,65 +100,56 @@ namespace DetourNavigator
* @param id is used to distinguish different objects
* @param shape members must live until object is updated by another shape removed from Navigator
* @param transform allows to setup objects geometry according to its world state
* @return true if object is added, false if there is already object with given id
*/
virtual bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0;
virtual void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0;
/**
* @brief addObject is used to add doors.
* @param id is used to distinguish different objects.
* @param shape members must live until object is updated by another shape or removed from Navigator.
* @param transform allows to setup objects geometry according to its world state.
* @return true if object is added, false if there is already object with given id.
*/
virtual bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0;
virtual void addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0;
/**
* @brief updateObject replace object geometry by given data.
* @param id is used to find object.
* @param shape members must live until object is updated by another shape removed from Navigator.
* @param transform allows to setup objects geometry according to its world state.
* @return true if object is updated, false if there is no object with given id.
*/
virtual bool updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0;
virtual void updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0;
/**
* @brief updateObject replace object geometry by given data.
* @param id is used to find object.
* @param shape members must live until object is updated by another shape removed from Navigator.
* @param transform allows to setup objects geometry according to its world state.
* @return true if object is updated, false if there is no object with given id.
*/
virtual bool updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0;
virtual void updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0;
/**
* @brief removeObject to make it no more available at the scene.
* @param id is used to find object.
* @return true if object is removed, false if there is no object with given id.
*/
virtual bool removeObject(const ObjectId id) = 0;
virtual void removeObject(const ObjectId id) = 0;
/**
* @brief addWater is used to set water level at given world cell.
* @param cellPosition allows to distinguish cells if there is many in current world.
* @param cellSize set cell borders. std::numeric_limits<int>::max() disables cell borders.
* @param shift set global shift of cell center.
* @return true if there was no water at given cell if cellSize != std::numeric_limits<int>::max() or there is
* at least single object is added to the scene, false if there is already water for given cell or there is no
* any other objects.
*/
virtual bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level) = 0;
virtual void addWater(const osg::Vec2i& cellPosition, int cellSize, float level) = 0;
/**
* @brief removeWater to make it no more available at the scene.
* @param cellPosition allows to find cell.
* @return true if there was water at given cell.
*/
virtual bool removeWater(const osg::Vec2i& cellPosition) = 0;
virtual void removeWater(const osg::Vec2i& cellPosition) = 0;
virtual bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) = 0;
virtual void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) = 0;
virtual bool removeHeightfield(const osg::Vec2i& cellPosition) = 0;
virtual void removeHeightfield(const osg::Vec2i& cellPosition) = 0;
virtual void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) = 0;
@ -170,17 +161,6 @@ namespace DetourNavigator
*/
virtual void update(const osg::Vec3f& playerPosition) = 0;
/**
* @brief updatePlayerPosition starts background navmesh update using current scene state only when player position has been changed.
* @param playerPosition setup initial point to order build tiles of navmesh.
*/
virtual void updatePlayerPosition(const osg::Vec3f& playerPosition) = 0;
/**
* @brief disable navigator updates
*/
virtual void setUpdatesEnabled(bool enabled) = 0;
/**
* @brief wait locks thread until tiles are updated from last update call based on passed condition type.
* @param waitConditionType defines when waiting will stop

@ -11,7 +11,6 @@ namespace DetourNavigator
NavigatorImpl::NavigatorImpl(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings)
, mNavMeshManager(mSettings, std::move(db))
, mUpdatesEnabled(true)
{
}
@ -42,7 +41,12 @@ namespace DetourNavigator
mNavMeshManager.updateBounds(playerPosition);
}
bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
void NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
{
addObjectImpl(id, shapes, transform);
}
bool NavigatorImpl::addObjectImpl(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
{
const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);
bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);
@ -59,78 +63,71 @@ namespace DetourNavigator
return result;
}
bool NavigatorImpl::addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)
void NavigatorImpl::addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)
{
if (addObject(id, static_cast<const ObjectShapes&>(shapes), transform))
if (addObjectImpl(id, static_cast<const ObjectShapes&>(shapes), transform))
{
const osg::Vec3f start = toNavMeshCoordinates(mSettings.mRecast, shapes.mConnectionStart);
const osg::Vec3f end = toNavMeshCoordinates(mSettings.mRecast, shapes.mConnectionEnd);
mNavMeshManager.addOffMeshConnection(id, start, end, AreaType_door);
mNavMeshManager.addOffMeshConnection(id, end, start, AreaType_door);
return true;
}
return false;
}
bool NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
void NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
{
const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);
bool result = mNavMeshManager.updateObject(id, collisionShape, transform, AreaType_ground);
mNavMeshManager.updateObject(id, collisionShape, transform, AreaType_ground);
if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->mAvoidCollisionShape.get())
{
const ObjectId avoidId(avoidShape);
const CollisionShape avoidCollisionShape(shapes.mShapeInstance, *avoidShape, shapes.mTransform);
if (mNavMeshManager.updateObject(avoidId, avoidCollisionShape, transform, AreaType_null))
{
updateAvoidShapeId(id, avoidId);
result = true;
}
}
return result;
}
bool NavigatorImpl::updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)
void NavigatorImpl::updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)
{
return updateObject(id, static_cast<const ObjectShapes&>(shapes), transform);
}
bool NavigatorImpl::removeObject(const ObjectId id)
void NavigatorImpl::removeObject(const ObjectId id)
{
bool result = mNavMeshManager.removeObject(id);
mNavMeshManager.removeObject(id);
const auto avoid = mAvoidIds.find(id);
if (avoid != mAvoidIds.end())
result = mNavMeshManager.removeObject(avoid->second) || result;
mNavMeshManager.removeObject(avoid->second);
const auto water = mWaterIds.find(id);
if (water != mWaterIds.end())
result = mNavMeshManager.removeObject(water->second) || result;
mNavMeshManager.removeObject(water->second);
mNavMeshManager.removeOffMeshConnections(id);
return result;
}
bool NavigatorImpl::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
void NavigatorImpl::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
{
return mNavMeshManager.addWater(cellPosition, cellSize, level);
mNavMeshManager.addWater(cellPosition, cellSize, level);
}
bool NavigatorImpl::removeWater(const osg::Vec2i& cellPosition)
void NavigatorImpl::removeWater(const osg::Vec2i& cellPosition)
{
return mNavMeshManager.removeWater(cellPosition);
mNavMeshManager.removeWater(cellPosition);
}
bool NavigatorImpl::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape)
void NavigatorImpl::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape)
{
return mNavMeshManager.addHeightfield(cellPosition, cellSize, shape);
mNavMeshManager.addHeightfield(cellPosition, cellSize, shape);
}
bool NavigatorImpl::removeHeightfield(const osg::Vec2i& cellPosition)
void NavigatorImpl::removeHeightfield(const osg::Vec2i& cellPosition)
{
return mNavMeshManager.removeHeightfield(cellPosition);
mNavMeshManager.removeHeightfield(cellPosition);
}
void NavigatorImpl::addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid)
{
Misc::CoordinateConverter converter(&cell);
for (auto& edge : pathgrid.mEdges)
for (const auto& edge : pathgrid.mEdges)
{
const auto src = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV0]));
const auto dst = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV1]));
@ -150,31 +147,15 @@ namespace DetourNavigator
void NavigatorImpl::update(const osg::Vec3f& playerPosition)
{
if (!mUpdatesEnabled)
return;
removeUnusedNavMeshes();
for (const auto& v : mAgents)
mNavMeshManager.update(playerPosition, v.first);
}
void NavigatorImpl::updatePlayerPosition(const osg::Vec3f& playerPosition)
{
const TilePosition tilePosition = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition));
if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition)
return;
mNavMeshManager.updateBounds(playerPosition);
update(playerPosition);
mLastPlayerPosition = tilePosition;
}
void NavigatorImpl::setUpdatesEnabled(bool enabled)
{
mUpdatesEnabled = enabled;
}
void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType)
{
mNavMeshManager.wait(listener, waitConditionType);
if (mSettings.mWaitUntilMinDistanceToPlayer > 0)
mNavMeshManager.wait(listener, waitConditionType);
}
SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const AgentBounds& agentBounds) const

@ -26,23 +26,23 @@ namespace DetourNavigator
void updateBounds(const osg::Vec3f& playerPosition) override;
bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;
void addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;
bool updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
void updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
bool updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;
void updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;
bool removeObject(const ObjectId id) override;
void removeObject(const ObjectId id) override;
bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level) override;
void addWater(const osg::Vec2i& cellPosition, int cellSize, float level) override;
bool removeWater(const osg::Vec2i& cellPosition) override;
void removeWater(const osg::Vec2i& cellPosition) override;
bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) override;
void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) override;
bool removeHeightfield(const osg::Vec2i& cellPosition) override;
void removeHeightfield(const osg::Vec2i& cellPosition) override;
void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) override;
@ -50,10 +50,6 @@ namespace DetourNavigator
void update(const osg::Vec3f& playerPosition) override;
void updatePlayerPosition(const osg::Vec3f& playerPosition) override;
void setUpdatesEnabled(bool enabled) override;
void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override;
SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const override;
@ -71,12 +67,12 @@ namespace DetourNavigator
private:
Settings mSettings;
NavMeshManager mNavMeshManager;
bool mUpdatesEnabled;
std::optional<TilePosition> mLastPlayerPosition;
std::map<AgentBounds, std::size_t> mAgents;
std::unordered_map<ObjectId, ObjectId> mAvoidIds;
std::unordered_map<ObjectId, ObjectId> mWaterIds;
inline bool addObjectImpl(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform);
void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId);
void updateWaterShapeId(const ObjectId id, const ObjectId waterId);
void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map<ObjectId, ObjectId>& ids);

@ -22,50 +22,24 @@ namespace DetourNavigator
void setWorldspace(std::string_view /*worldspace*/) override {}
bool addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override
{
return false;
}
void addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override {}
bool addObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override
{
return false;
}
void addObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override {}
bool updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override
{
return false;
}
void updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override {}
bool updateObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override
{
return false;
}
void updateObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override {}
bool removeObject(const ObjectId /*id*/) override
{
return false;
}
void removeObject(const ObjectId /*id*/) override {}
bool addWater(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, float /*level*/) override
{
return false;
}
void addWater(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, float /*level*/) override {}
bool removeWater(const osg::Vec2i& /*cellPosition*/) override
{
return false;
}
void removeWater(const osg::Vec2i& /*cellPosition*/) override {}
bool addHeightfield(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, const HeightfieldShape& /*height*/) override
{
return false;
}
void addHeightfield(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, const HeightfieldShape& /*height*/)
override {}
bool removeHeightfield(const osg::Vec2i& /*cellPosition*/) override
{
return false;
}
void removeHeightfield(const osg::Vec2i& /*cellPosition*/) override {}
void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) override {}
@ -75,10 +49,6 @@ namespace DetourNavigator
void updateBounds(const osg::Vec3f& /*playerPosition*/) override {}
void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {};
void setUpdatesEnabled(bool /*enabled*/) override {}
void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {}
SharedNavMeshCacheItem getNavMesh(const AgentBounds& /*agentBounds*/) const override

@ -107,51 +107,46 @@ namespace DetourNavigator
[&] (const TilePosition& tile, ChangeType changeType) { addChangedTile(tile, changeType); });
}
bool NavMeshManager::removeObject(const ObjectId id)
void NavMeshManager::removeObject(const ObjectId id)
{
const auto object = mRecastMeshManager.removeObject(id);
if (!object)
return false;
return;
addChangedTiles(object->mShape, object->mTransform, ChangeType::remove);
return true;
}
bool NavMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
void NavMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
{
if (!mRecastMeshManager.addWater(cellPosition, cellSize, level))
return false;
return;
const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level));
addChangedTiles(cellSize, shift, ChangeType::add);
return true;
}
bool NavMeshManager::removeWater(const osg::Vec2i& cellPosition)
void NavMeshManager::removeWater(const osg::Vec2i& cellPosition)
{
const auto water = mRecastMeshManager.removeWater(cellPosition);
if (!water)
return false;
return;
const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, water->mCellSize, water->mLevel));
addChangedTiles(water->mCellSize, shift, ChangeType::remove);
return true;
}
bool NavMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape)
void NavMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape)
{
if (!mRecastMeshManager.addHeightfield(cellPosition, cellSize, shape))
return false;
return;
const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize);
addChangedTiles(cellSize, shift, ChangeType::add);
return true;
}
bool NavMeshManager::removeHeightfield(const osg::Vec2i& cellPosition)
void NavMeshManager::removeHeightfield(const osg::Vec2i& cellPosition)
{
const auto heightfield = mRecastMeshManager.removeHeightfield(cellPosition);
if (!heightfield)
return false;
return;
const btVector3 shift = getHeightfieldShift(heightfield->mShape, cellPosition, heightfield->mCellSize);
addChangedTiles(heightfield->mCellSize, shift, ChangeType::remove);
return true;
}
void NavMeshManager::addAgent(const AgentBounds& agentBounds)

@ -32,17 +32,17 @@ namespace DetourNavigator
bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType);
bool removeObject(const ObjectId id);
void removeObject(const ObjectId id);
void addAgent(const AgentBounds& agentBounds);
bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level);
void addWater(const osg::Vec2i& cellPosition, int cellSize, float level);
bool removeWater(const osg::Vec2i& cellPosition);
void removeWater(const osg::Vec2i& cellPosition);
bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape);
void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape);
bool removeHeightfield(const osg::Vec2i& cellPosition);
void removeHeightfield(const osg::Vec2i& cellPosition);
bool reset(const AgentBounds& agentBounds);

@ -170,7 +170,6 @@ namespace DetourNavigator
Version RecastMeshManager::getVersion() const
{
const std::lock_guard lock(mMutex);
return Version {mGeneration, mRevision};
}
}

@ -149,15 +149,15 @@ namespace DetourNavigator
else
{
const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level));
const auto worldspaceTiles = mWorldspaceTiles.lock();
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
[&] (const TilePosition& tilePosition)
{
const auto locked = mWorldspaceTiles.lock();
auto tile = locked->mTiles.find(tilePosition);
if (tile == locked->mTiles.end())
auto tile = worldspaceTiles->mTiles.find(tilePosition);
if (tile == worldspaceTiles->mTiles.end())
{
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
tile = locked->mTiles.emplace_hint(tile, tilePosition,
tile = worldspaceTiles->mTiles.emplace_hint(tile, tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
}
if (tile->second->addWater(cellPosition, cellSize, level))
@ -180,20 +180,22 @@ namespace DetourNavigator
if (object == mWaterTilesPositions.end())
return std::nullopt;
std::optional<Water> result;
for (const auto& tilePosition : object->second)
{
const auto locked = mWorldspaceTiles.lock();
const auto tile = locked->mTiles.find(tilePosition);
if (tile == locked->mTiles.end())
continue;
const auto tileResult = tile->second->removeWater(cellPosition);
if (tile->second->isEmpty())
const auto worldspaceTiles = mWorldspaceTiles.lock();
for (const auto& tilePosition : object->second)
{
locked->mTiles.erase(tile);
++mTilesGeneration;
const auto tile = worldspaceTiles->mTiles.find(tilePosition);
if (tile == worldspaceTiles->mTiles.end())
continue;
const auto tileResult = tile->second->removeWater(cellPosition);
if (tile->second->isEmpty())
{
worldspaceTiles->mTiles.erase(tile);
++mTilesGeneration;
}
if (tileResult && !result)
result = tileResult;
}
if (tileResult && !result)
result = tileResult;
}
mWaterTilesPositions.erase(object);
if (result)
@ -214,23 +216,25 @@ namespace DetourNavigator
bool result = false;
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
[&] (const TilePosition& tilePosition)
{
const auto locked = mWorldspaceTiles.lock();
auto tile = locked->mTiles.find(tilePosition);
if (tile == locked->mTiles.end())
{
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
tile = locked->mTiles.emplace_hint(tile, tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
}
if (tile->second->addHeightfield(cellPosition, cellSize, shape))
{
const auto worldspaceTiles = mWorldspaceTiles.lock();
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
[&] (const TilePosition& tilePosition)
{
tilesPositions.push_back(tilePosition);
result = true;
}
});
auto tile = worldspaceTiles->mTiles.find(tilePosition);
if (tile == worldspaceTiles->mTiles.end())
{
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
tile = worldspaceTiles->mTiles.emplace_hint(tile, tilePosition,
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
}
if (tile->second->addHeightfield(cellPosition, cellSize, shape))
{
tilesPositions.push_back(tilePosition);
result = true;
}
});
}
if (result)
++mRevision;
@ -244,20 +248,22 @@ namespace DetourNavigator
if (object == mHeightfieldTilesPositions.end())
return std::nullopt;
std::optional<SizedHeightfieldShape> result;
for (const auto& tilePosition : object->second)
{
const auto locked = mWorldspaceTiles.lock();
const auto tile = locked->mTiles.find(tilePosition);
if (tile == locked->mTiles.end())
continue;
const auto tileResult = tile->second->removeHeightfield(cellPosition);
if (tile->second->isEmpty())
const auto worldspaceTiles = mWorldspaceTiles.lock();
for (const auto& tilePosition : object->second)
{
locked->mTiles.erase(tile);
++mTilesGeneration;
const auto tile = worldspaceTiles->mTiles.find(tilePosition);
if (tile == worldspaceTiles->mTiles.end())
continue;
const auto tileResult = tile->second->removeHeightfield(cellPosition);
if (tile->second->isEmpty())
{
worldspaceTiles->mTiles.erase(tile);
++mTilesGeneration;
}
if (tileResult && !result)
result = tileResult;
}
if (tileResult && !result)
result = tileResult;
}
mHeightfieldTilesPositions.erase(object);
if (result)

@ -165,7 +165,7 @@ namespace DetourNavigator
bool addTile(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles);
bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType,
static bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType,
const TilePosition& tilePosition, TilesMap& tiles);
std::optional<RemovedRecastMeshObject> removeTile(const ObjectId id, const TilePosition& tilePosition,

@ -2,6 +2,7 @@
#define COMPONENTS_LOADINGLISTENER_H
#include <string>
#include <memory>
namespace Loading
{
@ -33,12 +34,26 @@ namespace Loading
virtual ~Listener() = default;
};
struct LoadingOff
{
void operator()(Listener* listener) const
{
if (listener != nullptr)
listener->loadingOff();
}
};
/// @brief Used for stopping a loading sequence when the object goes out of scope
struct ScopedLoad
{
ScopedLoad(Listener* l) : mListener(l) { mListener->loadingOn(); }
~ScopedLoad() { mListener->loadingOff(); }
Listener* mListener;
std::unique_ptr<Listener, LoadingOff> mListener;
explicit ScopedLoad(Listener* listener)
: mListener(listener)
{
if (mListener != nullptr)
mListener->loadingOn();
}
};
}

@ -157,7 +157,7 @@ def draw_timeseries(sources, keys, add_sum, begin_frame, end_frame):
for key in keys:
ax.plot(x, frames[key], label=f'{key}:{name}')
if add_sum:
ax.plot(x, numpy.sum(list(frames[k] for k in keys), axis=0), label=f'sum:{name}')
ax.plot(x, numpy.sum(list(frames[k] for k in keys), axis=0), label=f'sum:{name}', linestyle='--')
ax.grid(True)
ax.legend()
fig.canvas.manager.set_window_title('timeseries')
@ -170,7 +170,8 @@ def draw_commulative_timeseries(sources, keys, add_sum, begin_frame, end_frame):
for key in keys:
ax.plot(x, numpy.cumsum(frames[key]), label=f'{key}:{name}')
if add_sum:
ax.plot(x, numpy.cumsum(numpy.sum(list(frames[k] for k in keys), axis=0)), label=f'sum:{name}')
ax.plot(x, numpy.cumsum(numpy.sum(list(frames[k] for k in keys), axis=0)), label=f'sum:{name}',
linestyle='--')
ax.grid(True)
ax.legend()
fig.canvas.manager.set_window_title('commulative_timeseries')
@ -313,6 +314,7 @@ def make_stats(source, key, values, precision):
number=len(values),
min=fixed_float(min(values), precision),
max=fixed_float(max(values), precision),
sum=fixed_float(sum(values), precision),
mean=fixed_float(statistics.mean(values), precision),
median=fixed_float(statistics.median(values), precision),
stdev=fixed_float(statistics.stdev(values), precision),

Loading…
Cancel
Save