From d122e184ccd6008069a5f544a9d2dfde7b9639a7 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 01:09:58 +0200 Subject: [PATCH 1/6] Report navmesh change for not posted tiles Corresponding recast mesh tiles can be updated but navmesh tiles may never appear for them. Report back zero navmesh version to allow oscillating recast objects detection to work. This version is always less than any generated navmesh tile version so any report for generated navmesh will override it. If zero navmesh version is reported after recast mesh tile got report about generated navmesh tile it is a no-op since generated version is always greater than zero. --- .../detournavigator/tilecachedrecastmeshmanager.cpp | 2 +- components/detournavigator/cachedrecastmeshmanager.cpp | 5 +++++ components/detournavigator/cachedrecastmeshmanager.hpp | 2 ++ components/detournavigator/navmeshmanager.cpp | 8 +++++--- components/detournavigator/recastmeshmanager.cpp | 5 +++++ components/detournavigator/recastmeshmanager.hpp | 2 ++ .../detournavigator/tilecachedrecastmeshmanager.hpp | 6 +++--- 7 files changed, 23 insertions(+), 7 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index 2d6d9f66b..83622c0b7 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -54,7 +54,7 @@ namespace { TileCachedRecastMeshManager manager(mSettings); std::size_t calls = 0; - manager.forEachTilePosition([&] (const TilePosition&) { ++calls; }); + manager.forEachTile([&] (const TilePosition&, const CachedRecastMeshManager&) { ++calls; }); EXPECT_EQ(calls, 0); } diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index 22b047fbd..2788b8046 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -66,4 +66,9 @@ namespace DetourNavigator { mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion); } + + Version CachedRecastMeshManager::getVersion() const + { + return mImpl.getVersion(); + } } diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index a19f017a4..ea55348f7 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -28,6 +28,8 @@ namespace DetourNavigator void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion); + Version getVersion() const; + private: RecastMeshManager mImpl; std::shared_ptr mCached; diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 8f1aa86d4..de4c21c68 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -171,7 +171,7 @@ namespace DetourNavigator } } const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles); - mRecastMeshManager.forEachTilePosition([&] (const TilePosition& tile) + mRecastMeshManager.forEachTile([&] (const TilePosition& tile, CachedRecastMeshManager& recastMeshManager) { if (tilesToPost.count(tile)) return; @@ -181,6 +181,8 @@ namespace DetourNavigator tilesToPost.insert(std::make_pair(tile, ChangeType::add)); else if (!shouldAdd && presentInNavMesh) tilesToPost.insert(std::make_pair(tile, ChangeType::mixed)); + else + recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0}); }); } mAsyncNavMeshUpdater.post(agentHalfExtents, cached, playerTile, tilesToPost); @@ -214,8 +216,8 @@ namespace DetourNavigator RecastMeshTiles NavMeshManager::getRecastMeshTiles() { std::vector tiles; - mRecastMeshManager.forEachTilePosition( - [&tiles] (const TilePosition& tile) { tiles.push_back(tile); }); + mRecastMeshManager.forEachTile( + [&tiles] (const TilePosition& tile, const CachedRecastMeshManager&) { tiles.push_back(tile); }); RecastMeshTiles result; std::transform(tiles.begin(), tiles.end(), std::inserter(result, result.end()), [this] (const TilePosition& tile) { return std::make_pair(tile, mRecastMeshManager.getMesh(tile)); }); diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index cdb9169d9..5fbcedff6 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -95,6 +95,11 @@ namespace DetourNavigator mLastNavMeshReportedChange = mLastNavMeshReport; } + Version RecastMeshManager::getVersion() const + { + return Version {mGeneration, mRevision}; + } + void RecastMeshManager::rebuild() { mMeshBuilder.reset(); diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index daa123fcb..ac17a347b 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -53,6 +53,8 @@ namespace DetourNavigator void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion); + Version getVersion() const; + private: struct Report { diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 68683f410..4c6f21c42 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -81,10 +81,10 @@ namespace DetourNavigator bool hasTile(const TilePosition& tilePosition); template - void forEachTilePosition(Function&& function) + void forEachTile(Function&& function) { - for (const auto& tile : *mTiles.lock()) - function(tile.first); + for (auto& [tilePosition, recastMeshManager] : *mTiles.lock()) + function(tilePosition, recastMeshManager); } std::size_t getRevision() const; From f4f9fa4701c8a41a13289c2b7a8f421f6a21be81 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 01:59:07 +0200 Subject: [PATCH 2/6] Limit oscillating recast mesh object AABB by tile bounds AABB change outside recast mesh tile should not affect navmesh for this tile. --- .../oscillatingrecastmeshobject.cpp | 17 ++++++++++++++++- .../oscillatingrecastmeshobject.hpp | 4 +++- .../detournavigator/recastmeshmanager.cpp | 3 ++- .../detournavigator/recastmeshmanager.hpp | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/components/detournavigator/oscillatingrecastmeshobject.cpp b/components/detournavigator/oscillatingrecastmeshobject.cpp index 5b8423183..fbe4b77ff 100644 --- a/components/detournavigator/oscillatingrecastmeshobject.cpp +++ b/components/detournavigator/oscillatingrecastmeshobject.cpp @@ -1,9 +1,23 @@ #include "oscillatingrecastmeshobject.hpp" +#include "tilebounds.hpp" #include +#include + namespace DetourNavigator { + namespace + { + void limitBy(btAABB& aabb, const TileBounds& bounds) + { + aabb.m_min.setX(std::max(aabb.m_min.x(), static_cast(bounds.mMin.x()))); + aabb.m_min.setY(std::max(aabb.m_min.y(), static_cast(bounds.mMin.y()))); + aabb.m_max.setX(std::min(aabb.m_max.x(), static_cast(bounds.mMax.x()))); + aabb.m_max.setY(std::min(aabb.m_max.y(), static_cast(bounds.mMax.y()))); + } + } + OscillatingRecastMeshObject::OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision) : mImpl(std::move(impl)) , mLastChangeRevision(lastChangeRevision) @@ -19,7 +33,7 @@ namespace DetourNavigator } bool OscillatingRecastMeshObject::update(const btTransform& transform, const AreaType areaType, - std::size_t lastChangeRevision) + std::size_t lastChangeRevision, const TileBounds& bounds) { const btTransform oldTransform = mImpl.getTransform(); if (!mImpl.update(transform, areaType)) @@ -37,6 +51,7 @@ namespace DetourNavigator } const btAABB currentAabb = mAabb; mAabb.merge(BulletHelpers::getAabb(mImpl.getShape(), transform)); + limitBy(mAabb, bounds); return currentAabb != mAabb; } } diff --git a/components/detournavigator/oscillatingrecastmeshobject.hpp b/components/detournavigator/oscillatingrecastmeshobject.hpp index 78a0c4b68..f8aabce62 100644 --- a/components/detournavigator/oscillatingrecastmeshobject.hpp +++ b/components/detournavigator/oscillatingrecastmeshobject.hpp @@ -3,6 +3,7 @@ #include "areatype.hpp" #include "recastmeshobject.hpp" +#include "tilebounds.hpp" #include #include @@ -15,7 +16,8 @@ namespace DetourNavigator explicit OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision); explicit OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision); - bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision); + bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision, + const TileBounds& bounds); const RecastMeshObject& getImpl() const { return mImpl; } diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 5fbcedff6..e168a3304 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -5,6 +5,7 @@ namespace DetourNavigator RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation) : mGeneration(generation) , mMeshBuilder(settings, bounds) + , mTileBounds(bounds) { } @@ -29,7 +30,7 @@ namespace DetourNavigator return false; const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value() ? mLastNavMeshReportedChange->mRevision : mRevision; - if (!object->second->update(transform, areaType, lastChangeRevision)) + if (!object->second->update(transform, areaType, lastChangeRevision, mTileBounds)) return false; ++mRevision; return true; diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index ac17a347b..e1606f0a9 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -65,6 +65,7 @@ namespace DetourNavigator std::size_t mRevision = 0; std::size_t mGeneration; RecastMeshBuilder mMeshBuilder; + TileBounds mTileBounds; std::list mObjectsOrder; std::unordered_map::iterator> mObjects; std::list mWaterOrder; From 22c2f106b755365ee0d85074a784a6c19fe5f0da Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 15:25:21 +0200 Subject: [PATCH 3/6] Store object tiles position as sorted vector instead of set --- .../tilecachedrecastmeshmanager.cpp | 20 ++++++++++--------- .../tilecachedrecastmeshmanager.hpp | 20 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 20ebc8fea..42ae93e80 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -3,6 +3,9 @@ #include "gettilespositions.hpp" #include "settingsutils.hpp" +#include +#include + namespace DetourNavigator { TileCachedRecastMeshManager::TileCachedRecastMeshManager(const Settings& settings) @@ -12,23 +15,22 @@ namespace DetourNavigator bool TileCachedRecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) { - bool result = false; - auto& tilesPositions = mObjectsTilesPositions[id]; + std::vector tilesPositions; const auto border = getBorderSize(mSettings); { auto tiles = mTiles.lock(); getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& tilePosition) { if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get())) - { - tilesPositions.insert(tilePosition); - result = true; - } + tilesPositions.push_back(tilePosition); }); } - if (result) - ++mRevision; - return result; + if (tilesPositions.empty()) + return false; + std::sort(tilesPositions.begin(), tilesPositions.end()); + mObjectsTilesPositions.insert_or_assign(id, std::move(tilesPositions)); + ++mRevision; + return true; } std::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 4c6f21c42..23ecc7763 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -9,9 +9,10 @@ #include +#include #include #include -#include +#include namespace DetourNavigator { @@ -33,14 +34,14 @@ namespace DetourNavigator auto& currentTiles = object->second; const auto border = getBorderSize(mSettings); bool changed = false; - std::set newTiles; + std::vector newTiles; { auto tiles = mTiles.lock(); const auto onTilePosition = [&] (const TilePosition& tilePosition) { - if (currentTiles.count(tilePosition)) + if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition)) { - newTiles.insert(tilePosition); + newTiles.push_back(tilePosition); if (updateTile(id, transform, areaType, tilePosition, tiles.get())) { onChangedTile(tilePosition); @@ -49,24 +50,27 @@ namespace DetourNavigator } else if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get())) { - newTiles.insert(tilePosition); + newTiles.push_back(tilePosition); onChangedTile(tilePosition); changed = true; } }; getTilesPositions(shape, transform, mSettings, onTilePosition); + std::sort(newTiles.begin(), newTiles.end()); for (const auto& tile : currentTiles) { - if (!newTiles.count(tile) && removeTile(id, tile, tiles.get())) + if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, tiles.get())) { onChangedTile(tile); changed = true; } } } - std::swap(currentTiles, newTiles); if (changed) + { + currentTiles = std::move(newTiles); ++mRevision; + } return changed; } @@ -94,7 +98,7 @@ namespace DetourNavigator private: const Settings& mSettings; Misc::ScopeGuarded> mTiles; - std::unordered_map> mObjectsTilesPositions; + std::unordered_map> mObjectsTilesPositions; std::map> mWaterTilesPositions; std::size_t mRevision = 0; std::size_t mTilesGeneration = 0; From ed91cf9397cdf8d14c5a5823ee5155eca6cb5cd9 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 16:38:04 +0200 Subject: [PATCH 4/6] Replace unordered_map by map for storing objects For small amount of items it gives better performance for find by key for update. --- components/detournavigator/recastmeshmanager.cpp | 9 ++++----- components/detournavigator/recastmeshmanager.hpp | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index e168a3304..bfc0409a5 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -12,13 +12,12 @@ namespace DetourNavigator bool RecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) { + const auto object = mObjects.lower_bound(id); + if (object != mObjects.end() && object->first == id) + return false; const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(), OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1)); - if (!mObjects.emplace(id, iterator).second) - { - mObjectsOrder.erase(iterator); - return false; - } + mObjects.emplace_hint(object, id, iterator); ++mRevision; return true; } diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index e1606f0a9..5922821f2 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -13,7 +13,6 @@ #include #include #include -#include class btCollisionShape; @@ -67,7 +66,7 @@ namespace DetourNavigator RecastMeshBuilder mMeshBuilder; TileBounds mTileBounds; std::list mObjectsOrder; - std::unordered_map::iterator> mObjects; + std::map::iterator> mObjects; std::list mWaterOrder; std::map::iterator> mWater; std::optional mLastNavMeshReportedChange; From 4a6961b365e5621e6d39524c2b94c5084cd21b06 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 16:52:01 +0200 Subject: [PATCH 5/6] Trigger navmesh update on moved player only when player tile has been changed --- apps/openmw/mwworld/scene.cpp | 2 +- components/detournavigator/navigator.hpp | 8 +++++++- components/detournavigator/navigatorimpl.cpp | 9 +++++++++ components/detournavigator/navigatorimpl.hpp | 3 +++ components/detournavigator/navigatorstub.hpp | 2 ++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 83e9805f6..ac62aab19 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -500,7 +500,7 @@ namespace MWWorld { const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - navigator->update(player.getRefData().getPosition().asVec3()); + navigator->updatePlayerPosition(player.getRefData().getPosition().asVec3()); if (!mCurrentCell || !mCurrentCell->isExterior()) return; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 8cf4cb80e..0dab7ba6b 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -155,11 +155,17 @@ namespace DetourNavigator virtual void removePathgrid(const ESM::Pathgrid& pathgrid) = 0; /** - * @brief update start background navmesh update using current scene state. + * @brief update starts background navmesh update using current scene state. * @param playerPosition setup initial point to order build tiles of navmesh. */ 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 */ diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 7522fe622..b7b3bbd58 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -146,6 +146,15 @@ namespace DetourNavigator mNavMeshManager.update(playerPosition, v.first); } + void NavigatorImpl::updatePlayerPosition(const osg::Vec3f& playerPosition) + { + const TilePosition tilePosition = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition)); + if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition) + return; + update(playerPosition); + mLastPlayerPosition = tilePosition; + } + void NavigatorImpl::setUpdatesEnabled(bool enabled) { mUpdatesEnabled = enabled; diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 324946261..80c6957d7 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -46,6 +46,8 @@ namespace DetourNavigator void update(const osg::Vec3f& playerPosition) override; + void updatePlayerPosition(const osg::Vec3f& playerPosition) override; + void setUpdatesEnabled(bool enabled) override; void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override; @@ -66,6 +68,7 @@ namespace DetourNavigator Settings mSettings; NavMeshManager mNavMeshManager; bool mUpdatesEnabled; + std::optional mLastPlayerPosition; std::map mAgents; std::unordered_map mAvoidIds; std::unordered_map mWaterIds; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 2c12c45eb..0a0881393 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -71,6 +71,8 @@ namespace DetourNavigator void update(const osg::Vec3f& /*playerPosition*/) override {} + void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {}; + void setUpdatesEnabled(bool /*enabled*/) override {} void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {} From e9433a91fbd6f2816d8ec2a170cb922ace2d4631 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 27 May 2021 20:18:52 +0200 Subject: [PATCH 6/6] Add more tests for TileCachedRecastMeshManager --- .../tilecachedrecastmeshmanager.cpp | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index 83622c0b7..fb0f97831 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -73,6 +73,16 @@ namespace EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); } + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + for (int x = -1; x < 1; ++x) + for (int y = -1; y < 1; ++y) + ASSERT_TRUE(manager.hasTile(TilePosition(x, y))); + } + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); @@ -238,4 +248,79 @@ namespace manager.removeObject(ObjectId(&manager)); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision); } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_new_water_should_return_true) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + EXPECT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_TRUE(manager.hasTile(TilePosition(x, y))); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = std::numeric_limits::max(); + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_EQ(manager.hasTile(TilePosition(x, y)), -1 <= x && x <= 0 && -1 <= y && y <= 0); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_return_nullopt) + { + TileCachedRecastMeshManager manager(mSettings); + EXPECT_EQ(manager.removeWater(osg::Vec2i(0, 0)), std::nullopt); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_return_removed_water) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + const auto result = manager.removeWater(cellPosition); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->mCellSize, cellSize); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + ASSERT_TRUE(manager.removeWater(cellPosition)); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_FALSE(manager.hasTile(TilePosition(x, y))); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles) + { + TileCachedRecastMeshManager manager(mSettings); + const btBoxShape boxShape(btVector3(20, 20, 100)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); + const osg::Vec2i cellPosition(0, 0); + const int cellSize = 8192; + ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); + ASSERT_TRUE(manager.removeWater(cellPosition)); + for (int x = -6; x < 6; ++x) + for (int y = -6; y < 6; ++y) + ASSERT_EQ(manager.hasTile(TilePosition(x, y)), -1 <= x && x <= 0 && -1 <= y && y <= 0); + } }