diff --git a/apps/navmeshtool/worldspacedata.cpp b/apps/navmeshtool/worldspacedata.cpp index 3ed7e29880..b3efa69430 100644 --- a/apps/navmeshtool/worldspacedata.cpp +++ b/apps/navmeshtool/worldspacedata.cpp @@ -318,13 +318,13 @@ namespace NavMeshTool const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform()); navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, - DetourNavigator::AreaType_ground, [] (const auto&) {}); + DetourNavigator::AreaType_ground); 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, [] (const auto&) {}); + DetourNavigator::AreaType_null); } data.mObjects.emplace_back(std::move(object)); diff --git a/apps/navmeshtool/worldspacedata.hpp b/apps/navmeshtool/worldspacedata.hpp index 87febb4b0c..ca229ad1fa 100644 --- a/apps/navmeshtool/worldspacedata.hpp +++ b/apps/navmeshtool/worldspacedata.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp index 692b82ef96..b43cdf329f 100644 --- a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp +++ b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp @@ -42,7 +42,7 @@ namespace osg::ref_ptr(new Resource::BulletShapeInstance(bulletShape)), shape, objectTransform ); - recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground, [] (auto) {}); + recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground); } struct DetourNavigatorAsyncNavMeshUpdaterTest : Test diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index 4803f452b3..4ccd4c6712 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include @@ -16,8 +18,6 @@ namespace struct DetourNavigatorTileCachedRecastMeshManagerTest : Test { RecastSettings mSettings; - 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,16 +29,6 @@ namespace mSettings.mRecastScaleFactor = 0.017647058823529415f; mSettings.mTileSize = 64; } - - void onAddedTile(const TilePosition& tilePosition) - { - mAddedTiles.push_back(tilePosition); - } - - void onChangedTile(const TilePosition& tilePosition, ChangeType changeType) - { - mChangedTiles.emplace_back(tilePosition, changeType); - } }; TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_empty_should_return_nullptr) @@ -66,7 +56,7 @@ 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, [] (auto) {})); + EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false) @@ -74,8 +64,8 @@ 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, [] (auto) {}); - EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) @@ -84,13 +74,13 @@ 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, [] (auto) {})); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); 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) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_return_add_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); @@ -99,12 +89,11 @@ namespace 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))); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + EXPECT_THAT(manager.takeChangedTiles(), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::add))); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_add_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); @@ -114,10 +103,10 @@ namespace 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( + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + manager.takeChangedTiles(); + EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + EXPECT_THAT(manager.takeChangedTiles(), ElementsAre( std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(-1, 0), ChangeType::add), std::pair(TilePosition(0, -1), ChangeType::update), @@ -127,15 +116,30 @@ namespace )); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_return_empty) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_not_add_changed_tiles) + { + 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.takeChangedTiles(); + EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + EXPECT_THAT(manager.takeChangedTiles(), IsEmpty()); + } + + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_return_add_changed_tiles) { 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, [] (auto) {}); - EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, - [&] (const auto& ... v) { onChangedTile(v ...); })); - EXPECT_THAT(mChangedTiles, IsEmpty()); + 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); + manager.takeChangedTiles(); + manager.removeObject(ObjectId(&boxShape)); + EXPECT_THAT(manager.takeChangedTiles(), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::remove))); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile) @@ -144,7 +148,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); 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); @@ -157,7 +161,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); } @@ -174,13 +178,13 @@ 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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); 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, auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); 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); @@ -196,11 +200,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); 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, auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); } @@ -211,7 +215,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.removeObject(ObjectId(&boxShape)); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); @@ -226,13 +230,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); 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, auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); 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); @@ -245,7 +249,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getRevision(), initialRevision + 1); } @@ -254,9 +258,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeAddRevision = manager.getRevision(); - EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {})); + EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_EQ(manager.getRevision(), beforeAddRevision); } @@ -266,9 +270,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); } @@ -278,9 +282,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {}); + manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); } @@ -289,7 +293,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, [] (auto) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); const auto beforeRemoveRevision = manager.getRevision(); manager.removeObject(ObjectId(&boxShape)); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1); @@ -303,12 +307,17 @@ namespace EXPECT_EQ(manager.getRevision(), beforeRemoveRevision); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_new_water_should_return_true) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_new_water_should_add_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - EXPECT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); + manager.addWater(cellPosition, cellSize, 0.0f); + const auto changedTiles = manager.takeChangedTiles(); + EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1)); + EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11)); + for (const auto& [k, v] : changedTiles) + EXPECT_EQ(v, ChangeType::add) << k; } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles) @@ -317,7 +326,7 @@ namespace manager.setWorldspace("worldspace"); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); + manager.addWater(cellPosition, cellSize, 0.0f); for (int x = -1; x < 12; ++x) for (int y = -1; y < 12; ++y) ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); @@ -329,30 +338,35 @@ 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, [] (auto) {})); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); const osg::Vec2i cellPosition(0, 0); const int cellSize = std::numeric_limits::max(); - ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); + manager.addWater(cellPosition, cellSize, 0.0f); for (int x = -6; x < 6; ++x) for (int y = -6; y < 6; ++y) ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_return_nullopt) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_not_add_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); - EXPECT_EQ(manager.removeWater(osg::Vec2i(0, 0)), std::nullopt); + manager.removeWater(osg::Vec2i(0, 0)); + EXPECT_THAT(manager.takeChangedTiles(), ElementsAre()); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_return_removed_water) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_add_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); - const auto result = manager.removeWater(cellPosition); - ASSERT_TRUE(result.has_value()); - EXPECT_EQ(result->mCellSize, cellSize); + manager.addWater(cellPosition, cellSize, 0.0f); + manager.takeChangedTiles(); + manager.removeWater(cellPosition); + const auto changedTiles = manager.takeChangedTiles(); + EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1)); + EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11)); + for (const auto& [k, v] : changedTiles) + EXPECT_EQ(v, ChangeType::remove) << k; } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles) @@ -361,8 +375,8 @@ namespace manager.setWorldspace("worldspace"); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); - ASSERT_TRUE(manager.removeWater(cellPosition)); + manager.addWater(cellPosition, cellSize, 0.0f); + manager.removeWater(cellPosition); for (int x = -6; x < 6; ++x) for (int y = -6; y < 6; ++y) ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); @@ -374,11 +388,11 @@ 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, [] (auto) {})); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); - ASSERT_TRUE(manager.removeWater(cellPosition)); + manager.addWater(cellPosition, cellSize, 0.0f); + manager.removeWater(cellPosition); for (int x = -6; x < 6; ++x) for (int y = -6; y < 6; ++y) ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)) != nullptr, -1 <= x && x <= 0 && -1 <= y && y <= 0); @@ -392,9 +406,9 @@ 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, [] (auto) {})); - ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); - ASSERT_TRUE(manager.removeObject(ObjectId(&boxShape))); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + manager.addWater(cellPosition, cellSize, 0.0f); + manager.removeObject(ObjectId(&boxShape)); for (int x = -1; x < 12; ++x) for (int y = -1; y < 12; ++y) ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); @@ -406,14 +420,14 @@ 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, [] (auto) {})); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); 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) + TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_bounds_should_add_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); @@ -422,10 +436,12 @@ namespace 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) {}); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); bounds.mMin = osg::Vec2f(-1000, -1000); bounds.mMax = osg::Vec2f(0, -182); - EXPECT_THAT(manager.setBounds(bounds), ElementsAre( + manager.takeChangedTiles(); + manager.setBounds(bounds); + EXPECT_THAT(manager.takeChangedTiles(), ElementsAre( std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(0, 0), ChangeType::remove) )); diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index 8038de2bc0..61c2a36345 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -23,12 +23,12 @@ namespace DetourNavigator return true; } - std::optional CachedRecastMeshManager::removeObject(const ObjectId id) + bool CachedRecastMeshManager::removeObject(const ObjectId id) { - auto object = mImpl.removeObject(id); - if (object) + const bool result = mImpl.removeObject(id); + if (result) mOutdatedCache = true; - return object; + return result; } bool CachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) @@ -39,12 +39,12 @@ namespace DetourNavigator return true; } - std::optional CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + bool CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) { - const auto water = mImpl.removeWater(cellPosition); - if (water) + const bool result = mImpl.removeWater(cellPosition); + if (result) mOutdatedCache = true; - return water; + return result; } bool CachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, @@ -56,12 +56,12 @@ namespace DetourNavigator return true; } - std::optional CachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) + bool CachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) { - const auto heightfield = mImpl.removeHeightfield(cellPosition); - if (heightfield) + const bool result = mImpl.removeHeightfield(cellPosition); + if (result) mOutdatedCache = true; - return heightfield; + return result; } std::shared_ptr CachedRecastMeshManager::getMesh() diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index b92d39efa4..1fcb8e5a28 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -21,15 +21,15 @@ namespace DetourNavigator bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType); - std::optional removeObject(const ObjectId id); + bool removeObject(const ObjectId id); bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level); - std::optional removeWater(const osg::Vec2i& cellPosition); + bool removeWater(const osg::Vec2i& cellPosition); bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); - std::optional removeHeightfield(const osg::Vec2i& cellPosition); + bool removeHeightfield(const osg::Vec2i& cellPosition); std::shared_ptr getMesh(); diff --git a/components/detournavigator/changetype.hpp b/components/detournavigator/changetype.hpp index 15b3056e55..e6d0bce0a9 100644 --- a/components/detournavigator/changetype.hpp +++ b/components/detournavigator/changetype.hpp @@ -10,6 +10,11 @@ namespace DetourNavigator add = 2, update = 3, }; + + inline ChangeType addChangeType(const ChangeType current, const ChangeType add) + { + return current == add ? current : ChangeType::mixed; + } } #endif diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 0211d3dd50..95dc057adb 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -149,8 +149,7 @@ namespace DetourNavigator void NavigatorImpl::update(const osg::Vec3f& playerPosition) { removeUnusedNavMeshes(); - for (const auto& v : mAgents) - mNavMeshManager.update(playerPosition, v.first); + mNavMeshManager.update(playerPosition); } void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType) diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 811e97d826..126b5a1da5 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -7,6 +7,7 @@ #include "settings.hpp" #include "waitconditiontype.hpp" #include "settingsutils.hpp" +#include "cachedrecastmeshmanager.hpp" #include #include @@ -20,13 +21,6 @@ namespace { - using DetourNavigator::ChangeType; - - ChangeType addChangeType(const ChangeType current, const ChangeType add) - { - return current == add ? current : ChangeType::mixed; - } - /// Safely reset shared_ptr with definite underlying object destrutor call. /// Assuming there is another thread holding copy of this shared_ptr or weak_ptr to this shared_ptr. template @@ -78,75 +72,44 @@ namespace DetourNavigator { 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); - } - } + mRecastMeshManager.setBounds(bounds); } bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, const AreaType areaType) { - return mRecastMeshManager.addObject(id, shape, transform, areaType, - [&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::add); }); + return mRecastMeshManager.addObject(id, shape, transform, areaType); } 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, ChangeType changeType) { addChangedTile(tile, changeType); }); + return mRecastMeshManager.updateObject(id, shape, transform, areaType); } void NavMeshManager::removeObject(const ObjectId id) { - const auto object = mRecastMeshManager.removeObject(id); - if (!object) - return; - addChangedTiles(object->mShape, object->mTransform, ChangeType::remove); + mRecastMeshManager.removeObject(id); } void NavMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) { - if (!mRecastMeshManager.addWater(cellPosition, cellSize, level)) - return; - const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level)); - addChangedTiles(cellSize, shift, ChangeType::add); + mRecastMeshManager.addWater(cellPosition, cellSize, level); } void NavMeshManager::removeWater(const osg::Vec2i& cellPosition) { - const auto water = mRecastMeshManager.removeWater(cellPosition); - if (!water) - return; - const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, water->mCellSize, water->mLevel)); - addChangedTiles(water->mCellSize, shift, ChangeType::remove); + mRecastMeshManager.removeWater(cellPosition); } void NavMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) { - if (!mRecastMeshManager.addHeightfield(cellPosition, cellSize, shape)) - return; - const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize); - addChangedTiles(cellSize, shift, ChangeType::add); + mRecastMeshManager.addHeightfield(cellPosition, cellSize, shape); } void NavMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) { - const auto heightfield = mRecastMeshManager.removeHeightfield(cellPosition); - if (!heightfield) - return; - const btVector3 shift = getHeightfieldShift(heightfield->mShape, cellPosition, heightfield->mCellSize); - addChangedTiles(heightfield->mCellSize, shift, ChangeType::remove); + mRecastMeshManager.removeHeightfield(cellPosition); } void NavMeshManager::addAgent(const AgentBounds& agentBounds) @@ -156,6 +119,7 @@ namespace DetourNavigator return; mCache.insert(std::make_pair(agentBounds, std::make_shared(makeEmptyNavMesh(mSettings), ++mGenerationCounter))); + mPlayerTile.reset(); Log(Debug::Debug) << "cache add for agent=" << agentBounds; } @@ -167,9 +131,7 @@ namespace DetourNavigator if (!resetIfUnique(it->second)) return false; mCache.erase(agentBounds); - mChangedTiles.erase(agentBounds); - mPlayerTile.erase(agentBounds); - mLastRecastMeshManagerRevision.erase(agentBounds); + mPlayerTile.reset(); return true; } @@ -180,55 +142,50 @@ namespace DetourNavigator const auto startTilePosition = getTilePosition(mSettings.mRecast, start); const auto endTilePosition = getTilePosition(mSettings.mRecast, end); - addChangedTile(startTilePosition, ChangeType::add); + mRecastMeshManager.addChangedTile(startTilePosition, ChangeType::add); if (startTilePosition != endTilePosition) - addChangedTile(endTilePosition, ChangeType::add); + mRecastMeshManager.addChangedTile(endTilePosition, ChangeType::add); } void NavMeshManager::removeOffMeshConnections(const ObjectId id) { const auto changedTiles = mOffMeshConnectionsManager.remove(id); for (const auto& tile : changedTiles) - addChangedTile(tile, ChangeType::update); + mRecastMeshManager.addChangedTile(tile, ChangeType::update); } - void NavMeshManager::update(const osg::Vec3f& playerPosition, const AgentBounds& agentBounds) + void NavMeshManager::update(const osg::Vec3f& playerPosition) { - const auto playerTile = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition)); - auto& lastRevision = mLastRecastMeshManagerRevision[agentBounds]; - auto lastPlayerTile = mPlayerTile.find(agentBounds); - if (lastRevision == mRecastMeshManager.getRevision() && lastPlayerTile != mPlayerTile.end() - && lastPlayerTile->second == playerTile) + const auto playerTile = getTilePosition(mSettings.mRecast, + toNavMeshCoordinates(mSettings.mRecast, playerPosition)); + if (mLastRecastMeshManagerRevision == mRecastMeshManager.getRevision() + && mPlayerTile.has_value() && *mPlayerTile == playerTile) return; - lastRevision = mRecastMeshManager.getRevision(); - if (lastPlayerTile == mPlayerTile.end()) - lastPlayerTile = mPlayerTile.insert(std::make_pair(agentBounds, playerTile)).first; - else - lastPlayerTile->second = playerTile; + mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision(); + mPlayerTile = playerTile; + const auto changedTiles = mRecastMeshManager.takeChangedTiles(); + for (const auto& [agentBounds, cached] : mCache) + update(agentBounds, playerTile, cached, changedTiles); + } + + void NavMeshManager::update(const AgentBounds& agentBounds, const TilePosition& playerTile, + const SharedNavMeshCacheItem& cached, const std::map& changedTiles) + { std::map tilesToPost; - const auto cached = getCached(agentBounds); - if (!cached) - { - std::ostringstream stream; - stream << "Agent with half extents is not found: " << agentBounds; - throw InvalidArgument(stream.str()); - } - const auto changedTiles = mChangedTiles.find(agentBounds); { const auto locked = cached->lockConst(); const auto& navMesh = locked->getImpl(); - if (changedTiles != mChangedTiles.end()) + for (const auto& [tilePosition, changeType] : changedTiles) { - for (const auto& tile : changedTiles->second) - if (navMesh.getTileAt(tile.first.x(), tile.first.y(), 0)) - { - auto tileToPost = tilesToPost.find(tile.first); - if (tileToPost == tilesToPost.end()) - tilesToPost.insert(tile); - else - tileToPost->second = addChangeType(tileToPost->second, tile.second); - } + if (navMesh.getTileAt(tilePosition.x(), tilePosition.y(), 0)) + { + auto tileToPost = tilesToPost.find(tilePosition); + if (tileToPost == tilesToPost.end()) + tilesToPost.emplace(tilePosition, changeType); + else + tileToPost->second = addChangeType(tileToPost->second, changeType); + } } const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles); mRecastMeshManager.forEachTile([&] (const TilePosition& tile, CachedRecastMeshManager& recastMeshManager) @@ -238,19 +195,16 @@ namespace DetourNavigator const auto shouldAdd = shouldAddTile(tile, playerTile, maxTiles); const auto presentInNavMesh = bool(navMesh.getTileAt(tile.x(), tile.y(), 0)); if (shouldAdd && !presentInNavMesh) - tilesToPost.insert(std::make_pair(tile, locked->isEmptyTile(tile) ? ChangeType::update : ChangeType::add)); + tilesToPost.emplace(tile, locked->isEmptyTile(tile) ? ChangeType::update : ChangeType::add); else if (!shouldAdd && presentInNavMesh) - tilesToPost.insert(std::make_pair(tile, ChangeType::mixed)); + tilesToPost.emplace(tile, ChangeType::mixed); else recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0}); }); } mAsyncNavMeshUpdater.post(agentBounds, cached, playerTile, mRecastMeshManager.getWorldspace(), tilesToPost); - if (changedTiles != mChangedTiles.end()) - changedTiles->second.clear(); Log(Debug::Debug) << "Cache update posted for agent=" << agentBounds << - " playerTile=" << lastPlayerTile->second << - " recastMeshManagerRevision=" << lastRevision; + " playerTile=" << playerTile << " recastMeshManagerRevision=" << mLastRecastMeshManagerRevision; } void NavMeshManager::wait(Loading::Listener& listener, WaitConditionType waitConditionType) @@ -286,37 +240,6 @@ namespace DetourNavigator return result; } - void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, - const ChangeType changeType) - { - const auto bounds = mRecastMeshManager.getBounds(); - getTilesPositions(makeTilesPositionsRange(shape, transform, bounds, mSettings.mRecast), - [&] (const TilePosition& v) { addChangedTile(v, changeType); }); - } - - void NavMeshManager::addChangedTiles(const int cellSize, const btVector3& shift, - const ChangeType changeType) - { - if (cellSize == std::numeric_limits::max()) - return; - - getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings.mRecast), - [&] (const TilePosition& v) { addChangedTile(v, changeType); }); - } - - void NavMeshManager::addChangedTile(const TilePosition& tilePosition, const ChangeType changeType) - { - for (const auto& cached : mCache) - { - auto& tiles = mChangedTiles[cached.first]; - auto tile = tiles.find(tilePosition); - if (tile == tiles.end()) - tiles.insert(std::make_pair(tilePosition, changeType)); - else - tile->second = addChangeType(tile->second, changeType); - } - } - SharedNavMeshCacheItem NavMeshManager::getCached(const AgentBounds& agentBounds) const { const auto cached = mCache.find(agentBounds); diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index c479834aa8..adaefb14c4 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -50,7 +50,7 @@ namespace DetourNavigator void removeOffMeshConnections(const ObjectId id); - void update(const osg::Vec3f& playerPosition, const AgentBounds& agentBounds); + void update(const osg::Vec3f& playerPosition); void wait(Loading::Listener& listener, WaitConditionType waitConditionType); @@ -69,18 +69,14 @@ namespace DetourNavigator OffMeshConnectionsManager mOffMeshConnectionsManager; AsyncNavMeshUpdater mAsyncNavMeshUpdater; std::map mCache; - std::map> mChangedTiles; std::size_t mGenerationCounter = 0; - std::map mPlayerTile; - std::map mLastRecastMeshManagerRevision; + std::optional mPlayerTile; + std::size_t mLastRecastMeshManagerRevision = 0; - void addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType); + inline SharedNavMeshCacheItem getCached(const AgentBounds& agentBounds) const; - void addChangedTiles(const int cellSize, const btVector3& shift, const ChangeType changeType); - - void addChangedTile(const TilePosition& tilePosition, const ChangeType changeType); - - SharedNavMeshCacheItem getCached(const AgentBounds& agentBounds) const; + inline void update(const AgentBounds& agentBounds, const TilePosition& playerTile, + const SharedNavMeshCacheItem& cached, const std::map& changedTiles); }; } diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 5d09aaea33..8eb8b462a9 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -62,16 +62,15 @@ namespace DetourNavigator return true; } - std::optional RecastMeshManager::removeObject(const ObjectId id) + bool RecastMeshManager::removeObject(const ObjectId id) { const std::lock_guard lock(mMutex); const auto object = mObjects.find(id); if (object == mObjects.end()) - return std::nullopt; - const RemovedRecastMeshObject result {object->second.getImpl().getShape(), object->second.getImpl().getTransform()}; + return false; mObjects.erase(object); ++mRevision; - return result; + return true; } bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) @@ -83,16 +82,15 @@ namespace DetourNavigator return true; } - std::optional RecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + bool RecastMeshManager::removeWater(const osg::Vec2i& cellPosition) { const std::lock_guard lock(mMutex); const auto water = mWater.find(cellPosition); if (water == mWater.end()) - return std::nullopt; + return false; ++mRevision; - Water result = water->second; mWater.erase(water); - return result; + return true; } bool RecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, @@ -105,16 +103,15 @@ namespace DetourNavigator return true; } - std::optional RecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) + bool RecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) { const std::lock_guard lock(mMutex); const auto it = mHeightfields.find(cellPosition); if (it == mHeightfields.end()) - return std::nullopt; + return false; ++mRevision; - auto result = std::make_optional(it->second); mHeightfields.erase(it); - return result; + return true; } std::shared_ptr RecastMeshManager::getMesh() const diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index e897c797fc..8618d749e6 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -23,12 +23,6 @@ namespace DetourNavigator struct Settings; class RecastMesh; - struct RemovedRecastMeshObject - { - std::reference_wrapper mShape; - btTransform mTransform; - }; - struct SizedHeightfieldShape { int mCellSize; @@ -45,15 +39,15 @@ namespace DetourNavigator bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType); - std::optional removeObject(const ObjectId id); + bool removeObject(const ObjectId id); bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level); - std::optional removeWater(const osg::Vec2i& cellPosition); + bool removeWater(const osg::Vec2i& cellPosition); bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); - std::optional removeHeightfield(const osg::Vec2i& cellPosition); + bool removeHeightfield(const osg::Vec2i& cellPosition); std::shared_ptr getMesh() const; diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 046eb7750d..18c8ef26ca 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -2,6 +2,7 @@ #include "gettilespositions.hpp" #include "settingsutils.hpp" #include "changetype.hpp" +#include "cachedrecastmeshmanager.hpp" #include #include @@ -18,6 +19,13 @@ namespace DetourNavigator osg::Vec2f(-std::numeric_limits::max(), -std::numeric_limits::max()), osg::Vec2f(std::numeric_limits::max(), std::numeric_limits::max()) }; + + bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType, + const TilePosition& tilePosition, std::map>& tiles) + { + const auto tile = tiles.find(tilePosition); + return tile != tiles.end() && tile->second->updateObject(id, transform, areaType); + } } TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings) @@ -26,18 +34,12 @@ namespace DetourNavigator , mRange(makeTilesPositionsRange(mBounds.mMin, mBounds.mMax, mSettings)) {} - TileBounds TileCachedRecastMeshManager::getBounds() const - { - return mBounds; - } - - std::vector> TileCachedRecastMeshManager::setBounds(const TileBounds& bounds) + void TileCachedRecastMeshManager::setBounds(const TileBounds& bounds) { - std::vector> changedTiles; - if (mBounds == bounds) - return changedTiles; + return; + bool changed = false; const auto newRange = makeTilesPositionsRange(bounds.mMin, bounds.mMax, mSettings); if (mBounds != infiniteTileBounds) @@ -58,7 +60,10 @@ namespace DetourNavigator return; data.mTiles.erase(it); if (removeTile(id, position, locked->mTiles)) - changedTiles.emplace_back(position, ChangeType::remove); + { + addChangedTile(position, ChangeType::remove); + changed = true; + } }; getTilesPositions(getIntersection(mRange, objectRange), onOldTilePosition); @@ -69,23 +74,18 @@ namespace DetourNavigator if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, locked->mTiles)) { data.mTiles.insert(position); - changedTiles.emplace_back(position, ChangeType::add); + addChangedTile(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()) + if (changed) ++mRevision; mBounds = bounds; mRange = newRange; - - return changedTiles; } std::string TileCachedRecastMeshManager::getWorldspace() const @@ -102,47 +102,125 @@ namespace DetourNavigator locked->mWorldspace = worldspace; } - std::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) + bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, + const btTransform& transform, const AreaType areaType) + { + 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 auto locked = mWorldspaceTiles.lock(); + getTilesPositions(range, + [&] (const TilePosition& tilePosition) + { + if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles)) + { + tilesPositions.insert(tilePosition); + addChangedTile(tilePosition, ChangeType::add); + } + }); + } + mObjects.emplace_hint(it, id, ObjectData {shape, transform, areaType, std::move(tilesPositions)}); + ++mRevision; + return true; + } + + bool TileCachedRecastMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, + const btTransform& transform, AreaType areaType) + { + const auto object = mObjects.find(id); + if (object == mObjects.end()) + return false; + auto& data = object->second; + 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, locked->mTiles)) + { + addChangedTile(tilePosition, ChangeType::update); + changed = true; + } + } + else if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles)) + { + newTiles.insert(tilePosition); + addChangedTile(tilePosition, ChangeType::add); + changed = true; + } + }; + getTilesPositions(range, onTilePosition); + for (const auto& tile : data.mTiles) + { + if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, locked->mTiles)) + { + addChangedTile(tile, ChangeType::remove); + changed = true; + } + } + } + if (changed) + { + data.mTiles = std::move(newTiles); + ++mRevision; + } + return changed; + } + + void TileCachedRecastMeshManager::removeObject(const ObjectId id) { const auto object = mObjects.find(id); if (object == mObjects.end()) - return std::nullopt; - std::optional result; + return; + bool changed = false; { const auto locked = mWorldspaceTiles.lock(); for (const auto& tilePosition : object->second.mTiles) { - const auto removed = removeTile(id, tilePosition, locked->mTiles); - if (removed && !result) - result = removed; + if (removeTile(id, tilePosition, locked->mTiles)) + { + addChangedTile(tilePosition, ChangeType::remove); + changed = true; + } } } mObjects.erase(object); - if (result) + if (changed) ++mRevision; - return result; } - bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) + void TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const float level) { const auto it = mWaterTilesPositions.find(cellPosition); if (it != mWaterTilesPositions.end()) - return false; + return; std::vector& tilesPositions = mWaterTilesPositions.emplace_hint( it, cellPosition, std::vector())->second; - bool result = false; + bool changed = false; if (cellSize == std::numeric_limits::max()) { const auto locked = mWorldspaceTiles.lock(); - for (auto& tile : locked->mTiles) + for (auto& [tilePosition, data] : locked->mTiles) { - if (tile.second->addWater(cellPosition, cellSize, level)) + if (data->addWater(cellPosition, cellSize, level)) { - tilesPositions.push_back(tile.first); - result = true; + tilesPositions.push_back(tilePosition); + addChangedTile(tilePosition, ChangeType::add); + changed = true; } } } @@ -163,23 +241,22 @@ namespace DetourNavigator if (tile->second->addWater(cellPosition, cellSize, level)) { tilesPositions.push_back(tilePosition); - result = true; + addChangedTile(tilePosition, ChangeType::add); + changed = true; } }); } - if (result) + if (changed) ++mRevision; - - return result; } - std::optional TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + void TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) { const auto object = mWaterTilesPositions.find(cellPosition); if (object == mWaterTilesPositions.end()) - return std::nullopt; - std::optional result; + return; + bool changed = false; { const auto worldspaceTiles = mWorldspaceTiles.lock(); for (const auto& tilePosition : object->second) @@ -187,34 +264,35 @@ namespace DetourNavigator const auto tile = worldspaceTiles->mTiles.find(tilePosition); if (tile == worldspaceTiles->mTiles.end()) continue; - const auto tileResult = tile->second->removeWater(cellPosition); + if (tile->second->removeWater(cellPosition)) + { + addChangedTile(tilePosition, ChangeType::remove); + changed = true; + } if (tile->second->isEmpty()) { worldspaceTiles->mTiles.erase(tile); ++mTilesGeneration; } - if (tileResult && !result) - result = tileResult; } } mWaterTilesPositions.erase(object); - if (result) + if (changed) ++mRevision; - return result; } - bool TileCachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, + void TileCachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, const int cellSize, const HeightfieldShape& shape) { const auto it = mHeightfieldTilesPositions.find(cellPosition); if (it != mHeightfieldTilesPositions.end()) - return false; + return; std::vector& tilesPositions = mHeightfieldTilesPositions.emplace_hint( it, cellPosition, std::vector())->second; const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize); - bool result = false; + bool changed = false; { const auto worldspaceTiles = mWorldspaceTiles.lock(); @@ -231,23 +309,22 @@ namespace DetourNavigator if (tile->second->addHeightfield(cellPosition, cellSize, shape)) { tilesPositions.push_back(tilePosition); - result = true; + addChangedTile(tilePosition, ChangeType::add); + changed = true; } }); } - if (result) + if (changed) ++mRevision; - - return result; } - std::optional TileCachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) + void TileCachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) { const auto object = mHeightfieldTilesPositions.find(cellPosition); if (object == mHeightfieldTilesPositions.end()) - return std::nullopt; - std::optional result; + return; + bool changed = false; { const auto worldspaceTiles = mWorldspaceTiles.lock(); for (const auto& tilePosition : object->second) @@ -255,20 +332,21 @@ namespace DetourNavigator const auto tile = worldspaceTiles->mTiles.find(tilePosition); if (tile == worldspaceTiles->mTiles.end()) continue; - const auto tileResult = tile->second->removeHeightfield(cellPosition); + if (tile->second->removeHeightfield(cellPosition)) + { + addChangedTile(tilePosition, ChangeType::remove); + changed = true; + } if (tile->second->isEmpty()) { worldspaceTiles->mTiles.erase(tile); ++mTilesGeneration; } - if (tileResult && !result) - result = tileResult; } } mHeightfieldTilesPositions.erase(object); - if (result) + if (changed) ++mRevision; - return result; } std::shared_ptr TileCachedRecastMeshManager::getMesh(std::string_view worldspace, const TilePosition& tilePosition) const @@ -292,11 +370,6 @@ namespace DetourNavigator return nullptr; } - std::size_t TileCachedRecastMeshManager::getRevision() const - { - return mRevision; - } - void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const { const auto locked = mWorldspaceTiles.lockConst(); @@ -306,6 +379,15 @@ namespace DetourNavigator it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion); } + void TileCachedRecastMeshManager::addChangedTile(const TilePosition& tilePosition, ChangeType changeType) + { + auto tile = mChangedTiles.find(tilePosition); + if (tile == mChangedTiles.end()) + mChangedTiles.emplace(tilePosition, changeType); + else + tile->second = addChangeType(tile->second, changeType); + } + bool TileCachedRecastMeshManager::addTile(const ObjectId id, const CollisionShape& shape, const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles) @@ -320,26 +402,19 @@ namespace DetourNavigator return tile->second->addObject(id, shape, transform, areaType); } - bool TileCachedRecastMeshManager::updateTile(const ObjectId id, const btTransform& transform, - const AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles) - { - const auto tile = tiles.find(tilePosition); - return tile != tiles.end() && tile->second->updateObject(id, transform, areaType); - } - - std::optional TileCachedRecastMeshManager::removeTile(const ObjectId id, + bool TileCachedRecastMeshManager::removeTile(const ObjectId id, const TilePosition& tilePosition, TilesMap& tiles) { const auto tile = tiles.find(tilePosition); if (tile == tiles.end()) - return std::optional(); - auto tileResult = tile->second->removeObject(id); + return false; + const bool result = tile->second->removeObject(id); if (tile->second->isEmpty()) { tiles.erase(tile); ++mTilesGeneration; } - return tileResult; + return result; } std::shared_ptr TileCachedRecastMeshManager::getManager(std::string_view worldspace, diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index f6ef24ceed..b1bcfa9319 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -1,16 +1,17 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H -#include "cachedrecastmeshmanager.hpp" #include "tileposition.hpp" #include "gettilespositions.hpp" #include "version.hpp" #include "heightfieldshape.hpp" #include "changetype.hpp" +#include "objectid.hpp" +#include "areatype.hpp" +#include "recastmeshobject.hpp" #include -#include #include #include #include @@ -18,104 +19,33 @@ namespace DetourNavigator { + class CachedRecastMeshManager; + class RecastMesh; + class TileCachedRecastMeshManager { public: explicit TileCachedRecastMeshManager(const RecastSettings& settings); - TileBounds getBounds() const; - - std::vector> setBounds(const TileBounds& bounds); + void 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, 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 auto locked = mWorldspaceTiles.lock(); - getTilesPositions(range, - [&] (const TilePosition& tilePosition) - { - if (addTile(id, shape, transform, areaType, tilePosition, locked->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; - } + bool addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType); - template - bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType, OnChangedTile&& onChangedTile) - { - const auto object = mObjects.find(id); - if (object == mObjects.end()) - return false; - auto& data = object->second; - 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, locked->mTiles)) - { - onChangedTile(tilePosition, ChangeType::update); - changed = true; - } - } - else if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles)) - { - newTiles.insert(tilePosition); - onChangedTile(tilePosition, ChangeType::add); - changed = true; - } - }; - getTilesPositions(range, onTilePosition); - for (const auto& tile : data.mTiles) - { - if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, locked->mTiles)) - { - onChangedTile(tile, ChangeType::remove); - changed = true; - } - } - } - if (changed) - { - data.mTiles = std::move(newTiles); - ++mRevision; - } - return changed; - } + bool updateObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType); - std::optional removeObject(const ObjectId id); + void removeObject(ObjectId id); - bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level); + void addWater(const osg::Vec2i& cellPosition, int cellSize, float level); - std::optional removeWater(const osg::Vec2i& cellPosition); + void removeWater(const osg::Vec2i& cellPosition); - bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); + void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); - std::optional removeHeightfield(const osg::Vec2i& cellPosition); + void removeHeightfield(const osg::Vec2i& cellPosition); std::shared_ptr getMesh(std::string_view worldspace, const TilePosition& tilePosition) const; @@ -131,10 +61,14 @@ namespace DetourNavigator function(tilePosition, *recastMeshManager); } - std::size_t getRevision() const; + std::size_t getRevision() const { return mRevision; } void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const; + void addChangedTile(const TilePosition& tilePosition, ChangeType changeType); + + std::map takeChangedTiles() { return std::move(mChangedTiles); } + private: using TilesMap = std::map>; @@ -159,17 +93,14 @@ namespace DetourNavigator std::unordered_map mObjects; std::map> mWaterTilesPositions; std::map> mHeightfieldTilesPositions; + std::map mChangedTiles; std::size_t mRevision = 0; std::size_t mTilesGeneration = 0; - bool addTile(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles); - - static bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType, - const TilePosition& tilePosition, TilesMap& tiles); + inline bool addTile(ObjectId id, const CollisionShape& shape, const btTransform& transform, + AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles); - std::optional removeTile(const ObjectId id, const TilePosition& tilePosition, - TilesMap& tiles); + inline bool removeTile(ObjectId id, const TilePosition& tilePosition, TilesMap& tiles); inline std::shared_ptr getManager(std::string_view worldspace, const TilePosition& tilePosition) const;