diff --git a/apps/navmeshtool/navmesh.cpp b/apps/navmeshtool/navmesh.cpp index 4187197947..100ddaf22a 100644 --- a/apps/navmeshtool/navmesh.cpp +++ b/apps/navmeshtool/navmesh.cpp @@ -35,6 +35,7 @@ #include #include #include +#include namespace NavMeshTool { @@ -180,26 +181,36 @@ namespace NavMeshTool SceneUtil::WorkQueue workQueue(threadsNumber); auto navMeshTileConsumer = std::make_shared(std::move(db)); std::size_t tiles = 0; + std::mt19937_64 random; for (const std::unique_ptr& input : data.mNavMeshInputs) { + std::vector worldspaceTiles; + DetourNavigator::getTilesPositions( - Misc::Convert::toOsg(input->mAabb.m_min), Misc::Convert::toOsg(input->mAabb.m_max), settings.mRecast, - [&] (const TilePosition& tilePosition) - { - workQueue.addWorkItem(new GenerateNavMeshTile( - input->mWorldspace, - tilePosition, - RecastMeshProvider(input->mTileCachedRecastMeshManager), - agentHalfExtents, - settings, - navMeshTileConsumer - )); - - ++tiles; - }); + DetourNavigator::makeTilesPositionsRange( + Misc::Convert::toOsg(input->mAabb.m_min), + Misc::Convert::toOsg(input->mAabb.m_max), + settings.mRecast + ), + [&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); } + ); + + tiles += worldspaceTiles.size(); navMeshTileConsumer->mExpected = tiles; + + std::shuffle(worldspaceTiles.begin(), worldspaceTiles.end(), random); + + for (const TilePosition& tilePosition : worldspaceTiles) + workQueue.addWorkItem(new GenerateNavMeshTile( + input->mWorldspace, + tilePosition, + RecastMeshProvider(input->mTileCachedRecastMeshManager), + agentHalfExtents, + settings, + navMeshTileConsumer + )); } navMeshTileConsumer->wait(); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 054e26e855..fc02f205ff 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -511,8 +511,7 @@ namespace MWWorld if (mCurrentCell == nullptr) return; - const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - mNavigator.updatePlayerPosition(player.getRefData().getPosition().asVec3()); + mNavigator.updatePlayerPosition(pos); if (!mCurrentCell->isExterior()) return; @@ -823,6 +822,8 @@ namespace MWWorld loadingListener->setProgressRange(cell->count()); + mNavigator.updatePlayerPosition(position.asVec3()); + // Load cell. mPagedRefs.clear(); loadCell(cell, loadingListener, changeEvent); @@ -856,6 +857,8 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); + mNavigator.updatePlayerPosition(position.asVec3()); + changeCellGrid(position.asVec3(), x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); diff --git a/apps/openmw_test_suite/detournavigator/gettilespositions.cpp b/apps/openmw_test_suite/detournavigator/gettilespositions.cpp index ced33a99f0..8121c19205 100644 --- a/apps/openmw_test_suite/detournavigator/gettilespositions.cpp +++ b/apps/openmw_test_suite/detournavigator/gettilespositions.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -36,35 +37,35 @@ namespace TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_in_single_tile_should_return_one_tile) { - getTilesPositions(osg::Vec3f(2, 2, 0), osg::Vec3f(31, 31, 1), mSettings, mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec3f(2, 2, 0), osg::Vec3f(31, 31, 1), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0))); } TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_x_bounds_in_two_tiles_should_return_two_tiles) { - getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 31, 1), mSettings, mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 31, 1), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(1, 0))); } TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_y_bounds_in_two_tiles_should_return_two_tiles) { - getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 32, 1), mSettings, mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 32, 1), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(0, 1))); } TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_works_only_for_x_and_y_coordinates) { - getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 31, 32), mSettings, mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 31, 32), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0))); } TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_should_work_with_negative_coordinates) { - getTilesPositions(osg::Vec3f(-31, -31, 0), osg::Vec3f(31, 31, 1), mSettings, mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec3f(-31, -31, 0), osg::Vec3f(31, 31, 1), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre( TilePosition(-1, -1), @@ -78,7 +79,7 @@ namespace { mSettings.mBorderSize = 1; - getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(31.5, 31.5, 1), mSettings, mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31.5, 31.5, 1), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre( TilePosition(-1, -1), @@ -97,7 +98,7 @@ namespace { mSettings.mRecastScaleFactor = 0.5; - getTilesPositions(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 32, 1), mSettings, mCollect); + getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 32, 1), mSettings), mCollect); EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0))); } diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index c44ebc5155..631e4105ba 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -90,6 +90,10 @@ 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); + 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); EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [&] (const auto& v) { onChangedTile(v); })); @@ -137,6 +141,10 @@ namespace TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile) { TileCachedRecastMeshManager manager(mSettings); + TileBounds bounds; + bounds.mMin = osg::Vec2f(-1000, -1000); + bounds.mMax = osg::Vec2f(1000, 1000); + manager.setBounds(bounds); manager.setWorldspace("worldspace"); const btBoxShape boxShape(btVector3(20, 20, 100)); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 89b27b0d94..ae06f3f2b0 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -208,6 +208,7 @@ add_component_dir(detournavigator serialization navmeshdbutils recast + gettilespositions ) add_component_dir(loadinglistener diff --git a/components/detournavigator/gettilespositions.cpp b/components/detournavigator/gettilespositions.cpp new file mode 100644 index 0000000000..e74a22e5ba --- /dev/null +++ b/components/detournavigator/gettilespositions.cpp @@ -0,0 +1,66 @@ +#include "gettilespositions.hpp" +#include "settings.hpp" +#include "settingsutils.hpp" +#include "tileposition.hpp" +#include "tilebounds.hpp" + +#include + +#include + +namespace DetourNavigator +{ + TilesPositionsRange makeTilesPositionsRange(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax, + const RecastSettings& settings) + { + osg::Vec3f min = toNavMeshCoordinates(settings, aabbMin); + osg::Vec3f max = toNavMeshCoordinates(settings, aabbMax); + + const float border = getBorderSize(settings); + min -= osg::Vec3f(border, border, border); + max += osg::Vec3f(border, border, border); + + TilePosition minTile = getTilePosition(settings, min); + TilePosition maxTile = getTilePosition(settings, max); + + if (minTile.x() > maxTile.x()) + std::swap(minTile.x(), maxTile.x()); + + if (minTile.y() > maxTile.y()) + std::swap(minTile.y(), maxTile.y()); + + return {minTile, maxTile}; + } + + TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform, + const TileBounds& bounds, const RecastSettings& settings) + { + btVector3 aabbMin; + btVector3 aabbMax; + shape.getAabb(transform, aabbMin, aabbMax); + aabbMin.setX(std::max(aabbMin.x(), bounds.mMin.x())); + aabbMin.setY(std::max(aabbMin.y(), bounds.mMin.y())); + aabbMax.setX(std::min(aabbMax.x(), bounds.mMax.x())); + aabbMax.setY(std::min(aabbMax.y(), bounds.mMax.y())); + return makeTilesPositionsRange(Misc::Convert::toOsg(aabbMin), Misc::Convert::toOsg(aabbMax), settings); + } + + TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift, + const RecastSettings& settings) + { + using Misc::Convert::toOsg; + + const int halfCellSize = cellSize / 2; + const btTransform transform(btMatrix3x3::getIdentity(), shift); + btVector3 aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0)); + btVector3 aabbMax = transform(btVector3(halfCellSize, halfCellSize, 0)); + + aabbMin.setX(std::min(aabbMin.x(), aabbMax.x())); + aabbMin.setY(std::min(aabbMin.y(), aabbMax.y())); + + aabbMax.setX(std::max(aabbMin.x(), aabbMax.x())); + aabbMax.setY(std::max(aabbMin.y(), aabbMax.y())); + + return makeTilesPositionsRange(toOsg(aabbMin), toOsg(aabbMax), settings); + } +} diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index 707db0b512..9d3df7ef65 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -1,72 +1,43 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H -#include "settings.hpp" -#include "settingsutils.hpp" +#include "tilebounds.hpp" #include "tileposition.hpp" -#include +class btVector3; +class btTransform; +class btCollisionShape; -#include - -#include +namespace osg +{ + class Vec3f; +} namespace DetourNavigator { - template - void getTilesPositions(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax, - const RecastSettings& settings, Callback&& callback) - { - auto min = toNavMeshCoordinates(settings, aabbMin); - auto max = toNavMeshCoordinates(settings, aabbMax); + struct RecastSettings; - const auto border = getBorderSize(settings); - min -= osg::Vec3f(border, border, border); - max += osg::Vec3f(border, border, border); + struct TilesPositionsRange + { + TilePosition mMin; + TilePosition mMax; + }; - auto minTile = getTilePosition(settings, min); - auto maxTile = getTilePosition(settings, max); + TilesPositionsRange makeTilesPositionsRange(const osg::Vec3f& aabbMin, + const osg::Vec3f& aabbMax, const RecastSettings& settings); - if (minTile.x() > maxTile.x()) - std::swap(minTile.x(), maxTile.x()); + TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, + const btTransform& transform, const TileBounds& bounds, const RecastSettings& settings); - if (minTile.y() > maxTile.y()) - std::swap(minTile.y(), maxTile.y()); - - for (int tileX = minTile.x(); tileX <= maxTile.x(); ++tileX) - for (int tileY = minTile.y(); tileY <= maxTile.y(); ++tileY) - callback(TilePosition {tileX, tileY}); - } + TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift, + const RecastSettings& settings); template - void getTilesPositions(const btCollisionShape& shape, const btTransform& transform, - const RecastSettings& settings, Callback&& callback) + void getTilesPositions(const TilesPositionsRange& range, Callback&& callback) { - btVector3 aabbMin; - btVector3 aabbMax; - shape.getAabb(transform, aabbMin, aabbMax); - - getTilesPositions(Misc::Convert::toOsg(aabbMin), Misc::Convert::toOsg(aabbMax), settings, std::forward(callback)); - } - - template - void getTilesPositions(const int cellSize, const btVector3& shift, - const RecastSettings& settings, Callback&& callback) - { - using Misc::Convert::toOsg; - - const auto halfCellSize = cellSize / 2; - const btTransform transform(btMatrix3x3::getIdentity(), shift); - auto aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0)); - auto aabbMax = transform(btVector3(halfCellSize, halfCellSize, 0)); - - aabbMin.setX(std::min(aabbMin.x(), aabbMax.x())); - aabbMin.setY(std::min(aabbMin.y(), aabbMax.y())); - - aabbMax.setX(std::max(aabbMin.x(), aabbMax.x())); - aabbMax.setY(std::max(aabbMin.y(), aabbMax.y())); - - getTilesPositions(toOsg(aabbMin), toOsg(aabbMax), settings, std::forward(callback)); + for (int tileX = range.mMin.x(); tileX <= range.mMax.x(); ++tileX) + for (int tileY = range.mMin.y(); tileY <= range.mMax.y(); ++tileY) + callback(TilePosition {tileX, tileY}); } } diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index 85d86e6b2b..82a156bb97 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace DetourNavigator { diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 399af8a6a9..e8da5a0d61 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -9,6 +9,7 @@ #include #include +#include #include @@ -41,6 +42,18 @@ namespace namespace DetourNavigator { + namespace + { + TileBounds makeBounds(const RecastSettings& settings, const osg::Vec2f& center, int maxTiles) + { + const float radius = fromNavMeshCoordinates(settings, std::ceil(std::sqrt(static_cast(maxTiles) / osg::PIf) + 1) * getTileSize(settings)); + TileBounds result; + result.mMin = center - osg::Vec2f(radius, radius); + result.mMax = center + osg::Vec2f(radius, radius); + return result; + } + } + NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr&& db) : mSettings(settings) , mRecastMeshManager(settings.mRecast) @@ -204,6 +217,7 @@ namespace DetourNavigator } } const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles); + mRecastMeshManager.setBounds(makeBounds(mSettings.mRecast, osg::Vec2f(playerPosition.x(), playerPosition.y()), maxTiles)); mRecastMeshManager.forEachTile([&] (const TilePosition& tile, CachedRecastMeshManager& recastMeshManager) { if (tilesToPost.count(tile)) @@ -262,7 +276,7 @@ namespace DetourNavigator void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType) { - getTilesPositions(shape, transform, mSettings.mRecast, + getTilesPositions(makeTilesPositionsRange(shape, transform, mRecastMeshManager.getBounds(), mSettings.mRecast), [&] (const TilePosition& v) { addChangedTile(v, changeType); }); } @@ -272,7 +286,7 @@ namespace DetourNavigator if (cellSize == std::numeric_limits::max()) return; - getTilesPositions(cellSize, shift, mSettings.mRecast, + getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings.mRecast), [&] (const TilePosition& v) { addChangedTile(v, changeType); }); } diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index 0f7552aa77..08e7002cf8 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace DetourNavigator { @@ -40,6 +41,16 @@ namespace DetourNavigator { return static_cast(cellSize) / (dataSize - 1); } + + bool isNan(const RecastMeshTriangle& triangle) + { + for (std::size_t i = 0; i < 3; ++i) + if (std::isnan(triangle.mVertices[i].x()) + || std::isnan(triangle.mVertices[i].y()) + || std::isnan(triangle.mVertices[i].z())) + return true; + return false; + } } Mesh makeMesh(std::vector&& triangles, const osg::Vec3f& shift) @@ -264,6 +275,7 @@ namespace DetourNavigator std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) && { + mTriangles.erase(std::remove_if(mTriangles.begin(), mTriangles.end(), isNan), mTriangles.end()); std::sort(mTriangles.begin(), mTriangles.end()); std::sort(mWater.begin(), mWater.end()); Mesh mesh = makeMesh(std::move(mTriangles)); diff --git a/components/detournavigator/settingsutils.hpp b/components/detournavigator/settingsutils.hpp index 285920e5a0..e38da2d0e1 100644 --- a/components/detournavigator/settingsutils.hpp +++ b/components/detournavigator/settingsutils.hpp @@ -37,6 +37,11 @@ namespace DetourNavigator }; } + inline float fromNavMeshCoordinates(const RecastSettings& settings, float value) + { + return value / settings.mRecastScaleFactor; + } + inline osg::Vec3f fromNavMeshCoordinates(const RecastSettings& settings, osg::Vec3f position) { const auto factor = 1.0f / settings.mRecastScaleFactor; diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index bf3df92d6e..b3750ad171 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -4,6 +4,7 @@ #include "settingsutils.hpp" #include +#include #include #include @@ -14,6 +15,18 @@ namespace DetourNavigator : mSettings(settings) {} + TileBounds TileCachedRecastMeshManager::getBounds() const + { + const std::lock_guard lock(mMutex); + return mBounds; + } + + void TileCachedRecastMeshManager::setBounds(const TileBounds& bounds) + { + const std::lock_guard lock(mMutex); + mBounds = bounds; + } + std::string TileCachedRecastMeshManager::getWorldspace() const { const std::lock_guard lock(mMutex); @@ -35,7 +48,8 @@ namespace DetourNavigator std::vector tilesPositions; { const std::lock_guard lock(mMutex); - getTilesPositions(shape.getShape(), transform, mSettings, [&] (const TilePosition& tilePosition) + getTilesPositions(makeTilesPositionsRange(shape.getShape(), transform, mBounds, mSettings), + [&] (const TilePosition& tilePosition) { if (addTile(id, shape, transform, areaType, tilePosition, mTiles)) tilesPositions.push_back(tilePosition); @@ -90,7 +104,8 @@ namespace DetourNavigator else { const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level)); - getTilesPositions(cellSize, shift, mSettings, [&] (const TilePosition& tilePosition) + getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings), + [&] (const TilePosition& tilePosition) { const std::lock_guard lock(mMutex); auto tile = mTiles.find(tilePosition); @@ -148,7 +163,8 @@ namespace DetourNavigator bool result = false; - getTilesPositions(cellSize, shift, mSettings, [&] (const TilePosition& tilePosition) + getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings), + [&] (const TilePosition& tilePosition) { const std::lock_guard lock(mMutex); auto tile = mTiles.find(tilePosition); diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 23171f0925..d99dc3e27e 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -20,6 +20,10 @@ namespace DetourNavigator public: explicit TileCachedRecastMeshManager(const RecastSettings& settings); + TileBounds getBounds() const; + + void setBounds(const TileBounds& bounds); + std::string getWorldspace() const; void setWorldspace(std::string_view worldspace); @@ -57,7 +61,7 @@ namespace DetourNavigator changed = true; } }; - getTilesPositions(shape.getShape(), transform, mSettings, onTilePosition); + getTilesPositions(makeTilesPositionsRange(shape.getShape(), transform, mBounds, mSettings), onTilePosition); std::sort(newTiles.begin(), newTiles.end()); for (const auto& tile : currentTiles) { @@ -109,6 +113,7 @@ namespace DetourNavigator const RecastSettings& mSettings; mutable std::mutex mMutex; + TileBounds mBounds; std::string mWorldspace; TilesMap mTiles; std::unordered_map> mObjectsTilesPositions;