1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-19 08:09:44 +00:00

Cull navmesh objects by scene bounds

If object is too big iteration over all tiles covering it can take too much
time. Limit bounds to a square around a player position to cover only tiles
that will be present in navmesh based on max tiles number option.

Each object is associated with a set of tiles its present in. Culling can
reduce this set but it has to be update when bounds change position. Do this
in TileCachedRecastMeshManager::setBounds updating the set and adding/removing
objects to the corresponding CachedRecastMeshManagers.
This commit is contained in:
elsid 2022-02-01 01:21:47 +01:00
parent 563f3f87dd
commit 05b54cbfb8
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
19 changed files with 378 additions and 127 deletions

View file

@ -298,12 +298,14 @@ namespace NavMeshTool
const ObjectId objectId(++objectsCounter); const ObjectId objectId(++objectsCounter);
const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform()); const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform());
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, DetourNavigator::AreaType_ground); navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform,
DetourNavigator::AreaType_ground, [] (const auto&) {});
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get()) if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
{ {
const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform()); const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform());
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform, DetourNavigator::AreaType_null); navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform,
DetourNavigator::AreaType_null, [] (const auto&) {});
} }
data.mObjects.emplace_back(std::move(object)); data.mObjects.emplace_back(std::move(object));

View file

@ -534,6 +534,8 @@ namespace MWWorld
unloadCell (cell); unloadCell (cell);
} }
mNavigator.updateBounds(pos);
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
mRendering.setActiveGrid(newGrid); mRendering.setActiveGrid(newGrid);
@ -821,6 +823,8 @@ namespace MWWorld
loadingListener->setProgressRange(cell->count()); loadingListener->setProgressRange(cell->count());
mNavigator.updateBounds(position.asVec3());
// Load cell. // Load cell.
mPagedRefs.clear(); mPagedRefs.clear();
loadCell(cell, loadingListener, changeEvent, position.asVec3()); loadCell(cell, loadingListener, changeEvent, position.asVec3());

View file

@ -40,7 +40,7 @@ namespace
osg::ref_ptr<Resource::BulletShapeInstance>(new Resource::BulletShapeInstance(bulletShape)), osg::ref_ptr<Resource::BulletShapeInstance>(new Resource::BulletShapeInstance(bulletShape)),
shape, objectTransform shape, objectTransform
); );
recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground); recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground, [] (auto) {});
} }
struct DetourNavigatorAsyncNavMeshUpdaterTest : Test struct DetourNavigatorAsyncNavMeshUpdaterTest : Test

View file

