diff --git a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp index d245838858..b23446cea7 100644 --- a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp +++ b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp @@ -26,6 +26,23 @@ namespace recastMeshManager.addHeightfield(cellPosition, cellSize, HeightfieldPlane {0}); } + void addObject(const btBoxShape& shape, TileCachedRecastMeshManager& recastMeshManager) + { + const ObjectId id(&shape); + osg::ref_ptr bulletShape(new Resource::BulletShape); + bulletShape->mFileName = "test.nif"; + bulletShape->mFileHash = "test_hash"; + ObjectTransform objectTransform; + std::fill(std::begin(objectTransform.mPosition.pos), std::end(objectTransform.mPosition.pos), 0.1f); + std::fill(std::begin(objectTransform.mPosition.rot), std::end(objectTransform.mPosition.rot), 0.2f); + objectTransform.mScale = 3.14f; + const CollisionShape collisionShape( + osg::ref_ptr(new Resource::BulletShapeInstance(bulletShape)), + shape, objectTransform + ); + recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground); + } + struct DetourNavigatorAsyncNavMeshUpdaterTest : Test { Settings mSettings = makeSettings(); @@ -34,6 +51,7 @@ namespace const osg::Vec3f mAgentHalfExtents {29, 29, 66}; const TilePosition mPlayerTile {0, 0}; const std::string mWorldspace = "sys::default"; + const btBoxShape mBox {btVector3(100, 100, 20)}; Loading::Listener mListener; }; @@ -111,6 +129,7 @@ namespace { mRecastMeshManager.setWorldspace(mWorldspace); addHeightFieldPlane(mRecastMeshManager); + addObject(mBox, mRecastMeshManager); auto db = std::make_unique(":memory:"); NavMeshDb* const dbPtr = db.get(); AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db)); @@ -130,10 +149,11 @@ namespace EXPECT_EQ(tile->mVersion, mSettings.mNavMeshVersion); } - TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write) + TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_tiles) { mRecastMeshManager.setWorldspace(mWorldspace); addHeightFieldPlane(mRecastMeshManager); + addObject(mBox, mRecastMeshManager); auto db = std::make_unique(":memory:"); NavMeshDb* const dbPtr = db.get(); mSettings.mWriteToNavMeshDb = false; @@ -152,6 +172,27 @@ namespace ASSERT_FALSE(tile.has_value()); } + TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_when_writing_to_db_disabled_should_not_write_shapes) + { + mRecastMeshManager.setWorldspace(mWorldspace); + addHeightFieldPlane(mRecastMeshManager); + addObject(mBox, mRecastMeshManager); + auto db = std::make_unique(":memory:"); + NavMeshDb* const dbPtr = db.get(); + mSettings.mWriteToNavMeshDb = false; + AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db)); + const auto navMeshCacheItem = std::make_shared(makeEmptyNavMesh(mSettings), 1); + const TilePosition tilePosition {0, 0}; + const std::map changedTiles {{tilePosition, ChangeType::add}}; + updater.post(mAgentHalfExtents, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); + updater.wait(mListener, WaitConditionType::allJobsDone); + const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); + ASSERT_NE(recastMesh, nullptr); + const auto objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(), + [&] (const MeshSource& v) { return resolveMeshSource(*dbPtr, v); }); + EXPECT_FALSE(objects.has_value()); + } + TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, post_should_read_from_db_on_cache_miss) { mRecastMeshManager.setWorldspace(mWorldspace); diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 47d984030f..657befd9d7 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -89,7 +89,8 @@ namespace DetourNavigator { if (db == nullptr) return nullptr; - return std::make_unique(updater, std::move(db), TileVersion(settings.mNavMeshVersion), settings.mRecast); + return std::make_unique(updater, std::move(db), TileVersion(settings.mNavMeshVersion), + settings.mRecast, settings.mWriteToNavMeshDb); } void updateJobs(std::deque& jobs, TilePosition playerTile, int maxTiles) @@ -704,11 +705,12 @@ namespace DetourNavigator } DbWorker::DbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr&& db, - TileVersion version, const RecastSettings& recastSettings) + TileVersion version, const RecastSettings& recastSettings, bool writeToDb) : mUpdater(updater) , mRecastSettings(recastSettings) , mDb(std::move(db)) , mVersion(version) + , mWriteToDb(writeToDb) , mNextTileId(mDb->getMaxTileId() + 1) , mNextShapeId(mDb->getMaxShapeId() + 1) , mThread([this] { run(); }) @@ -799,12 +801,23 @@ namespace DetourNavigator if (job->mInput.empty()) { Log(Debug::Debug) << "Serializing input for job " << job->mId; - const ShapeId shapeId = mNextShapeId; - const std::vector objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(), - [&] (const MeshSource& v) { return resolveMeshSource(*mDb, v, mNextShapeId); }); - if (shapeId != mNextShapeId) - ++mWrites; - job->mInput = serialize(mRecastSettings, *job->mRecastMesh, objects); + if (mWriteToDb) + { + const ShapeId shapeId = mNextShapeId; + const auto objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(), + [&] (const MeshSource& v) { return resolveMeshSource(*mDb, v, mNextShapeId); }); + if (shapeId != mNextShapeId) + ++mWrites; + job->mInput = serialize(mRecastSettings, *job->mRecastMesh, objects); + } + else + { + const auto objects = makeDbRefGeometryObjects(job->mRecastMesh->getMeshSources(), + [&] (const MeshSource& v) { return resolveMeshSource(*mDb, v); }); + if (!objects.has_value()) + return; + job->mInput = serialize(mRecastSettings, *job->mRecastMesh, *objects); + } } job->mCachedTileData = mDb->getTileData(job->mWorldspace, job->mChangedTile, job->mInput); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 541d86fe9c..31778f8c26 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -136,7 +136,7 @@ namespace DetourNavigator }; DbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr&& db, - TileVersion version, const RecastSettings& recastSettings); + TileVersion version, const RecastSettings& recastSettings, bool writeToDb); ~DbWorker(); @@ -153,6 +153,7 @@ namespace DetourNavigator const RecastSettings& mRecastSettings; const std::unique_ptr mDb; const TileVersion mVersion; + const bool mWriteToDb; TileId mNextTileId; ShapeId mNextShapeId; DbJobQueue mQueue; diff --git a/components/detournavigator/dbrefgeometryobject.hpp b/components/detournavigator/dbrefgeometryobject.hpp index 2be44ca175..acf2a58b19 100644 --- a/components/detournavigator/dbrefgeometryobject.hpp +++ b/components/detournavigator/dbrefgeometryobject.hpp @@ -4,10 +4,14 @@ #include "objecttransform.hpp" #include "recastmesh.hpp" +#include + #include #include #include #include +#include +#include namespace DetourNavigator { @@ -28,16 +32,27 @@ namespace DetourNavigator }; template - inline std::vector makeDbRefGeometryObjects(const std::vector& meshSources, - ResolveMeshSource&& resolveMeshSource) + inline auto makeDbRefGeometryObjects(const std::vector& meshSources, ResolveMeshSource&& resolveMeshSource) + -> std::conditional_t< + Misc::isOptional>, + std::optional>, + std::vector + > { std::vector result; result.reserve(meshSources.size()); - std::transform(meshSources.begin(), meshSources.end(), std::back_inserter(result), - [&] (const MeshSource& meshSource) + for (const MeshSource& meshSource : meshSources) + { + const auto shapeId = resolveMeshSource(meshSource); + if constexpr (Misc::isOptional>) { - return DbRefGeometryObject {resolveMeshSource(meshSource), meshSource.mObjectTransform}; - }); + if (!shapeId.has_value()) + return std::nullopt; + result.push_back(DbRefGeometryObject {*shapeId, meshSource.mObjectTransform}); + } + else + result.push_back(DbRefGeometryObject {shapeId, meshSource.mObjectTransform}); + } std::sort(result.begin(), result.end()); return result; } diff --git a/components/detournavigator/navmeshdbutils.cpp b/components/detournavigator/navmeshdbutils.cpp index dce6ef3a72..86f81bfc51 100644 --- a/components/detournavigator/navmeshdbutils.cpp +++ b/components/detournavigator/navmeshdbutils.cpp @@ -5,12 +5,21 @@ #include #include +#include namespace DetourNavigator { namespace { - ShapeId getShapeId(NavMeshDb& db, const std::string& name, ShapeType type, const std::string& hash, ShapeId& nextShapeId) + std::optional findShapeId(NavMeshDb& db, const std::string& name, ShapeType type, + const std::string& hash) + { + const Sqlite3::ConstBlob hashData {hash.data(), static_cast(hash.size())}; + return db.findShapeId(name, type, hashData); + } + + ShapeId getShapeId(NavMeshDb& db, const std::string& name, ShapeType type, + const std::string& hash, ShapeId& nextShapeId) { const Sqlite3::ConstBlob hashData {hash.data(), static_cast(hash.size())}; if (const auto existingShapeId = db.findShapeId(name, type, hashData)) @@ -37,4 +46,18 @@ namespace DetourNavigator return ShapeId(0); } } + + std::optional resolveMeshSource(NavMeshDb& db, const MeshSource& source) + { + switch (source.mAreaType) + { + case AreaType_null: + return findShapeId(db, source.mShape->mFileName, ShapeType::Avoid, source.mShape->mFileHash); + case AreaType_ground: + return findShapeId(db, source.mShape->mFileName, ShapeType::Collision, source.mShape->mFileHash); + default: + Log(Debug::Warning) << "Trying to resolve recast mesh source with unsupported area type: " << source.mAreaType; + return std::nullopt; + } + } } diff --git a/components/detournavigator/navmeshdbutils.hpp b/components/detournavigator/navmeshdbutils.hpp index 02b3bdbb00..aafde3307c 100644 --- a/components/detournavigator/navmeshdbutils.hpp +++ b/components/detournavigator/navmeshdbutils.hpp @@ -3,11 +3,15 @@ #include "navmeshdb.hpp" +#include + namespace DetourNavigator { struct MeshSource; ShapeId resolveMeshSource(NavMeshDb& db, const MeshSource& source, ShapeId& nextShapeId); + + std::optional resolveMeshSource(NavMeshDb& db, const MeshSource& source); } #endif diff --git a/components/misc/typetraits.hpp b/components/misc/typetraits.hpp new file mode 100644 index 0000000000..4c6a7e731b --- /dev/null +++ b/components/misc/typetraits.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_COMPONENTS_MISC_TYPETRAITS_H +#define OPENMW_COMPONENTS_MISC_TYPETRAITS_H + +#include +#include + +namespace Misc +{ + template + struct IsOptional : std::false_type {}; + + template + struct IsOptional> : std::true_type {}; + + template + inline constexpr bool isOptional = IsOptional::value; +} + +#endif