1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-20 21:11:33 +00:00

Merge branch 'optimize_navigator_update' into 'master'

Optimize navigator per frame update

See merge request OpenMW/openmw!2308
This commit is contained in:
psi29a 2022-08-21 20:12:30 +00:00
commit 2a2268ea8b
21 changed files with 212 additions and 265 deletions

View file

@ -7,8 +7,6 @@
#include <components/esm3/esmwriter.hpp> #include <components/esm3/esmwriter.hpp>
#include <components/esm3/stolenitems.hpp> #include <components/esm3/stolenitems.hpp>
#include <components/detournavigator/navigator.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
@ -813,12 +811,6 @@ namespace MWMechanics
bool MechanicsManager::toggleAI() bool MechanicsManager::toggleAI()
{ {
mAI = !mAI; 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; return mAI;
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -439,8 +439,17 @@ namespace
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1); const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
EXPECT_TRUE(mNavigator->addHeightfield(mCellPosition, cellSize1, surface1)); mNavigator->addHeightfield(mCellPosition, cellSize1, surface1);
EXPECT_FALSE(mNavigator->addHeightfield(mCellPosition, cellSize2, surface2)); 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) TEST_F(DetourNavigatorNavigatorTest, path_should_be_around_avoid_shape)
@ -1142,7 +1151,16 @@ namespace
const float level2 = 2; const float level2 = 2;
mNavigator->addAgent(mAgentBounds); mNavigator->addAgent(mAgentBounds);
EXPECT_TRUE(mNavigator->addWater(mCellPosition, cellSize1, level1)); mNavigator->addWater(mCellPosition, cellSize1, level1);
EXPECT_FALSE(mNavigator->addWater(mCellPosition, cellSize2, level2)); 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);
} }
} }

View file

@ -216,27 +216,13 @@ namespace DetourNavigator
void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType) 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) switch (waitConditionType)
{ {
case WaitConditionType::requiredTilesPresent: case WaitConditionType::requiredTilesPresent:
{ waitUntilJobsDoneForNotPresentTiles(listener);
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; break;
}
case WaitConditionType::allJobsDone: case WaitConditionType::allJobsDone:
waitUntilAllJobsDone(); waitUntilAllJobsDone();
listener.setProgress(maxProgress);
break; break;
} }
} }
@ -255,8 +241,10 @@ namespace DetourNavigator
thread.join(); 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 prevJobsLeft = initialJobsLeft;
std::size_t jobsDone = 0; std::size_t jobsDone = 0;
std::size_t jobsLeft = 0; std::size_t jobsLeft = 0;
@ -275,7 +263,13 @@ namespace DetourNavigator
return minDistanceToPlayer >= maxDistanceToPlayer; 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)) 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) if (maxProgress < jobsLeft)
{ {
@ -291,14 +285,19 @@ namespace DetourNavigator
listener.increaseProgress(newJobsDone); 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() void AsyncNavMeshUpdater::waitUntilAllJobsDone()
{ {
{ {
std::unique_lock<std::mutex> lock(mMutex); 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(); }); mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
} }

View file

@ -223,9 +223,9 @@ namespace DetourNavigator
void cleanupLastUpdates(); 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); void reportStats(const AsyncNavMeshUpdater::Stats& stats, unsigned int frameNumber, osg::Stats& out);

View file

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

View file

@ -18,6 +18,8 @@ class dtNavMesh;
namespace DetourNavigator namespace DetourNavigator
{ {
struct Version;
std::ostream& operator<<(std::ostream& stream, const TileBounds& value); std::ostream& operator<<(std::ostream& stream, const TileBounds& value);
std::ostream& operator<<(std::ostream& stream, Status 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, ChangeType value);
std::ostream& operator<<(std::ostream& stream, const Version& value);
class RecastMesh; class RecastMesh;
struct RecastSettings; struct RecastSettings;

View file

@ -100,65 +100,56 @@ namespace DetourNavigator
* @param id is used to distinguish different objects * @param id is used to distinguish different objects
* @param shape members must live until object is updated by another shape removed from Navigator * @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 * @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. * @brief addObject is used to add doors.
* @param id is used to distinguish different objects. * @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 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. * @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. * @brief updateObject replace object geometry by given data.
* @param id is used to find object. * @param id is used to find object.
* @param shape members must live until object is updated by another shape removed from Navigator. * @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. * @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. * @brief updateObject replace object geometry by given data.
* @param id is used to find object. * @param id is used to find object.
* @param shape members must live until object is updated by another shape removed from Navigator. * @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. * @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. * @brief removeObject to make it no more available at the scene.
* @param id is used to find object. * @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. * @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 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 cellSize set cell borders. std::numeric_limits<int>::max() disables cell borders.
* @param shift set global shift of cell center. * @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. * @brief removeWater to make it no more available at the scene.
* @param cellPosition allows to find cell. * @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; 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; 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. * @brief wait locks thread until tiles are updated from last update call based on passed condition type.
* @param waitConditionType defines when waiting will stop * @param waitConditionType defines when waiting will stop

View file

@ -11,7 +11,6 @@ namespace DetourNavigator
NavigatorImpl::NavigatorImpl(const Settings& settings, std::unique_ptr<NavMeshDb>&& db) NavigatorImpl::NavigatorImpl(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings) : mSettings(settings)
, mNavMeshManager(mSettings, std::move(db)) , mNavMeshManager(mSettings, std::move(db))
, mUpdatesEnabled(true)
{ {
} }
@ -42,7 +41,12 @@ namespace DetourNavigator
mNavMeshManager.updateBounds(playerPosition); 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); const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);
bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground); bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);
@ -59,78 +63,71 @@ namespace DetourNavigator
return result; 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 start = toNavMeshCoordinates(mSettings.mRecast, shapes.mConnectionStart);
const osg::Vec3f end = toNavMeshCoordinates(mSettings.mRecast, shapes.mConnectionEnd); const osg::Vec3f end = toNavMeshCoordinates(mSettings.mRecast, shapes.mConnectionEnd);
mNavMeshManager.addOffMeshConnection(id, start, end, AreaType_door); mNavMeshManager.addOffMeshConnection(id, start, end, AreaType_door);
mNavMeshManager.addOffMeshConnection(id, end, start, 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); 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()) if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->mAvoidCollisionShape.get())
{ {
const ObjectId avoidId(avoidShape); const ObjectId avoidId(avoidShape);
const CollisionShape avoidCollisionShape(shapes.mShapeInstance, *avoidShape, shapes.mTransform); const CollisionShape avoidCollisionShape(shapes.mShapeInstance, *avoidShape, shapes.mTransform);
if (mNavMeshManager.updateObject(avoidId, avoidCollisionShape, transform, AreaType_null)) if (mNavMeshManager.updateObject(avoidId, avoidCollisionShape, transform, AreaType_null))
{
updateAvoidShapeId(id, avoidId); 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); 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); const auto avoid = mAvoidIds.find(id);
if (avoid != mAvoidIds.end()) if (avoid != mAvoidIds.end())
result = mNavMeshManager.removeObject(avoid->second) || result; mNavMeshManager.removeObject(avoid->second);
const auto water = mWaterIds.find(id); const auto water = mWaterIds.find(id);
if (water != mWaterIds.end()) if (water != mWaterIds.end())
result = mNavMeshManager.removeObject(water->second) || result; mNavMeshManager.removeObject(water->second);
mNavMeshManager.removeOffMeshConnections(id); 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) void NavigatorImpl::addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid)
{ {
Misc::CoordinateConverter converter(&cell); 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 src = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV0]));
const auto dst = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV1])); const auto dst = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV1]));
@ -150,30 +147,14 @@ namespace DetourNavigator
void NavigatorImpl::update(const osg::Vec3f& playerPosition) void NavigatorImpl::update(const osg::Vec3f& playerPosition)
{ {
if (!mUpdatesEnabled)
return;
removeUnusedNavMeshes(); removeUnusedNavMeshes();
for (const auto& v : mAgents) for (const auto& v : mAgents)
mNavMeshManager.update(playerPosition, v.first); 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) void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType)
{ {
if (mSettings.mWaitUntilMinDistanceToPlayer > 0)
mNavMeshManager.wait(listener, waitConditionType); mNavMeshManager.wait(listener, waitConditionType);
} }