@ -12,14 +12,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
namespace DetourNavigator
{
static inline bool operator ==(const TileBounds& lhs, const TileBounds& rhs)
{
return lhs.mMin == rhs.mMin && lhs.mMax == rhs.mMax;
}
}
namespace namespace
{ {
template <class T> template <class T>

View file

@ -16,7 +16,8 @@ namespace
struct DetourNavigatorTileCachedRecastMeshManagerTest : Test struct DetourNavigatorTileCachedRecastMeshManagerTest : Test
{ {
RecastSettings mSettings; RecastSettings mSettings;
std::vector<TilePosition> mChangedTiles; std::vector<TilePosition> mAddedTiles;
std::vector<std::pair<TilePosition, ChangeType>> mChangedTiles;
const ObjectTransform mObjectTransform {ESM::Position {{0, 0, 0}, {0, 0, 0}}, 0.0f}; const ObjectTransform mObjectTransform {ESM::Position {{0, 0, 0}, {0, 0, 0}}, 0.0f};
const osg::ref_ptr<const Resource::BulletShape> mShape = new Resource::BulletShape; const osg::ref_ptr<const Resource::BulletShape> mShape = new Resource::BulletShape;
const osg::ref_ptr<const Resource::BulletShapeInstance> mInstance = new Resource::BulletShapeInstance(mShape); const osg::ref_ptr<const Resource::BulletShapeInstance> mInstance = new Resource::BulletShapeInstance(mShape);
@ -29,9 +30,14 @@ namespace
mSettings.mTileSize = 64; mSettings.mTileSize = 64;
} }
void onChangedTile(const TilePosition& tilePosition) void onAddedTile(const TilePosition& tilePosition)
{ {
mChangedTiles.push_back(tilePosition); mAddedTiles.push_back(tilePosition);
}
void onChangedTile(const TilePosition& tilePosition, ChangeType changeType)
{
mChangedTiles.emplace_back(tilePosition, changeType);
} }
}; };
@ -60,16 +66,16 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_throw_exception) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)
@ -78,26 +84,47 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
for (int x = -1; x < 1; ++x) for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y) for (int y = -1; y < 1; ++y)
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_return_added_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
TileBounds bounds;
bounds.mMin = osg::Vec2f(182, 182);
bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
[&] (const auto& v) { onAddedTile(v); });
EXPECT_THAT(mAddedTiles, ElementsAre(TilePosition(0, 0)));
}
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); TileBounds bounds;
bounds.mMin = osg::Vec2f(-1000, -1000);
bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
[&] (const auto& v) { onChangedTile(v); })); [&] (const auto& ... v) { onChangedTile(v ...); }));
EXPECT_THAT( EXPECT_THAT(mChangedTiles, ElementsAre(
mChangedTiles, std::pair(TilePosition(-1, -1), ChangeType::add),
ElementsAre(TilePosition(-1, -1), TilePosition(-1, 0), TilePosition(0, -1), TilePosition(0, 0), std::pair(TilePosition(-1, 0), ChangeType::add),
TilePosition(1, -1), TilePosition(1, 0)) std::pair(TilePosition(0, -1), ChangeType::update),
); std::pair(TilePosition(0, 0), ChangeType::update),
std::pair(TilePosition(1, -1), ChangeType::remove),
std::pair(TilePosition(1, 0), ChangeType::remove)
));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_return_empty) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_return_empty)
@ -105,10 +132,10 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
[&] (const auto& v) { onChangedTile(v); })); [&] (const auto& ... v) { onChangedTile(v ...); }));
EXPECT_EQ(mChangedTiles, std::vector<TilePosition>()); EXPECT_THAT(mChangedTiles, IsEmpty());
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile)
@ -117,7 +144,7 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), 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, -1)), nullptr);
@ -130,26 +157,30 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
TileBounds bounds;
bounds.mMin = osg::Vec2f(-1000, -1000);
bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds);
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), 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, 0)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), 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, -1)), nullptr);
@ -165,11 +196,11 @@ namespace
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
} }
@ -180,7 +211,7 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape));
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
@ -195,13 +226,13 @@ namespace
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), 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, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), 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, -1)), nullptr);
@ -214,7 +245,7 @@ namespace
const auto initialRevision = manager.getRevision(); const auto initialRevision = manager.getRevision();
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_EQ(manager.getRevision(), initialRevision + 1); EXPECT_EQ(manager.getRevision(), initialRevision + 1);
} }
@ -223,9 +254,9 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
const auto beforeAddRevision = manager.getRevision(); const auto beforeAddRevision = manager.getRevision();
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
EXPECT_EQ(manager.getRevision(), beforeAddRevision); EXPECT_EQ(manager.getRevision(), beforeAddRevision);
} }
@ -235,9 +266,9 @@ namespace
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
const auto beforeUpdateRevision = manager.getRevision(); const auto beforeUpdateRevision = manager.getRevision();
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1);
} }
@ -247,9 +278,9 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
const auto beforeUpdateRevision = manager.getRevision(); const auto beforeUpdateRevision = manager.getRevision();
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision);
} }
@ -258,7 +289,7 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
const auto beforeRemoveRevision = manager.getRevision(); const auto beforeRemoveRevision = manager.getRevision();
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape));
EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1);
@ -298,7 +329,7 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = std::numeric_limits<int>::max(); const int cellSize = std::numeric_limits<int>::max();
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
@ -343,7 +374,7 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
@ -361,7 +392,7 @@ namespace
const int cellSize = 8192; const int cellSize = 8192;
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform); const CollisionShape shape(mInstance, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f)); ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
ASSERT_TRUE(manager.removeObject(ObjectId(&boxShape))); ASSERT_TRUE(manager.removeObject(ObjectId(&boxShape)));
for (int x = -1; x < 12; ++x) for (int x = -1; x < 12; ++x)
@ -375,10 +406,28 @@ namespace
manager.setWorldspace("worldspace"); manager.setWorldspace("worldspace");
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(nullptr, boxShape, mObjectTransform); const CollisionShape shape(nullptr, boxShape, mObjectTransform);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
manager.setWorldspace("other"); manager.setWorldspace("other");
for (int x = -1; x < 1; ++x) for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y) for (int y = -1; y < 1; ++y)
ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr); ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr);
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_bounds_should_return_changed_tiles)
{
TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
TileBounds bounds;
bounds.mMin = osg::Vec2f(182, 0);
bounds.mMax = osg::Vec2f(1000, 1000);
manager.setBounds(bounds);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
bounds.mMin = osg::Vec2f(-1000, -1000);
bounds.mMax = osg::Vec2f(0, -182);
EXPECT_THAT(manager.setBounds(bounds), ElementsAre(
std::pair(TilePosition(-1, -1), ChangeType::add),
std::pair(TilePosition(0, 0), ChangeType::remove)
));
}
} }

View file

@ -8,6 +8,7 @@
#include "navmeshtilescache.hpp" #include "navmeshtilescache.hpp"
#include "waitconditiontype.hpp" #include "waitconditiontype.hpp"
#include "navmeshdb.hpp" #include "navmeshdb.hpp"
#include "changetype.hpp"
#include <osg/Vec3f> #include <osg/Vec3f>
@ -32,29 +33,6 @@ namespace Loading
namespace DetourNavigator namespace DetourNavigator
{ {
enum class ChangeType
{
remove = 0,
mixed = 1,
add = 2,
update = 3,
};
inline std::ostream& operator <<(std::ostream& stream, ChangeType value)
{
switch (value) {
case ChangeType::remove:
return stream << "ChangeType::remove";
case ChangeType::mixed:
return stream << "ChangeType::mixed";
case ChangeType::add:
return stream << "ChangeType::add";
case ChangeType::update:
return stream << "ChangeType::update";
}
return stream << "ChangeType::" << static_cast<int>(value);
}
enum class JobState enum class JobState
{ {
Initial, Initial,

View file

@ -0,0 +1,33 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CHANGETYPE_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CHANGETYPE_H
#include <ostream>
namespace DetourNavigator
{
enum class ChangeType
{
remove = 0,
mixed = 1,
add = 2,
update = 3,
};
inline std::ostream& operator <<(std::ostream& stream, ChangeType value)
{
switch (value)
{
case ChangeType::remove:
return stream << "ChangeType::remove";
case ChangeType::mixed:
return stream << "ChangeType::mixed";
case ChangeType::add:
return stream << "ChangeType::add";
case ChangeType::update:
return stream << "ChangeType::update";
}
return stream << "ChangeType::" << static_cast<int>(value);
}
}
#endif

View file

@ -39,6 +39,14 @@ namespace DetourNavigator
return makeTilesPositionsRange(bounds.mMin, bounds.mMax, settings); return makeTilesPositionsRange(bounds.mMin, bounds.mMax, settings);
} }
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform,
const TileBounds& bounds, const RecastSettings& settings)
{
if (const auto intersection = getIntersection(bounds, makeObjectTileBounds(shape, transform)))
return makeTilesPositionsRange(intersection->mMin, intersection->mMax, settings);
return {};
}
TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift, TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift,
const RecastSettings& settings) const RecastSettings& settings)
{ {
@ -55,4 +63,17 @@ namespace DetourNavigator
return makeTilesPositionsRange(Misc::Convert::toOsgXY(aabbMin), Misc::Convert::toOsgXY(aabbMax), settings); return makeTilesPositionsRange(Misc::Convert::toOsgXY(aabbMin), Misc::Convert::toOsgXY(aabbMax), settings);
} }
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept
{
const int beginX = std::max(a.mBegin.x(), b.mBegin.x());
const int endX = std::min(a.mEnd.x(), b.mEnd.x());
if (beginX > endX)
return {};
const int beginY = std::max(a.mBegin.y(), b.mBegin.y());
const int endY = std::min(a.mEnd.y(), b.mEnd.y());
if (beginY > endY)
return {};
return TilesPositionsRange {TilePosition(beginX, beginY), TilePosition(endX, endY)};
}
} }

View file

