From 69cf507db85845446b394129dc8955f3803dfde0 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 8 Jan 2024 22:17:02 +0100 Subject: [PATCH] Fix navmesh update on player changing tile In cases when objects are not present on the scene (e.g. generated exterior cells) navmesh is not updated because area that suppose to be covered with it was not updated. It was updated only during cell change. This is a regression from d15e1dca84. Set TileCachedRecastMeshManager range on NavMeshManager update to make sure it always covers correct area around player. Return a union of objects, heightfields and water ranges from getLimitedObjectsRange intersected with range provided above. --- .../detournavigator/gettilespositions.cpp | 76 +++++++ .../detournavigator/navigator.cpp | 185 +++++++++++++++--- .../detournavigator/operators.hpp | 18 ++ .../detournavigator/gettilespositions.cpp | 9 + .../detournavigator/gettilespositions.hpp | 2 + components/detournavigator/navmeshmanager.cpp | 1 + .../tilecachedrecastmeshmanager.cpp | 39 +++- 7 files changed, 299 insertions(+), 31 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/gettilespositions.cpp b/apps/openmw_test_suite/detournavigator/gettilespositions.cpp index fb32022010..729d11ddb5 100644 --- a/apps/openmw_test_suite/detournavigator/gettilespositions.cpp +++ b/apps/openmw_test_suite/detournavigator/gettilespositions.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -86,4 +87,79 @@ namespace EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0))); } + + struct TilesPositionsRangeParams + { + TilesPositionsRange mA; + TilesPositionsRange mB; + TilesPositionsRange mResult; + }; + + struct DetourNavigatorGetIntersectionTest : TestWithParam + { + }; + + TEST_P(DetourNavigatorGetIntersectionTest, should_return_expected_result) + { + EXPECT_EQ(getIntersection(GetParam().mA, GetParam().mB), GetParam().mResult); + EXPECT_EQ(getIntersection(GetParam().mB, GetParam().mA), GetParam().mResult); + } + + const TilesPositionsRangeParams getIntersectionParams[] = { + { .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} }, + { + .mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } }, + .mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } }, + .mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } }, + }, + { + .mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } }, + .mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 2 }, .mEnd = TilePosition{ 3, 3 } }, + .mResult = TilesPositionsRange{}, + }, + { + .mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } }, + .mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } }, + .mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 1, 1 } }, + }, + { + .mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } }, + .mB = TilesPositionsRange{ .mBegin = TilePosition{ 0, 2 }, .mEnd = TilePosition{ 3, 3 } }, + .mResult = TilesPositionsRange{}, + }, + { + .mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } }, + .mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 0 }, .mEnd = TilePosition{ 3, 3 } }, + .mResult = TilesPositionsRange{}, + }, + }; + + INSTANTIATE_TEST_SUITE_P( + GetIntersectionParams, DetourNavigatorGetIntersectionTest, ValuesIn(getIntersectionParams)); + + struct DetourNavigatorGetUnionTest : TestWithParam + { + }; + + TEST_P(DetourNavigatorGetUnionTest, should_return_expected_result) + { + EXPECT_EQ(getUnion(GetParam().mA, GetParam().mB), GetParam().mResult); + EXPECT_EQ(getUnion(GetParam().mB, GetParam().mA), GetParam().mResult); + } + + const TilesPositionsRangeParams getUnionParams[] = { + { .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} }, + { + .mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } }, + .mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } }, + .mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 3, 3 } }, + }, + { + .mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } }, + .mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } }, + .mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } }, + }, + }; + + INSTANTIATE_TEST_SUITE_P(GetUnionParams, DetourNavigatorGetUnionTest, ValuesIn(getUnionParams)); } diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index df4d7a1e99..aba8598f18 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -39,6 +39,8 @@ namespace using namespace DetourNavigator; using namespace DetourNavigator::Tests; + constexpr int heightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1); + struct DetourNavigatorNavigatorTest : Test { Settings mSettings = makeSettings(); @@ -53,7 +55,6 @@ namespace AreaCosts mAreaCosts; Loading::Listener mListener; const osg::Vec2i mCellPosition{ 0, 0 }; - const int mHeightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1); const float mEndTolerance = 0; const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) }; const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f }; @@ -129,7 +130,7 @@ namespace { } - T& shape() { return static_cast(*mInstance->mCollisionShape); } + T& shape() const { return static_cast(*mInstance->mCollisionShape); } const osg::ref_ptr& instance() const { return mInstance; } private: @@ -167,7 +168,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); auto updateGuard = mNavigator->makeUpdateGuard(); @@ -189,7 +190,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_path_to_the_start_position_should_contain_single_point) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); auto updateGuard = mNavigator->makeUpdateGuard(); @@ -211,7 +212,7 @@ namespace mSettings, std::make_unique(":memory:", std::numeric_limits::max()))); const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); CollisionShapeInstance compound(std::make_unique()); compound.shape().addChildShape( @@ -256,7 +257,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); CollisionShapeInstance compound(std::make_unique()); compound.shape().addChildShape( @@ -335,7 +336,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, only_one_heightfield_per_cell_is_allowed) { const HeightfieldSurface surface1 = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize1 = mHeightfieldTileSize * (surface1.mSize - 1); + const int cellSize1 = heightfieldTileSize * (surface1.mSize - 1); const std::array heightfieldData2{ { -25, -25, -25, -25, -25, // row 0 @@ -345,7 +346,7 @@ namespace -25, -25, -25, -25, -25, // row 4 } }; const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2); - const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1); + const int cellSize2 = heightfieldTileSize * (surface2.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr); @@ -412,7 +413,7 @@ namespace 0, -50, -100, -100, -100, // row 4 } }; const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addWater(mCellPosition, cellSize, 300, nullptr); @@ -446,7 +447,7 @@ namespace 0, 0, 0, 0, 0, 0, 0, // row 6 } }; const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addWater(mCellPosition, cellSize, -25, nullptr); @@ -480,7 +481,7 @@ namespace 0, 0, 0, 0, 0, 0, 0, // row 6 } }; const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); @@ -513,7 +514,7 @@ namespace 0, 0, 0, 0, 0, 0, 0, // row 6 } }; const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addWater(mCellPosition, cellSize, -25, nullptr); @@ -566,7 +567,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, update_heightfield_remove_and_update_then_find_path_should_return_path) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); @@ -602,7 +603,7 @@ namespace 0, -25, -100, -100, -100, -100, // row 5 } }; const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); @@ -629,7 +630,7 @@ namespace mSettings, std::make_unique(":memory:", std::numeric_limits::max()))); const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); const btVector3 shift = getHeightfieldShift(mCellPosition, cellSize, surface.mMinHeight, surface.mMaxHeight); std::vector> boxes; @@ -718,7 +719,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); @@ -737,7 +738,7 @@ namespace update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); CollisionShapeInstance oscillatingBox(std::make_unique(btVector3(20, 20, 20))); const btVector3 oscillatingBoxShapePosition(288, 288, 400); @@ -777,7 +778,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, should_provide_path_over_flat_heightfield) { const HeightfieldPlane plane{ 100 }; - const int cellSize = mHeightfieldTileSize * 4; + const int cellSize = heightfieldTileSize * 4; ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr); @@ -796,7 +797,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, for_not_reachable_destination_find_path_should_provide_partial_path) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); CollisionShapeInstance compound(std::make_unique()); compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)), @@ -822,7 +823,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, end_tolerance_should_extent_available_destinations) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); CollisionShapeInstance compound(std::make_unique()); compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)), @@ -948,7 +949,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nav_mesh_position) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); auto updateGuard = mNavigator->makeUpdateGuard(); @@ -966,7 +967,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_too_far) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); auto updateGuard = mNavigator->makeUpdateGuard(); @@ -984,7 +985,7 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_flags_do_not_match) { const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData); - const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds)); auto updateGuard = mNavigator->makeUpdateGuard(); @@ -998,4 +999,142 @@ namespace EXPECT_EQ(findNearestNavMeshPosition(*mNavigator, mAgentBounds, position, searchAreaHalfExtents, Flag_swim), std::nullopt); } + + struct DetourNavigatorUpdateTest : TestWithParam> + { + }; + + std::vector getUsedTiles(const NavMeshCacheItem& navMesh) + { + std::vector result; + navMesh.forEachUsedTile([&](const TilePosition& position, const auto&...) { result.push_back(position); }); + return result; + } + + TEST_P(DetourNavigatorUpdateTest, update_should_change_covered_area_when_player_moves) + { + Loading::Listener listener; + Settings settings = makeSettings(); + settings.mMaxTilesNumber = 5; + NavigatorImpl navigator(settings, nullptr); + const AgentBounds agentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } }; + ASSERT_TRUE(navigator.addAgent(agentBounds)); + + GetParam()(navigator); + + { + auto updateGuard = navigator.makeUpdateGuard(); + navigator.update(osg::Vec3f(3000, 3000, 0), updateGuard.get()); + } + + navigator.wait(WaitConditionType::allJobsDone, &listener); + + { + const auto navMesh = navigator.getNavMesh(agentBounds); + ASSERT_NE(navMesh, nullptr); + + const TilePosition expectedTiles[] = { { 3, 4 }, { 4, 3 }, { 4, 4 }, { 4, 5 }, { 5, 4 } }; + const auto usedTiles = getUsedTiles(*navMesh->lockConst()); + EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles; + } + + { + auto updateGuard = navigator.makeUpdateGuard(); + navigator.update(osg::Vec3f(4000, 3000, 0), updateGuard.get()); + } + + navigator.wait(WaitConditionType::allJobsDone, &listener); + + { + const auto navMesh = navigator.getNavMesh(agentBounds); + ASSERT_NE(navMesh, nullptr); + + const TilePosition expectedTiles[] = { { 4, 4 }, { 5, 3 }, { 5, 4 }, { 5, 5 }, { 6, 4 } }; + const auto usedTiles = getUsedTiles(*navMesh->lockConst()); + EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles; + } + } + + struct AddHeightfieldSurface + { + static constexpr std::size_t sSize = 65; + static constexpr float sHeights[sSize * sSize]{}; + + void operator()(Navigator& navigator) const + { + const osg::Vec2i cellPosition(0, 0); + const HeightfieldSurface surface{ + .mHeights = sHeights, + .mSize = sSize, + .mMinHeight = -1, + .mMaxHeight = 1, + }; + const int cellSize = heightfieldTileSize * static_cast(surface.mSize - 1); + navigator.addHeightfield(cellPosition, cellSize, surface, nullptr); + } + }; + + struct AddHeightfieldPlane + { + void operator()(Navigator& navigator) const + { + const osg::Vec2i cellPosition(0, 0); + const HeightfieldPlane plane{ .mHeight = 0 }; + const int cellSize = 8192; + navigator.addHeightfield(cellPosition, cellSize, plane, nullptr); + } + }; + + struct AddWater + { + void operator()(Navigator& navigator) const + { + const osg::Vec2i cellPosition(0, 0); + const float level = 0; + const int cellSize = 8192; + navigator.addWater(cellPosition, cellSize, level, nullptr); + } + }; + + struct AddObject + { + const float mSize = 8192; + CollisionShapeInstance mBox{ std::make_unique(btVector3(mSize, mSize, 1)) }; + const ObjectTransform mTransform{ + .mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } }, + .mScale = 1.0f, + }; + + void operator()(Navigator& navigator) const + { + navigator.addObject(ObjectId(&mBox.shape()), ObjectShapes(mBox.instance(), mTransform), + btTransform::getIdentity(), nullptr); + } + }; + + struct AddAll + { + AddHeightfieldSurface mAddHeightfieldSurface; + AddHeightfieldPlane mAddHeightfieldPlane; + AddWater mAddWater; + AddObject mAddObject; + + void operator()(Navigator& navigator) const + { + mAddHeightfieldSurface(navigator); + mAddHeightfieldPlane(navigator); + mAddWater(navigator); + mAddObject(navigator); + } + }; + + const std::function addNavMeshData[] = { + AddHeightfieldSurface{}, + AddHeightfieldPlane{}, + AddWater{}, + AddObject{}, + AddAll{}, + }; + + INSTANTIATE_TEST_SUITE_P(DifferentNavMeshData, DetourNavigatorUpdateTest, ValuesIn(addNavMeshData)); } diff --git a/apps/openmw_test_suite/detournavigator/operators.hpp b/apps/openmw_test_suite/detournavigator/operators.hpp index 4e42af78e4..4c043027eb 100644 --- a/apps/openmw_test_suite/detournavigator/operators.hpp +++ b/apps/openmw_test_suite/detournavigator/operators.hpp @@ -42,12 +42,24 @@ namespace testing << ", " << value.y() << ", " << value.z() << ')'; } + template <> + inline testing::Message& Message::operator<<(const osg::Vec2i& value) + { + return (*this) << "{" << value.x() << ", " << value.y() << '}'; + } + template <> inline testing::Message& Message::operator<<(const Wrapper& value) { return (*this) << value.mValue; } + template <> + inline testing::Message& Message::operator<<(const Wrapper& value) + { + return (*this) << value.mValue; + } + template <> inline testing::Message& Message::operator<<(const Wrapper& value) { @@ -72,6 +84,12 @@ namespace testing return writeRange(*this, value, 1); } + template <> + inline testing::Message& Message::operator<<(const std::vector& value) + { + return writeRange(*this, value, 1); + } + template <> inline testing::Message& Message::operator<<(const std::vector& value) { diff --git a/components/detournavigator/gettilespositions.cpp b/components/detournavigator/gettilespositions.cpp index a3f46f3f85..343140633f 100644 --- a/components/detournavigator/gettilespositions.cpp +++ b/components/detournavigator/gettilespositions.cpp @@ -76,4 +76,13 @@ namespace DetourNavigator return {}; return TilesPositionsRange{ TilePosition(beginX, beginY), TilePosition(endX, endY) }; } + + TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept + { + const int beginX = std::min(a.mBegin.x(), b.mBegin.x()); + const int endX = std::max(a.mEnd.x(), b.mEnd.x()); + const int beginY = std::min(a.mBegin.y(), b.mBegin.y()); + const int endY = std::max(a.mEnd.y(), b.mEnd.y()); + return TilesPositionsRange{ .mBegin = TilePosition(beginX, beginY), .mEnd = TilePosition(endX, endY) }; + } } diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index 32733224f3..66c3a90d1b 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -50,6 +50,8 @@ namespace DetourNavigator } TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept; + + TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept; } #endif diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 9fda0566d9..f4a82b850f 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -166,6 +166,7 @@ namespace DetourNavigator return; mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision(); mPlayerTile = playerTile; + mRecastMeshManager.setRange(makeRange(playerTile, mSettings.mMaxTilesNumber), guard); const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard); const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange(); for (const auto& [agentBounds, cached] : mCache) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 1e55719c13..0bab808300 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -54,6 +54,15 @@ namespace DetourNavigator private: const std::optional> mImpl; }; + + TilesPositionsRange getIndexRange(const auto& index) + { + const auto bounds = index.bounds(); + return TilesPositionsRange{ + .mBegin = makeTilePosition(bounds.min_corner()), + .mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1), + }; + } } TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings) @@ -104,14 +113,28 @@ namespace DetourNavigator TilesPositionsRange TileCachedRecastMeshManager::getLimitedObjectsRange() const { - if (mObjects.empty()) - return {}; - const auto bounds = mObjectIndex.bounds(); - const TilesPositionsRange objectsRange{ - .mBegin = makeTilePosition(bounds.min_corner()), - .mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1), - }; - return getIntersection(mRange, objectsRange); + std::optional result; + if (!mWater.empty()) + result = getIndexRange(mWaterIndex); + if (!mHeightfields.empty()) + { + const TilesPositionsRange range = getIndexRange(mHeightfieldIndex); + if (result.has_value()) + result = getUnion(*result, range); + else + result = range; + } + if (!mObjects.empty()) + { + const TilesPositionsRange range = getIndexRange(mObjectIndex); + if (result.has_value()) + result = getUnion(*result, range); + else + result = range; + } + if (result.has_value()) + return getIntersection(mRange, *result); + return {}; } void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard)