diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index a227df8031..a2a47722d1 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -21,19 +21,23 @@ namespace struct DetourNavigatorRecastMeshBuilderTest : Test { Settings mSettings; - RecastMeshBuilder mBuilder; + TileBounds mBounds; DetourNavigatorRecastMeshBuilderTest() - : mBuilder(mSettings) { mSettings.mRecastScaleFactor = 1.0f; mSettings.mTrianglesPerChunk = 256; + mBounds.mMin = osg::Vec2f(-std::numeric_limits::max() * std::numeric_limits::epsilon(), + -std::numeric_limits::max() * std::numeric_limits::epsilon()); + mBounds.mMax = osg::Vec2f(std::numeric_limits::max() * std::numeric_limits::epsilon(), + std::numeric_limits::max() * std::numeric_limits::epsilon()); } }; TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty) { - const auto recastMesh = mBuilder.create(); + RecastMeshBuilder builder(mSettings, mBounds); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector()); EXPECT_EQ(recastMesh->getIndices(), std::vector()); } @@ -43,8 +47,9 @@ namespace btTriangleMesh mesh; mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); btBvhTriangleMeshShape shape(&mesh, true); - mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); - const auto recastMesh = mBuilder.create(); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 0, -1, -1, 0, 1, @@ -58,9 +63,10 @@ namespace btTriangleMesh mesh; mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); btBvhTriangleMeshShape shape(&mesh, true); - mBuilder.addObject(static_cast(shape), + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); - const auto recastMesh = mBuilder.create(); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 2, 3, 0, 0, 3, 4, @@ -73,8 +79,9 @@ namespace { const std::array heightfieldData {{0, 0, 0, 0}}; btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); - mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); - const auto recastMesh = mBuilder.create(); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ -0.5, 0, -0.5, -0.5, 0, 0.5, @@ -89,8 +96,9 @@ namespace TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles) { btBoxShape shape(btVector3(1, 1, 2)); - mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); - const auto recastMesh = mBuilder.create(); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 2, 1, -1, 2, 1, @@ -130,8 +138,9 @@ namespace shape.addChildShape(btTransform::getIdentity(), &triangle1); shape.addChildShape(btTransform::getIdentity(), &box); shape.addChildShape(btTransform::getIdentity(), &triangle2); - mBuilder.addObject(static_cast(shape), btTransform::getIdentity()); - const auto recastMesh = mBuilder.create(); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 0, -1, -1, 0, 1, @@ -173,9 +182,10 @@ namespace btBvhTriangleMeshShape triangle(&mesh, true); btCompoundShape shape; shape.addChildShape(btTransform::getIdentity(), &triangle); - mBuilder.addObject(static_cast(shape), - btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); - const auto recastMesh = mBuilder.create(); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), + btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 2, 3, 0, 0, 3, 4, @@ -192,9 +202,10 @@ namespace btCompoundShape shape; shape.addChildShape(btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), &triangle); - mBuilder.addObject(static_cast(shape), - btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); - const auto recastMesh = mBuilder.create(); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), + btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); + const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 3, 12, 2, 1, 12, 10, @@ -202,4 +213,104 @@ namespace })); EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, without_bounds_add_transformed_compound_shape_with_transformed_bhv_triangle_shape_should_not_filter_by_bounds) + { + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0)); + btBvhTriangleMeshShape shape(&mesh, true); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = builder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + 1, 0, -1, + -1, 0, 1, + -1, 0, -1, + -2, 0, -3, + -3, 0, -2, + -3, 0, -3, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2, 3, 4, 5})); + } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_transformed_compound_shape_with_transformed_bhv_triangle_shape_should_filter_by_bounds) + { + mSettings.mRecastScaleFactor = 0.1f; + mBounds.mMin = osg::Vec2f(-3, -3) * mSettings.mRecastScaleFactor; + mBounds.mMax = osg::Vec2f(-2, -2) * mSettings.mRecastScaleFactor; + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0)); + btBvhTriangleMeshShape shape(&mesh, true); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), btTransform::getIdentity()); + const auto recastMesh = builder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + -0.2f, 0, -0.3f, + -0.3f, 0, -0.2f, + -0.3f, 0, -0.3f, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); + } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_x_bhv_triangle_shape_should_filter_by_bounds) + { + mBounds.mMin = osg::Vec2f(-5, -5); + mBounds.mMax = osg::Vec2f(5, -3); + btTriangleMesh mesh; + mesh.addTriangle(btVector3(0, -1, -1), btVector3(0, -1, -1), btVector3(0, 1, -1)); + mesh.addTriangle(btVector3(0, -3, -3), btVector3(0, -3, -2), btVector3(0, -2, -3)); + btBvhTriangleMeshShape shape(&mesh, true); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), + btTransform(btQuaternion(btVector3(1, 0, 0), static_cast(-osg::PI_4)))); + const auto recastMesh = builder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + 0, -0.70710659027099609375, -3.535533905029296875, + 0, 0.707107067108154296875, -3.535533905029296875, + 0, 2.384185791015625e-07, -4.24264049530029296875, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); + } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_y_bhv_triangle_shape_should_filter_by_bounds) + { + mBounds.mMin = osg::Vec2f(-5, -5); + mBounds.mMax = osg::Vec2f(-3, 5); + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, 0, -1), btVector3(-1, 0, 1), btVector3(1, 0, -1)); + mesh.addTriangle(btVector3(-3, 0, -3), btVector3(-3, 0, -2), btVector3(-2, 0, -3)); + btBvhTriangleMeshShape shape(&mesh, true); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), + btTransform(btQuaternion(btVector3(0, 1, 0), static_cast(osg::PI_4)))); + const auto recastMesh = builder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + -3.535533905029296875, -0.70710659027099609375, 0, + -3.535533905029296875, 0.707107067108154296875, 0, + -4.24264049530029296875, 2.384185791015625e-07, 0, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); + } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_z_bhv_triangle_shape_should_filter_by_bounds) + { + mBounds.mMin = osg::Vec2f(-5, -5); + mBounds.mMax = osg::Vec2f(-1, -1); + btTriangleMesh mesh; + mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); + mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0)); + btBvhTriangleMeshShape shape(&mesh, true); + RecastMeshBuilder builder(mSettings, mBounds); + builder.addObject(static_cast(shape), + btTransform(btQuaternion(btVector3(0, 0, 1), static_cast(osg::PI_4)))); + const auto recastMesh = builder.create(); + EXPECT_EQ(recastMesh->getVertices(), std::vector({ + 0.707107067108154296875, 0, -3.535533905029296875, + -0.70710659027099609375, 0, -3.535533905029296875, + 2.384185791015625e-07, 0, -4.24264049530029296875, + })); + EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); + } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f7aea8f379..fbb74a65a7 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -168,6 +168,7 @@ add_component_dir(detournavigator asyncnavmeshupdater chunkytrimesh recastmesh + tilecachedrecastmeshmanager ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index b809fc27c9..fcfc342ebf 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -40,8 +40,9 @@ namespace DetourNavigator return stream << "unknown"; } - AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings) - : mSettings(std::cref(settings)) + AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager) + : mSettings(settings) + , mRecastMeshManager(recastMeshManager) , mShouldStop() , mThread([&] { process(); }) { @@ -57,11 +58,11 @@ namespace DetourNavigator mThread.join(); } - void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr& recastMesh, + void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr& navMeshCacheItem, const TilePosition& playerTile, const std::set& changedTiles) { - setRecastMesh(recastMesh); + log("post jobs playerTile=", playerTile); if (changedTiles.empty()) return; @@ -107,9 +108,9 @@ namespace DetourNavigator setFirstStart(start); - const auto recastMesh = getRecastMesh(); + const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile); - const auto status = updateNavMesh(job.mAgentHalfExtents, *recastMesh, job.mChangedTile, mSettings, + const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, mSettings, *job.mNavMeshCacheItem); const auto finish = std::chrono::steady_clock::now(); @@ -162,18 +163,6 @@ namespace DetourNavigator writeToFile(*job.mNavMeshCacheItem->mValue.lock(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); } - std::shared_ptr AsyncNavMeshUpdater::getRecastMesh() - { - const std::lock_guard lock(mRecastMeshMutex); - return mRecastMesh; - } - - void AsyncNavMeshUpdater::setRecastMesh(const std::shared_ptr& value) - { - const std::lock_guard lock(mRecastMeshMutex); - mRecastMesh = value; - } - std::chrono::steady_clock::time_point AsyncNavMeshUpdater::getFirstStart() { const std::lock_guard lock(mFirstStartMutex); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 721b89cec7..e31446a92a 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -2,7 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H #include "navmeshcacheitem.hpp" -#include "recastmesh.hpp" +#include "tilecachedrecastmeshmanager.hpp" #include "tileposition.hpp" #include @@ -25,12 +25,11 @@ namespace DetourNavigator class AsyncNavMeshUpdater { public: - AsyncNavMeshUpdater(const Settings& settings); + AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager); ~AsyncNavMeshUpdater(); - void post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr& recastMesh, - const std::shared_ptr& navMeshCacheItem, const TilePosition& playerTile, - const std::set& changedTiles); + void post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr& mNavMeshCacheItem, + const TilePosition& playerTile, const std::set& changedTiles); void wait(); @@ -51,13 +50,12 @@ namespace DetourNavigator using Jobs = std::priority_queue>; std::reference_wrapper mSettings; + std::reference_wrapper mRecastMeshManager; std::atomic_bool mShouldStop; std::mutex mMutex; std::condition_variable mHasJob; std::condition_variable mDone; Jobs mJobs; - std::mutex mRecastMeshMutex; - std::shared_ptr mRecastMesh; std::mutex mFirstStartMutex; boost::optional mFirstStart; std::thread mThread; @@ -72,10 +70,6 @@ namespace DetourNavigator void writeDebugFiles(const Job& job, const RecastMesh& recastMesh) const; - std::shared_ptr getRecastMesh(); - - void setRecastMesh(const std::shared_ptr& value); - std::chrono::steady_clock::time_point getFirstStart(); void setFirstStart(const std::chrono::steady_clock::time_point& value); diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index 60306ecc68..3699bc77d0 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -3,9 +3,10 @@ namespace DetourNavigator { - CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings) - : mImpl(settings) - {} + CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds) + : mImpl(settings, bounds) + { + } bool CachedRecastMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform) { @@ -29,4 +30,9 @@ namespace DetourNavigator mCached = mImpl.getMesh(); return mCached; } + + bool CachedRecastMeshManager::isEmpty() const + { + return mImpl.isEmpty(); + } } diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index 6705a26f19..5185c38169 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -10,7 +10,7 @@ namespace DetourNavigator class CachedRecastMeshManager { public: - CachedRecastMeshManager(const Settings& settings); + CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds); bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform); @@ -18,6 +18,8 @@ namespace DetourNavigator std::shared_ptr getMesh(); + bool isEmpty() const; + private: RecastMeshManager mImpl; std::shared_ptr mCached; diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index 96ab89fd73..6f5a372600 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -17,7 +17,7 @@ namespace DetourNavigator template void getTilesPositions(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax, - const Settings& settings, Callback&& callback) + const Settings& settings, Callback&& callback) { auto min = toNavMeshCoordinates(settings, aabbMin); auto max = toNavMeshCoordinates(settings, aabbMax); @@ -42,7 +42,7 @@ namespace DetourNavigator template void getTilesPositions(const btCollisionShape& shape, const btTransform& transform, - const Settings& settings, Callback&& callback) + const Settings& settings, Callback&& callback) { btVector3 aabbMin; btVector3 aabbMax; diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index fb2fb2bb5b..4d54eac6cb 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -7,6 +7,7 @@ #include "settings.hpp" #include "settingsutils.hpp" #include "sharednavmesh.hpp" +#include "settingsutils.hpp" #include #include @@ -269,8 +270,9 @@ namespace DetourNavigator return navMesh; } - UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, - const TilePosition& changedTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem) + UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, + const RecastMesh* recastMesh, const TilePosition& changedTile, const Settings& settings, + NavMeshCacheItem& navMeshCacheItem) { log("update NavMesh with mutiple tiles:", " agentHeight=", std::setprecision(std::numeric_limits::max_exponent10), @@ -298,8 +300,14 @@ namespace DetourNavigator incRev.mNavMeshChanged = removed; - const auto& boundsMin = recastMesh.getBoundsMin(); - const auto& boundsMax = recastMesh.getBoundsMax(); + if (!recastMesh) + { + log("ignore add tile: recastMesh is null"); + return makeUpdateNavMeshStatus(removed, false); + } + + const auto& boundsMin = recastMesh->getBoundsMin(); + const auto& boundsMax = recastMesh->getBoundsMax(); if (boundsMin == boundsMax) { @@ -311,7 +319,7 @@ namespace DetourNavigator const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), boundsMin.y() - 1, tileBounds.mMin.y()); const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), boundsMax.y() + 1, tileBounds.mMax.y()); - auto navMeshData = makeNavMeshTileData(agentHalfExtents, recastMesh, x, y, + auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, x, y, tileBorderMin, tileBorderMax, settings); if (!navMeshData.mValue) diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 96e655be8b..c2767dc75a 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -4,6 +4,7 @@ #include "settings.hpp" #include "navmeshcacheitem.hpp" #include "tileposition.hpp" +#include "tilebounds.hpp" #include @@ -30,8 +31,8 @@ namespace DetourNavigator NavMeshPtr makeEmptyNavMesh(const Settings& settings); - UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, - const TilePosition& changedTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem); + UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, + const TilePosition& changedTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem); } #endif diff --git a/components/detournavigator/navmeshcacheitem.hpp b/components/detournavigator/navmeshcacheitem.hpp index ffea1fbddc..537f6df5d7 100644 --- a/components/detournavigator/navmeshcacheitem.hpp +++ b/components/detournavigator/navmeshcacheitem.hpp @@ -11,11 +11,10 @@ namespace DetourNavigator { SharedNavMesh mValue; std::size_t mGeneration; - std::size_t mRecastMeshRevision; std::atomic_size_t mNavMeshRevision; - NavMeshCacheItem(const NavMeshPtr& value, std::size_t generation, std::size_t revision) - : mValue(value), mGeneration(generation), mRecastMeshRevision(revision), mNavMeshRevision(0) {} + NavMeshCacheItem(const NavMeshPtr& value, std::size_t generation) + : mValue(value), mGeneration(generation), mNavMeshRevision(0) {} }; } diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index ad9a3a5e70..a9fb700b70 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -3,6 +3,7 @@ #include "exceptions.hpp" #include "gettilespositions.hpp" #include "makenavmesh.hpp" +#include "navmeshcacheitem.hpp" #include "settings.hpp" #include "sharednavmesh.hpp" @@ -17,14 +18,14 @@ namespace DetourNavigator NavMeshManager::NavMeshManager(const Settings& settings) : mSettings(settings) , mRecastMeshManager(settings) - , mAsyncNavMeshUpdater(settings) - {} + , mAsyncNavMeshUpdater(settings, mRecastMeshManager) + { + } bool NavMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform) { if (!mRecastMeshManager.addObject(id, shape, transform)) return false; - ++mRevision; addChangedTiles(shape, transform); return true; } @@ -34,7 +35,6 @@ namespace DetourNavigator const auto object = mRecastMeshManager.removeObject(id); if (!object) return false; - ++mRevision; addChangedTiles(*object->mShape, object->mTransform); return true; } @@ -45,8 +45,7 @@ namespace DetourNavigator if (cached != mCache.end()) return; mCache.insert(std::make_pair(agentHalfExtents, - std::make_shared(makeEmptyNavMesh(mSettings), ++mGenerationCounter, mRevision)) - ); + std::make_shared(makeEmptyNavMesh(mSettings), ++mGenerationCounter))); log("cache add for agent=", agentHalfExtents); } @@ -58,18 +57,15 @@ namespace DetourNavigator void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents) { const auto& cached = getCached(agentHalfExtents); - if (cached->mRecastMeshRevision >= mRevision) - return; - cached->mRecastMeshRevision = mRevision; const auto changedTiles = mChangedTiles.find(agentHalfExtents); if (changedTiles != mChangedTiles.end()) { playerPosition *= mSettings.mRecastScaleFactor; std::swap(playerPosition.y(), playerPosition.z()); - mAsyncNavMeshUpdater.post(agentHalfExtents, mRecastMeshManager.getMesh(), cached, - getTilePosition(mSettings, playerPosition), changedTiles->second); + mAsyncNavMeshUpdater.post(agentHalfExtents, cached, getTilePosition(mSettings, playerPosition), + changedTiles->second); + log("cache update posted for agent=", agentHalfExtents, " changedTiles=", changedTiles->second.size()); mChangedTiles.erase(changedTiles); - log("cache update posted for agent=", agentHalfExtents); } } diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 63733cf018..21328454e2 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -40,9 +40,8 @@ namespace DetourNavigator std::map> getNavMeshes() const; private: - std::size_t mRevision = 0; const Settings& mSettings; - CachedRecastMeshManager mRecastMeshManager; + TileCachedRecastMeshManager mRecastMeshManager; std::map> mCache; std::map> mChangedTiles; AsyncNavMeshUpdater mAsyncNavMeshUpdater; diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index 1e0d2dfc80..a34b114acb 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -27,9 +27,13 @@ namespace DetourNavigator { using BulletHelpers::makeProcessTriangleCallback; - RecastMeshBuilder::RecastMeshBuilder(const Settings& settings) + RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds) : mSettings(settings) - {} + , mBounds(bounds) + { + mBounds.mMin /= mSettings.get().mRecastScaleFactor; + mBounds.mMax /= mSettings.get().mRecastScaleFactor; + } void RecastMeshBuilder::addObject(const btCollisionShape& shape, const btTransform& transform) { @@ -54,7 +58,7 @@ namespace DetourNavigator void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform) { - return addObject(shape, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) + return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { for (std::size_t i = 3; i > 0; --i) addTriangleVertex(transform(triangle[i - 1])); @@ -63,7 +67,7 @@ namespace DetourNavigator void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform) { - return addObject(shape, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) + return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { for (std::size_t i = 0; i < 3; ++i) addTriangleVertex(transform(triangle[i])); @@ -111,11 +115,29 @@ namespace DetourNavigator mVertices.clear(); } - void RecastMeshBuilder::addObject(const btConcaveShape& shape, btTriangleCallback&& callback) + void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform, + btTriangleCallback&& callback) { btVector3 aabbMin; btVector3 aabbMax; shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); + const btVector3 boundsMinMin(mBounds.mMin.x(), mBounds.mMin.y(), 0); + const btVector3 boundsMinMax(mBounds.mMin.x(), mBounds.mMax.y(), 0); + const btVector3 boundsMaxMin(mBounds.mMax.x(), mBounds.mMin.y(), 0); + const btVector3 boundsMaxMax(mBounds.mMax.x(), mBounds.mMax.y(), 0); + const auto inversedTransform = transform.inverse(); + const auto localBoundsMinMin = inversedTransform(boundsMinMin); + const auto localBoundsMinMax = inversedTransform(boundsMinMax); + const auto localBoundsMaxMin = inversedTransform(boundsMaxMin); + const auto localBoundsMaxMax = inversedTransform(boundsMaxMax); + aabbMin.setX(std::min({localBoundsMinMin.x(), localBoundsMinMax.x(), + localBoundsMaxMin.x(), localBoundsMaxMax.x()})); + aabbMin.setY(std::min({localBoundsMinMin.y(), localBoundsMinMax.y(), + localBoundsMaxMin.y(), localBoundsMaxMax.y()})); + aabbMax.setX(std::max({localBoundsMinMin.x(), localBoundsMinMax.x(), + localBoundsMaxMin.x(), localBoundsMaxMax.x()})); + aabbMax.setY(std::max({localBoundsMinMin.y(), localBoundsMinMax.y(), + localBoundsMaxMin.y(), localBoundsMaxMax.y()})); shape.processAllTriangles(&callback, aabbMin, aabbMax); } diff --git a/components/detournavigator/recastmeshbuilder.hpp b/components/detournavigator/recastmeshbuilder.hpp index 28ca1f713a..d06f2cdd65 100644 --- a/components/detournavigator/recastmeshbuilder.hpp +++ b/components/detournavigator/recastmeshbuilder.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHBUILDER_H #include "recastmesh.hpp" +#include "tilebounds.hpp" class btBoxShape; class btCollisionShape; @@ -17,7 +18,7 @@ namespace DetourNavigator class RecastMeshBuilder { public: - RecastMeshBuilder(const Settings& settings); + RecastMeshBuilder(const Settings& settings, const TileBounds& bounds); void addObject(const btCollisionShape& shape, const btTransform& transform); @@ -35,10 +36,11 @@ namespace DetourNavigator private: std::reference_wrapper mSettings; + TileBounds mBounds; std::vector mIndices; std::vector mVertices; - void addObject(const btConcaveShape& shape, btTriangleCallback&& callback); + void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback); void addTriangleVertex(const btVector3& worldPosition); diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index ccfa253724..919609190a 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -4,9 +4,9 @@ namespace DetourNavigator { - RecastMeshManager::RecastMeshManager(const Settings& settings) + RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds) : mShouldRebuild(false) - , mMeshBuilder(settings) + , mMeshBuilder(settings, bounds) { } @@ -35,6 +35,11 @@ namespace DetourNavigator return mMeshBuilder.create(); } + bool RecastMeshManager::isEmpty() const + { + return mObjects.empty(); + } + void RecastMeshManager::rebuild() { if (!mShouldRebuild) diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index 429430707b..f2aa7f871d 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -22,7 +22,7 @@ namespace DetourNavigator btTransform mTransform; }; - RecastMeshManager(const Settings& settings); + RecastMeshManager(const Settings& settings, const TileBounds& bounds); bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform); @@ -30,6 +30,8 @@ namespace DetourNavigator std::shared_ptr getMesh(); + bool isEmpty() const; + private: bool mShouldRebuild; RecastMeshBuilder mMeshBuilder; diff --git a/components/detournavigator/settingsutils.hpp b/components/detournavigator/settingsutils.hpp index d0d51e1670..0b327717f5 100644 --- a/components/detournavigator/settingsutils.hpp +++ b/components/detournavigator/settingsutils.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H #include "settings.hpp" +#include "tilebounds.hpp" #include "tileposition.hpp" #include "tilebounds.hpp" diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp new file mode 100644 index 0000000000..9c4f63e59f --- /dev/null +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -0,0 +1,71 @@ +#include "tilecachedrecastmeshmanager.hpp" +#include "makenavmesh.hpp" +#include "gettilespositions.hpp" +#include "settingsutils.hpp" + +namespace DetourNavigator +{ + TileCachedRecastMeshManager::TileCachedRecastMeshManager(const Settings& settings) + : mSettings(settings) + { + } + + bool TileCachedRecastMeshManager::addObject(std::size_t id, const btCollisionShape& shape, + const btTransform& transform) + { + bool result = false; + auto& tilesPositions = mObjectsTilesPositions[id]; + const auto border = getBorderSize(mSettings); + getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& tilePosition) + { + std::unique_lock lock(mTilesMutex); + auto tile = mTiles.find(tilePosition); + if (tile == mTiles.end()) + { + auto tileBounds = makeTileBounds(mSettings, tilePosition); + tileBounds.mMin -= osg::Vec2f(border, border); + tileBounds.mMax += osg::Vec2f(border, border); + tile = mTiles.insert(std::make_pair(tilePosition, + CachedRecastMeshManager(mSettings, tileBounds))).first; + } + if (tile->second.addObject(id, shape, transform)) + { + lock.unlock(); + tilesPositions.push_back(tilePosition); + result = true; + } + }); + return result; + } + + boost::optional TileCachedRecastMeshManager::removeObject(std::size_t id) + { + const auto object = mObjectsTilesPositions.find(id); + if (object == mObjectsTilesPositions.end()) + return boost::none; + boost::optional result; + for (const auto& tilePosition : object->second) + { + std::unique_lock lock(mTilesMutex); + const auto tile = mTiles.find(tilePosition); + if (tile == mTiles.end()) + continue; + const auto tileResult = tile->second.removeObject(id); + if (tile->second.isEmpty()) + mTiles.erase(tile); + lock.unlock(); + if (tileResult && !result) + result = tileResult; + } + return result; + } + + std::shared_ptr TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition) + { + const std::lock_guard lock(mTilesMutex); + const auto it = mTiles.find(tilePosition); + if (it == mTiles.end()) + return nullptr; + return it->second.getMesh(); + } +} diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp new file mode 100644 index 0000000000..f957dec1dc --- /dev/null +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H + +#include "cachedrecastmeshmanager.hpp" +#include "tileposition.hpp" + +#include +#include + +namespace DetourNavigator +{ + class TileCachedRecastMeshManager + { + public: + TileCachedRecastMeshManager(const Settings& settings); + + bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform); + + boost::optional removeObject(std::size_t id); + + std::shared_ptr getMesh(const TilePosition& tilePosition); + + private: + const Settings& mSettings; + std::mutex mTilesMutex; + std::map mTiles; + std::unordered_map> mObjectsTilesPositions; + }; +} + +#endif