@ -1,6 +1,7 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H
#include "tilebounds.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
class btVector3; class btVector3;
@ -28,6 +29,9 @@ namespace DetourNavigator
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape,
const btTransform& transform, const RecastSettings& settings); const btTransform& transform, const RecastSettings& settings);
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape,
const btTransform& transform, const TileBounds& bounds, const RecastSettings& settings);
TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift, TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift,
const RecastSettings& settings); const RecastSettings& settings);
@ -38,6 +42,19 @@ namespace DetourNavigator
for (int tileY = range.mBegin.y(); tileY < range.mEnd.y(); ++tileY) for (int tileY = range.mBegin.y(); tileY < range.mEnd.y(); ++tileY)
callback(TilePosition {tileX, tileY}); callback(TilePosition {tileX, tileY});
} }
inline bool isInTilesPositionsRange(int begin, int end, int coordinate)
{
return begin <= coordinate && coordinate < end;
}
inline bool isInTilesPositionsRange(const TilesPositionsRange& range, const TilePosition& position)
{
return isInTilesPositionsRange(range.mBegin.x(), range.mEnd.x(), position.x())
&& isInTilesPositionsRange(range.mBegin.y(), range.mEnd.y(), position.y());
}
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
} }
#endif #endif

View file

@ -83,6 +83,12 @@ namespace DetourNavigator
*/ */
virtual void setWorldspace(std::string_view worldspace) = 0; virtual void setWorldspace(std::string_view worldspace) = 0;
/**
* @brief updateBounds should be called before adding object from loading cell
* @param playerPosition corresponds to the bounds center
*/
virtual void updateBounds(const osg::Vec3f& playerPosition) = 0;
/** /**
* @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes * @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes
* @param id is used to distinguish different objects * @param id is used to distinguish different objects

View file

@ -38,6 +38,11 @@ namespace DetourNavigator
mNavMeshManager.setWorldspace(worldspace); mNavMeshManager.setWorldspace(worldspace);
} }
void NavigatorImpl::updateBounds(const osg::Vec3f& playerPosition)
{
mNavMeshManager.updateBounds(playerPosition);
}
bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
{ {
const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform); const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);
@ -158,6 +163,7 @@ namespace DetourNavigator
const TilePosition tilePosition = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition)); const TilePosition tilePosition = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition));
if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition) if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition)
return; return;
mNavMeshManager.updateBounds(playerPosition);
update(playerPosition); update(playerPosition);
mLastPlayerPosition = tilePosition; mLastPlayerPosition = tilePosition;
} }

View file

@ -24,6 +24,8 @@ namespace DetourNavigator
void setWorldspace(std::string_view worldspace) override; void setWorldspace(std::string_view worldspace) override;
void updateBounds(const osg::Vec3f& playerPosition) override;
bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override; bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override; bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;

View file

@ -72,6 +72,8 @@ namespace DetourNavigator
void update(const osg::Vec3f& /*playerPosition*/) override {} void update(const osg::Vec3f& /*playerPosition*/) override {}
void updateBounds(const osg::Vec3f& /*playerPosition*/) override {}
void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {}; void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {};
void setUpdatesEnabled(bool /*enabled*/) override {} void setUpdatesEnabled(bool /*enabled*/) override {}

View file

