diff --git a/apps/navmeshtool/worldspacedata.cpp b/apps/navmeshtool/worldspacedata.cpp index b3efa69430..e43cfec96c 100644 --- a/apps/navmeshtool/worldspacedata.cpp +++ b/apps/navmeshtool/worldspacedata.cpp @@ -276,11 +276,13 @@ namespace NavMeshTool { it = navMeshInputs.emplace(cell.mCellId.mWorldspace, std::make_unique(cell.mCellId.mWorldspace, settings.mRecast)).first; - it->second->mTileCachedRecastMeshManager.setWorldspace(cell.mCellId.mWorldspace); + it->second->mTileCachedRecastMeshManager.setWorldspace(cell.mCellId.mWorldspace, nullptr); } return *it->second; } (); + const TileCachedRecastMeshManager::UpdateGuard guard(navMeshInput.mTileCachedRecastMeshManager); + if (exterior) { const auto it = std::lower_bound(esmData.mLands.begin(), esmData.mLands.end(), cellPosition, LessByXY {}); @@ -292,14 +294,14 @@ namespace NavMeshTool mergeOrAssign(getAabb(cellPosition, minHeight, maxHeight), navMeshInput.mAabb, navMeshInput.mAabbInitialized); - navMeshInput.mTileCachedRecastMeshManager.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, heightfieldShape); + navMeshInput.mTileCachedRecastMeshManager.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, heightfieldShape, &guard); - navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, ESM::Land::REAL_SIZE, -1); + navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, ESM::Land::REAL_SIZE, -1, &guard); } else { if ((cell.mData.mFlags & ESM::Cell::HasWater) != 0) - navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, std::numeric_limits::max(), cell.mWater); + navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, std::numeric_limits::max(), cell.mWater, &guard); } forEachObject(cell, esmData, vfs, bulletShapeManager, readers, @@ -318,13 +320,13 @@ namespace NavMeshTool const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform()); navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, - DetourNavigator::AreaType_ground); + DetourNavigator::AreaType_ground, &guard); 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); + DetourNavigator::AreaType_null, &guard); } data.mObjects.emplace_back(std::move(object)); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 29c8b6f76b..09c757a874 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -135,7 +136,7 @@ namespace } void addObject(const MWWorld::Ptr& ptr, const MWWorld::World& world, const MWPhysics::PhysicsSystem& physics, - DetourNavigator::Navigator& navigator) + DetourNavigator::Navigator& navigator, const DetourNavigator::UpdateGuard* navigatorUpdateGuard = nullptr) { if (const auto object = physics.getObject(ptr)) { @@ -173,7 +174,8 @@ namespace navigator.addObject( DetourNavigator::ObjectId(object), DetourNavigator::DoorShapes(object->getShapeInstance(), objectTransform, connectionStart, connectionEnd), - transform + transform, + navigatorUpdateGuard ); } else if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None) @@ -181,7 +183,8 @@ namespace navigator.addObject( DetourNavigator::ObjectId(object), DetourNavigator::ObjectShapes(object->getShapeInstance(), objectTransform), - object->getTransform() + object->getTransform(), + navigatorUpdateGuard ); } } @@ -320,7 +323,7 @@ namespace MWWorld preloadCells(duration); } - void Scene::unloadCell(CellStore* cell) + void Scene::unloadCell(CellStore* cell, const DetourNavigator::UpdateGuard* navigatorUpdateGuard) { if (mActiveCells.find(cell) == mActiveCells.end()) return; @@ -335,7 +338,7 @@ namespace MWWorld if (const auto object = mPhysics->getObject(ptr)) { if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None) - mNavigator.removeObject(DetourNavigator::ObjectId(object)); + mNavigator.removeObject(DetourNavigator::ObjectId(object), navigatorUpdateGuard); mPhysics->remove(ptr); ptr.mRef->mData.mPhysicsPostponed = false; } @@ -354,13 +357,13 @@ namespace MWWorld if (cell->getCell()->isExterior()) { if (mPhysics->getHeightField(cellX, cellY) != nullptr) - mNavigator.removeHeightfield(osg::Vec2i(cellX, cellY)); + mNavigator.removeHeightfield(osg::Vec2i(cellX, cellY), navigatorUpdateGuard); mPhysics->removeHeightField(cellX, cellY); } if (cell->getCell()->hasWater()) - mNavigator.removeWater(osg::Vec2i(cellX, cellY)); + mNavigator.removeWater(osg::Vec2i(cellX, cellY), navigatorUpdateGuard); if (const auto pathgrid = mWorld.getStore().get().search(*cell->getCell())) mNavigator.removePathgrid(*pathgrid); @@ -379,7 +382,8 @@ namespace MWWorld mRendering.notifyWorldSpaceChanged(); } - void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position) + void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position, + const DetourNavigator::UpdateGuard* navigatorUpdateGuard) { using DetourNavigator::HeightfieldShape; @@ -428,7 +432,7 @@ namespace MWWorld return heights; } } (); - mNavigator.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, shape); + mNavigator.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, shape, navigatorUpdateGuard); } } @@ -442,7 +446,7 @@ namespace MWWorld if (respawn) cell->respawn(); - insertCell(*cell, loadingListener); + insertCell(*cell, loadingListener, navigatorUpdateGuard); mRendering.addCell(cell); @@ -458,18 +462,18 @@ namespace MWWorld if (cell->getCell()->isExterior()) { if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) - mNavigator.addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, waterLevel); + mNavigator.addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, waterLevel, + navigatorUpdateGuard); } else { - mNavigator.addWater(osg::Vec2i(cellX, cellY), std::numeric_limits::max(), waterLevel); + mNavigator.addWater(osg::Vec2i(cellX, cellY), std::numeric_limits::max(), waterLevel, + navigatorUpdateGuard); } } else mPhysics->disableWater(); - mNavigator.update(position); - if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) mRendering.configureAmbient(cell->getCell()); @@ -478,11 +482,13 @@ namespace MWWorld void Scene::clear() { + auto navigatorUpdateGuard = mNavigator.makeUpdateGuard(); for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); ) { auto* cell = *iter++; - unloadCell (cell); + unloadCell(cell, navigatorUpdateGuard.get()); } + navigatorUpdateGuard.reset(); assert(mActiveCells.empty()); mCurrentCell = nullptr; @@ -525,6 +531,8 @@ namespace MWWorld void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent) { + auto navigatorUpdateGuard = mNavigator.makeUpdateGuard(); + for (auto iter = mActiveCells.begin(); iter != mActiveCells.end(); ) { auto* cell = *iter++; @@ -533,14 +541,15 @@ namespace MWWorld const auto dx = std::abs(playerCellX - cell->getCell()->getGridX()); const auto dy = std::abs(playerCellY - cell->getCell()->getGridY()); if (dx > mHalfGridSize || dy > mHalfGridSize) - unloadCell(cell); + unloadCell(cell, navigatorUpdateGuard.get()); } else - unloadCell (cell); + unloadCell(cell, navigatorUpdateGuard.get()); } - mNavigator.setWorldspace(mWorld.getExterior(playerCellX, playerCellY)->getCell()->mCellId.mWorldspace); - mNavigator.updateBounds(pos); + mNavigator.setWorldspace(mWorld.getExterior(playerCellX, playerCellY)->getCell()->mCellId.mWorldspace, + navigatorUpdateGuard.get()); + mNavigator.updateBounds(pos, navigatorUpdateGuard.get()); mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); @@ -601,10 +610,14 @@ namespace MWWorld if (!isCellInCollection(x, y, mActiveCells)) { CellStore *cell = mWorld.getExterior(x, y); - loadCell(cell, loadingListener, changeEvent, pos); + loadCell(cell, loadingListener, changeEvent, pos, navigatorUpdateGuard.get()); } } + mNavigator.update(pos, navigatorUpdateGuard.get()); + + navigatorUpdateGuard.reset(); + CellStore* current = mWorld.getExterior(playerCellX, playerCellY); MWBase::Environment::get().getWindowManager()->changeCell(current); @@ -653,15 +666,21 @@ namespace MWWorld MWWorld::Store::iterator it = cells.extBegin(); int i = 1; + auto navigatorUpdateGuard = mNavigator.makeUpdateGuard(); for (; it != cells.extEnd(); ++it) { loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")..."); CellStore *cell = mWorld.getExterior(it->mData.mX, it->mData.mY); - mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace); + mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace, navigatorUpdateGuard.get()); const osg::Vec3f position = osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits; - mNavigator.updateBounds(position); - loadCell(cell, nullptr, false, position); + mNavigator.updateBounds(position, navigatorUpdateGuard.get()); + loadCell(cell, nullptr, false, position, navigatorUpdateGuard.get()); + + mNavigator.update(position, navigatorUpdateGuard.get()); + navigatorUpdateGuard.reset(); + mNavigator.wait(DetourNavigator::WaitConditionType::requiredTilesPresent, nullptr); + navigatorUpdateGuard = mNavigator.makeUpdateGuard(); auto iter = mActiveCells.begin(); while (iter != mActiveCells.end()) @@ -669,7 +688,7 @@ namespace MWWorld if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() && it->mData.mY == (*iter)->getCell()->getGridY()) { - unloadCell(*iter); + unloadCell(*iter, navigatorUpdateGuard.get()); break; } @@ -701,16 +720,22 @@ namespace MWWorld int i = 1; MWWorld::Store::iterator it = cells.intBegin(); + auto navigatorUpdateGuard = mNavigator.makeUpdateGuard(); for (; it != cells.intEnd(); ++it) { loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")..."); CellStore *cell = mWorld.getInterior(it->mName); - mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace); + mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace, navigatorUpdateGuard.get()); ESM::Position position; mWorld.findInteriorPosition(it->mName, position); - mNavigator.updateBounds(position.asVec3()); - loadCell(cell, nullptr, false, position.asVec3()); + mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get()); + loadCell(cell, nullptr, false, position.asVec3(), navigatorUpdateGuard.get()); + + mNavigator.update(position.asVec3(), navigatorUpdateGuard.get()); + navigatorUpdateGuard.reset(); + mNavigator.wait(DetourNavigator::WaitConditionType::requiredTilesPresent, nullptr); + navigatorUpdateGuard = mNavigator.makeUpdateGuard(); auto iter = mActiveCells.begin(); while (iter != mActiveCells.end()) @@ -719,7 +744,7 @@ namespace MWWorld if (it->mName == (*iter)->getCell()->mName) { - unloadCell(*iter); + unloadCell(*iter, navigatorUpdateGuard.get()); break; } @@ -838,22 +863,26 @@ namespace MWWorld Log(Debug::Info) << "Changing to interior"; + auto navigatorUpdateGuard = mNavigator.makeUpdateGuard(); + // unload for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); ) { auto* cellToUnload = *iter++; - unloadCell(cellToUnload); + unloadCell(cellToUnload, navigatorUpdateGuard.get()); } assert(mActiveCells.empty()); loadingListener->setProgressRange(cell->count()); - mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace); - mNavigator.updateBounds(position.asVec3()); + mNavigator.setWorldspace(cell->getCell()->mCellId.mWorldspace, navigatorUpdateGuard.get()); + mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get()); // Load cell. mPagedRefs.clear(); - loadCell(cell, loadingListener, changeEvent, position.asVec3()); + loadCell(cell, loadingListener, changeEvent, position.asVec3(), navigatorUpdateGuard.get()); + + navigatorUpdateGuard.reset(); changePlayerCell(cell, position, adjustPlayerPos); @@ -904,12 +933,13 @@ namespace MWWorld mCellChanged = false; } - void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener) + void Scene::insertCell(CellStore &cell, Loading::Listener* loadingListener, + const DetourNavigator::UpdateGuard* navigatorUpdateGuard) { InsertVisitor insertVisitor(cell, loadingListener); cell.forEach (insertVisitor); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, mPagedRefs, *mPhysics, mRendering); }); - insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, *mPhysics, mNavigator); }); + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, mWorld, *mPhysics, mNavigator, navigatorUpdateGuard); }); } void Scene::addObjectToScene (const Ptr& ptr) @@ -938,7 +968,7 @@ namespace MWWorld if (const auto object = mPhysics->getObject(ptr)) { if (object->getShapeInstance()->mVisualCollisionType == Resource::VisualCollisionType::None) - mNavigator.removeObject(DetourNavigator::ObjectId(object)); + mNavigator.removeObject(DetourNavigator::ObjectId(object), nullptr); } else if (mPhysics->getActor(ptr)) { diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 7e69e8e97d..f0246f0882 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -38,6 +38,7 @@ namespace Loading namespace DetourNavigator { struct Navigator; + class UpdateGuard; } namespace MWRender @@ -110,7 +111,9 @@ namespace MWWorld std::optional mChangeCellGridRequest; - void insertCell(CellStore &cell, Loading::Listener* loadingListener); + void insertCell(CellStore &cell, Loading::Listener* loadingListener, + const DetourNavigator::UpdateGuard* navigatorUpdateGuard); + osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center @@ -128,8 +131,9 @@ namespace MWWorld osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; - void unloadCell(CellStore* cell); - void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position); + void unloadCell(CellStore* cell, const DetourNavigator::UpdateGuard* navigatorUpdateGuard); + void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position, + const DetourNavigator::UpdateGuard* navigatorUpdateGuard); public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fc90a14b25..375351e606 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -1541,28 +1542,32 @@ namespace MWWorld void World::updateNavigator() { + auto navigatorUpdateGuard = mNavigator->makeUpdateGuard(); + mPhysics->forEachAnimatedObject([&] (const auto& pair) { const auto [object, changed] = pair; if (changed) - updateNavigatorObject(*object); + updateNavigatorObject(*object, navigatorUpdateGuard.get()); }); for (const auto& door : mDoorStates) if (const auto object = mPhysics->getObject(door.first)) - updateNavigatorObject(*object); + updateNavigatorObject(*object, navigatorUpdateGuard.get()); - mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3()); + mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3(), navigatorUpdateGuard.get()); } - void World::updateNavigatorObject(const MWPhysics::Object& object) + void World::updateNavigatorObject(const MWPhysics::Object& object, + const DetourNavigator::UpdateGuard* navigatorUpdateGuard) { if (object.getShapeInstance()->mVisualCollisionType != Resource::VisualCollisionType::None) return; const MWWorld::Ptr ptr = object.getPtr(); const DetourNavigator::ObjectShapes shapes(object.getShapeInstance(), DetourNavigator::ObjectTransform {ptr.getRefData().getPosition(), ptr.getCellRef().getScale()}); - mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform()); + mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform(), + navigatorUpdateGuard); } const MWPhysics::RayCastingInterface* World::getRayCasting() const @@ -1854,8 +1859,8 @@ namespace MWWorld if (mWorldScene->hasCellLoaded()) { - mNavigator->wait(*MWBase::Environment::get().getWindowManager()->getLoadingScreen(), - DetourNavigator::WaitConditionType::requiredTilesPresent); + mNavigator->wait(DetourNavigator::WaitConditionType::requiredTilesPresent, + MWBase::Environment::get().getWindowManager()->getLoadingScreen()); mWorldScene->resetCellLoaded(); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1110478074..e6085b7f79 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -166,7 +166,8 @@ namespace MWWorld void updateNavigator(); - void updateNavigatorObject(const MWPhysics::Object& object); + void updateNavigatorObject(const MWPhysics::Object& object, + const DetourNavigator::UpdateGuard* navigatorUpdateGuard = nullptr); void ensureNeededRecords(); diff --git a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp index b43cdf329f..1c7db0d31d 100644 --- a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp +++ b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp @@ -25,7 +25,7 @@ namespace void addHeightFieldPlane(TileCachedRecastMeshManager& recastMeshManager, const osg::Vec2i cellPosition = osg::Vec2i(0, 0)) { const int cellSize = 8192; - recastMeshManager.addHeightfield(cellPosition, cellSize, HeightfieldPlane {0}); + recastMeshManager.addHeightfield(cellPosition, cellSize, HeightfieldPlane {0}, nullptr); } void addObject(const btBoxShape& shape, TileCachedRecastMeshManager& recastMeshManager) @@ -42,7 +42,7 @@ namespace osg::ref_ptr(new Resource::BulletShapeInstance(bulletShape)), shape, objectTransform ); - recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground); + recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground, nullptr); } struct DetourNavigatorAsyncNavMeshUpdaterTest : Test @@ -60,43 +60,43 @@ namespace TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, for_all_jobs_done_when_empty_wait_should_terminate) { AsyncNavMeshUpdater updater {mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr}; - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); } TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, for_required_tiles_present_when_empty_wait_should_terminate) { AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); - updater.wait(mListener, WaitConditionType::requiredTilesPresent); + updater.wait(WaitConditionType::requiredTilesPresent, &mListener); } TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_generate_navmesh_tile) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); const auto navMeshCacheItem = std::make_shared(makeEmptyNavMesh(mSettings), 1); const std::map changedTiles {{TilePosition {0, 0}, ChangeType::add}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u); } TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, repeated_post_should_lead_to_cache_hit) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); const auto navMeshCacheItem = std::make_shared(makeEmptyNavMesh(mSettings), 1); const std::map changedTiles {{TilePosition {0, 0}, ChangeType::add}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); { const auto stats = updater.getStats(); ASSERT_EQ(stats.mCache.mGetCount, 1); ASSERT_EQ(stats.mCache.mHitCount, 0); } updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); { const auto stats = updater.getStats(); EXPECT_EQ(stats.mCache.mGetCount, 2); @@ -106,20 +106,20 @@ namespace TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_for_update_change_type_should_not_update_cache) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); const auto navMeshCacheItem = std::make_shared(makeEmptyNavMesh(mSettings), 1); const std::map changedTiles {{TilePosition {0, 0}, ChangeType::update}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); { const auto stats = updater.getStats(); ASSERT_EQ(stats.mCache.mGetCount, 1); ASSERT_EQ(stats.mCache.mHitCount, 0); } updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); { const auto stats = updater.getStats(); EXPECT_EQ(stats.mCache.mGetCount, 2); @@ -129,7 +129,7 @@ namespace TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_write_generated_tile_to_db) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); addObject(mBox, mRecastMeshManager); auto db = std::make_unique(":memory:", std::numeric_limits::max()); @@ -139,7 +139,7 @@ namespace const TilePosition tilePosition {0, 0}; const std::map changedTiles {{tilePosition, ChangeType::add}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); updater.stop(); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); ASSERT_NE(recastMesh, nullptr); @@ -155,7 +155,7 @@ namespace TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_tiles) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); addObject(mBox, mRecastMeshManager); auto db = std::make_unique(":memory:", std::numeric_limits::max()); @@ -166,7 +166,7 @@ namespace const TilePosition tilePosition {0, 0}; const std::map changedTiles {{tilePosition, ChangeType::add}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); updater.stop(); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); ASSERT_NE(recastMesh, nullptr); @@ -180,7 +180,7 @@ namespace TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_shapes) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); addObject(mBox, mRecastMeshManager); auto db = std::make_unique(":memory:", std::numeric_limits::max()); @@ -191,7 +191,7 @@ namespace const TilePosition tilePosition {0, 0}; const std::map changedTiles {{tilePosition, ChangeType::add}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); updater.stop(); const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); ASSERT_NE(recastMesh, nullptr); @@ -202,7 +202,7 @@ namespace TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_read_from_db_on_cache_miss) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); mSettings.mMaxNavMeshTilesCacheSize = 0; AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, @@ -210,7 +210,7 @@ namespace const auto navMeshCacheItem = std::make_shared(makeEmptyNavMesh(mSettings), 1); const std::map changedTiles {{TilePosition {0, 0}, ChangeType::add}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); { const auto stats = updater.getStats(); ASSERT_EQ(stats.mCache.mGetCount, 1); @@ -220,7 +220,7 @@ namespace ASSERT_EQ(stats.mDbGetTileHits, 0); } updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); { const auto stats = updater.getStats(); EXPECT_EQ(stats.mCache.mGetCount, 2); @@ -233,24 +233,24 @@ namespace TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, on_changing_player_tile_post_should_remove_tiles_out_of_range) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); addHeightFieldPlane(mRecastMeshManager); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr); const auto navMeshCacheItem = std::make_shared(makeEmptyNavMesh(mSettings), 1); const std::map changedTilesAdd {{TilePosition {0, 0}, ChangeType::add}}; updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTilesAdd); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); ASSERT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u); const std::map changedTilesRemove {{TilePosition {0, 0}, ChangeType::remove}}; const TilePosition playerTile(100, 100); updater.post(mAgentBounds, navMeshCacheItem, playerTile, mWorldspace, changedTilesRemove); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u); } TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_stop_writing_to_db_when_size_limit_is_reached) { - mRecastMeshManager.setWorldspace(mWorldspace); + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); for (int x = -1; x <= 1; ++x) for (int y = -1; y <= 1; ++y) addHeightFieldPlane(mRecastMeshManager, osg::Vec2i(x, y)); @@ -264,7 +264,7 @@ namespace for (int y = -5; y <= 5; ++y) changedTiles.emplace(TilePosition {x, y}, ChangeType::add); updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); - updater.wait(mListener, WaitConditionType::allJobsDone); + updater.wait(WaitConditionType::allJobsDone, &mListener); updater.stop(); const std::set present { TilePosition(-2, 0), diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 765f27e88a..23df6c1e93 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -155,9 +155,11 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); + auto updateGuard = mNavigator->makeUpdateGuard(); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, updateGuard.get()); + mNavigator->update(mPlayerPosition, updateGuard.get()); + updateGuard.reset(); + mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -207,9 +209,9 @@ namespace compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -239,9 +241,12 @@ namespace Vec3fEq(460, 56.66666412353515625, 1.99998295307159423828125) )) << mPath; - mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + { + auto updateGuard = mNavigator->makeUpdateGuard(); + mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, updateGuard.get()); + mNavigator->update(mPlayerPosition, updateGuard.get()); + } + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mPath.clear(); mOut = std::back_inserter(mPath); @@ -291,10 +296,10 @@ namespace compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -327,9 +332,9 @@ namespace compound.shape().updateChildTransform(0, btTransform(btMatrix3x3::getIdentity(), btVector3(1000, 0, 0))); - mNavigator->updateObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->updateObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mPath.clear(); mOut = std::back_inserter(mPath); @@ -385,10 +390,10 @@ namespace heightfield2.shape().setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentBounds); - mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform), mTransform); - mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -442,15 +447,15 @@ namespace const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize1, surface1); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(); - mNavigator->addHeightfield(mCellPosition, cellSize2, surface2); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize2, surface2, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version); } @@ -484,9 +489,9 @@ namespace osg::ref_ptr instance(new Resource::BulletShapeInstance(bulletShape)); mNavigator->addAgent(mAgentBounds); - mNavigator->addObject(ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addObject(ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -531,10 +536,10 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addWater(mCellPosition, cellSize, 300); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addWater(mCellPosition, cellSize, 300, nullptr); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mStart.x() = 256; mStart.z() = 300; @@ -579,10 +584,10 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addWater(mCellPosition, cellSize, -25); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addWater(mCellPosition, cellSize, -25, nullptr); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mStart.x() = 256; mEnd.x() = 256; @@ -625,10 +630,10 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->addWater(mCellPosition, std::numeric_limits::max(), -25); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->addWater(mCellPosition, std::numeric_limits::max(), -25, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mStart.x() = 256; mEnd.x() = 256; @@ -671,10 +676,10 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addWater(mCellPosition, cellSize, -25); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addWater(mCellPosition, cellSize, -25, nullptr); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mStart.x() = 256; mEnd.x() = 256; @@ -715,17 +720,17 @@ namespace heightfield.shape().setLocalScaling(btVector3(128, 128, 1)); mNavigator->addAgent(mAgentBounds); - mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); - mNavigator->removeObject(ObjectId(&heightfield.shape())); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->removeObject(ObjectId(&heightfield.shape()), nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); - mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -769,17 +774,17 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); - mNavigator->removeHeightfield(mCellPosition); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->removeHeightfield(mCellPosition, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -824,9 +829,9 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); Misc::Rng::init(42); @@ -862,12 +867,12 @@ namespace mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); for (std::size_t i = 0; i < boxes.size(); ++i) { const btTransform transform(btMatrix3x3::getIdentity(), btVector3(shift.x() + i * 10, shift.y() + i * 10, i * 10)); - mNavigator->addObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform); + mNavigator->addObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform, nullptr); } std::this_thread::sleep_for(std::chrono::microseconds(1)); @@ -875,11 +880,11 @@ namespace for (std::size_t i = 0; i < boxes.size(); ++i) { const btTransform transform(btMatrix3x3::getIdentity(), btVector3(shift.x() + i * 10 + 1, shift.y() + i * 10 + 1, i * 10 + 1)); - mNavigator->updateObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform); + mNavigator->updateObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance(), mObjectTransform), transform, nullptr); } - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -921,27 +926,27 @@ namespace for (std::size_t i = 0; i < shapes.size(); ++i) { const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32, i * 32, i * 32)); - mNavigator->addObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform); + mNavigator->addObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform, nullptr); } - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); const auto start = std::chrono::steady_clock::now(); for (std::size_t i = 0; i < shapes.size(); ++i) { const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 1, i * 32 + 1, i * 32 + 1)); - mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform); + mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform, nullptr); } - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); for (std::size_t i = 0; i < shapes.size(); ++i) { const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 2, i * 32 + 2, i * 32 + 2)); - mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform); + mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance(), mObjectTransform), transform, nullptr); } - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); const auto duration = std::chrono::steady_clock::now() - start; @@ -962,9 +967,9 @@ namespace const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); const osg::Vec3f start(57, 460, 1); const osg::Vec3f end(460, 57, 1); @@ -991,14 +996,14 @@ namespace CollisionShapeInstance borderBox(std::make_unique(btVector3(50, 50, 50))); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform), - btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition)); + btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition), nullptr); // add this box to make navmesh bound box independent from oscillatingBoxShape rotations mNavigator->addObject(ObjectId(&borderBox.shape()), ObjectShapes(borderBox.instance(), mObjectTransform), - btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200)), nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); const Version expectedVersion {1, 4}; @@ -1010,9 +1015,9 @@ namespace { const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10), oscillatingBoxShapePosition); - mNavigator->updateObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform), transform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->updateObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance(), mObjectTransform), transform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); } ASSERT_EQ(navMeshes.size(), 1); @@ -1025,9 +1030,9 @@ namespace const int cellSize = mHeightfieldTileSize * 4; mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, plane); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); + mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::Success); @@ -1075,10 +1080,10 @@ namespace new btBoxShape(btVector3(200, 200, 1000))); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), Status::PartialPath); @@ -1114,10 +1119,10 @@ namespace new btBoxShape(btVector3(100, 100, 1000))); mNavigator->addAgent(mAgentBounds); - mNavigator->addHeightfield(mCellPosition, cellSize, surface); - mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); + mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); const float endTolerance = 1000.0f; @@ -1154,15 +1159,15 @@ namespace const float level2 = 2; mNavigator->addAgent(mAgentBounds); - mNavigator->addWater(mCellPosition, cellSize1, level1); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addWater(mCellPosition, cellSize1, level1, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); const Version version = mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(); - mNavigator->addWater(mCellPosition, cellSize2, level2); - mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener, WaitConditionType::allJobsDone); + mNavigator->addWater(mCellPosition, cellSize2, level2, nullptr); + mNavigator->update(mPlayerPosition, nullptr); + mNavigator->wait(WaitConditionType::allJobsDone, &mListener); EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version); } diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index 4ccd4c6712..1068bedea0 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -43,20 +43,12 @@ namespace EXPECT_EQ(manager.getRevision(), 0); } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, for_each_tile_position_for_empty_should_call_none) - { - TileCachedRecastMeshManager manager(mSettings); - std::size_t calls = 0; - manager.forEachTile([&] (const TilePosition&, const CachedRecastMeshManager&) { ++calls; }); - EXPECT_EQ(calls, 0); - } - TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_new_object_should_return_true) { TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false) @@ -64,17 +56,17 @@ 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); - EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); + EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); for (int x = -1; x < 1; ++x) for (int y = -1; y < 1; ++y) ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); @@ -88,9 +80,9 @@ namespace 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); - EXPECT_THAT(manager.takeChangedTiles(), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::add))); + manager.setBounds(bounds, nullptr); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); + EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::add))); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_add_changed_tiles) @@ -102,11 +94,11 @@ namespace 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); - manager.takeChangedTiles(); - EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); - EXPECT_THAT(manager.takeChangedTiles(), ElementsAre( + manager.setBounds(bounds, nullptr); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr); + manager.takeChangedTiles(nullptr); + EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); + EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre( std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(-1, 0), ChangeType::add), std::pair(TilePosition(0, -1), ChangeType::update), @@ -121,10 +113,10 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); - manager.takeChangedTiles(); - EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); - EXPECT_THAT(manager.takeChangedTiles(), IsEmpty()); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); + manager.takeChangedTiles(nullptr); + EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); + EXPECT_THAT(manager.takeChangedTiles(nullptr), IsEmpty()); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_return_add_changed_tiles) @@ -135,20 +127,20 @@ namespace 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))); + manager.setBounds(bounds, nullptr); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); + manager.takeChangedTiles(nullptr); + manager.removeObject(ObjectId(&boxShape), nullptr); + EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre(std::pair(TilePosition(0, 0), ChangeType::remove))); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, 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(0, -1)), nullptr); @@ -158,10 +150,10 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_nullptr_for_unused_tile) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); } @@ -171,20 +163,20 @@ namespace TileBounds bounds; bounds.mMin = osg::Vec2f(-1000, -1000); bounds.mMax = osg::Vec2f(1000, 1000); - manager.setBounds(bounds); - manager.setWorldspace("worldspace"); + manager.setBounds(bounds, nullptr); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, 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(1, 0)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, 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(0, -1)), nullptr); @@ -194,17 +186,17 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_nullptr_for_unused_tile) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr); 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); + manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr); } @@ -212,11 +204,11 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_removed_object_should_return_nullptr_for_all_previously_used_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); - manager.removeObject(ObjectId(&boxShape)); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); + manager.removeObject(ObjectId(&boxShape), 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(0, -1)), nullptr); @@ -226,17 +218,17 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_not_changed_object_after_update_should_return_recast_mesh_for_same_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, 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(0, -1)), nullptr); EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, 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(0, -1)), nullptr); @@ -249,7 +241,7 @@ namespace const auto initialRevision = manager.getRevision(); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); EXPECT_EQ(manager.getRevision(), initialRevision + 1); } @@ -258,9 +250,9 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); 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, nullptr)); EXPECT_EQ(manager.getRevision(), beforeAddRevision); } @@ -270,21 +262,21 @@ namespace const btBoxShape boxShape(btVector3(20, 20, 100)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, nullptr); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_revision_after_update_not_changed_object_should_return_same_value) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); const auto beforeUpdateRevision = manager.getRevision(); - manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); } @@ -293,9 +285,9 @@ namespace TileCachedRecastMeshManager manager(mSettings); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); const auto beforeRemoveRevision = manager.getRevision(); - manager.removeObject(ObjectId(&boxShape)); + manager.removeObject(ObjectId(&boxShape), nullptr); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1); } @@ -303,7 +295,7 @@ namespace { TileCachedRecastMeshManager manager(mSettings); const auto beforeRemoveRevision = manager.getRevision(); - manager.removeObject(ObjectId(&manager)); + manager.removeObject(ObjectId(&manager), nullptr); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision); } @@ -312,8 +304,8 @@ namespace TileCachedRecastMeshManager manager(mSettings); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - manager.addWater(cellPosition, cellSize, 0.0f); - const auto changedTiles = manager.takeChangedTiles(); + manager.addWater(cellPosition, cellSize, 0.0f, nullptr); + const auto changedTiles = manager.takeChangedTiles(nullptr); EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1)); EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11)); for (const auto& [k, v] : changedTiles) @@ -323,10 +315,10 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_not_max_int_should_add_new_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - manager.addWater(cellPosition, cellSize, 0.0f); + manager.addWater(cellPosition, cellSize, 0.0f, nullptr); for (int x = -1; x < 12; ++x) for (int y = -1; y < 12; ++y) ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); @@ -335,13 +327,13 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_water_for_max_int_should_not_add_new_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); const osg::Vec2i cellPosition(0, 0); const int cellSize = std::numeric_limits::max(); - manager.addWater(cellPosition, cellSize, 0.0f); + manager.addWater(cellPosition, cellSize, 0.0f, nullptr); 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); @@ -350,8 +342,8 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_absent_cell_should_not_add_changed_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.removeWater(osg::Vec2i(0, 0)); - EXPECT_THAT(manager.takeChangedTiles(), ElementsAre()); + manager.removeWater(osg::Vec2i(0, 0), nullptr); + EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre()); } TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_add_changed_tiles) @@ -359,10 +351,10 @@ namespace TileCachedRecastMeshManager manager(mSettings); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - manager.addWater(cellPosition, cellSize, 0.0f); - manager.takeChangedTiles(); - manager.removeWater(cellPosition); - const auto changedTiles = manager.takeChangedTiles(); + manager.addWater(cellPosition, cellSize, 0.0f, nullptr); + manager.takeChangedTiles(nullptr); + manager.removeWater(cellPosition, nullptr); + const auto changedTiles = manager.takeChangedTiles(nullptr); EXPECT_EQ(changedTiles.begin()->first, TilePosition(-1, -1)); EXPECT_EQ(changedTiles.rbegin()->first, TilePosition(11, 11)); for (const auto& [k, v] : changedTiles) @@ -372,11 +364,11 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_remove_empty_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - manager.addWater(cellPosition, cellSize, 0.0f); - manager.removeWater(cellPosition); + manager.addWater(cellPosition, cellSize, 0.0f, nullptr); + manager.removeWater(cellPosition, nullptr); for (int x = -6; x < 6; ++x) for (int y = -6; y < 6; ++y) ASSERT_EQ(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); @@ -385,14 +377,14 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_water_for_existing_cell_should_leave_not_empty_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const btBoxShape boxShape(btVector3(20, 20, 100)); const CollisionShape shape(mInstance, boxShape, mObjectTransform); - ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); const osg::Vec2i cellPosition(0, 0); const int cellSize = 8192; - manager.addWater(cellPosition, cellSize, 0.0f); - manager.removeWater(cellPosition); + manager.addWater(cellPosition, cellSize, 0.0f, nullptr); + manager.removeWater(cellPosition, nullptr); 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); @@ -401,14 +393,14 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, remove_object_should_not_remove_tile_with_water) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); const osg::Vec2i cellPosition(0, 0); 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)); - manager.addWater(cellPosition, cellSize, 0.0f); - manager.removeObject(ObjectId(&boxShape)); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); + manager.addWater(cellPosition, cellSize, 0.0f, nullptr); + manager.removeObject(ObjectId(&boxShape), nullptr); for (int x = -1; x < 12; ++x) for (int y = -1; y < 12; ++y) ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr); @@ -417,11 +409,11 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_new_worldspace_should_remove_tiles) { TileCachedRecastMeshManager manager(mSettings); - manager.setWorldspace("worldspace"); + manager.setWorldspace("worldspace", nullptr); 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)); - manager.setWorldspace("other"); + ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr)); + manager.setWorldspace("other", nullptr); for (int x = -1; x < 1; ++x) for (int y = -1; y < 1; ++y) ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr); @@ -435,13 +427,13 @@ namespace 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); + manager.setBounds(bounds, nullptr); + manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, nullptr); bounds.mMin = osg::Vec2f(-1000, -1000); bounds.mMax = osg::Vec2f(0, -182); - manager.takeChangedTiles(); - manager.setBounds(bounds); - EXPECT_THAT(manager.takeChangedTiles(), ElementsAre( + manager.takeChangedTiles(nullptr); + manager.setBounds(bounds, nullptr); + EXPECT_THAT(manager.takeChangedTiles(nullptr), ElementsAre( std::pair(TilePosition(-1, -1), ChangeType::add), std::pair(TilePosition(0, 0), ChangeType::remove) )); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 9dc752e4aa..cbfcece2c2 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -290,8 +290,6 @@ add_component_dir(detournavigator makenavmesh findsmoothpath recastmeshbuilder - recastmeshmanager - cachedrecastmeshmanager navmeshmanager navigatorimpl asyncnavmeshupdater @@ -304,7 +302,6 @@ add_component_dir(detournavigator findrandompointaroundcircle raycast navmeshtileview - oscillatingrecastmeshobject offmeshconnectionsmanager preparednavmeshdata navmeshcacheitem @@ -317,6 +314,7 @@ add_component_dir(detournavigator gettilespositions collisionshapetype stats + commulativeaabb ) add_component_dir(loadinglistener diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 28f8666943..933046798a 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -227,7 +227,7 @@ namespace DetourNavigator mDbWorker->updateJobs(playerTile, maxTiles); } - void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType) + void AsyncNavMeshUpdater::wait(WaitConditionType waitConditionType, Loading::Listener* listener) { switch (waitConditionType) { @@ -254,7 +254,7 @@ namespace DetourNavigator thread.join(); } - void AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(Loading::Listener& listener) + void AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(Loading::Listener* listener) { const int maxDistanceToPlayer = mSettings.get().mWaitUntilMinDistanceToPlayer; if (maxDistanceToPlayer <= 0) @@ -276,23 +276,28 @@ namespace DetourNavigator if (!isAbsentTileTooClose(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles, mProcessingTiles) || mJobs.empty()) return; - Loading::ScopedLoad load(&listener); - listener.setLabel("#{Navigation:BuildingNavigationMesh}"); - listener.setProgressRange(maxProgress); + const Loading::ScopedLoad load(listener); + if (listener != nullptr) + { + listener->setLabel("#{Navigation:BuildingNavigationMesh}"); + listener->setProgressRange(maxProgress); + } while (!mDone.wait_for(lock, std::chrono::milliseconds(20), isDone)) { + if (listener == nullptr) + continue; if (maxProgress < jobsLeft) { maxProgress = jobsLeft; - listener.setProgressRange(maxProgress); - listener.setProgress(jobsDone); + listener->setProgressRange(maxProgress); + listener->setProgress(jobsDone); } else if (jobsLeft < prevJobsLeft) { const std::size_t newJobsDone = prevJobsLeft - jobsLeft; jobsDone += newJobsDone; prevJobsLeft = jobsLeft; - listener.increaseProgress(newJobsDone); + listener->increaseProgress(newJobsDone); } } } diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 541e39f18c..b19ef9006d 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -151,7 +151,7 @@ namespace DetourNavigator const TilePosition& playerTile, std::string_view worldspace, const std::map& changedTiles); - void wait(Loading::Listener& listener, WaitConditionType waitConditionType); + void wait(WaitConditionType waitConditionType, Loading::Listener* listener); void stop(); @@ -209,7 +209,7 @@ namespace DetourNavigator void cleanupLastUpdates(); - inline void waitUntilJobsDoneForNotPresentTiles(Loading::Listener& listener); + inline void waitUntilJobsDoneForNotPresentTiles(Loading::Listener* listener); inline void waitUntilAllJobsDone(); }; diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp deleted file mode 100644 index 65f99fae1e..0000000000 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "cachedrecastmeshmanager.hpp" - -namespace DetourNavigator -{ - CachedRecastMeshManager::CachedRecastMeshManager(const TileBounds& bounds, std::size_t generation) - : mImpl(bounds, generation) - {} - - bool CachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, - const btTransform& transform, const AreaType areaType) - { - if (!mImpl.addObject(id, shape, transform, areaType)) - return false; - mOutdatedCache = true; - return true; - } - - bool CachedRecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType) - { - if (!mImpl.updateObject(id, transform, areaType)) - return false; - mOutdatedCache = true; - return true; - } - - bool CachedRecastMeshManager::removeObject(const ObjectId id) - { - const bool result = mImpl.removeObject(id); - if (result) - mOutdatedCache = true; - return result; - } - - bool CachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) - { - if (!mImpl.addWater(cellPosition, cellSize, level)) - return false; - mOutdatedCache = true; - return true; - } - - bool CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) - { - const bool result = mImpl.removeWater(cellPosition); - if (result) - mOutdatedCache = true; - return result; - } - - bool CachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, - const HeightfieldShape& shape) - { - if (!mImpl.addHeightfield(cellPosition, cellSize, shape)) - return false; - mOutdatedCache = true; - return true; - } - - bool CachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) - { - const bool result = mImpl.removeHeightfield(cellPosition); - if (result) - mOutdatedCache = true; - return result; - } - - std::shared_ptr CachedRecastMeshManager::getMesh() - { - bool outdated = true; - if (!mOutdatedCache.compare_exchange_strong(outdated, false)) - { - std::shared_ptr cached = getCachedMesh(); - if (cached != nullptr) - return cached; - } - std::shared_ptr mesh = mImpl.getMesh(); - *mCached.lock() = mesh; - return mesh; - } - - std::shared_ptr CachedRecastMeshManager::getCachedMesh() const - { - return *mCached.lockConst(); - } - - std::shared_ptr CachedRecastMeshManager::getNewMesh() const - { - return mImpl.getMesh(); - } - - bool CachedRecastMeshManager::isEmpty() const - { - return mImpl.isEmpty(); - } - - void CachedRecastMeshManager::reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion) - { - mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion); - } -} diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp deleted file mode 100644 index df4a5b3319..0000000000 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H -#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H - -#include "recastmeshmanager.hpp" -#include "version.hpp" -#include "heightfieldshape.hpp" - -#include - -#include - -namespace DetourNavigator -{ - class CachedRecastMeshManager - { - public: - explicit CachedRecastMeshManager(const TileBounds& bounds, std::size_t generation); - - bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType); - - bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType); - - bool removeObject(const ObjectId id); - - bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level); - - bool removeWater(const osg::Vec2i& cellPosition); - - bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); - - bool removeHeightfield(const osg::Vec2i& cellPosition); - - std::shared_ptr getMesh(); - - std::shared_ptr getCachedMesh() const; - - std::shared_ptr getNewMesh() const; - - bool isEmpty() const; - - void reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion); - - Version getVersion() const { return mImpl.getVersion(); } - - private: - RecastMeshManager mImpl; - Misc::ScopeGuarded> mCached; - std::atomic_bool mOutdatedCache {true}; - }; -} - -#endif diff --git a/components/detournavigator/commulativeaabb.cpp b/components/detournavigator/commulativeaabb.cpp new file mode 100644 index 0000000000..1274c3203d --- /dev/null +++ b/components/detournavigator/commulativeaabb.cpp @@ -0,0 +1,27 @@ +#include "commulativeaabb.hpp" + +#include + +namespace DetourNavigator +{ + CommulativeAabb::CommulativeAabb(std::size_t lastChangeRevision, const btAABB& aabb) + : mLastChangeRevision(lastChangeRevision) + , mAabb(aabb) + { + } + + bool CommulativeAabb::update(std::size_t lastChangeRevision, const btAABB& aabb) + { + if (mLastChangeRevision != lastChangeRevision) + { + mLastChangeRevision = lastChangeRevision; + // btAABB doesn't have copy-assignment operator + mAabb.m_min = aabb.m_min; + mAabb.m_max = aabb.m_max; + return true; + } + const btAABB currentAabb = mAabb; + mAabb.merge(aabb); + return currentAabb != mAabb; + } +} diff --git a/components/detournavigator/commulativeaabb.hpp b/components/detournavigator/commulativeaabb.hpp new file mode 100644 index 0000000000..3559d3b000 --- /dev/null +++ b/components/detournavigator/commulativeaabb.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_COMMULATIVEAABB_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_COMMULATIVEAABB_H + +#include +#include + +#include + +namespace DetourNavigator +{ + class CommulativeAabb + { + public: + explicit CommulativeAabb(std::size_t lastChangeRevision, const btAABB& aabb); + + bool update(std::size_t lastChangeRevision, const btAABB& aabb); + + private: + std::size_t mLastChangeRevision; + btAABB mAabb; + }; +} + +#endif diff --git a/components/detournavigator/debug.cpp b/components/detournavigator/debug.cpp index 740be10337..2c3cb998d7 100644 --- a/components/detournavigator/debug.cpp +++ b/components/detournavigator/debug.cpp @@ -4,6 +4,7 @@ #include "settings.hpp" #include "settingsutils.hpp" #include "version.hpp" +#include "tilespositionsrange.hpp" #include @@ -196,6 +197,11 @@ namespace DetourNavigator return stream << "Version {" << value.mGeneration << ", " << value.mRevision << "}"; } + std::ostream& operator<<(std::ostream& stream, const TilesPositionsRange& value) + { + return stream << "TilesPositionsRange {" << value.mBegin << ", " << value.mEnd << "}"; + } + void writeToFile(const RecastMesh& recastMesh, const std::string& pathPrefix, const std::string& revision, const RecastSettings& settings) { diff --git a/components/detournavigator/debug.hpp b/components/detournavigator/debug.hpp index 5dcbe55ffb..328eafa514 100644 --- a/components/detournavigator/debug.hpp +++ b/components/detournavigator/debug.hpp @@ -19,6 +19,7 @@ class dtNavMesh; namespace DetourNavigator { struct Version; + struct TilesPositionsRange; std::ostream& operator<<(std::ostream& stream, const TileBounds& value); @@ -58,6 +59,8 @@ namespace DetourNavigator std::ostream& operator<<(std::ostream& stream, const Version& value); + std::ostream& operator<<(std::ostream& stream, const TilesPositionsRange& value); + class RecastMesh; struct RecastSettings; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index c110fd3eb2..f78ce30c2b 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -55,6 +55,8 @@ namespace DetourNavigator {} }; + class UpdateGuard; + /** * @brief Top level interface of detournavigator component. Navigator allows to build a scene with navmesh and find * a path for an agent there. Scene contains agents, geometry objects and water. Agent are distinguished only by @@ -66,6 +68,8 @@ namespace DetourNavigator { virtual ~Navigator() = default; + virtual std::unique_ptr makeUpdateGuard() = 0; + /** * @brief addAgent should be called for each agent even if all of them has same half extents. * @param agentBounds allows to setup bounding cylinder for each agent, for each different half extents @@ -83,13 +87,13 @@ namespace DetourNavigator * @brief setWorldspace should be called before adding object from new worldspace * @param worldspace */ - virtual void setWorldspace(std::string_view worldspace) = 0; + virtual void setWorldspace(std::string_view worldspace, const UpdateGuard* guard) = 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; + virtual void updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard) = 0; /** * @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes @@ -97,7 +101,8 @@ namespace DetourNavigator * @param shape members must live until object is updated by another shape removed from Navigator * @param transform allows to setup objects geometry according to its world state */ - virtual void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0; + virtual void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) = 0; /** * @brief addObject is used to add doors. @@ -105,7 +110,8 @@ namespace DetourNavigator * @param shape members must live until object is updated by another shape or removed from Navigator. * @param transform allows to setup objects geometry according to its world state. */ - virtual void addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0; + virtual void addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) = 0; /** * @brief updateObject replace object geometry by given data. @@ -113,7 +119,8 @@ namespace DetourNavigator * @param shape members must live until object is updated by another shape removed from Navigator. * @param transform allows to setup objects geometry according to its world state. */ - virtual void updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) = 0; + virtual void updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) = 0; /** * @brief updateObject replace object geometry by given data. @@ -121,13 +128,14 @@ namespace DetourNavigator * @param shape members must live until object is updated by another shape removed from Navigator. * @param transform allows to setup objects geometry according to its world state. */ - virtual void updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0; + virtual void updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) = 0; /** * @brief removeObject to make it no more available at the scene. * @param id is used to find object. */ - virtual void removeObject(const ObjectId id) = 0; + virtual void removeObject(const ObjectId id, const UpdateGuard* guard) = 0; /** * @brief addWater is used to set water level at given world cell. @@ -135,17 +143,19 @@ namespace DetourNavigator * @param cellSize set cell borders. std::numeric_limits::max() disables cell borders. * @param shift set global shift of cell center. */ - virtual void addWater(const osg::Vec2i& cellPosition, int cellSize, float level) = 0; + virtual void addWater(const osg::Vec2i& cellPosition, int cellSize, float level, + const UpdateGuard* guard) = 0; /** * @brief removeWater to make it no more available at the scene. * @param cellPosition allows to find cell. */ - virtual void removeWater(const osg::Vec2i& cellPosition) = 0; + virtual void removeWater(const osg::Vec2i& cellPosition, const UpdateGuard* guard) = 0; - virtual void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) = 0; + virtual void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape, + const UpdateGuard* guard) = 0; - virtual void removeHeightfield(const osg::Vec2i& cellPosition) = 0; + virtual void removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard) = 0; virtual void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) = 0; @@ -155,13 +165,14 @@ namespace DetourNavigator * @brief update starts background navmesh update using current scene state. * @param playerPosition setup initial point to order build tiles of navmesh. */ - virtual void update(const osg::Vec3f& playerPosition) = 0; + virtual void update(const osg::Vec3f& playerPosition, const UpdateGuard* guard) = 0; /** * @brief wait locks thread until tiles are updated from last update call based on passed condition type. * @param waitConditionType defines when waiting will stop + * @param listener optional listener for a progress bar */ - virtual void wait(Loading::Listener& listener, WaitConditionType waitConditionType) = 0; + virtual void wait(WaitConditionType waitConditionType, Loading::Listener* listener) = 0; /** * @brief getNavMesh returns navmesh for specific agent half extents diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 6a194a7615..f23da1eb22 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -32,41 +32,44 @@ namespace DetourNavigator --it->second; } - void NavigatorImpl::setWorldspace(std::string_view worldspace) + void NavigatorImpl::setWorldspace(std::string_view worldspace, const UpdateGuard* guard) { - mNavMeshManager.setWorldspace(worldspace); + mNavMeshManager.setWorldspace(worldspace, getImpl(guard)); } - void NavigatorImpl::updateBounds(const osg::Vec3f& playerPosition) + void NavigatorImpl::updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard) { - mNavMeshManager.updateBounds(playerPosition); + mNavMeshManager.updateBounds(playerPosition, getImpl(guard)); } - void NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) + void NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) { - addObjectImpl(id, shapes, transform); + addObjectImpl(id, shapes, transform, guard); } - bool NavigatorImpl::addObjectImpl(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) + bool NavigatorImpl::addObjectImpl(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) { const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform); - bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground); + bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground, getImpl(guard)); if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->mAvoidCollisionShape.get()) { const ObjectId avoidId(avoidShape); const CollisionShape avoidCollisionShape(shapes.mShapeInstance, *avoidShape, shapes.mTransform); - if (mNavMeshManager.addObject(avoidId, avoidCollisionShape, transform, AreaType_null)) + if (mNavMeshManager.addObject(avoidId, avoidCollisionShape, transform, AreaType_null, getImpl(guard))) { - updateAvoidShapeId(id, avoidId); + updateAvoidShapeId(id, avoidId, guard); result = true; } } return result; } - void NavigatorImpl::addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) + void NavigatorImpl::addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) { - if (addObjectImpl(id, static_cast(shapes), transform)) + if (addObjectImpl(id, static_cast(shapes), transform, guard)) { const osg::Vec3f start = toNavMeshCoordinates(mSettings.mRecast, shapes.mConnectionStart); const osg::Vec3f end = toNavMeshCoordinates(mSettings.mRecast, shapes.mConnectionEnd); @@ -75,54 +78,55 @@ namespace DetourNavigator } } - void NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) + void NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) { - const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform); - mNavMeshManager.updateObject(id, collisionShape, transform, AreaType_ground); + mNavMeshManager.updateObject(id, transform, AreaType_ground, getImpl(guard)); if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->mAvoidCollisionShape.get()) { const ObjectId avoidId(avoidShape); - const CollisionShape avoidCollisionShape(shapes.mShapeInstance, *avoidShape, shapes.mTransform); - if (mNavMeshManager.updateObject(avoidId, avoidCollisionShape, transform, AreaType_null)) - updateAvoidShapeId(id, avoidId); + if (mNavMeshManager.updateObject(avoidId, transform, AreaType_null, getImpl(guard))) + updateAvoidShapeId(id, avoidId, guard); } } - void NavigatorImpl::updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) + void NavigatorImpl::updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) { - return updateObject(id, static_cast(shapes), transform); + return updateObject(id, static_cast(shapes), transform, guard); } - void NavigatorImpl::removeObject(const ObjectId id) + void NavigatorImpl::removeObject(const ObjectId id, const UpdateGuard* guard) { - mNavMeshManager.removeObject(id); + mNavMeshManager.removeObject(id, getImpl(guard)); const auto avoid = mAvoidIds.find(id); if (avoid != mAvoidIds.end()) - mNavMeshManager.removeObject(avoid->second); + mNavMeshManager.removeObject(avoid->second, getImpl(guard)); const auto water = mWaterIds.find(id); if (water != mWaterIds.end()) - mNavMeshManager.removeObject(water->second); + mNavMeshManager.removeObject(water->second, getImpl(guard)); mNavMeshManager.removeOffMeshConnections(id); } - void NavigatorImpl::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) + void NavigatorImpl::addWater(const osg::Vec2i& cellPosition, int cellSize, float level, const UpdateGuard* guard) { - mNavMeshManager.addWater(cellPosition, cellSize, level); + mNavMeshManager.addWater(cellPosition, cellSize, level, getImpl(guard)); } - void NavigatorImpl::removeWater(const osg::Vec2i& cellPosition) + void NavigatorImpl::removeWater(const osg::Vec2i& cellPosition, const UpdateGuard* guard) { - mNavMeshManager.removeWater(cellPosition); + mNavMeshManager.removeWater(cellPosition, getImpl(guard)); } - void NavigatorImpl::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) + void NavigatorImpl::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape, + const UpdateGuard* guard) { - mNavMeshManager.addHeightfield(cellPosition, cellSize, shape); + mNavMeshManager.addHeightfield(cellPosition, cellSize, shape, getImpl(guard)); } - void NavigatorImpl::removeHeightfield(const osg::Vec2i& cellPosition) + void NavigatorImpl::removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard) { - mNavMeshManager.removeHeightfield(cellPosition); + mNavMeshManager.removeHeightfield(cellPosition, getImpl(guard)); } void NavigatorImpl::addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) @@ -146,15 +150,15 @@ namespace DetourNavigator mNavMeshManager.removeOffMeshConnections(ObjectId(&pathgrid)); } - void NavigatorImpl::update(const osg::Vec3f& playerPosition) + void NavigatorImpl::update(const osg::Vec3f& playerPosition, const UpdateGuard* guard) { removeUnusedNavMeshes(); - mNavMeshManager.update(playerPosition); + mNavMeshManager.update(playerPosition, getImpl(guard)); } - void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType) + void NavigatorImpl::wait(WaitConditionType waitConditionType, Loading::Listener* listener) { - mNavMeshManager.wait(listener, waitConditionType); + mNavMeshManager.wait(waitConditionType, listener); } SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const AgentBounds& agentBounds) const @@ -182,22 +186,18 @@ namespace DetourNavigator return mNavMeshManager.getRecastMeshTiles(); } - void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId) - { - updateId(id, avoidId, mWaterIds); - } - - void NavigatorImpl::updateWaterShapeId(const ObjectId id, const ObjectId waterId) + void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId, const UpdateGuard* guard) { - updateId(id, waterId, mWaterIds); + updateId(id, avoidId, mWaterIds, guard); } - void NavigatorImpl::updateId(const ObjectId id, const ObjectId updateId, std::unordered_map& ids) + void NavigatorImpl::updateId(const ObjectId id, const ObjectId updateId, + std::unordered_map& ids, const UpdateGuard* guard) { auto inserted = ids.insert(std::make_pair(id, updateId)); if (!inserted.second) { - mNavMeshManager.removeObject(inserted.first->second); + mNavMeshManager.removeObject(inserted.first->second, getImpl(guard)); inserted.first->second = updateId; } } diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index f671c7e9bd..a33197b8d4 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -18,39 +18,49 @@ namespace DetourNavigator */ explicit NavigatorImpl(const Settings& settings, std::unique_ptr&& db); + std::unique_ptr makeUpdateGuard() override + { + return std::make_unique(*this); + } + void addAgent(const AgentBounds& agentBounds) override; void removeAgent(const AgentBounds& agentBounds) override; - void setWorldspace(std::string_view worldspace) override; + void setWorldspace(std::string_view worldspace, const UpdateGuard* guard) override; - void updateBounds(const osg::Vec3f& playerPosition) override; + void updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard) override; - void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override; + void addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) override; - void addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override; + void addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) override; - void updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override; + void updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) override; - void updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override; + void updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform, + const UpdateGuard* guard) override; - void removeObject(const ObjectId id) override; + void removeObject(const ObjectId id, const UpdateGuard* guard) override; - void addWater(const osg::Vec2i& cellPosition, int cellSize, float level) override; + void addWater(const osg::Vec2i& cellPosition, int cellSize, float level, const UpdateGuard* guard) override; - void removeWater(const osg::Vec2i& cellPosition) override; + void removeWater(const osg::Vec2i& cellPosition, const UpdateGuard* guard) override; - void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) override; + void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape, + const UpdateGuard* guard) override; - void removeHeightfield(const osg::Vec2i& cellPosition) override; + void removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard) override; void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) override; void removePathgrid(const ESM::Pathgrid& pathgrid) override; - void update(const osg::Vec3f& playerPosition) override; + void update(const osg::Vec3f& playerPosition, const UpdateGuard* guard) override; - void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override; + void wait(WaitConditionType waitConditionType, Loading::Listener* listener) override; SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const override; @@ -72,11 +82,31 @@ namespace DetourNavigator std::unordered_map mAvoidIds; std::unordered_map mWaterIds; - inline bool addObjectImpl(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform); - void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId); - void updateWaterShapeId(const ObjectId id, const ObjectId waterId); - void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map& ids); - void removeUnusedNavMeshes(); + inline bool addObjectImpl(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform, + const UpdateGuard* guard); + + inline void updateAvoidShapeId(const ObjectId id, const ObjectId avoidId, const UpdateGuard* guard); + + inline void updateId(const ObjectId id, const ObjectId waterId, std::unordered_map& ids, + const UpdateGuard* guard); + + inline void removeUnusedNavMeshes(); + + friend class UpdateGuard; + }; + + class UpdateGuard + { + public: + explicit UpdateGuard(NavigatorImpl& navigator) : mImpl(navigator.mNavMeshManager) {} + + private: + NavMeshManager::UpdateGuard mImpl; + + friend inline const NavMeshManager::UpdateGuard* getImpl(const UpdateGuard* guard) + { + return guard == nullptr ? nullptr : &guard->mImpl; + } }; } diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 771df43f64..8b3d3328d1 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -17,40 +17,47 @@ namespace DetourNavigator public: NavigatorStub() = default; + std::unique_ptr makeUpdateGuard() override { return nullptr; } + void addAgent(const AgentBounds& /*agentBounds*/) override {} void removeAgent(const AgentBounds& /*agentBounds*/) override {} - void setWorldspace(std::string_view /*worldspace*/) override {} + void setWorldspace(std::string_view /*worldspace*/, const UpdateGuard* /*guard*/) override {} - void addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override {} + void addObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/, + const UpdateGuard* /*guard*/) override {} - void addObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override {} + void addObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/, + const UpdateGuard* /*guard*/) override {} - void updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override {} + void updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/, + const UpdateGuard* /*guard*/) override {} - void updateObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/) override {} + void updateObject(const ObjectId /*id*/, const DoorShapes& /*shapes*/, const btTransform& /*transform*/, + const UpdateGuard* /*guard*/) override {} - void removeObject(const ObjectId /*id*/) override {} + void removeObject(const ObjectId /*id*/, const UpdateGuard* /*guard*/) override {} - void addWater(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, float /*level*/) override {} + void addWater(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, float /*level*/, + const UpdateGuard* /*guard*/) override {} - void removeWater(const osg::Vec2i& /*cellPosition*/) override {} + void removeWater(const osg::Vec2i& /*cellPosition*/, const UpdateGuard* /*guard*/) override {} - void addHeightfield(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, const HeightfieldShape& /*height*/) - override {} + void addHeightfield(const osg::Vec2i& /*cellPosition*/, int /*cellSize*/, const HeightfieldShape& /*height*/, + const UpdateGuard* /*guard*/) override {} - void removeHeightfield(const osg::Vec2i& /*cellPosition*/) override {} + void removeHeightfield(const osg::Vec2i& /*cellPosition*/, const UpdateGuard* /*guard*/) override {} void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) override {} void removePathgrid(const ESM::Pathgrid& /*pathgrid*/) override {} - void update(const osg::Vec3f& /*playerPosition*/) override {} + void update(const osg::Vec3f& /*playerPosition*/, const UpdateGuard* /*guard*/) override {} - void updateBounds(const osg::Vec3f& /*playerPosition*/) override {} + void updateBounds(const osg::Vec3f& /*playerPosition*/, const UpdateGuard* /*guard*/) override {} - void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {} + void wait(WaitConditionType /*waitConditionType*/, Loading::Listener* /*listener*/) override {} SharedNavMeshCacheItem getNavMesh(const AgentBounds& /*agentBounds*/) const override { diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 07035b17cc..a86dae75e8 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -7,7 +7,6 @@ #include "settings.hpp" #include "waitconditiontype.hpp" #include "settingsutils.hpp" -#include "cachedrecastmeshmanager.hpp" #include #include @@ -58,58 +57,57 @@ namespace DetourNavigator , mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db)) {} - void NavMeshManager::setWorldspace(std::string_view worldspace) + void NavMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard) { if (worldspace == mWorldspace) return; - mRecastMeshManager.setWorldspace(worldspace); + mRecastMeshManager.setWorldspace(worldspace, getImpl(guard)); for (auto& [agent, cache] : mCache) cache = std::make_shared(makeEmptyNavMesh(mSettings), ++mGenerationCounter); mWorldspace = worldspace; } - void NavMeshManager::updateBounds(const osg::Vec3f& playerPosition) + void NavMeshManager::updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard) { const TileBounds bounds = makeBounds(mSettings.mRecast, osg::Vec2f(playerPosition.x(), playerPosition.y()), mSettings.mMaxTilesNumber); - mRecastMeshManager.setBounds(bounds); + mRecastMeshManager.setBounds(bounds, getImpl(guard)); } bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType) + const AreaType areaType, const UpdateGuard* guard) { - return mRecastMeshManager.addObject(id, shape, transform, areaType); + return mRecastMeshManager.addObject(id, shape, transform, areaType, getImpl(guard)); } - bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType) + bool NavMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType, const UpdateGuard* guard) { - return mRecastMeshManager.updateObject(id, shape, transform, areaType); + return mRecastMeshManager.updateObject(id, transform, areaType, getImpl(guard)); } - void NavMeshManager::removeObject(const ObjectId id) + void NavMeshManager::removeObject(const ObjectId id, const UpdateGuard* guard) { - mRecastMeshManager.removeObject(id); + mRecastMeshManager.removeObject(id, getImpl(guard)); } - void NavMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) + void NavMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level, const UpdateGuard* guard) { - mRecastMeshManager.addWater(cellPosition, cellSize, level); + mRecastMeshManager.addWater(cellPosition, cellSize, level, getImpl(guard)); } - void NavMeshManager::removeWater(const osg::Vec2i& cellPosition) + void NavMeshManager::removeWater(const osg::Vec2i& cellPosition, const UpdateGuard* guard) { - mRecastMeshManager.removeWater(cellPosition); + mRecastMeshManager.removeWater(cellPosition, getImpl(guard)); } - void NavMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape) + void NavMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape, const UpdateGuard* guard) { - mRecastMeshManager.addHeightfield(cellPosition, cellSize, shape); + mRecastMeshManager.addHeightfield(cellPosition, cellSize, shape, getImpl(guard)); } - void NavMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) + void NavMeshManager::removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard) { - mRecastMeshManager.removeHeightfield(cellPosition); + mRecastMeshManager.removeHeightfield(cellPosition, getImpl(guard)); } void NavMeshManager::addAgent(const AgentBounds& agentBounds) @@ -155,7 +153,7 @@ namespace DetourNavigator mRecastMeshManager.addChangedTile(tile, ChangeType::update); } - void NavMeshManager::update(const osg::Vec3f& playerPosition) + void NavMeshManager::update(const osg::Vec3f& playerPosition, const UpdateGuard* guard) { const auto playerTile = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition)); @@ -164,44 +162,41 @@ namespace DetourNavigator return; mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision(); mPlayerTile = playerTile; - const auto changedTiles = mRecastMeshManager.takeChangedTiles(); + const auto changedTiles = mRecastMeshManager.takeChangedTiles(getImpl(guard)); + const TilesPositionsRange range = mRecastMeshManager.getRange(); for (const auto& [agentBounds, cached] : mCache) - update(agentBounds, playerTile, cached, changedTiles); + update(agentBounds, playerTile, range, cached, changedTiles); } void NavMeshManager::update(const AgentBounds& agentBounds, const TilePosition& playerTile, - const SharedNavMeshCacheItem& cached, const std::map& changedTiles) + const TilesPositionsRange& range, const SharedNavMeshCacheItem& cached, + const std::map& changedTiles) { - std::map tilesToPost; + std::map tilesToPost = changedTiles; { const auto locked = cached->lockConst(); const auto& navMesh = locked->getImpl(); - for (const auto& [tilePosition, changeType] : changedTiles) - if (navMesh.getTileAt(tilePosition.x(), tilePosition.y(), 0)) - tilesToPost.emplace(tilePosition, changeType); const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles); - mRecastMeshManager.forEachTile([&] (const TilePosition& tile, CachedRecastMeshManager& recastMeshManager) + getTilesPositions(range, [&] (const TilePosition& tile) { - if (tilesToPost.count(tile)) + if (changedTiles.find(tile) != changedTiles.end()) return; - const auto shouldAdd = shouldAddTile(tile, playerTile, maxTiles); - const auto presentInNavMesh = bool(navMesh.getTileAt(tile.x(), tile.y(), 0)); + const bool shouldAdd = shouldAddTile(tile, playerTile, maxTiles); + const bool presentInNavMesh = navMesh.getTileAt(tile.x(), tile.y(), 0) != nullptr; if (shouldAdd && !presentInNavMesh) tilesToPost.emplace(tile, locked->isEmptyTile(tile) ? ChangeType::update : ChangeType::add); else if (!shouldAdd && presentInNavMesh) tilesToPost.emplace(tile, ChangeType::mixed); - else - recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0}); }); } - mAsyncNavMeshUpdater.post(agentBounds, cached, playerTile, mRecastMeshManager.getWorldspace(), tilesToPost); + mAsyncNavMeshUpdater.post(agentBounds, cached, playerTile, mWorldspace, tilesToPost); Log(Debug::Debug) << "Cache update posted for agent=" << agentBounds << " playerTile=" << playerTile << " recastMeshManagerRevision=" << mLastRecastMeshManagerRevision; } - void NavMeshManager::wait(Loading::Listener& listener, WaitConditionType waitConditionType) + void NavMeshManager::wait(WaitConditionType waitConditionType, Loading::Listener* listener) { - mAsyncNavMeshUpdater.wait(listener, waitConditionType); + mAsyncNavMeshUpdater.wait(waitConditionType, listener); } SharedNavMeshCacheItem NavMeshManager::getNavMesh(const AgentBounds& agentBounds) const @@ -221,14 +216,12 @@ namespace DetourNavigator RecastMeshTiles NavMeshManager::getRecastMeshTiles() const { - std::vector tiles; - mRecastMeshManager.forEachTile( - [&tiles] (const TilePosition& tile, const CachedRecastMeshManager&) { tiles.push_back(tile); }); - const std::string worldspace = mRecastMeshManager.getWorldspace(); RecastMeshTiles result; - for (const TilePosition& tile : tiles) - if (auto mesh = mRecastMeshManager.getCachedMesh(worldspace, tile)) - result.emplace(tile, std::move(mesh)); + getTilesPositions(mRecastMeshManager.getRange(), [&] (const TilePosition& v) + { + if (auto mesh = mRecastMeshManager.getCachedMesh(mWorldspace, v)) + result.emplace(v, std::move(mesh)); + }); return result; } diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index adaefb14c4..d9c378a19c 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -20,29 +20,44 @@ namespace DetourNavigator class NavMeshManager { public: + class UpdateGuard + { + public: + explicit UpdateGuard(NavMeshManager& manager) : mImpl(manager.mRecastMeshManager) {} + + friend const TileCachedRecastMeshManager::UpdateGuard* getImpl(const UpdateGuard* guard) + { + return guard == nullptr ? nullptr : &guard->mImpl; + } + + private: + const TileCachedRecastMeshManager::UpdateGuard mImpl; + }; + explicit NavMeshManager(const Settings& settings, std::unique_ptr&& db); - void setWorldspace(std::string_view worldspace); + void setWorldspace(std::string_view worldspace, const UpdateGuard* guard); - void updateBounds(const osg::Vec3f& playerPosition); + void updateBounds(const osg::Vec3f& playerPosition, const UpdateGuard* guard); bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType); + const AreaType areaType, const UpdateGuard* guard); - bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType); + bool updateObject(ObjectId id, const btTransform& transform, AreaType areaType, + const UpdateGuard* guard); - void removeObject(const ObjectId id); + void removeObject(const ObjectId id, const UpdateGuard* guard); void addAgent(const AgentBounds& agentBounds); - void addWater(const osg::Vec2i& cellPosition, int cellSize, float level); + void addWater(const osg::Vec2i& cellPosition, int cellSize, float level, const UpdateGuard* guard); - void removeWater(const osg::Vec2i& cellPosition); + void removeWater(const osg::Vec2i& cellPosition, const UpdateGuard* guard); - void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); + void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape, + const UpdateGuard* guard); - void removeHeightfield(const osg::Vec2i& cellPosition); + void removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard); bool reset(const AgentBounds& agentBounds); @@ -50,9 +65,9 @@ namespace DetourNavigator void removeOffMeshConnections(const ObjectId id); - void update(const osg::Vec3f& playerPosition); + void update(const osg::Vec3f& playerPosition, const UpdateGuard* guard); - void wait(Loading::Listener& listener, WaitConditionType waitConditionType); + void wait(WaitConditionType waitConditionType, Loading::Listener* listener); SharedNavMeshCacheItem getNavMesh(const AgentBounds& agentBounds) const; @@ -76,7 +91,8 @@ namespace DetourNavigator inline SharedNavMeshCacheItem getCached(const AgentBounds& agentBounds) const; inline void update(const AgentBounds& agentBounds, const TilePosition& playerTile, - const SharedNavMeshCacheItem& cached, const std::map& changedTiles); + const TilesPositionsRange& range, const SharedNavMeshCacheItem& cached, + const std::map& changedTiles); }; } diff --git a/components/detournavigator/oscillatingrecastmeshobject.cpp b/components/detournavigator/oscillatingrecastmeshobject.cpp deleted file mode 100644 index fbe4b77ffd..0000000000 --- a/components/detournavigator/oscillatingrecastmeshobject.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "oscillatingrecastmeshobject.hpp" -#include "tilebounds.hpp" - -#include - -#include - -namespace DetourNavigator -{ - namespace - { - void limitBy(btAABB& aabb, const TileBounds& bounds) - { - aabb.m_min.setX(std::max(aabb.m_min.x(), static_cast(bounds.mMin.x()))); - aabb.m_min.setY(std::max(aabb.m_min.y(), static_cast(bounds.mMin.y()))); - aabb.m_max.setX(std::min(aabb.m_max.x(), static_cast(bounds.mMax.x()))); - aabb.m_max.setY(std::min(aabb.m_max.y(), static_cast(bounds.mMax.y()))); - } - } - - OscillatingRecastMeshObject::OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision) - : mImpl(std::move(impl)) - , mLastChangeRevision(lastChangeRevision) - , mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform())) - { - } - - OscillatingRecastMeshObject::OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision) - : mImpl(impl) - , mLastChangeRevision(lastChangeRevision) - , mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform())) - { - } - - bool OscillatingRecastMeshObject::update(const btTransform& transform, const AreaType areaType, - std::size_t lastChangeRevision, const TileBounds& bounds) - { - const btTransform oldTransform = mImpl.getTransform(); - if (!mImpl.update(transform, areaType)) - return false; - if (transform == oldTransform) - return true; - if (mLastChangeRevision != lastChangeRevision) - { - mLastChangeRevision = lastChangeRevision; - // btAABB doesn't have copy-assignment operator - const btAABB aabb = BulletHelpers::getAabb(mImpl.getShape(), transform); - mAabb.m_min = aabb.m_min; - mAabb.m_max = aabb.m_max; - return true; - } - const btAABB currentAabb = mAabb; - mAabb.merge(BulletHelpers::getAabb(mImpl.getShape(), transform)); - limitBy(mAabb, bounds); - return currentAabb != mAabb; - } -} diff --git a/components/detournavigator/oscillatingrecastmeshobject.hpp b/components/detournavigator/oscillatingrecastmeshobject.hpp deleted file mode 100644 index f8aabce628..0000000000 --- a/components/detournavigator/oscillatingrecastmeshobject.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H -#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H - -#include "areatype.hpp" -#include "recastmeshobject.hpp" -#include "tilebounds.hpp" - -#include -#include - -namespace DetourNavigator -{ - class OscillatingRecastMeshObject - { - public: - explicit OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision); - explicit OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision); - - bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision, - const TileBounds& bounds); - - const RecastMeshObject& getImpl() const { return mImpl; } - - private: - RecastMeshObject mImpl; - std::size_t mLastChangeRevision; - btAABB mAabb; - }; -} - -#endif diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index ff216b0b0e..b8e455c732 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -277,6 +277,8 @@ namespace DetourNavigator mTriangles.erase(std::remove_if(mTriangles.begin(), mTriangles.end(), isNan), mTriangles.end()); std::sort(mTriangles.begin(), mTriangles.end()); std::sort(mWater.begin(), mWater.end()); + std::sort(mHeightfields.begin(), mHeightfields.end()); + std::sort(mFlatHeightfields.begin(), mFlatHeightfields.end()); Mesh mesh = makeMesh(std::move(mTriangles)); return std::make_shared(version, std::move(mesh), std::move(mWater), std::move(mHeightfields), std::move(mFlatHeightfields), diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp deleted file mode 100644 index a883f7839b..0000000000 --- a/components/detournavigator/recastmeshmanager.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "recastmeshmanager.hpp" -#include "recastmeshbuilder.hpp" -#include "heightfieldshape.hpp" - -#include -#include - -#include - -namespace -{ - struct AddHeightfield - { - osg::Vec2i mCellPosition; - int mCellSize; - DetourNavigator::RecastMeshBuilder& mBuilder; - - void operator()(const DetourNavigator::HeightfieldSurface& v) - { - mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeights, v.mSize, v.mMinHeight, v.mMaxHeight); - } - - void operator()(DetourNavigator::HeightfieldPlane v) - { - mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeight); - } - }; -} - -namespace DetourNavigator -{ - RecastMeshManager::RecastMeshManager(const TileBounds& bounds, std::size_t generation) - : mGeneration(generation) - , mTileBounds(bounds) - { - } - - bool RecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType) - { - const std::lock_guard lock(mMutex); - const auto object = mObjects.lower_bound(id); - if (object != mObjects.end() && object->first == id) - return false; - mObjects.emplace_hint(object, id, - OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1)); - ++mRevision; - return true; - } - - bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType) - { - const std::lock_guard lock(mMutex); - const auto object = mObjects.find(id); - if (object == mObjects.end()) - return false; - const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value() - ? mLastNavMeshReportedChange->mRevision : mRevision; - if (!object->second.update(transform, areaType, lastChangeRevision, mTileBounds)) - return false; - ++mRevision; - return true; - } - - bool RecastMeshManager::removeObject(const ObjectId id) - { - const std::lock_guard lock(mMutex); - const auto object = mObjects.find(id); - if (object == mObjects.end()) - return false; - mObjects.erase(object); - ++mRevision; - return true; - } - - bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level) - { - const std::lock_guard lock(mMutex); - if (!mWater.emplace(cellPosition, Water {cellSize, level}).second) - return false; - ++mRevision; - return true; - } - - bool RecastMeshManager::removeWater(const osg::Vec2i& cellPosition) - { - const std::lock_guard lock(mMutex); - const auto water = mWater.find(cellPosition); - if (water == mWater.end()) - return false; - ++mRevision; - mWater.erase(water); - return true; - } - - bool RecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize, - const HeightfieldShape& shape) - { - const std::lock_guard lock(mMutex); - if (!mHeightfields.emplace(cellPosition, SizedHeightfieldShape {cellSize, shape}).second) - return false; - ++mRevision; - return true; - } - - bool RecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) - { - const std::lock_guard lock(mMutex); - const auto it = mHeightfields.find(cellPosition); - if (it == mHeightfields.end()) - return false; - ++mRevision; - mHeightfields.erase(it); - return true; - } - - std::shared_ptr RecastMeshManager::getMesh() const - { - RecastMeshBuilder builder(mTileBounds); - using Object = std::tuple< - osg::ref_ptr, - ObjectTransform, - std::reference_wrapper, - btTransform, - AreaType - >; - std::vector objects; - std::size_t revision; - { - const std::lock_guard lock(mMutex); - for (const auto& [k, v] : mWater) - builder.addWater(k, v); - for (const auto& [cellPosition, v] : mHeightfields) - std::visit(AddHeightfield {cellPosition, v.mCellSize, builder}, v.mShape); - objects.reserve(mObjects.size()); - for (const auto& [k, object] : mObjects) - { - const RecastMeshObject& impl = object.getImpl(); - objects.emplace_back(impl.getInstance(), impl.getObjectTransform(), impl.getShape(), - impl.getTransform(), impl.getAreaType()); - } - revision = mRevision; - } - for (const auto& [instance, objectTransform, shape, transform, areaType] : objects) - builder.addObject(shape, transform, areaType, instance->getSource(), objectTransform); - return std::move(builder).create(Version {.mGeneration = mGeneration, .mRevision = revision}); - } - - bool RecastMeshManager::isEmpty() const - { - const std::lock_guard lock(mMutex); - return mObjects.empty() && mWater.empty() && mHeightfields.empty(); - } - - void RecastMeshManager::reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion) - { - if (recastMeshVersion.mGeneration != mGeneration) - return; - const std::lock_guard lock(mMutex); - if (mLastNavMeshReport.has_value() && navMeshVersion < mLastNavMeshReport->mNavMeshVersion) - return; - mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion}; - if (!mLastNavMeshReportedChange.has_value() - || mLastNavMeshReportedChange->mNavMeshVersion < mLastNavMeshReport->mNavMeshVersion) - mLastNavMeshReportedChange = mLastNavMeshReport; - } -} diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp deleted file mode 100644 index ad27933807..0000000000 --- a/components/detournavigator/recastmeshmanager.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H -#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H - -#include "oscillatingrecastmeshobject.hpp" -#include "objectid.hpp" -#include "version.hpp" -#include "recastmesh.hpp" -#include "heightfieldshape.hpp" - -#include - -#include - -#include -#include -#include -#include - -class btCollisionShape; - -namespace DetourNavigator -{ - struct Settings; - class RecastMesh; - - struct SizedHeightfieldShape - { - int mCellSize; - HeightfieldShape mShape; - }; - - class RecastMeshManager - { - public: - explicit RecastMeshManager(const TileBounds& bounds, std::size_t generation); - - bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform, - const AreaType areaType); - - bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType); - - bool removeObject(const ObjectId id); - - bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level); - - bool removeWater(const osg::Vec2i& cellPosition); - - bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); - - bool removeHeightfield(const osg::Vec2i& cellPosition); - - std::shared_ptr getMesh() const; - - bool isEmpty() const; - - void reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion); - - Version getVersion() const { return Version {mGeneration, mRevision}; } - - private: - struct Report - { - std::size_t mRevision; - Version mNavMeshVersion; - }; - - const std::size_t mGeneration; - const TileBounds mTileBounds; - mutable std::mutex mMutex; - std::size_t mRevision = 0; - std::map mObjects; - std::map mWater; - std::map mHeightfields; - std::optional mLastNavMeshReportedChange; - std::optional mLastNavMeshReport; - }; -} - -#endif diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 18c8ef26ca..d8eb773e22 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -2,10 +2,12 @@ #include "gettilespositions.hpp" #include "settingsutils.hpp" #include "changetype.hpp" -#include "cachedrecastmeshmanager.hpp" +#include "recastmeshbuilder.hpp" -#include #include +#include + +#include #include #include @@ -20,12 +22,39 @@ namespace DetourNavigator 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) + struct AddHeightfield + { + osg::Vec2i mCellPosition; + int mCellSize; + RecastMeshBuilder& mBuilder; + + void operator()(const HeightfieldSurface& v) + { + mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeights, v.mSize, v.mMinHeight, v.mMaxHeight); + } + + void operator()(HeightfieldPlane v) + { + mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeight); + } + }; + + TilePosition makeTilePosition(const boost::geometry::model::point& v) { - const auto tile = tiles.find(tilePosition); - return tile != tiles.end() && tile->second->updateObject(id, transform, areaType); + return TilePosition(v.get<0>(), v.get<1>()); } + + template + class MaybeLockGuard + { + public: + explicit MaybeLockGuard(Mutex& mutex, const TileCachedRecastMeshManager::UpdateGuard* guard) + : mImpl(guard == nullptr ? std::optional>(mutex) : std::nullopt) + {} + + private: + const std::optional> mImpl; + }; } TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings) @@ -34,352 +63,325 @@ namespace DetourNavigator , mRange(makeTilesPositionsRange(mBounds.mMin, mBounds.mMax, mSettings)) {} - void TileCachedRecastMeshManager::setBounds(const TileBounds& bounds) + void TileCachedRecastMeshManager::setBounds(const TileBounds& bounds, const UpdateGuard* guard) { if (mBounds == bounds) return; bool changed = false; const auto newRange = makeTilesPositionsRange(bounds.mMin, bounds.mMax, mSettings); - if (mBounds != infiniteTileBounds) { - const auto locked = mWorldspaceTiles.lock(); - for (auto& object : mObjects) + for (const auto& [id, data] : mObjects) { - const ObjectId id = object.first; - ObjectData& data = object.second; - const TilesPositionsRange objectRange = makeTilesPositionsRange(data.mShape.getShape(), data.mTransform, mSettings); + const TilesPositionsRange objectRange = makeTilesPositionsRange(data->mObject.getShape(), + data->mObject.getTransform(), mSettings); - const auto onOldTilePosition = [&] (const TilePosition& position) + getTilesPositions(getIntersection(mRange, objectRange), [&] (const TilePosition& v) { - 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, locked->mTiles)) + if (!isInTilesPositionsRange(newRange, v)) { - addChangedTile(position, ChangeType::remove); + addChangedTile(v, ChangeType::remove); changed = true; } - }; - getTilesPositions(getIntersection(mRange, objectRange), onOldTilePosition); + }); - const auto onNewTilePosition = [&] (const TilePosition& position) + getTilesPositions(getIntersection(newRange, objectRange), [&] (const TilePosition& v) { - if (data.mTiles.find(position) != data.mTiles.end()) - return; - if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, locked->mTiles)) + if (!isInTilesPositionsRange(mRange, v)) { - data.mTiles.insert(position); - addChangedTile(position, ChangeType::add); + addChangedTile(v, ChangeType::add); + changed = true; } - }; - getTilesPositions(getIntersection(newRange, objectRange), onNewTilePosition); + }); } } if (changed) + { + const MaybeLockGuard lock(mMutex, guard); ++mRevision; + } mBounds = bounds; mRange = newRange; } - std::string TileCachedRecastMeshManager::getWorldspace() const + TilesPositionsRange TileCachedRecastMeshManager::getRange() const { - return mWorldspaceTiles.lockConst()->mWorldspace; + const auto bounds = mObjectIndex.bounds(); + return TilesPositionsRange { + .mBegin = makeTilePosition(bounds.min_corner()), + .mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1), + }; } - void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace) + void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard) { - const auto locked = mWorldspaceTiles.lock(); - if (locked->mWorldspace == worldspace) + const MaybeLockGuard lock(mMutex, guard); + if (mWorldspace == worldspace) return; - locked->mTiles.clear(); - locked->mWorldspace = worldspace; + mWorldspace = worldspace; + ++mGeneration; + ++mRevision; + mObjectIndex.clear(); + mObjects.clear(); + mWater.clear(); + mHeightfields.clear(); + mCache.clear(); } - bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, - const btTransform& transform, const AreaType areaType) + bool TileCachedRecastMeshManager::addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, + const AreaType areaType, const UpdateGuard* guard) { - 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 TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings); { - 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); - } - }); + const MaybeLockGuard lock(mMutex, guard); + const auto it = mObjects.find(id); + if (it != mObjects.end()) + return false; + const std::size_t revision = mRevision + 1; + ObjectData* const dataPtr = mObjects.emplace_hint(it, id, std::unique_ptr(new ObjectData { + .mObject = RecastMeshObject(shape, transform, areaType), + .mRange = range, + .mAabb = CommulativeAabb(revision, BulletHelpers::getAabb(shape.getShape(), transform)), + .mGeneration = mGeneration, + .mRevision = revision, + .mLastNavMeshReportedChange = {}, + .mLastNavMeshReport = {}, + }))->second.get(); + assert(range.mBegin != range.mEnd); + mObjectIndex.insert(makeObjectIndexValue(range, dataPtr)); + mRevision = revision; } - mObjects.emplace_hint(it, id, ObjectData {shape, transform, areaType, std::move(tilesPositions)}); - ++mRevision; + getTilesPositions(getIntersection(range, mRange), + [&] (const TilePosition& v) { addChangedTile(v, ChangeType::add); }); return true; } - bool TileCachedRecastMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, - const btTransform& transform, AreaType areaType) + bool TileCachedRecastMeshManager::updateObject(ObjectId id, const btTransform& transform, const AreaType areaType, const UpdateGuard* guard) { - const auto object = mObjects.find(id); - if (object == mObjects.end()) - return false; - auto& data = object->second; - bool changed = false; - std::set newTiles; + TilesPositionsRange newRange; + TilesPositionsRange oldRange; { - 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) + const MaybeLockGuard lock(mMutex, guard); + const auto it = mObjects.find(id); + if (it == mObjects.end()) + return false; + if (!it->second->mObject.update(transform, areaType)) + return false; + const std::size_t lastChangeRevision = it->second->mLastNavMeshReportedChange.has_value() + ? it->second->mLastNavMeshReportedChange->mRevision : mRevision; + const btCollisionShape& shape = it->second->mObject.getShape(); + if (!it->second->mAabb.update(lastChangeRevision, BulletHelpers::getAabb(shape, transform))) + return false; + newRange = makeTilesPositionsRange(shape, transform, mSettings); + oldRange = it->second->mRange; + if (newRange != oldRange) { - 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; - } + mObjectIndex.remove(makeObjectIndexValue(oldRange, it->second.get())); + mObjectIndex.insert(makeObjectIndexValue(newRange, it->second.get())); + it->second->mRange = newRange; } + ++mRevision; + it->second->mRevision = mRevision; } - if (changed) + if (newRange == oldRange) { - data.mTiles = std::move(newTiles); - ++mRevision; + getTilesPositions(getIntersection(newRange, mRange), + [&] (const TilePosition& v) { addChangedTile(v, ChangeType::update); }); } - return changed; - } - - void TileCachedRecastMeshManager::removeObject(const ObjectId id) - { - const auto object = mObjects.find(id); - if (object == mObjects.end()) - return; - bool changed = false; + else { - const auto locked = mWorldspaceTiles.lock(); - for (const auto& tilePosition : object->second.mTiles) + getTilesPositions(getIntersection(newRange, mRange), [&] (const TilePosition& v) { - if (removeTile(id, tilePosition, locked->mTiles)) - { - addChangedTile(tilePosition, ChangeType::remove); - changed = true; - } - } + const ChangeType changeType = isInTilesPositionsRange(oldRange, v) + ? ChangeType::update : ChangeType::add; + addChangedTile(v, changeType); + }); + getTilesPositions(getIntersection(oldRange, mRange), [&] (const TilePosition& v) + { + if (!isInTilesPositionsRange(newRange, v)) + addChangedTile(v, ChangeType::remove); + }); } - mObjects.erase(object); - if (changed) - ++mRevision; + return true; } - void TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const float level) + void TileCachedRecastMeshManager::removeObject(ObjectId id, const UpdateGuard* guard) { - const auto it = mWaterTilesPositions.find(cellPosition); - if (it != mWaterTilesPositions.end()) - return; - - std::vector& tilesPositions = mWaterTilesPositions.emplace_hint( - it, cellPosition, std::vector())->second; - - bool changed = false; - - if (cellSize == std::numeric_limits::max()) + TilesPositionsRange range; { - const auto locked = mWorldspaceTiles.lock(); - for (auto& [tilePosition, data] : locked->mTiles) - { - if (data->addWater(cellPosition, cellSize, level)) - { - tilesPositions.push_back(tilePosition); - addChangedTile(tilePosition, ChangeType::add); - changed = true; - } - } + const MaybeLockGuard lock(mMutex, guard); + const auto it = mObjects.find(id); + if (it == mObjects.end()) + return; + range = it->second->mRange; + mObjectIndex.remove(makeObjectIndexValue(range, it->second.get())); + mObjects.erase(it); + ++mRevision; } - else + getTilesPositions(getIntersection(range, mRange), + [&] (const TilePosition& v) { addChangedTile(v, ChangeType::remove); }); + } + + void TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const float level, const UpdateGuard* guard) + { + const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level)); + const std::optional range = cellSize == std::numeric_limits::max() + ? std::optional() + : makeTilesPositionsRange(cellSize, shift, mSettings); { - const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level)); - const auto worldspaceTiles = mWorldspaceTiles.lock(); - getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings), - [&] (const TilePosition& tilePosition) - { - auto tile = worldspaceTiles->mTiles.find(tilePosition); - if (tile == worldspaceTiles->mTiles.end()) - { - const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition); - tile = worldspaceTiles->mTiles.emplace_hint(tile, tilePosition, - std::make_shared(tileBounds, mTilesGeneration)); - } - if (tile->second->addWater(cellPosition, cellSize, level)) - { - tilesPositions.push_back(tilePosition); - addChangedTile(tilePosition, ChangeType::add); - changed = true; - } - }); + const MaybeLockGuard lock(mMutex, guard); + auto it = mWater.find(cellPosition); + if (it != mWater.end()) + return; + const std::size_t revision = mRevision + 1; + it = mWater.emplace_hint(it, cellPosition, WaterData { + .mWater = Water {.mCellSize = cellSize, .mLevel = level}, + .mRange = range, + .mRevision = revision, + }); + if (range.has_value()) + mWaterIndex.insert(makeWaterIndexValue(*range, it)); + else + mInfiniteWater = it; + mRevision = revision; } - - if (changed) - ++mRevision; + addChangedTiles(range, ChangeType::add); } - void TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + void TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition, const UpdateGuard* guard) { - const auto object = mWaterTilesPositions.find(cellPosition); - if (object == mWaterTilesPositions.end()) - return; - bool changed = false; + std::optional range; { - const auto worldspaceTiles = mWorldspaceTiles.lock(); - for (const auto& tilePosition : object->second) - { - const auto tile = worldspaceTiles->mTiles.find(tilePosition); - if (tile == worldspaceTiles->mTiles.end()) - continue; - if (tile->second->removeWater(cellPosition)) - { - addChangedTile(tilePosition, ChangeType::remove); - changed = true; - } - if (tile->second->isEmpty()) - { - worldspaceTiles->mTiles.erase(tile); - ++mTilesGeneration; - } - } - } - mWaterTilesPositions.erase(object); - if (changed) + const MaybeLockGuard lock(mMutex, guard); + const auto it = mWater.find(cellPosition); + if (it == mWater.end()) + return; + range = it->second.mRange; + if (range.has_value()) + mWaterIndex.remove(makeWaterIndexValue(*range, it)); + else + mInfiniteWater = mWater.end(); + mWater.erase(it); ++mRevision; + } + addChangedTiles(range, ChangeType::remove); } void TileCachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, const int cellSize, - const HeightfieldShape& shape) + const HeightfieldShape& shape, const UpdateGuard* guard) { - const auto it = mHeightfieldTilesPositions.find(cellPosition); - if (it != mHeightfieldTilesPositions.end()) - return; - - std::vector& tilesPositions = mHeightfieldTilesPositions.emplace_hint( - it, cellPosition, std::vector())->second; const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize); - - bool changed = false; - + const std::optional range = cellSize == std::numeric_limits::max() + ? std::optional() + : makeTilesPositionsRange(cellSize, shift, mSettings); { - const auto worldspaceTiles = mWorldspaceTiles.lock(); - getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings), - [&] (const TilePosition& tilePosition) - { - auto tile = worldspaceTiles->mTiles.find(tilePosition); - if (tile == worldspaceTiles->mTiles.end()) - { - const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition); - tile = worldspaceTiles->mTiles.emplace_hint(tile, tilePosition, - std::make_shared(tileBounds, mTilesGeneration)); - } - if (tile->second->addHeightfield(cellPosition, cellSize, shape)) - { - tilesPositions.push_back(tilePosition); - addChangedTile(tilePosition, ChangeType::add); - changed = true; - } - }); + const MaybeLockGuard lock(mMutex, guard); + auto it = mHeightfields.find(cellPosition); + if (it != mHeightfields.end()) + return; + const std::size_t revision = mRevision + 1; + it = mHeightfields.emplace_hint(it, cellPosition, HeightfieldData { + .mCellSize = cellSize, + .mShape = shape, + .mRange = range, + .mRevision = revision, + }); + if (range.has_value()) + mHeightfieldIndex.insert(makeHeightfieldIndexValue(*range, it)); + else + mInfiniteHeightfield = it; + mRevision = revision; } - - if (changed) - ++mRevision; + addChangedTiles(range, ChangeType::add); } - void TileCachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition) + void TileCachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard) { - const auto object = mHeightfieldTilesPositions.find(cellPosition); - if (object == mHeightfieldTilesPositions.end()) - return; - bool changed = false; + std::optional range; { - const auto worldspaceTiles = mWorldspaceTiles.lock(); - for (const auto& tilePosition : object->second) - { - const auto tile = worldspaceTiles->mTiles.find(tilePosition); - if (tile == worldspaceTiles->mTiles.end()) - continue; - if (tile->second->removeHeightfield(cellPosition)) - { - addChangedTile(tilePosition, ChangeType::remove); - changed = true; - } - if (tile->second->isEmpty()) - { - worldspaceTiles->mTiles.erase(tile); - ++mTilesGeneration; - } - } - } - mHeightfieldTilesPositions.erase(object); - if (changed) + const MaybeLockGuard lock(mMutex, guard); + const auto it = mHeightfields.find(cellPosition); + if (it == mHeightfields.end()) + return; + range = it->second.mRange; + if (range.has_value()) + mHeightfieldIndex.remove(makeHeightfieldIndexValue(*range, it)); + else + mInfiniteHeightfield = mHeightfields.end(); + mHeightfields.erase(it); ++mRevision; + } + addChangedTiles(range, ChangeType::remove); } - std::shared_ptr TileCachedRecastMeshManager::getMesh(std::string_view worldspace, const TilePosition& tilePosition) const + std::shared_ptr TileCachedRecastMeshManager::getMesh(std::string_view worldspace, + const TilePosition& tilePosition) { - if (const auto manager = getManager(worldspace, tilePosition)) - return manager->getMesh(); - return nullptr; + { + const std::lock_guard lock(mMutex); + if (mWorldspace != worldspace) + return nullptr; + const auto it = mCache.find(tilePosition); + if (it != mCache.end() && it->second.mRecastMesh->getVersion() == it->second.mVersion) + return it->second.mRecastMesh; + } + auto result = makeMesh(tilePosition); + if (result != nullptr) + { + const std::lock_guard lock(mMutex); + mCache.insert_or_assign(tilePosition, CachedTile { + .mVersion = result->getVersion(), + .mRecastMesh = result, + }); + } + return result; } - std::shared_ptr TileCachedRecastMeshManager::getCachedMesh(std::string_view worldspace, const TilePosition& tilePosition) const + std::shared_ptr TileCachedRecastMeshManager::getCachedMesh(std::string_view worldspace, + const TilePosition& tilePosition) const { - if (const auto manager = getManager(worldspace, tilePosition)) - return manager->getCachedMesh(); - return nullptr; + const std::lock_guard lock(mMutex); + if (mWorldspace != worldspace) + return nullptr; + const auto it = mCache.find(tilePosition); + if (it == mCache.end()) + return nullptr; + return it->second.mRecastMesh; } - std::shared_ptr TileCachedRecastMeshManager::getNewMesh(std::string_view worldspace, const TilePosition& tilePosition) const + std::shared_ptr TileCachedRecastMeshManager::getNewMesh(std::string_view worldspace, + const TilePosition& tilePosition) const { - if (const auto manager = getManager(worldspace, tilePosition)) - return manager->getNewMesh(); - return nullptr; + { + const std::lock_guard lock(mMutex); + if (mWorldspace != worldspace) + return nullptr; + } + return makeMesh(tilePosition); } - void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const + void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, + Version recastMeshVersion, Version navMeshVersion) { - const auto locked = mWorldspaceTiles.lockConst(); - const auto it = locked->mTiles.find(tilePosition); - if (it == locked->mTiles.end()) - return; - it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion); + const std::lock_guard lock(mMutex); + for (auto it = mObjectIndex.qbegin(makeIndexQuery(tilePosition)); it != mObjectIndex.qend(); ++it) + { + ObjectData& object = *it->second; + if (recastMeshVersion.mGeneration != object.mGeneration) + continue; + if (object.mLastNavMeshReport.has_value() && navMeshVersion < object.mLastNavMeshReport->mNavMeshVersion) + continue; + object.mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion}; + if (!object.mLastNavMeshReportedChange.has_value() + || object.mLastNavMeshReportedChange->mNavMeshVersion < object.mLastNavMeshReport->mNavMeshVersion) + object.mLastNavMeshReportedChange = object.mLastNavMeshReport; + } } - void TileCachedRecastMeshManager::addChangedTile(const TilePosition& tilePosition, ChangeType changeType) + void TileCachedRecastMeshManager::addChangedTile(const TilePosition& tilePosition, const ChangeType changeType) { auto tile = mChangedTiles.find(tilePosition); if (tile == mChangedTiles.end()) @@ -388,44 +390,112 @@ namespace DetourNavigator 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) + std::map TileCachedRecastMeshManager::takeChangedTiles(const UpdateGuard* guard) { - auto tile = tiles.find(tilePosition); - if (tile == tiles.end()) { - const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition); - tile = tiles.emplace_hint(tile, tilePosition, - std::make_shared(tileBounds, mTilesGeneration)); + const MaybeLockGuard lock(mMutex, guard); + for (const auto& [tilePosition, changeType] : mChangedTiles) + if (const auto it = mCache.find(tilePosition); it != mCache.end()) + ++it->second.mVersion.mRevision; } - return tile->second->addObject(id, shape, transform, areaType); + return std::move(mChangedTiles); } - bool TileCachedRecastMeshManager::removeTile(const ObjectId id, - const TilePosition& tilePosition, TilesMap& tiles) + TileCachedRecastMeshManager::IndexPoint TileCachedRecastMeshManager::makeIndexPoint(const TilePosition& tilePosition) { - const auto tile = tiles.find(tilePosition); - if (tile == tiles.end()) - return false; - const bool result = tile->second->removeObject(id); - if (tile->second->isEmpty()) + return IndexPoint(tilePosition.x(), tilePosition.y()); + } + + TileCachedRecastMeshManager::IndexBox TileCachedRecastMeshManager::makeIndexBox(const TilesPositionsRange& range) + { + assert(range.mBegin != range.mEnd); + return IndexBox(makeIndexPoint(range.mBegin), makeIndexPoint(range.mEnd - TilePosition(1, 1))); + } + + TileCachedRecastMeshManager::ObjectIndexValue TileCachedRecastMeshManager::makeObjectIndexValue( + const TilesPositionsRange& range, ObjectData* id) + { + return {makeIndexBox(range), id}; + } + + TileCachedRecastMeshManager::WaterIndexValue TileCachedRecastMeshManager::makeWaterIndexValue( + const TilesPositionsRange& range, std::map::const_iterator it) + { + return {makeIndexBox(range), it}; + } + + TileCachedRecastMeshManager::HeightfieldIndexValue TileCachedRecastMeshManager::makeHeightfieldIndexValue( + const TilesPositionsRange& range, std::map::const_iterator it) + { + return {makeIndexBox(range), it}; + } + + auto TileCachedRecastMeshManager::makeIndexQuery(const TilePosition& tilePosition) + -> decltype(boost::geometry::index::intersects(IndexBox())) + { + const IndexPoint point = makeIndexPoint(tilePosition); + return boost::geometry::index::intersects(IndexBox(point, point)); + } + + std::shared_ptr TileCachedRecastMeshManager::makeMesh(const TilePosition& tilePosition) const + { + RecastMeshBuilder builder(makeRealTileBoundsWithBorder(mSettings, tilePosition)); + using Object = std::tuple< + osg::ref_ptr, + ObjectTransform, + std::reference_wrapper, + btTransform, + AreaType + >; + std::vector objects; + Version version; + bool hasInput = false; { - tiles.erase(tile); - ++mTilesGeneration; + const std::lock_guard lock(mMutex); + for (auto it = mWaterIndex.qbegin(makeIndexQuery(tilePosition)); it != mWaterIndex.qend(); ++it) + { + const auto& [cellPosition, data] = *it->second; + builder.addWater(cellPosition, data.mWater); + hasInput = true; + } + for (auto it = mHeightfieldIndex.qbegin(makeIndexQuery(tilePosition)); it != mHeightfieldIndex.qend(); ++it) + { + const auto& [cellPosition, data] = *it->second; + std::visit(AddHeightfield {cellPosition, data.mCellSize, builder}, data.mShape); + hasInput = true; + } + objects.reserve(mObjects.size()); + for (auto it = mObjectIndex.qbegin(makeIndexQuery(tilePosition)); it != mObjectIndex.qend(); ++it) + { + const auto& object = it->second->mObject; + objects.emplace_back(object.getInstance(), object.getObjectTransform(), object.getShape(), + object.getTransform(), object.getAreaType()); + hasInput = true; + } + if (hasInput) + { + if (mInfiniteWater != mWater.end()) + builder.addWater(mInfiniteWater->first, mInfiniteWater->second.mWater); + if (mInfiniteHeightfield != mHeightfields.end()) + std::visit( + AddHeightfield {mInfiniteHeightfield->first, mInfiniteHeightfield->second.mCellSize, builder}, + mInfiniteHeightfield->second.mShape + ); + version.mGeneration = mGeneration; + version.mRevision = mRevision; + } } - return result; + if (!hasInput) + return nullptr; + for (const auto& [instance, objectTransform, shape, transform, areaType] : objects) + builder.addObject(shape, transform, areaType, instance->getSource(), objectTransform); + return std::move(builder).create(version); } - std::shared_ptr TileCachedRecastMeshManager::getManager(std::string_view worldspace, - const TilePosition& tilePosition) const + void TileCachedRecastMeshManager::addChangedTiles(const std::optional& range, + ChangeType changeType) { - const auto locked = mWorldspaceTiles.lockConst(); - if (locked->mWorldspace != worldspace) - return nullptr; - const auto it = locked->mTiles.find(tilePosition); - if (it == locked->mTiles.end()) - return nullptr; - return it->second; + if (range.has_value()) + getTilesPositions(*range, [&] (const TilePosition& v) { addChangedTile(v, changeType); }); } } diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index b1bcfa9319..52fc1736cd 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -9,101 +9,156 @@ #include "objectid.hpp" #include "areatype.hpp" #include "recastmeshobject.hpp" +#include "commulativeaabb.hpp" +#include "version.hpp" +#include "recastmesh.hpp" #include +#include +#include +#include + #include #include #include -#include +#include namespace DetourNavigator { - class CachedRecastMeshManager; class RecastMesh; class TileCachedRecastMeshManager { public: + class UpdateGuard + { + public: + explicit UpdateGuard(TileCachedRecastMeshManager& manager) : mImpl(manager.mMutex) {} + + private: + const std::lock_guard mImpl; + }; + explicit TileCachedRecastMeshManager(const RecastSettings& settings); - void setBounds(const TileBounds& bounds); + void setBounds(const TileBounds& bounds, const UpdateGuard* guard); - std::string getWorldspace() const; + TilesPositionsRange getRange() const; - void setWorldspace(std::string_view worldspace); + void setWorldspace(std::string_view worldspace, const UpdateGuard* guard); - bool addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType); + bool addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType, + const UpdateGuard* guard); - bool updateObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType); + bool updateObject(ObjectId id, const btTransform& transform, AreaType areaType, const UpdateGuard* guard); - void removeObject(ObjectId id); + void removeObject(ObjectId id, const UpdateGuard* guard); - void addWater(const osg::Vec2i& cellPosition, int cellSize, float level); + void addWater(const osg::Vec2i& cellPosition, int cellSize, float level, const UpdateGuard* guard); - void removeWater(const osg::Vec2i& cellPosition); + void removeWater(const osg::Vec2i& cellPosition, const UpdateGuard* guard); - void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape); + void addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape, + const UpdateGuard* guard); - void removeHeightfield(const osg::Vec2i& cellPosition); + void removeHeightfield(const osg::Vec2i& cellPosition, const UpdateGuard* guard); - std::shared_ptr getMesh(std::string_view worldspace, const TilePosition& tilePosition) const; + std::shared_ptr getMesh(std::string_view worldspace, const TilePosition& tilePosition); std::shared_ptr getCachedMesh(std::string_view worldspace, const TilePosition& tilePosition) const; std::shared_ptr getNewMesh(std::string_view worldspace, const TilePosition& tilePosition) const; - template - void forEachTile(Function&& function) const - { - const auto& locked = mWorldspaceTiles.lockConst(); - for (const auto& [tilePosition, recastMeshManager] : locked->mTiles) - function(tilePosition, *recastMeshManager); - } - std::size_t getRevision() const { return mRevision; } - void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const; + void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion); void addChangedTile(const TilePosition& tilePosition, ChangeType changeType); - std::map takeChangedTiles() { return std::move(mChangedTiles); } + std::map takeChangedTiles(const UpdateGuard* guard); private: - using TilesMap = std::map>; + struct Report + { + std::size_t mRevision; + Version mNavMeshVersion; + }; struct ObjectData { - const CollisionShape mShape; - const btTransform mTransform; - const AreaType mAreaType; - std::set mTiles; + RecastMeshObject mObject; + TilesPositionsRange mRange; + CommulativeAabb mAabb; + std::size_t mGeneration = 0; + std::size_t mRevision = 0; + std::optional mLastNavMeshReportedChange; + std::optional mLastNavMeshReport; + }; + + struct WaterData + { + Water mWater; + std::optional mRange; + std::size_t mRevision; + }; + + struct HeightfieldData + { + int mCellSize; + HeightfieldShape mShape; + std::optional mRange; + std::size_t mRevision; }; - struct WorldspaceTiles + struct CachedTile { - std::string mWorldspace; - TilesMap mTiles; + Version mVersion; + std::shared_ptr mRecastMesh; }; + using IndexPoint = boost::geometry::model::point; + using IndexBox = boost::geometry::model::box; + using ObjectIndexValue = std::pair; + using WaterIndexValue = std::pair::const_iterator>; + using HeightfieldIndexValue = std::pair::const_iterator>; + const RecastSettings& mSettings; TileBounds mBounds; TilesPositionsRange mRange; - Misc::ScopeGuarded mWorldspaceTiles; - std::unordered_map mObjects; - std::map> mWaterTilesPositions; - std::map> mHeightfieldTilesPositions; + std::string mWorldspace; + std::unordered_map> mObjects; + boost::geometry::index::rtree> mObjectIndex; + std::map mWater; + std::map::const_iterator mInfiniteWater = mWater.end(); + boost::geometry::index::rtree> mWaterIndex; + std::map mHeightfields; + std::map::const_iterator mInfiniteHeightfield = mHeightfields.end(); + boost::geometry::index::rtree> mHeightfieldIndex; std::map mChangedTiles; + std::map mCache; + std::size_t mGeneration = 0; std::size_t mRevision = 0; - std::size_t mTilesGeneration = 0; + mutable std::mutex mMutex; + + inline static IndexPoint makeIndexPoint(const TilePosition& tilePosition); + + inline static IndexBox makeIndexBox(const TilesPositionsRange& range); + + inline static ObjectIndexValue makeObjectIndexValue(const TilesPositionsRange& range, ObjectData* data); + + inline static WaterIndexValue makeWaterIndexValue(const TilesPositionsRange& range, + std::map::const_iterator it); + + inline static HeightfieldIndexValue makeHeightfieldIndexValue(const TilesPositionsRange& range, + std::map::const_iterator it); - inline bool addTile(ObjectId id, const CollisionShape& shape, const btTransform& transform, - AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles); + inline static auto makeIndexQuery(const TilePosition& tilePosition) + -> decltype(boost::geometry::index::intersects(IndexBox())); - inline bool removeTile(ObjectId id, const TilePosition& tilePosition, TilesMap& tiles); + inline std::shared_ptr makeMesh(const TilePosition& tilePosition) const; - inline std::shared_ptr getManager(std::string_view worldspace, - const TilePosition& tilePosition) const; + inline void addChangedTiles(const std::optional& range, ChangeType changeType); }; } diff --git a/components/detournavigator/tilespositionsrange.hpp b/components/detournavigator/tilespositionsrange.hpp index d5f2622ba1..0d92dd0e6d 100644 --- a/components/detournavigator/tilespositionsrange.hpp +++ b/components/detournavigator/tilespositionsrange.hpp @@ -3,6 +3,8 @@ #include "tileposition.hpp" +#include + namespace DetourNavigator { struct TilesPositionsRange @@ -10,6 +12,16 @@ namespace DetourNavigator TilePosition mBegin; TilePosition mEnd; }; + + inline auto tie(const TilesPositionsRange& value) + { + return std::tie(value.mBegin, value.mEnd); + } + + inline bool operator==(const TilesPositionsRange& lhs, const TilesPositionsRange& rhs) + { + return tie(lhs) == tie(rhs); + } } #endif