View file

@ -26,23 +26,23 @@ namespace DetourNavigator
void updateBounds(const osg::Vec3f& playerPosition) override; 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; 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 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; void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override;
SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const override; SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const override;
@ -71,12 +67,12 @@ namespace DetourNavigator
private: private:
Settings mSettings; Settings mSettings;
NavMeshManager mNavMeshManager; NavMeshManager mNavMeshManager;
bool mUpdatesEnabled;
std::optional<TilePosition> mLastPlayerPosition; std::optional<TilePosition> mLastPlayerPosition;
std::map<AgentBounds, std::size_t> mAgents; std::map<AgentBounds, std::size_t> mAgents;
std::unordered_map<ObjectId, ObjectId> mAvoidIds; std::unordered_map<ObjectId, ObjectId> mAvoidIds;
std::unordered_map<ObjectId, ObjectId> mWaterIds; 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 updateAvoidShapeId(const ObjectId id, const ObjectId avoidId);
void updateWaterShapeId(const ObjectId id, const ObjectId waterId); void updateWaterShapeId(const ObjectId id, const ObjectId waterId);
void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map<ObjectId, ObjectId>& ids); void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map<ObjectId, ObjectId>& ids);

View file

@ -22,50 +22,24 @@ namespace DetourNavigator
void setWorldspace(std::string_view /*worldspace*/) override {} void setWorldspace(std::string_view /*worldspace*/) 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 {}
{
return false;
}
bool addObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override void addObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override {}
{
return false;
}
bool updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override void updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override {}
{
return false;
}
bool updateObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override void updateObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override {}
{
return false;
}
bool removeObject(const ObjectId /*id*/) override void removeObject(const ObjectId /*id*/) override {}
{
return false;
}
bool addWater(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, float /*level*/) override void addWater(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, float /*level*/) override {}
{
return false;
}
bool removeWater(const osg::Vec2i& /*cellPosition*/) override void removeWater(const osg::Vec2i& /*cellPosition*/) override {}
{
return false;
}
bool addHeightfield(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, const HeightfieldShape& /*height*/) override void addHeightfield(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, const HeightfieldShape& /*height*/)
{ override {}
return false;
}
bool removeHeightfield(const osg::Vec2i& /*cellPosition*/) override void removeHeightfield(const osg::Vec2i& /*cellPosition*/) override {}
{
return false;
}
void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) 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 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 {} void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {}
SharedNavMeshCacheItem getNavMesh(const AgentBounds& /*agentBounds*/) const override SharedNavMeshCacheItem getNavMesh(const AgentBounds& /*agentBounds*/) const override

View file

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

View file

@ -32,17 +32,17 @@ namespace DetourNavigator
bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);
bool removeObject(const ObjectId id); void removeObject(const ObjectId id);
void addAgent(const AgentBounds& agentBounds); 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); bool reset(const AgentBounds& agentBounds);

View file

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

View file

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

View file

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

View file

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

View file

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