@ -42,6 +42,18 @@ namespace
namespace DetourNavigator namespace DetourNavigator
{ {
namespace
{
TileBounds makeBounds(const RecastSettings& settings, const osg::Vec2f& center, int maxTiles)
{
const float radius = fromNavMeshCoordinates(settings, std::ceil(std::sqrt(static_cast<float>(maxTiles) / osg::PIf) + 1) * getTileSize(settings));
TileBounds result;
result.mMin = center - osg::Vec2f(radius, radius);
result.mMax = center + osg::Vec2f(radius, radius);
return result;
}
}
NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db) NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
: mSettings(settings) : mSettings(settings)
, mRecastMeshManager(settings.mRecast) , mRecastMeshManager(settings.mRecast)
@ -59,21 +71,37 @@ namespace DetourNavigator
mWorldspace = worldspace; mWorldspace = worldspace;
} }
void NavMeshManager::updateBounds(const osg::Vec3f& playerPosition)
{
const TileBounds bounds = makeBounds(mSettings.mRecast, osg::Vec2f(playerPosition.x(), playerPosition.y()),
mSettings.mMaxTilesNumber);
const auto changedTiles = mRecastMeshManager.setBounds(bounds);
for (const auto& [agent, cache] : mCache)
{
auto& tiles = mChangedTiles[agent];
for (const auto& [tilePosition, changeType] : changedTiles)
{
auto tile = tiles.find(tilePosition);
if (tile == tiles.end())
tiles.emplace_hint(tile, tilePosition, changeType);
else
tile->second = addChangeType(tile->second, changeType);
}
}
}
bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
{ {
const btCollisionShape& collisionShape = shape.getShape(); return mRecastMeshManager.addObject(id, shape, transform, areaType,
if (!mRecastMeshManager.addObject(id, shape, transform, areaType)) [&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::add); });
return false;
addChangedTiles(collisionShape, transform, ChangeType::add);
return true;
} }
bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
{ {
return mRecastMeshManager.updateObject(id, shape, transform, areaType, return mRecastMeshManager.updateObject(id, shape, transform, areaType,
[&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::update); }); [&] (const TilePosition& tile, ChangeType changeType) { addChangedTile(tile, changeType); });
} }
bool NavMeshManager::removeObject(const ObjectId id) bool NavMeshManager::removeObject(const ObjectId id)
@ -263,7 +291,8 @@ namespace DetourNavigator
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
const ChangeType changeType) const ChangeType changeType)
{ {
getTilesPositions(makeTilesPositionsRange(shape, transform, mSettings.mRecast), const auto bounds = mRecastMeshManager.getBounds();
getTilesPositions(makeTilesPositionsRange(shape, transform, bounds, mSettings.mRecast),
[&] (const TilePosition& v) { addChangedTile(v, changeType); }); [&] (const TilePosition& v) { addChangedTile(v, changeType); });
} }

View file

@ -26,6 +26,8 @@ namespace DetourNavigator
void setWorldspace(std::string_view worldspace); void setWorldspace(std::string_view worldspace);
void updateBounds(const osg::Vec3f& playerPosition);
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);

View file

@ -37,6 +37,11 @@ namespace DetourNavigator
}; };
} }
inline float fromNavMeshCoordinates(const RecastSettings& settings, float value)
{
return value / settings.mRecastScaleFactor;
}
inline osg::Vec3f fromNavMeshCoordinates(const RecastSettings& settings, osg::Vec3f position) inline osg::Vec3f fromNavMeshCoordinates(const RecastSettings& settings, osg::Vec3f position)
{ {
const auto factor = 1.0f / settings.mRecastScaleFactor; const auto factor = 1.0f / settings.mRecastScaleFactor;

View file

@ -21,9 +21,24 @@ namespace DetourNavigator
osg::Vec2f mMax; osg::Vec2f mMax;
}; };
inline auto tie(const TileBounds& value) noexcept
{
return std::tie(value.mMin, value.mMax);
}
inline bool operator<(const TileBounds& lhs, const TileBounds& rhs) noexcept inline bool operator<(const TileBounds& lhs, const TileBounds& rhs) noexcept
{ {
return std::tie(lhs.mMin, lhs.mMax) < std::tie(rhs.mMin, rhs.mMax); return tie(lhs) < tie(rhs);
}
inline bool operator ==(const TileBounds& lhs, const TileBounds& rhs) noexcept
{
return tie(lhs) == tie(rhs);
}
inline bool operator !=(const TileBounds& lhs, const TileBounds& rhs) noexcept
{
return !(lhs == rhs);
} }
inline std::optional<TileBounds> getIntersection(const TileBounds& a, const TileBounds& b) noexcept inline std::optional<TileBounds> getIntersection(const TileBounds& a, const TileBounds& b) noexcept

View file

