From 542717394a7f18553843bcaf0ee4b976b5baf2e8 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 1 Feb 2022 21:48:06 +0100 Subject: [PATCH 01/10] Remove objects, water and heightfields when no longer required --- .../detournavigator/navigator.cpp | 12 ++++++++++ .../tilecachedrecastmeshmanager.cpp | 4 ++-- .../tilecachedrecastmeshmanager.cpp | 22 ++++++++++++++++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 09a2be58ee..50bf1fe735 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -1132,4 +1132,16 @@ namespace Vec3fEq(306, 56.66666412353515625, -2.6667339801788330078125) )) << mPath; } + + TEST_F(DetourNavigatorNavigatorTest, only_one_water_per_cell_is_allowed) + { + const int cellSize1 = 100; + const float level1 = 1; + const int cellSize2 = 200; + const float level2 = 2; + + mNavigator->addAgent(mAgentHalfExtents); + EXPECT_TRUE(mNavigator->addWater(mCellPosition, cellSize1, level1)); + EXPECT_FALSE(mNavigator->addWater(mCellPosition, cellSize2, level2)); + } } diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index c44ebc5155..c637d35424 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -63,7 +63,7 @@ namespace EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_throw_exception) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); @@ -225,7 +225,7 @@ namespace const CollisionShape shape(mInstance, boxShape, mObjectTransform); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeAddRevision = manager.getRevision(); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_EQ(manager.getRevision(), beforeAddRevision); } diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 17ba7afc39..d1f586f85c 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -33,6 +33,9 @@ namespace DetourNavigator bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, const AreaType areaType) { + const auto it = mObjectsTilesPositions.find(id); + if (it != mObjectsTilesPositions.end()) + return false; std::vector tilesPositions; { const std::lock_guard lock(mMutex); @@ -46,7 +49,7 @@ namespace DetourNavigator if (tilesPositions.empty()) return false; std::sort(tilesPositions.begin(), tilesPositions.end()); - mObjectsTilesPositions.insert_or_assign(id, std::move(tilesPositions)); + mObjectsTilesPositions.emplace_hint(it, id, std::move(tilesPositions)); ++mRevision; return true; } @@ -66,6 +69,7 @@ namespace DetourNavigator result = removed; } } + mObjectsTilesPositions.erase(object); if (result) ++mRevision; return result; @@ -73,7 +77,12 @@ namespace DetourNavigator bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) { - auto& tilesPositions = mWaterTilesPositions[cellPosition]; + const auto it = mWaterTilesPositions.find(cellPosition); + if (it != mWaterTilesPositions.end()) + return false; + + std::vector& tilesPositions = mWaterTilesPositions.emplace_hint( + it, cellPosition, std::vector())->second; bool result = false; @@ -138,6 +147,7 @@ namespace DetourNavigator if (tileResult && !result) result = tileResult; } + mWaterTilesPositions.erase(object); if (result) ++mRevision; return result; @@ -146,8 +156,13 @@ namespace DetourNavigator bool TileCachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) { + const auto it = mHeightfieldTilesPositions.find(cellPosition); + if (it != mHeightfieldTilesPositions.end()) + return false; + + std::vector& tilesPositions = mHeightfieldTilesPositions.emplace_hint( + it, cellPosition, std::vector())->second; const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize); - auto& tilesPositions = mHeightfieldTilesPositions[cellPosition]; bool result = false; @@ -196,6 +211,7 @@ namespace DetourNavigator if (tileResult && !result) result = tileResult; } + mHeightfieldTilesPositions.erase(object); if (result) ++mRevision; return result; From 783411fa1f6e958c473d14ad69f818464c56ea90 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 31 Jan 2022 22:33:42 +0100 Subject: [PATCH 02/10] Use new player position when updating navigator on cell loading --- apps/openmw/mwworld/scene.cpp | 14 ++++++++------ apps/openmw/mwworld/scene.hpp | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0d2db8b99a..3b2f9cd608 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -360,7 +360,7 @@ namespace MWWorld mActiveCells.erase(cell); } - void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn) + void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position) { using DetourNavigator::HeightfieldShape; @@ -461,7 +461,7 @@ namespace MWWorld mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f); } - mNavigator.update(player.getRefData().getPosition().asVec3()); + mNavigator.update(position); if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) mRendering.configureAmbient(cell->getCell()); @@ -608,7 +608,7 @@ namespace MWWorld if (!isCellInCollection(x, y, mActiveCells)) { CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); - loadCell (cell, loadingListener, changeEvent); + loadCell(cell, loadingListener, changeEvent, pos); } } @@ -641,7 +641,7 @@ namespace MWWorld loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")..."); CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY); - loadCell(cell, nullptr, false); + loadCell(cell, nullptr, false, osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits); auto iter = mActiveCells.begin(); while (iter != mActiveCells.end()) @@ -686,7 +686,9 @@ namespace MWWorld loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")..."); CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName); - loadCell(cell, nullptr, false); + ESM::Position position; + MWBase::Environment::get().getWorld()->findInteriorPosition(it->mName, position); + loadCell(cell, nullptr, false, position.asVec3()); auto iter = mActiveCells.begin(); while (iter != mActiveCells.end()) @@ -821,7 +823,7 @@ namespace MWWorld // Load cell. mPagedRefs.clear(); - loadCell(cell, loadingListener, changeEvent); + loadCell(cell, loadingListener, changeEvent, position.asVec3()); changePlayerCell(cell, position, adjustPlayerPos); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 623bddbc82..c0b6f5ae46 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -116,7 +116,7 @@ namespace MWWorld osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; void unloadCell(CellStore* cell); - void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn); + void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position); public: From 1a52a2a02909707a84e6afac6ea54a1926bc6f4f Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 1 Feb 2022 00:24:56 +0100 Subject: [PATCH 03/10] Clamp tile position --- components/detournavigator/settingsutils.hpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/components/detournavigator/settingsutils.hpp b/components/detournavigator/settingsutils.hpp index 285920e5a0..a4c68e3cd5 100644 --- a/components/detournavigator/settingsutils.hpp +++ b/components/detournavigator/settingsutils.hpp @@ -50,12 +50,24 @@ namespace DetourNavigator return static_cast(settings.mTileSize) * settings.mCellSize; } + inline int getTilePosition(const RecastSettings& settings, float position) + { + const float v = std::floor(position / getTileSize(settings)); + if (v < static_cast(std::numeric_limits::min())) + return std::numeric_limits::min(); + if (v > static_cast(std::numeric_limits::max() - 1)) + return std::numeric_limits::max() - 1; + return static_cast(v); + } + + inline TilePosition getTilePosition(const RecastSettings& settings, const osg::Vec2f& position) + { + return TilePosition(getTilePosition(settings, position.x()), getTilePosition(settings, position.y())); + } + inline TilePosition getTilePosition(const RecastSettings& settings, const osg::Vec3f& position) { - return TilePosition( - static_cast(std::floor(position.x() / getTileSize(settings))), - static_cast(std::floor(position.z() / getTileSize(settings))) - ); + return getTilePosition(settings, osg::Vec2f(position.x(), position.z())); } inline TileBounds makeTileBounds(const RecastSettings& settings, const TilePosition& tilePosition) From 1b2954f2db6545c68e7f4f2eaaecdbfbb1b6239a Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 1 Feb 2022 00:22:53 +0100 Subject: [PATCH 04/10] Remove unused z coordinate --- apps/navmeshtool/navmesh.cpp | 4 ++-- .../detournavigator/gettilespositions.cpp | 14 ++++++------ .../detournavigator/gettilespositions.cpp | 22 ++++++++----------- .../detournavigator/gettilespositions.hpp | 6 ++--- components/detournavigator/tilebounds.hpp | 13 +++++++++++ components/misc/convert.hpp | 7 +++++- 6 files changed, 40 insertions(+), 26 deletions(-) diff --git a/apps/navmeshtool/navmesh.cpp b/apps/navmeshtool/navmesh.cpp index 39f2b2f37c..ca614d0cf6 100644 --- a/apps/navmeshtool/navmesh.cpp +++ b/apps/navmeshtool/navmesh.cpp @@ -177,8 +177,8 @@ namespace NavMeshTool DetourNavigator::getTilesPositions( DetourNavigator::makeTilesPositionsRange( - Misc::Convert::toOsg(input->mAabb.m_min), - Misc::Convert::toOsg(input->mAabb.m_max), + Misc::Convert::toOsgXY(input->mAabb.m_min), + Misc::Convert::toOsgXY(input->mAabb.m_max), settings.mRecast ), [&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); } diff --git a/apps/openmw_test_suite/detournavigator/gettilespositions.cpp b/apps/openmw_test_suite/detournavigator/gettilespositions.cpp index 8121c19205..70c6ef9a09 100644 --- a/apps/openmw_test_suite/detournavigator/gettilespositions.cpp +++ b/apps/openmw_test_suite/detournavigator/gettilespositions.cpp @@ -37,35 +37,35 @@ namespace TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_in_single_tile_should_return_one_tile) { - getTilesPositions(makeTilesPositionsRange(osg::Vec3f(2, 2, 0), osg::Vec3f(31, 31, 1), mSettings), mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec2f(2, 2), osg::Vec2f(31, 31), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0))); } TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_x_bounds_in_two_tiles_should_return_two_tiles) { - getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 31, 1), mSettings), mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(32, 31), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(1, 0))); } TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_y_bounds_in_two_tiles_should_return_two_tiles) { - getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 32, 1), mSettings), mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(31, 32), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(0, 1))); } TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_works_only_for_x_and_y_coordinates) { - getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 31, 32), mSettings), mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(31, 31), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0))); } TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_should_work_with_negative_coordinates) { - getTilesPositions(makeTilesPositionsRange(osg::Vec3f(-31, -31, 0), osg::Vec3f(31, 31, 1), mSettings), mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec2f(-31, -31), osg::Vec2f(31, 31), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre( TilePosition(-1, -1), @@ -79,7 +79,7 @@ namespace { mSettings.mBorderSize = 1; - getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31.5, 31.5, 1), mSettings), mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(31.5, 31.5), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre( TilePosition(-1, -1), @@ -98,7 +98,7 @@ namespace { mSettings.mRecastScaleFactor = 0.5; - getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 32, 1), mSettings), mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(32, 32), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0))); } diff --git a/components/detournavigator/gettilespositions.cpp b/components/detournavigator/gettilespositions.cpp index d427eb3e12..4bf5159a88 100644 --- a/components/detournavigator/gettilespositions.cpp +++ b/components/detournavigator/gettilespositions.cpp @@ -2,6 +2,7 @@ #include "settings.hpp" #include "settingsutils.hpp" #include "tileposition.hpp" +#include "tilebounds.hpp" #include @@ -9,15 +10,15 @@ namespace DetourNavigator { - TilesPositionsRange makeTilesPositionsRange(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax, + TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin, const osg::Vec2f& aabbMax, const RecastSettings& settings) { - osg::Vec3f min = toNavMeshCoordinates(settings, aabbMin); - osg::Vec3f max = toNavMeshCoordinates(settings, aabbMax); + osg::Vec2f min = toNavMeshCoordinates(settings, aabbMin); + osg::Vec2f max = toNavMeshCoordinates(settings, aabbMax); const float border = getBorderSize(settings); - min -= osg::Vec3f(border, border, border); - max += osg::Vec3f(border, border, border); + min -= osg::Vec2f(border, border); + max += osg::Vec2f(border, border); TilePosition minTile = getTilePosition(settings, min); TilePosition maxTile = getTilePosition(settings, max); @@ -34,18 +35,13 @@ namespace DetourNavigator TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform, const RecastSettings& settings) { - btVector3 aabbMin; - btVector3 aabbMax; - shape.getAabb(transform, aabbMin, aabbMax); - - return makeTilesPositionsRange(Misc::Convert::toOsg(aabbMin), Misc::Convert::toOsg(aabbMax), settings); + const TileBounds bounds = makeObjectTileBounds(shape, transform); + return makeTilesPositionsRange(bounds.mMin, bounds.mMax, settings); } TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift, const RecastSettings& settings) { - using Misc::Convert::toOsg; - const int halfCellSize = cellSize / 2; const btTransform transform(btMatrix3x3::getIdentity(), shift); btVector3 aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0)); @@ -57,6 +53,6 @@ namespace DetourNavigator aabbMax.setX(std::max(aabbMin.x(), aabbMax.x())); aabbMax.setY(std::max(aabbMin.y(), aabbMax.y())); - return makeTilesPositionsRange(toOsg(aabbMin), toOsg(aabbMax), settings); + return makeTilesPositionsRange(Misc::Convert::toOsgXY(aabbMin), Misc::Convert::toOsgXY(aabbMax), settings); } } diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index 946f3e64f2..8a8b2b7c32 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -9,7 +9,7 @@ class btCollisionShape; namespace osg { - class Vec3f; + class Vec2f; } namespace DetourNavigator @@ -22,8 +22,8 @@ namespace DetourNavigator TilePosition mMax; }; - TilesPositionsRange makeTilesPositionsRange(const osg::Vec3f& aabbMin, - const osg::Vec3f& aabbMax, const RecastSettings& settings); + TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin, + const osg::Vec2f& aabbMax, const RecastSettings& settings); TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform, const RecastSettings& settings); diff --git a/components/detournavigator/tilebounds.hpp b/components/detournavigator/tilebounds.hpp index 693a382740..79f6370ff2 100644 --- a/components/detournavigator/tilebounds.hpp +++ b/components/detournavigator/tilebounds.hpp @@ -1,9 +1,14 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H +#include + #include #include +#include +#include + #include #include #include @@ -41,6 +46,14 @@ namespace DetourNavigator osg::Vec2f(position.x() + 1, position.y() + 1) * size }; } + + inline TileBounds makeObjectTileBounds(const btCollisionShape& shape, const btTransform& transform) + { + btVector3 aabbMin; + btVector3 aabbMax; + shape.getAabb(transform, aabbMin, aabbMax); + return TileBounds {Misc::Convert::toOsgXY(aabbMin), Misc::Convert::toOsgXY(aabbMax)}; + } } #endif diff --git a/components/misc/convert.hpp b/components/misc/convert.hpp index f31f1cc08e..0b84c32059 100644 --- a/components/misc/convert.hpp +++ b/components/misc/convert.hpp @@ -70,6 +70,11 @@ namespace Misc::Convert { return btTransform(makeBulletQuaternion(position), toBullet(position.asVec3())); } + + inline osg::Vec2f toOsgXY(const btVector3& value) + { + return osg::Vec2f(static_cast(value.x()), static_cast(value.y())); + } } -#endif \ No newline at end of file +#endif From a5b078e9a714ef0c078bf74ab041b02ded8c07e2 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 1 Feb 2022 00:35:03 +0100 Subject: [PATCH 05/10] Allow to represent empty range with TilesPositionsRange --- components/detournavigator/gettilespositions.cpp | 2 +- components/detournavigator/gettilespositions.hpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/detournavigator/gettilespositions.cpp b/components/detournavigator/gettilespositions.cpp index 4bf5159a88..7a179aff24 100644 --- a/components/detournavigator/gettilespositions.cpp +++ b/components/detournavigator/gettilespositions.cpp @@ -29,7 +29,7 @@ namespace DetourNavigator if (minTile.y() > maxTile.y()) std::swap(minTile.y(), maxTile.y()); - return {minTile, maxTile}; + return {minTile, maxTile + osg::Vec2i(1, 1)}; } TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform, diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index 8a8b2b7c32..db88708314 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -18,8 +18,8 @@ namespace DetourNavigator struct TilesPositionsRange { - TilePosition mMin; - TilePosition mMax; + TilePosition mBegin; + TilePosition mEnd; }; TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin, @@ -32,10 +32,10 @@ namespace DetourNavigator const RecastSettings& settings); template - void getTilesPositions(const TilesPositionsRange& range, Callback&& callback) + inline void getTilesPositions(const TilesPositionsRange& range, Callback&& callback) { - for (int tileX = range.mMin.x(); tileX <= range.mMax.x(); ++tileX) - for (int tileY = range.mMin.y(); tileY <= range.mMax.y(); ++tileY) + for (int tileX = range.mBegin.x(); tileX < range.mEnd.x(); ++tileX) + for (int tileY = range.mBegin.y(); tileY < range.mEnd.y(); ++tileY) callback(TilePosition {tileX, tileY}); } } From 563f3f87dd2dd6f174eb00c72853204ae432ca0b Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 1 Feb 2022 00:42:03 +0100 Subject: [PATCH 06/10] Reduce critical sections size --- components/detournavigator/tilecachedrecastmeshmanager.cpp | 3 ++- components/detournavigator/tilecachedrecastmeshmanager.hpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index d1f586f85c..314e7641e3 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -38,8 +38,9 @@ namespace DetourNavigator return false; std::vector tilesPositions; { + const TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings); const std::lock_guard lock(mMutex); - getTilesPositions(makeTilesPositionsRange(shape.getShape(), transform, mSettings), + getTilesPositions(range, [&] (const TilePosition& tilePosition) { if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 88face24ce..efd70477d3 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -38,7 +38,6 @@ namespace DetourNavigator bool changed = false; std::vector newTiles; { - const std::lock_guard lock(mMutex); const auto onTilePosition = [&] (const TilePosition& tilePosition) { if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition)) @@ -57,7 +56,9 @@ namespace DetourNavigator changed = true; } }; - getTilesPositions(makeTilesPositionsRange(shape.getShape(), transform, mSettings), onTilePosition); + const TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings); + const std::lock_guard lock(mMutex); + getTilesPositions(range, onTilePosition); std::sort(newTiles.begin(), newTiles.end()); for (const auto& tile : currentTiles) { From 05b54cbfb86ec12e35278e0a69c108fabd4fb4b2 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 1 Feb 2022 01:21:47 +0100 Subject: [PATCH 07/10] Cull navmesh objects by scene bounds If object is too big iteration over all tiles covering it can take too much time. Limit bounds to a square around a player position to cover only tiles that will be present in navmesh based on max tiles number option. Each object is associated with a set of tiles its present in. Culling can reduce this set but it has to be update when bounds change position. Do this in TileCachedRecastMeshManager::setBounds updating the set and adding/removing objects to the corresponding CachedRecastMeshManagers. --- apps/navmeshtool/worldspacedata.cpp | 6 +- apps/openmw/mwworld/scene.cpp | 4 + .../detournavigator/asyncnavmeshupdater.cpp | 2 +- .../detournavigator/operators.hpp | 8 -- .../tilecachedrecastmeshmanager.cpp | 127 ++++++++++++------ .../detournavigator/asyncnavmeshupdater.hpp | 24 +--- components/detournavigator/changetype.hpp | 33 +++++ .../detournavigator/gettilespositions.cpp | 21 +++ .../detournavigator/gettilespositions.hpp | 17 +++ components/detournavigator/navigator.hpp | 6 + components/detournavigator/navigatorimpl.cpp | 6 + components/detournavigator/navigatorimpl.hpp | 2 + components/detournavigator/navigatorstub.hpp | 2 + components/detournavigator/navmeshmanager.cpp | 43 +++++- components/detournavigator/navmeshmanager.hpp | 2 + components/detournavigator/settingsutils.hpp | 5 + components/detournavigator/tilebounds.hpp | 17 ++- .../tilecachedrecastmeshmanager.cpp | 107 +++++++++++---- .../tilecachedrecastmeshmanager.hpp | 73 +++++++--- 19 files changed, 378 insertions(+), 127 deletions(-) create mode 100644 components/detournavigator/changetype.hpp diff --git a/apps/navmeshtool/worldspacedata.cpp b/apps/navmeshtool/worldspacedata.cpp index 742e6d775d..81bbd857f5 100644 --- a/apps/navmeshtool/worldspacedata.cpp +++ b/apps/navmeshtool/worldspacedata.cpp @@ -298,12 +298,14 @@ namespace NavMeshTool const ObjectId objectId(++objectsCounter); const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform()); - navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, DetourNavigator::AreaType_ground); + navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, + DetourNavigator::AreaType_ground, [] (const auto&) {}); if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get()) { const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform()); - navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform, DetourNavigator::AreaType_null); + navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform, + DetourNavigator::AreaType_null, [] (const auto&) {}); } data.mObjects.emplace_back(std::move(object)); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 3b2f9cd608..1cb80389ce 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -534,6 +534,8 @@ namespace MWWorld unloadCell (cell); } + mNavigator.updateBounds(pos); + mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); mRendering.setActiveGrid(newGrid); @@ -821,6 +823,8 @@ namespace MWWorld loadingListener->setProgressRange(cell->count()); + mNavigator.updateBounds(position.asVec3()); + // Load cell. mPagedRefs.clear(); loadCell(cell, loadingListener, changeEvent, position.asVec3()); diff --git a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp index b23446cea7..71c013d9f4 100644 --- a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp +++ b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp @@ -40,7 +40,7 @@ namespace osg::ref_ptr(new Resource::BulletShapeInstance(bulletShape)), shape, objectTransform ); - recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground); + recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground, [] (auto) {}); } struct DetourNavigatorAsyncNavMeshUpdaterTest : Test diff --git a/apps/openmw_test_suite/detournavigator/operators.hpp b/apps/openmw_test_suite/detournavigator/operators.hpp index fb6fcc5c39..60e1400764 100644 --- a/apps/openmw_test_suite/detournavigator/operators.hpp +++ b/apps/openmw_test_suite/detournavigator/operators.hpp @@ -12,14 +12,6 @@ #include -namespace DetourNavigator -{ - static inline bool operator ==(const TileBounds& lhs, const TileBounds& rhs) - { - return lhs.mMin == rhs.mMin && lhs.mMax == rhs.mMax; - } -} - namespace { template diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index c637d35424..4803f452b3 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -16,7 +16,8 @@ namespace struct DetourNavigatorTileCachedRecastMeshManagerTest : Test { RecastSettings mSettings; - std::vector mChangedTiles; + std::vector mAddedTiles; + std::vector> mChangedTiles; const ObjectTransform mObjectTransform {ESM::Position {{0, 0, 0}, {0, 0, 0}}, 0.0f}; const osg::ref_ptr mShape = new Resource::BulletShape; const osg::ref_ptr mInstance = new Resource::BulletShapeInstance(mShape); @@ -29,9 +30,14 @@ namespace mSettings.mTileSize = 64; } - void onChangedTile(const TilePosition& tilePosition) + void onAddedTile(const TilePosition& tilePosition) { - mChangedTiles.push_back(tilePosition); + mAddedTiles.push_back(tilePosition); + } + + void onChangedTile(const TilePosition& tilePosition, ChangeType changeType) + { + mChangedTiles.emplace_back(tilePosition, changeType); } }; @@ -60,16 +66,16 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_throw_exception) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); - EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); + EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) @@ -78,26 +84,47 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); for (int x = -1; x < 1; ++x) for (int y = -1; y < 1; ++y) ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); } + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_return_added_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + const CollisionShape shape(mInstance, boxShape, mObjectTransform); + TileBounds bounds; + bounds.mMin = osg::Vec2f(182, 182); + bounds.mMax = osg::Vec2f(1000, 1000); + manager.setBounds(bounds); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, + [&] (const auto& v) { onAddedTile(v); }); + EXPECT_THAT(mAddedTiles, ElementsAre(TilePosition(0, 0))); + } + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + TileBounds bounds; + bounds.mMin = osg::Vec2f(-1000, -1000); + bounds.mMax = osg::Vec2f(1000, 1000); + manager.setBounds(bounds); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {}); EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, - [&] (const auto& v) { onChangedTile(v); })); - EXPECT_THAT( - mChangedTiles, - ElementsAre(TilePosition(-1, -1), TilePosition(-1, 0), TilePosition(0, -1), TilePosition(0, 0), - TilePosition(1, -1), TilePosition(1, 0)) - ); + [&] (const auto& ... v) { onChangedTile(v ...); })); + EXPECT_THAT(mChangedTiles, ElementsAre( + std::pair(TilePosition(-1, -1), ChangeType::add), + std::pair(TilePosition(-1, 0), ChangeType::add), + std::pair(TilePosition(0, -1), ChangeType::update), + std::pair(TilePosition(0, 0), ChangeType::update), + std::pair(TilePosition(1, -1), ChangeType::remove), + std::pair(TilePosition(1, 0), ChangeType::remove) + )); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_return_empty) @@ -105,10 +132,10 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, - [&] (const auto& v) { onChangedTile(v); })); - EXPECT_EQ(mChangedTiles, std::vector()); + [&] (const auto& ... v) { onChangedTile(v ...); })); + EXPECT_THAT(mChangedTiles, IsEmpty()); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile) @@ -117,7 +144,7 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); @@ -130,26 +157,30 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile) { TileCachedRecastMeshManager manager(mSettings); + TileBounds bounds; + bounds.mMin = osg::Vec2f(-1000, -1000); + bounds.mMax = osg::Vec2f(1000, 1000); + manager.setBounds(bounds); manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {}); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {}); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); @@ -165,11 +196,11 @@ namespace const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {}); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {}); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); } @@ -180,7 +211,7 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.removeObject(ObjectId(&boxShape)); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); @@ -195,13 +226,13 @@ namespace const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {}); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); @@ -214,7 +245,7 @@ namespace const auto initialRevision = manager.getRevision(); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); EXPECT_EQ(manager.getRevision(), initialRevision + 1); } @@ -223,9 +254,9 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); const auto beforeAddRevision = manager.getRevision(); - EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); EXPECT_EQ(manager.getRevision(), beforeAddRevision); } @@ -235,9 +266,9 @@ namespace const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {}); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {}); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); } @@ -247,9 +278,9 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {}); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); } @@ -258,7 +289,7 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); const auto beforeRemoveRevision = manager.getRevision(); manager.removeObject(ObjectId(&boxShape)); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1); @@ -298,7 +329,7 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); const osg::Vec2i cellPosition(0, 0); const int cellSize = std::numeric_limits::max(); ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); @@ -343,7 +374,7 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); @@ -361,7 +392,7 @@ namespace const int cellSize = 8192; const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); ASSERT_TRUE(manager.removeObject(ObjectId(&boxShape))); for (int x = -1; x < 12; ++x) @@ -375,10 +406,28 @@ namespace manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(nullptr, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); manager.setWorldspace("other"); for (int x = -1; x < 1; ++x) for (int y = -1; y < 1; ++y) ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr); } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_bounds_should_return_changed_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + const CollisionShape shape(mInstance, boxShape, mObjectTransform); + TileBounds bounds; + bounds.mMin = osg::Vec2f(182, 0); + bounds.mMax = osg::Vec2f(1000, 1000); + manager.setBounds(bounds); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); + bounds.mMin = osg::Vec2f(-1000, -1000); + bounds.mMax = osg::Vec2f(0, -182); + EXPECT_THAT(manager.setBounds(bounds), ElementsAre( + std::pair(TilePosition(-1, -1), ChangeType::add), + std::pair(TilePosition(0, 0), ChangeType::remove) + )); + } } diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 31778f8c26..fa8cba03af 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -8,6 +8,7 @@ #include "navmeshtilescache.hpp" #include "waitconditiontype.hpp" #include "navmeshdb.hpp" +#include "changetype.hpp" #include @@ -32,29 +33,6 @@ namespace Loading namespace DetourNavigator { - enum class ChangeType - { - remove = 0, - mixed = 1, - add = 2, - update = 3, - }; - - inline std::ostream& operator <<(std::ostream& stream, ChangeType value) - { - switch (value) { - case ChangeType::remove: - return stream << "ChangeType::remove"; - case ChangeType::mixed: - return stream << "ChangeType::mixed"; - case ChangeType::add: - return stream << "ChangeType::add"; - case ChangeType::update: - return stream << "ChangeType::update"; - } - return stream << "ChangeType::" << static_cast(value); - } - enum class JobState { Initial, diff --git a/components/detournavigator/changetype.hpp b/components/detournavigator/changetype.hpp new file mode 100644 index 0000000000..1ede6aec13 --- /dev/null +++ b/components/detournavigator/changetype.hpp @@ -0,0 +1,33 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CHANGETYPE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CHANGETYPE_H + +#include + +namespace DetourNavigator +{ + enum class ChangeType + { + remove = 0, + mixed = 1, + add = 2, + update = 3, + }; + + inline std::ostream& operator <<(std::ostream& stream, ChangeType value) + { + switch (value) + { + case ChangeType::remove: + return stream << "ChangeType::remove"; + case ChangeType::mixed: + return stream << "ChangeType::mixed"; + case ChangeType::add: + return stream << "ChangeType::add"; + case ChangeType::update: + return stream << "ChangeType::update"; + } + return stream << "ChangeType::" << static_cast(value); + } +} + +#endif diff --git a/components/detournavigator/gettilespositions.cpp b/components/detournavigator/gettilespositions.cpp index 7a179aff24..2d42ec25bb 100644 --- a/components/detournavigator/gettilespositions.cpp +++ b/components/detournavigator/gettilespositions.cpp @@ -39,6 +39,14 @@ namespace DetourNavigator return makeTilesPositionsRange(bounds.mMin, bounds.mMax, settings); } + TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform, + const TileBounds& bounds, const RecastSettings& settings) + { + if (const auto intersection = getIntersection(bounds, makeObjectTileBounds(shape, transform))) + return makeTilesPositionsRange(intersection->mMin, intersection->mMax, settings); + return {}; + } + TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift, const RecastSettings& settings) { @@ -55,4 +63,17 @@ namespace DetourNavigator return makeTilesPositionsRange(Misc::Convert::toOsgXY(aabbMin), Misc::Convert::toOsgXY(aabbMax), settings); } + + TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept + { + const int beginX = std::max(a.mBegin.x(), b.mBegin.x()); + const int endX = std::min(a.mEnd.x(), b.mEnd.x()); + if (beginX > endX) + return {}; + const int beginY = std::max(a.mBegin.y(), b.mBegin.y()); + const int endY = std::min(a.mEnd.y(), b.mEnd.y()); + if (beginY > endY) + return {}; + return TilesPositionsRange {TilePosition(beginX, beginY), TilePosition(endX, endY)}; + } } diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index db88708314..79188868dc 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H +#include "tilebounds.hpp" #include "tileposition.hpp" class btVector3; @@ -28,6 +29,9 @@ namespace DetourNavigator TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform, const RecastSettings& settings); + TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, + const btTransform& transform, const TileBounds& bounds, const RecastSettings& settings); + TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift, const RecastSettings& settings); @@ -38,6 +42,19 @@ namespace DetourNavigator for (int tileY = range.mBegin.y(); tileY < range.mEnd.y(); ++tileY) callback(TilePosition {tileX, tileY}); } + + inline bool isInTilesPositionsRange(int begin, int end, int coordinate) + { + return begin <= coordinate && coordinate < end; + } + + inline bool isInTilesPositionsRange(const TilesPositionsRange& range, const TilePosition& position) + { + return isInTilesPositionsRange(range.mBegin.x(), range.mEnd.x(), position.x()) + && isInTilesPositionsRange(range.mBegin.y(), range.mEnd.y(), position.y()); + } + + TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept; } #endif diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 14731fcc5b..65f7c43fc1 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -83,6 +83,12 @@ namespace DetourNavigator */ virtual void setWorldspace(std::string_view worldspace) = 0; + /** + * @brief updateBounds should be called before adding object from loading cell + * @param playerPosition corresponds to the bounds center + */ + virtual void updateBounds(const osg::Vec3f& playerPosition) = 0; + /** * @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes * @param id is used to distinguish different objects diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 4f6ec79328..42cd8bd9d1 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -38,6 +38,11 @@ namespace DetourNavigator mNavMeshManager.setWorldspace(worldspace); } + void NavigatorImpl::updateBounds(const osg::Vec3f& playerPosition) + { + mNavMeshManager.updateBounds(playerPosition); + } + bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) { const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform); @@ -158,6 +163,7 @@ namespace DetourNavigator const TilePosition tilePosition = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition)); if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition) return; + mNavMeshManager.updateBounds(playerPosition); update(playerPosition); mLastPlayerPosition = tilePosition; } diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 116817395c..58c4c6c05d 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -24,6 +24,8 @@ namespace DetourNavigator void setWorldspace(std::string_view worldspace) override; + void updateBounds(const osg::Vec3f& playerPosition) override; + bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override; bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 6a320c1a08..466ff5c912 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -72,6 +72,8 @@ namespace DetourNavigator void update(const osg::Vec3f& /*playerPosition*/) override {} + void updateBounds(const osg::Vec3f& /*playerPosition*/) override {} + void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {}; void setUpdatesEnabled(bool /*enabled*/) override {} diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 9fba4ad611..bbaf010807 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -42,6 +42,18 @@ namespace namespace DetourNavigator { + namespace + { + TileBounds makeBounds(const RecastSettings& settings, const osg::Vec2f& center, int maxTiles) + { + const float radius = fromNavMeshCoordinates(settings, std::ceil(std::sqrt(static_cast(maxTiles) / osg::PIf) + 1) * getTileSize(settings)); + TileBounds result; + result.mMin = center - osg::Vec2f(radius, radius); + result.mMax = center + osg::Vec2f(radius, radius); + return result; + } + } + NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr&& db) : mSettings(settings) , mRecastMeshManager(settings.mRecast) @@ -59,21 +71,37 @@ namespace DetourNavigator mWorldspace = worldspace; } + void NavMeshManager::updateBounds(const osg::Vec3f& playerPosition) + { + const TileBounds bounds = makeBounds(mSettings.mRecast, osg::Vec2f(playerPosition.x(), playerPosition.y()), + mSettings.mMaxTilesNumber); + const auto changedTiles = mRecastMeshManager.setBounds(bounds); + for (const auto& [agent, cache] : mCache) + { + auto& tiles = mChangedTiles[agent]; + for (const auto& [tilePosition, changeType] : changedTiles) + { + auto tile = tiles.find(tilePosition); + if (tile == tiles.end()) + tiles.emplace_hint(tile, tilePosition, changeType); + else + tile->second = addChangeType(tile->second, changeType); + } + } + } + bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, const AreaType areaType) { - const btCollisionShape& collisionShape = shape.getShape(); - if (!mRecastMeshManager.addObject(id, shape, transform, areaType)) - return false; - addChangedTiles(collisionShape, transform, ChangeType::add); - return true; + return mRecastMeshManager.addObject(id, shape, transform, areaType, + [&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::add); }); } bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, const AreaType areaType) { return mRecastMeshManager.updateObject(id, shape, transform, areaType, - [&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::update); }); + [&] (const TilePosition& tile, ChangeType changeType) { addChangedTile(tile, changeType); }); } bool NavMeshManager::removeObject(const ObjectId id) @@ -263,7 +291,8 @@ namespace DetourNavigator void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType) { - getTilesPositions(makeTilesPositionsRange(shape, transform, mSettings.mRecast), + const auto bounds = mRecastMeshManager.getBounds(); + getTilesPositions(makeTilesPositionsRange(shape, transform, bounds, mSettings.mRecast), [&] (const TilePosition& v) { addChangedTile(v, changeType); }); } diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 3fd2d28d74..01937d22bd 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -26,6 +26,8 @@ namespace DetourNavigator void setWorldspace(std::string_view worldspace); + void updateBounds(const osg::Vec3f& playerPosition); + bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, const AreaType areaType); diff --git a/components/detournavigator/settingsutils.hpp b/components/detournavigator/settingsutils.hpp index a4c68e3cd5..c1e611aaf8 100644 --- a/components/detournavigator/settingsutils.hpp +++ b/components/detournavigator/settingsutils.hpp @@ -37,6 +37,11 @@ namespace DetourNavigator }; } + inline float fromNavMeshCoordinates(const RecastSettings& settings, float value) + { + return value / settings.mRecastScaleFactor; + } + inline osg::Vec3f fromNavMeshCoordinates(const RecastSettings& settings, osg::Vec3f position) { const auto factor = 1.0f / settings.mRecastScaleFactor; diff --git a/components/detournavigator/tilebounds.hpp b/components/detournavigator/tilebounds.hpp index 79f6370ff2..2dc32292e8 100644 --- a/components/detournavigator/tilebounds.hpp +++ b/components/detournavigator/tilebounds.hpp @@ -21,9 +21,24 @@ namespace DetourNavigator osg::Vec2f mMax; }; + inline auto tie(const TileBounds& value) noexcept + { + return std::tie(value.mMin, value.mMax); + } + inline bool operator<(const TileBounds& lhs, const TileBounds& rhs) noexcept { - return std::tie(lhs.mMin, lhs.mMax) < std::tie(rhs.mMin, rhs.mMax); + return tie(lhs) < tie(rhs); + } + + inline bool operator ==(const TileBounds& lhs, const TileBounds& rhs) noexcept + { + return tie(lhs) == tie(rhs); + } + + inline bool operator !=(const TileBounds& lhs, const TileBounds& rhs) noexcept + { + return !(lhs == rhs); } inline std::optional getIntersection(const TileBounds& a, const TileBounds& b) noexcept diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 314e7641e3..c7bd3bc867 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -2,19 +2,93 @@ #include "makenavmesh.hpp" #include "gettilespositions.hpp" #include "settingsutils.hpp" +#include "changetype.hpp" #include #include #include #include +#include namespace DetourNavigator { + namespace + { + const TileBounds infiniteTileBounds { + osg::Vec2f(-std::numeric_limits::max(), -std::numeric_limits::max()), + osg::Vec2f(std::numeric_limits::max(), std::numeric_limits::max()) + }; + } + TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings) : mSettings(settings) + , mBounds(infiniteTileBounds) + , mRange(makeTilesPositionsRange(mBounds.mMin, mBounds.mMax, mSettings)) {} + TileBounds TileCachedRecastMeshManager::getBounds() const + { + return mBounds; + } + + std::vector> TileCachedRecastMeshManager::setBounds(const TileBounds& bounds) + { + std::vector> changedTiles; + + if (mBounds == bounds) + return changedTiles; + + const auto newRange = makeTilesPositionsRange(bounds.mMin, bounds.mMax, mSettings); + + if (mBounds != infiniteTileBounds) + { + const std::lock_guard lock(mMutex); + for (auto& object : mObjects) + { + const ObjectId id = object.first; + ObjectData& data = object.second; + const TilesPositionsRange objectRange = makeTilesPositionsRange(data.mShape.getShape(), data.mTransform, mSettings); + + const auto onOldTilePosition = [&] (const TilePosition& position) + { + if (isInTilesPositionsRange(newRange, position)) + return; + const auto it = data.mTiles.find(position); + if (it == data.mTiles.end()) + return; + data.mTiles.erase(it); + if (removeTile(id, position, mTiles)) + changedTiles.emplace_back(position, ChangeType::remove); + }; + getTilesPositions(getIntersection(mRange, objectRange), onOldTilePosition); + + const auto onNewTilePosition = [&] (const TilePosition& position) + { + if (data.mTiles.find(position) != data.mTiles.end()) + return; + if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, mTiles)) + { + data.mTiles.insert(position); + changedTiles.emplace_back(position, ChangeType::add); + } + }; + getTilesPositions(getIntersection(newRange, objectRange), onNewTilePosition); + } + + std::sort(changedTiles.begin(), changedTiles.end()); + changedTiles.erase(std::unique(changedTiles.begin(), changedTiles.end()), changedTiles.end()); + } + + if (!changedTiles.empty()) + ++mRevision; + + mBounds = bounds; + mRange = newRange; + + return changedTiles; + } + std::string TileCachedRecastMeshManager::getWorldspace() const { const std::lock_guard lock(mMutex); @@ -30,47 +104,22 @@ namespace DetourNavigator mWorldspace = worldspace; } - bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, - const btTransform& transform, const AreaType areaType) - { - const auto it = mObjectsTilesPositions.find(id); - if (it != mObjectsTilesPositions.end()) - return false; - std::vector tilesPositions; - { - const TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings); - const std::lock_guard lock(mMutex); - getTilesPositions(range, - [&] (const TilePosition& tilePosition) - { - if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) - tilesPositions.push_back(tilePosition); - }); - } - if (tilesPositions.empty()) - return false; - std::sort(tilesPositions.begin(), tilesPositions.end()); - mObjectsTilesPositions.emplace_hint(it, id, std::move(tilesPositions)); - ++mRevision; - return true; - } - std::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) { - const auto object = mObjectsTilesPositions.find(id); - if (object == mObjectsTilesPositions.end()) + const auto object = mObjects.find(id); + if (object == mObjects.end()) return std::nullopt; std::optional result; { const std::lock_guard lock(mMutex); - for (const auto& tilePosition : object->second) + for (const auto& tilePosition : object->second.mTiles) { const auto removed = removeTile(id, tilePosition, mTiles); if (removed && !result) result = removed; } } - mObjectsTilesPositions.erase(object); + mObjects.erase(object); if (result) ++mRevision; return result; diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index efd70477d3..906355524e 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -7,11 +7,13 @@ #include "gettilespositions.hpp" #include "version.hpp" #include "heightfieldshape.hpp" +#include "changetype.hpp" #include #include #include #include +#include namespace DetourNavigator { @@ -20,58 +22,85 @@ namespace DetourNavigator public: explicit TileCachedRecastMeshManager(const RecastSettings& settings); + TileBounds getBounds() const; + + std::vector> setBounds(const TileBounds& bounds); + std::string getWorldspace() const; void setWorldspace(std::string_view worldspace); + template bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType); + const AreaType areaType, OnChangedTile&& onChangedTile) + { + auto it = mObjects.find(id); + if (it != mObjects.end()) + return false; + const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings); + const TilesPositionsRange range = getIntersection(mRange, objectRange); + std::set tilesPositions; + if (range.mBegin != range.mEnd) + { + const std::lock_guard lock(mMutex); + getTilesPositions(range, + [&] (const TilePosition& tilePosition) + { + if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) + tilesPositions.insert(tilePosition); + }); + } + it = mObjects.emplace_hint(it, id, ObjectData {shape, transform, areaType, std::move(tilesPositions)}); + std::for_each(it->second.mTiles.begin(), it->second.mTiles.end(), std::forward(onChangedTile)); + ++mRevision; + return true; + } template bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, const AreaType areaType, OnChangedTile&& onChangedTile) { - const auto object = mObjectsTilesPositions.find(id); - if (object == mObjectsTilesPositions.end()) + const auto object = mObjects.find(id); + if (object == mObjects.end()) return false; - auto& currentTiles = object->second; + auto& data = object->second; bool changed = false; - std::vector newTiles; + std::set newTiles; { const auto onTilePosition = [&] (const TilePosition& tilePosition) { - if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition)) + if (data.mTiles.find(tilePosition) != data.mTiles.end()) { - newTiles.push_back(tilePosition); + newTiles.insert(tilePosition); if (updateTile(id, transform, areaType, tilePosition, mTiles)) { - onChangedTile(tilePosition); + onChangedTile(tilePosition, ChangeType::update); changed = true; } } else if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) { - newTiles.push_back(tilePosition); - onChangedTile(tilePosition); + newTiles.insert(tilePosition); + onChangedTile(tilePosition, ChangeType::add); changed = true; } }; - const TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings); + const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings); + const TilesPositionsRange range = getIntersection(mRange, objectRange); const std::lock_guard lock(mMutex); getTilesPositions(range, onTilePosition); - std::sort(newTiles.begin(), newTiles.end()); - for (const auto& tile : currentTiles) + for (const auto& tile : data.mTiles) { - if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, mTiles)) + if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, mTiles)) { - onChangedTile(tile); + onChangedTile(tile, ChangeType::remove); changed = true; } } } if (changed) { - currentTiles = std::move(newTiles); + data.mTiles = std::move(newTiles); ++mRevision; } return changed; @@ -108,11 +137,21 @@ namespace DetourNavigator private: using TilesMap = std::map>; + struct ObjectData + { + const CollisionShape mShape; + const btTransform mTransform; + const AreaType mAreaType; + std::set mTiles; + }; + const RecastSettings& mSettings; mutable std::mutex mMutex; + TileBounds mBounds; + TilesPositionsRange mRange; std::string mWorldspace; TilesMap mTiles; - std::unordered_map> mObjectsTilesPositions; + std::unordered_map mObjects; std::map> mWaterTilesPositions; std::map> mHeightfieldTilesPositions; std::size_t mRevision = 0; From 832ab103cb864eda905f4c0aa52fe90145dca864 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 2 Feb 2022 01:09:34 +0100 Subject: [PATCH 08/10] Filter out unchanged animated objects for navigator update --- apps/openmw/mwphysics/physicssystem.cpp | 9 +++++++-- apps/openmw/mwphysics/physicssystem.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 7 ++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index ef7fffa034..f5f22529ff 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -490,7 +490,7 @@ namespace MWPhysics mObjects.emplace(ptr.mRef, obj); if (obj->isAnimated()) - mAnimatedObjects.insert(obj.get()); + mAnimatedObjects.emplace(obj.get(), false); } void PhysicsSystem::remove(const MWWorld::Ptr &ptr) @@ -739,13 +739,18 @@ namespace MWPhysics void PhysicsSystem::stepSimulation(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { - for (Object* animatedObject : mAnimatedObjects) + for (auto& [animatedObject, changed] : mAnimatedObjects) { if (animatedObject->animateCollisionShapes()) { auto obj = mObjects.find(animatedObject->getPtr().mRef); assert(obj != mObjects.end()); mTaskScheduler->updateSingleAabb(obj->second); + changed = true; + } + else + { + changed = false; } } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 1606ac084c..b165f10761 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -300,7 +300,7 @@ namespace MWPhysics using ObjectMap = std::unordered_map>; ObjectMap mObjects; - std::set mAnimatedObjects; // stores pointers to elements in mObjects + std::map mAnimatedObjects; // stores pointers to elements in mObjects ActorMap mActors; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 706ce4859f..61abb7fcc2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1525,7 +1525,12 @@ namespace MWWorld void World::updateNavigator() { - mPhysics->forEachAnimatedObject([&] (const MWPhysics::Object* object) { updateNavigatorObject(*object); }); + mPhysics->forEachAnimatedObject([&] (const auto& pair) + { + const auto [object, changed] = pair; + if (changed) + updateNavigatorObject(*object); + }); for (const auto& door : mDoorStates) if (const auto object = mPhysics->getObject(door.first)) From 3caeda7299508b8a80da254658bb3f73b11c66fd Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 2 Feb 2022 01:11:13 +0100 Subject: [PATCH 09/10] Consider animated object unchanged if no transform updates done --- apps/openmw/mwphysics/object.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/object.cpp b/apps/openmw/mwphysics/object.cpp index 08fcc7e47d..76661b86a0 100644 --- a/apps/openmw/mwphysics/object.cpp +++ b/apps/openmw/mwphysics/object.cpp @@ -112,6 +112,7 @@ namespace MWPhysics assert (mShapeInstance->mCollisionShape->isCompound()); btCompoundShape* compound = static_cast(mShapeInstance->mCollisionShape.get()); + bool result = false; for (const auto& [recIndex, shapeIndex] : mShapeInstance->mAnimatedShapes) { auto nodePathFound = mRecIndexToNodePath.find(recIndex); @@ -145,8 +146,11 @@ namespace MWPhysics // Note: we can not apply scaling here for now since we treat scaled shapes // as new shapes (btScaledBvhTriangleMeshShape) with 1.0 scale for now if (!(transform == compound->getChildTransform(shapeIndex))) + { compound->updateChildTransform(shapeIndex, transform); + result = true; + } } - return true; + return result; } } From 0b644a897e123370ea489cf43664a079fe532733 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 3 Feb 2022 22:24:26 +0100 Subject: [PATCH 10/10] Explicitly bind TileCachedRecastMeshManager with mutex --- .../tilecachedrecastmeshmanager.cpp | 71 +++++++++---------- .../tilecachedrecastmeshmanager.hpp | 32 +++++---- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index c7bd3bc867..e7e46e96df 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -43,7 +43,7 @@ namespace DetourNavigator if (mBounds != infiniteTileBounds) { - const std::lock_guard lock(mMutex); + const auto locked = mWorldspaceTiles.lock(); for (auto& object : mObjects) { const ObjectId id = object.first; @@ -58,7 +58,7 @@ namespace DetourNavigator if (it == data.mTiles.end()) return; data.mTiles.erase(it); - if (removeTile(id, position, mTiles)) + if (removeTile(id, position, locked->mTiles)) changedTiles.emplace_back(position, ChangeType::remove); }; getTilesPositions(getIntersection(mRange, objectRange), onOldTilePosition); @@ -67,7 +67,7 @@ namespace DetourNavigator { if (data.mTiles.find(position) != data.mTiles.end()) return; - if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, mTiles)) + if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, locked->mTiles)) { data.mTiles.insert(position); changedTiles.emplace_back(position, ChangeType::add); @@ -91,17 +91,16 @@ namespace DetourNavigator std::string TileCachedRecastMeshManager::getWorldspace() const { - const std::lock_guard lock(mMutex); - return mWorldspace; + return mWorldspaceTiles.lockConst()->mWorldspace; } void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace) { - const std::lock_guard lock(mMutex); - if (mWorldspace == worldspace) + const auto locked = mWorldspaceTiles.lock(); + if (locked->mWorldspace == worldspace) return; - mTiles.clear(); - mWorldspace = worldspace; + locked->mTiles.clear(); + locked->mWorldspace = worldspace; } std::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) @@ -111,10 +110,10 @@ namespace DetourNavigator return std::nullopt; std::optional result; { - const std::lock_guard lock(mMutex); + const auto locked = mWorldspaceTiles.lock(); for (const auto& tilePosition : object->second.mTiles) { - const auto removed = removeTile(id, tilePosition, mTiles); + const auto removed = removeTile(id, tilePosition, locked->mTiles); if (removed && !result) result = removed; } @@ -138,8 +137,8 @@ namespace DetourNavigator if (cellSize == std::numeric_limits::max()) { - const std::lock_guard lock(mMutex); - for (auto& tile : mTiles) + const auto locked = mWorldspaceTiles.lock(); + for (auto& tile : locked->mTiles) { if (tile.second->addWater(cellPosition, cellSize, level)) { @@ -154,12 +153,12 @@ namespace DetourNavigator getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings), [&] (const TilePosition& tilePosition) { - const std::lock_guard lock(mMutex); - auto tile = mTiles.find(tilePosition); - if (tile == mTiles.end()) + const auto locked = mWorldspaceTiles.lock(); + auto tile = locked->mTiles.find(tilePosition); + if (tile == locked->mTiles.end()) { const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition); - tile = mTiles.emplace_hint(tile, tilePosition, + tile = locked->mTiles.emplace_hint(tile, tilePosition, std::make_shared(tileBounds, mTilesGeneration)); } if (tile->second->addWater(cellPosition, cellSize, level)) @@ -184,14 +183,14 @@ namespace DetourNavigator std::optional result; for (const auto& tilePosition : object->second) { - const std::lock_guard lock(mMutex); - const auto tile = mTiles.find(tilePosition); - if (tile == mTiles.end()) + 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()) { - mTiles.erase(tile); + locked->mTiles.erase(tile); ++mTilesGeneration; } if (tileResult && !result) @@ -219,12 +218,12 @@ namespace DetourNavigator getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings), [&] (const TilePosition& tilePosition) { - const std::lock_guard lock(mMutex); - auto tile = mTiles.find(tilePosition); - if (tile == mTiles.end()) + const auto locked = mWorldspaceTiles.lock(); + auto tile = locked->mTiles.find(tilePosition); + if (tile == locked->mTiles.end()) { const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition); - tile = mTiles.emplace_hint(tile, tilePosition, + tile = locked->mTiles.emplace_hint(tile, tilePosition, std::make_shared(tileBounds, mTilesGeneration)); } if (tile->second->addHeightfield(cellPosition, cellSize, shape)) @@ -248,14 +247,14 @@ namespace DetourNavigator std::optional result; for (const auto& tilePosition : object->second) { - const std::lock_guard lock(mMutex); - const auto tile = mTiles.find(tilePosition); - if (tile == mTiles.end()) + 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()) { - mTiles.erase(tile); + locked->mTiles.erase(tile); ++mTilesGeneration; } if (tileResult && !result) @@ -295,9 +294,9 @@ namespace DetourNavigator void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const { - const std::lock_guard lock(mMutex); - const auto it = mTiles.find(tilePosition); - if (it == mTiles.end()) + const auto locked = mWorldspaceTiles.lockConst(); + const auto it = locked->mTiles.find(tilePosition); + if (it == locked->mTiles.end()) return; it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion); } @@ -341,11 +340,11 @@ namespace DetourNavigator std::shared_ptr TileCachedRecastMeshManager::getManager(std::string_view worldspace, const TilePosition& tilePosition) const { - const std::lock_guard lock(mMutex); - if (mWorldspace != worldspace) + const auto locked = mWorldspaceTiles.lockConst(); + if (locked->mWorldspace != worldspace) return nullptr; - const auto it = mTiles.find(tilePosition); - if (it == mTiles.end()) + const auto it = locked->mTiles.find(tilePosition); + if (it == locked->mTiles.end()) return nullptr; return it->second; } diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 906355524e..5e168efc16 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -9,6 +9,8 @@ #include "heightfieldshape.hpp" #include "changetype.hpp" +#include + #include #include #include @@ -42,11 +44,11 @@ namespace DetourNavigator std::set tilesPositions; if (range.mBegin != range.mEnd) { - const std::lock_guard lock(mMutex); + const auto locked = mWorldspaceTiles.lock(); getTilesPositions(range, [&] (const TilePosition& tilePosition) { - if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) + if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles)) tilesPositions.insert(tilePosition); }); } @@ -67,31 +69,31 @@ namespace DetourNavigator bool changed = false; std::set newTiles; { + const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings); + const TilesPositionsRange range = getIntersection(mRange, objectRange); + const auto locked = mWorldspaceTiles.lock(); const auto onTilePosition = [&] (const TilePosition& tilePosition) { if (data.mTiles.find(tilePosition) != data.mTiles.end()) { newTiles.insert(tilePosition); - if (updateTile(id, transform, areaType, tilePosition, mTiles)) + if (updateTile(id, transform, areaType, tilePosition, locked->mTiles)) { onChangedTile(tilePosition, ChangeType::update); changed = true; } } - else if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) + else if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles)) { newTiles.insert(tilePosition); onChangedTile(tilePosition, ChangeType::add); changed = true; } }; - const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings); - const TilesPositionsRange range = getIntersection(mRange, objectRange); - const std::lock_guard lock(mMutex); getTilesPositions(range, onTilePosition); for (const auto& tile : data.mTiles) { - if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, mTiles)) + if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, locked->mTiles)) { onChangedTile(tile, ChangeType::remove); changed = true; @@ -125,8 +127,8 @@ namespace DetourNavigator template void forEachTile(Function&& function) const { - const std::lock_guard lock(mMutex); - for (auto& [tilePosition, recastMeshManager] : mTiles) + const auto& locked = mWorldspaceTiles.lockConst(); + for (const auto& [tilePosition, recastMeshManager] : locked->mTiles) function(tilePosition, *recastMeshManager); } @@ -145,12 +147,16 @@ namespace DetourNavigator std::set mTiles; }; + struct WorldspaceTiles + { + std::string mWorldspace; + TilesMap mTiles; + }; + const RecastSettings& mSettings; - mutable std::mutex mMutex; TileBounds mBounds; TilesPositionsRange mRange; - std::string mWorldspace; - TilesMap mTiles; + Misc::ScopeGuarded mWorldspaceTiles; std::unordered_map mObjects; std::map> mWaterTilesPositions; std::map> mHeightfieldTilesPositions;