@ -2,19 +2,93 @@
#include "makenavmesh.hpp" #include "makenavmesh.hpp"
#include "gettilespositions.hpp" #include "gettilespositions.hpp"
#include "settingsutils.hpp" #include "settingsutils.hpp"
#include "changetype.hpp"
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/convert.hpp> #include <components/misc/convert.hpp>
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include <limits>
namespace DetourNavigator namespace DetourNavigator
{ {
namespace
{
const TileBounds infiniteTileBounds {
osg::Vec2f(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max()),
osg::Vec2f(std::numeric_limits<float>::max(), std::numeric_limits<float>::max())
};
}
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings) TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
: mSettings(settings) : mSettings(settings)
, mBounds(infiniteTileBounds)
, mRange(makeTilesPositionsRange(mBounds.mMin, mBounds.mMax, mSettings))
{} {}
TileBounds TileCachedRecastMeshManager::getBounds() const
{
return mBounds;
}
std::vector<std::pair<TilePosition, ChangeType>> TileCachedRecastMeshManager::setBounds(const TileBounds& bounds)
{
std::vector<std::pair<TilePosition, ChangeType>> changedTiles;
if (mBounds == bounds)
return changedTiles;
const auto newRange = makeTilesPositionsRange(bounds.mMin, bounds.mMax, mSettings);
if (mBounds != infiniteTileBounds)
{
const std::lock_guard lock(mMutex);
for (auto& object : mObjects)
{
const ObjectId id = object.first;
ObjectData& data = object.second;
const TilesPositionsRange objectRange = makeTilesPositionsRange(data.mShape.getShape(), data.mTransform, mSettings);
const auto onOldTilePosition = [&] (const TilePosition& position)
{
if (isInTilesPositionsRange(newRange, position))
return;
const auto it = data.mTiles.find(position);
if (it == data.mTiles.end())
return;
data.mTiles.erase(it);
if (removeTile(id, position, mTiles))
changedTiles.emplace_back(position, ChangeType::remove);
};
getTilesPositions(getIntersection(mRange, objectRange), onOldTilePosition);
const auto onNewTilePosition = [&] (const TilePosition& position)
{
if (data.mTiles.find(position) != data.mTiles.end())
return;
if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, mTiles))
{
data.mTiles.insert(position);
changedTiles.emplace_back(position, ChangeType::add);
}
};
getTilesPositions(getIntersection(newRange, objectRange), onNewTilePosition);
}
std::sort(changedTiles.begin(), changedTiles.end());
changedTiles.erase(std::unique(changedTiles.begin(), changedTiles.end()), changedTiles.end());
}
if (!changedTiles.empty())
++mRevision;
mBounds = bounds;
mRange = newRange;
return changedTiles;
}
std::string TileCachedRecastMeshManager::getWorldspace() const std::string TileCachedRecastMeshManager::getWorldspace() const
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
@ -30,47 +104,22 @@ namespace DetourNavigator
mWorldspace = worldspace; mWorldspace = worldspace;
} }
bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,
const btTransform& transform, const AreaType areaType)
{
const auto it = mObjectsTilesPositions.find(id);
if (it != mObjectsTilesPositions.end())
return false;
std::vector<TilePosition> tilesPositions;
{
const TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings);
const std::lock_guard lock(mMutex);
getTilesPositions(range,
[&] (const TilePosition& tilePosition)
{
if (addTile(id, shape, transform, areaType, tilePosition, mTiles))
tilesPositions.push_back(tilePosition);
});
}
if (tilesPositions.empty())
return false;
std::sort(tilesPositions.begin(), tilesPositions.end());
mObjectsTilesPositions.emplace_hint(it, id, std::move(tilesPositions));
++mRevision;
return true;
}
std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeObject(const ObjectId id) std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeObject(const ObjectId id)
{ {
const auto object = mObjectsTilesPositions.find(id); const auto object = mObjects.find(id);
if (object == mObjectsTilesPositions.end()) if (object == mObjects.end())
return std::nullopt; return std::nullopt;
std::optional<RemovedRecastMeshObject> result; std::optional<RemovedRecastMeshObject> result;
{ {
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
for (const auto& tilePosition : object->second) for (const auto& tilePosition : object->second.mTiles)
{ {
const auto removed = removeTile(id, tilePosition, mTiles); const auto removed = removeTile(id, tilePosition, mTiles);
if (removed && !result) if (removed && !result)
result = removed; result = removed;
} }
} }
mObjectsTilesPositions.erase(object); mObjects.erase(object);
if (result) if (result)
++mRevision; ++mRevision;
return result; return result;

View file

@ -7,11 +7,13 @@
#include "gettilespositions.hpp" #include "gettilespositions.hpp"
#include "version.hpp" #include "version.hpp"
#include "heightfieldshape.hpp" #include "heightfieldshape.hpp"
#include "changetype.hpp"
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include <set>
namespace DetourNavigator namespace DetourNavigator
{ {
@ -20,58 +22,85 @@ namespace DetourNavigator
public: public:
explicit TileCachedRecastMeshManager(const RecastSettings& settings); explicit TileCachedRecastMeshManager(const RecastSettings& settings);
TileBounds getBounds() const;
std::vector<std::pair<TilePosition, ChangeType>> setBounds(const TileBounds& bounds);
std::string getWorldspace() const; std::string getWorldspace() const;
void setWorldspace(std::string_view worldspace); void setWorldspace(std::string_view worldspace);
template <class OnChangedTile>
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType, OnChangedTile&& onChangedTile)
{
auto it = mObjects.find(id);
if (it != mObjects.end())
return false;
const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings);
const TilesPositionsRange range = getIntersection(mRange, objectRange);
std::set<TilePosition> tilesPositions;
if (range.mBegin != range.mEnd)
{
const std::lock_guard lock(mMutex);
getTilesPositions(range,
[&] (const TilePosition& tilePosition)
{
if (addTile(id, shape, transform, areaType, tilePosition, mTiles))
tilesPositions.insert(tilePosition);
});
}
it = mObjects.emplace_hint(it, id, ObjectData {shape, transform, areaType, std::move(tilesPositions)});
std::for_each(it->second.mTiles.begin(), it->second.mTiles.end(), std::forward<OnChangedTile>(onChangedTile));
++mRevision;
return true;
}
template <class OnChangedTile> template <class OnChangedTile>
bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType, OnChangedTile&& onChangedTile) const AreaType areaType, OnChangedTile&& onChangedTile)
{ {
const auto object = mObjectsTilesPositions.find(id); const auto object = mObjects.find(id);
if (object == mObjectsTilesPositions.end()) if (object == mObjects.end())
return false; return false;
auto& currentTiles = object->second; auto& data = object->second;
bool changed = false; bool changed = false;
std::vector<TilePosition> newTiles; std::set<TilePosition> newTiles;
{ {
const auto onTilePosition = [&] (const TilePosition& tilePosition) const auto onTilePosition = [&] (const TilePosition& tilePosition)
{ {
if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition)) if (data.mTiles.find(tilePosition) != data.mTiles.end())
{ {
newTiles.push_back(tilePosition); newTiles.insert(tilePosition);
if (updateTile(id, transform, areaType, tilePosition, mTiles)) if (updateTile(id, transform, areaType, tilePosition, mTiles))
{ {
onChangedTile(tilePosition); onChangedTile(tilePosition, ChangeType::update);
changed = true; changed = true;
} }
} }
else if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) else if (addTile(id, shape, transform, areaType, tilePosition, mTiles))
{ {
newTiles.push_back(tilePosition); newTiles.insert(tilePosition);
onChangedTile(tilePosition); onChangedTile(tilePosition, ChangeType::add);
changed = true; changed = true;
} }
}; };
const TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings); const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings);
const TilesPositionsRange range = getIntersection(mRange, objectRange);
const std::lock_guard lock(mMutex); const std::lock_guard lock(mMutex);
getTilesPositions(range, onTilePosition); getTilesPositions(range, onTilePosition);
std::sort(newTiles.begin(), newTiles.end()); for (const auto& tile : data.mTiles)
for (const auto& tile : currentTiles)
{ {
if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, mTiles)) if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, mTiles))
{ {
onChangedTile(tile); onChangedTile(tile, ChangeType::remove);
changed = true; changed = true;
} }
} }
} }
if (changed) if (changed)
{ {
currentTiles = std::move(newTiles); data.mTiles = std::move(newTiles);
++mRevision; ++mRevision;
} }
return changed; return changed;
@ -108,11 +137,21 @@ namespace DetourNavigator
private: private:
using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>; using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>;
struct ObjectData
{
const CollisionShape mShape;
const btTransform mTransform;
const AreaType mAreaType;
std::set<TilePosition> mTiles;
};
const RecastSettings& mSettings; const RecastSettings& mSettings;
mutable std::mutex mMutex; mutable std::mutex mMutex;
TileBounds mBounds;
TilesPositionsRange mRange;
std::string mWorldspace; std::string mWorldspace;
TilesMap mTiles; TilesMap mTiles;
std::unordered_map<ObjectId, std::vector<TilePosition>> mObjectsTilesPositions; std::unordered_map<ObjectId, ObjectData> mObjects;
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions; std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
std::map<osg::Vec2i, std::vector<TilePosition>> mHeightfieldTilesPositions; std::map<osg::Vec2i, std::vector<TilePosition>> mHeightfieldTilesPositions;
std::size_t mRevision = 0; std::size_t mRevision = 0;