From 62f32f454378bc2ba5c608f578701db9fd4f62e9 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 17 Apr 2021 18:48:11 +0200 Subject: [PATCH 1/4] Do not replace equivalent navmesh tiles --- components/CMakeLists.txt | 1 + .../detournavigator/navmeshcacheitem.hpp | 13 ++ .../detournavigator/navmeshtileview.cpp | 183 ++++++++++++++++++ .../detournavigator/navmeshtileview.hpp | 31 +++ 4 files changed, 228 insertions(+) create mode 100644 components/detournavigator/navmeshtileview.cpp create mode 100644 components/detournavigator/navmeshtileview.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index ad2783b57..970f029b4 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -182,6 +182,7 @@ add_component_dir(detournavigator navigator findrandompointaroundcircle raycast + navmeshtileview ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui diff --git a/components/detournavigator/navmeshcacheitem.hpp b/components/detournavigator/navmeshcacheitem.hpp index 76f74f266..a9a4ebee6 100644 --- a/components/detournavigator/navmeshcacheitem.hpp +++ b/components/detournavigator/navmeshcacheitem.hpp @@ -5,6 +5,7 @@ #include "tileposition.hpp" #include "navmeshtilescache.hpp" #include "dtstatus.hpp" +#include "navmeshtileview.hpp" #include @@ -141,6 +142,12 @@ namespace DetourNavigator template UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData) { + const dtMeshTile* currentTile = getTile(position); + if (currentTile != nullptr + && asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(getRawData(navMeshData))) + { + return UpdateNavMeshStatus::ignored; + } const auto removed = removeTileImpl(position); const auto addStatus = addTileImpl(getRawData(navMeshData), getSize(navMeshData)); if (dtStatusSucceed(addStatus)) @@ -206,6 +213,12 @@ namespace DetourNavigator int* const dataSize = nullptr; return dtStatusSucceed(mImpl->removeTile(tileRef, data, dataSize)); } + + const dtMeshTile* getTile(const TilePosition& position) const + { + const int layer = 0; + return mImpl->getTileAt(position.x(), position.y(), layer); + } }; using GuardedNavMeshCacheItem = Misc::ScopeGuarded; diff --git a/components/detournavigator/navmeshtileview.cpp b/components/detournavigator/navmeshtileview.cpp new file mode 100644 index 000000000..96f07c6b1 --- /dev/null +++ b/components/detournavigator/navmeshtileview.cpp @@ -0,0 +1,183 @@ +#include "navmeshtileview.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace +{ + template + struct Ref + { + T& mRef; + + explicit Ref(T& ref) : mRef(ref) {} + + friend bool operator==(const Ref& lhs, const Ref& rhs) + { + return lhs.mRef == rhs.mRef; + } + }; + + template + struct ArrayRef + { + T (&mRef)[size]; + + explicit ArrayRef(T (&ref)[size]) : mRef(ref) {} + + friend bool operator==(const ArrayRef& lhs, const ArrayRef& rhs) + { + return std::equal(std::begin(lhs.mRef), std::end(lhs.mRef), std::begin(rhs.mRef)); + } + }; + + template + struct Span + { + T* mBegin; + T* mEnd; + + explicit Span(T* data, int size) : mBegin(data), mEnd(data + static_cast(size)) {} + + friend bool operator==(const Span& lhs, const Span& rhs) + { + // size is already equal if headers are equal + assert((lhs.mEnd - lhs.mBegin) == (rhs.mEnd - rhs.mBegin)); + return std::equal(lhs.mBegin, lhs.mEnd, rhs.mBegin); + } + }; + + auto makeTuple(const dtMeshHeader& v) + { + return std::tuple( + v.x, + v.y, + v.layer, + v.userId, + v.polyCount, + v.vertCount, + v.maxLinkCount, + v.detailMeshCount, + v.detailVertCount, + v.detailTriCount, + v.bvNodeCount, + v.offMeshConCount, + v.offMeshBase, + v.walkableHeight, + v.walkableRadius, + v.walkableClimb, + v.detailVertCount, + ArrayRef(v.bmin), + ArrayRef(v.bmax), + v.bvQuantFactor + ); + } + + auto makeTuple(const dtPoly& v) + { + return std::tuple(ArrayRef(v.verts), ArrayRef(v.neis), v.flags, v.vertCount, v.areaAndtype); + } + + auto makeTuple(const dtPolyDetail& v) + { + return std::tuple(v.vertBase, v.triBase, v.vertCount, v.triCount); + } + + auto makeTuple(const dtBVNode& v) + { + return std::tuple(ArrayRef(v.bmin), ArrayRef(v.bmax), v.i); + } + + auto makeTuple(const dtOffMeshConnection& v) + { + return std::tuple(ArrayRef(v.pos), v.rad, v.poly, v.flags, v.side, v.userId); + } + + auto makeTuple(const DetourNavigator::NavMeshTileConstView& v) + { + return std::tuple( + Ref(*v.mHeader), + Span(v.mPolys, v.mHeader->polyCount), + Span(v.mVerts, v.mHeader->vertCount), + Span(v.mDetailMeshes, v.mHeader->detailMeshCount), + Span(v.mDetailVerts, v.mHeader->detailVertCount), + Span(v.mDetailTris, v.mHeader->detailTriCount), + Span(v.mBvTree, v.mHeader->bvNodeCount), + Span(v.mOffMeshCons, v.mHeader->offMeshConCount) + ); + } +} + +template +inline auto operator==(const T& lhs, const T& rhs) + -> std::enable_if_t, void>, bool> +{ + return makeTuple(lhs) == makeTuple(rhs); +} + +namespace DetourNavigator +{ + NavMeshTileConstView asNavMeshTileConstView(const unsigned char* data) + { + const dtMeshHeader* header = reinterpret_cast(data); + + if (header->magic != DT_NAVMESH_MAGIC) + throw std::logic_error("Invalid navmesh magic"); + + if (header->version != DT_NAVMESH_VERSION) + throw std::logic_error("Invalid navmesh version"); + + // Similar code to https://github.com/recastnavigation/recastnavigation/blob/c5cbd53024c8a9d8d097a4371215e3342d2fdc87/Detour/Source/DetourNavMesh.cpp#L978-L996 + const int headerSize = dtAlign4(sizeof(dtMeshHeader)); + const int vertsSize = dtAlign4(sizeof(float) * 3 * header->vertCount); + const int polysSize = dtAlign4(sizeof(dtPoly) * header->polyCount); + const int linksSize = dtAlign4(sizeof(dtLink) * (header->maxLinkCount)); + const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail) * header->detailMeshCount); + const int detailVertsSize = dtAlign4(sizeof(float) * 3 * header->detailVertCount); + const int detailTrisSize = dtAlign4(sizeof(unsigned char) * 4 * header->detailTriCount); + const int bvtreeSize = dtAlign4(sizeof(dtBVNode) * header->bvNodeCount); + const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->offMeshConCount); + + const unsigned char* ptr = data + headerSize; + + NavMeshTileConstView view; + + view.mHeader = header; + view.mVerts = dtGetThenAdvanceBufferPointer(ptr, vertsSize); + view.mPolys = dtGetThenAdvanceBufferPointer(ptr, polysSize); + ptr += linksSize; + view.mDetailMeshes = dtGetThenAdvanceBufferPointer(ptr, detailMeshesSize); + view.mDetailVerts = dtGetThenAdvanceBufferPointer(ptr, detailVertsSize); + view.mDetailTris = dtGetThenAdvanceBufferPointer(ptr, detailTrisSize); + view.mBvTree = dtGetThenAdvanceBufferPointer(ptr, bvtreeSize); + view.mOffMeshCons = dtGetThenAdvanceBufferPointer(ptr, offMeshLinksSize); + + return view; + } + + NavMeshTileConstView asNavMeshTileConstView(const dtMeshTile& tile) + { + NavMeshTileConstView view; + + view.mHeader = tile.header; + view.mPolys = tile.polys; + view.mVerts = tile.verts; + view.mDetailMeshes = tile.detailMeshes; + view.mDetailVerts = tile.detailVerts; + view.mDetailTris = tile.detailTris; + view.mBvTree = tile.bvTree; + view.mOffMeshCons = tile.offMeshCons; + + return view; + } + + bool operator==(const NavMeshTileConstView& lhs, const NavMeshTileConstView& rhs) + { + return makeTuple(lhs) == makeTuple(rhs); + } +} diff --git a/components/detournavigator/navmeshtileview.hpp b/components/detournavigator/navmeshtileview.hpp new file mode 100644 index 000000000..92017360c --- /dev/null +++ b/components/detournavigator/navmeshtileview.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILEVIEW_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILEVIEW_H + +struct dtMeshHeader; +struct dtPoly; +struct dtPolyDetail; +struct dtBVNode; +struct dtOffMeshConnection; +struct dtMeshTile; + +namespace DetourNavigator +{ + struct NavMeshTileConstView + { + const dtMeshHeader* mHeader; + const dtPoly* mPolys; + const float* mVerts; + const dtPolyDetail* mDetailMeshes; + const float* mDetailVerts; + const unsigned char* mDetailTris; + const dtBVNode* mBvTree; + const dtOffMeshConnection* mOffMeshCons; + + friend bool operator==(const NavMeshTileConstView& lhs, const NavMeshTileConstView& rhs); + }; + + NavMeshTileConstView asNavMeshTileConstView(const unsigned char* data); + NavMeshTileConstView asNavMeshTileConstView(const dtMeshTile& tile); +} + +#endif From 629cedb6d03a8cd317aa227915a4917f60cf8de2 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Apr 2021 17:01:09 +0200 Subject: [PATCH 2/4] Do not track last build revision in RecastMeshManger mLastBuildRevision == mRevision with current use cases does not happen. But even if this will happen when another use case will be added it does not save much computation. The most expensive operation is not adding objects to the MeshBuilder but to optimize resulting navmesh that will happen anyway in MeshBuilder::create call. --- components/detournavigator/recastmeshmanager.cpp | 5 +---- components/detournavigator/recastmeshmanager.hpp | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 3796c9816..0b0298bc0 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -74,7 +74,7 @@ namespace DetourNavigator std::shared_ptr RecastMeshManager::getMesh() { rebuild(); - return mMeshBuilder.create(mGeneration, mLastBuildRevision); + return mMeshBuilder.create(mGeneration, mRevision); } bool RecastMeshManager::isEmpty() const @@ -84,13 +84,10 @@ namespace DetourNavigator void RecastMeshManager::rebuild() { - if (mLastBuildRevision == mRevision) - return; mMeshBuilder.reset(); for (const auto& v : mWaterOrder) mMeshBuilder.addWater(v.mCellSize, v.mTransform); for (const auto& v : mObjectsOrder) mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType()); - mLastBuildRevision = mRevision; } } diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index 5b568e004..0ad111b5d 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -52,7 +52,6 @@ namespace DetourNavigator private: std::size_t mRevision = 0; - std::size_t mLastBuildRevision = 0; std::size_t mGeneration; RecastMeshBuilder mMeshBuilder; std::list mObjectsOrder; From 64fb700ae91ec98ee39a47dca4ecf3d9235190a3 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Apr 2021 17:31:21 +0200 Subject: [PATCH 3/4] Remove unnecessary relation between updateCompoundObject and RecastMeshObject --- .../detournavigator/recastmeshobject.cpp | 29 ++++++++++--------- .../detournavigator/recastmeshobject.hpp | 3 -- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/components/detournavigator/recastmeshobject.cpp b/components/detournavigator/recastmeshobject.cpp index aac0b4c3c..3f3546278 100644 --- a/components/detournavigator/recastmeshobject.cpp +++ b/components/detournavigator/recastmeshobject.cpp @@ -8,6 +8,22 @@ namespace DetourNavigator { + namespace + { + bool updateCompoundObject(const btCompoundShape& shape, const AreaType areaType, + std::vector& children) + { + assert(static_cast(shape.getNumChildShapes()) == children.size()); + bool result = false; + for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i) + { + assert(shape.getChildShape(i) == std::addressof(children[static_cast(i)].getShape())); + result = children[static_cast(i)].update(shape.getChildTransform(i), areaType) || result; + } + return result; + } + } + RecastMeshObject::RecastMeshObject(const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) : mShape(shape) @@ -42,19 +58,6 @@ namespace DetourNavigator return result; } - bool RecastMeshObject::updateCompoundObject(const btCompoundShape& shape, - const AreaType areaType, std::vector& children) - { - assert(static_cast(shape.getNumChildShapes()) == children.size()); - bool result = false; - for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i) - { - assert(shape.getChildShape(i) == std::addressof(children[static_cast(i)].mShape.get())); - result = children[static_cast(i)].update(shape.getChildTransform(i), areaType) || result; - } - return result; - } - std::vector makeChildrenObjects(const btCollisionShape& shape, const AreaType areaType) { if (shape.isCompound()) diff --git a/components/detournavigator/recastmeshobject.hpp b/components/detournavigator/recastmeshobject.hpp index f25647ae5..e659300e6 100644 --- a/components/detournavigator/recastmeshobject.hpp +++ b/components/detournavigator/recastmeshobject.hpp @@ -41,9 +41,6 @@ namespace DetourNavigator AreaType mAreaType; btVector3 mLocalScaling; std::vector mChildren; - - static bool updateCompoundObject(const btCompoundShape& shape, const AreaType areaType, - std::vector& children); }; std::vector makeChildrenObjects(const btCollisionShape& shape, const AreaType areaType); From 3e67f5ffa57eca6e42a480fec0ad2756414891c4 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Apr 2021 16:51:52 +0200 Subject: [PATCH 4/4] Detect and ignore updates for oscillating objects To avoid triggering NavMesh update when RecastMesh change should not change NavMesh. Based on the following assumption: Given a set of transformations and a bounding shape for all these tranformations, a new object transformation that does not change this bounding shape also should not change navmesh if for all of this object transformations resulting navmesh tiles are equivalent The idea is to report back to RecastMeshManager all changes of NavMesh if there are any assiciated with RecastMesh version. So we know the last time when RecastMesh change resulted into the NavMesh change. When later report shows that there was no NavMesh change for a new RecastMesh version we can assume that any object transformation within the same bounding box should not change NavMesh. --- CHANGELOG.md | 1 + .../detournavigator/navigator.cpp | 51 +++++++++++++++++++ components/CMakeLists.txt | 1 + components/bullethelpers/aabb.hpp | 29 +++++++++++ .../detournavigator/asyncnavmeshupdater.cpp | 14 +++++ .../cachedrecastmeshmanager.cpp | 5 ++ .../cachedrecastmeshmanager.hpp | 3 ++ .../oscillatingrecastmeshobject.cpp | 35 +++++++++++++ .../oscillatingrecastmeshobject.hpp | 28 ++++++++++ .../detournavigator/recastmeshmanager.cpp | 26 ++++++++-- .../detournavigator/recastmeshmanager.hpp | 17 +++++-- .../tilecachedrecastmeshmanager.cpp | 9 ++++ .../tilecachedrecastmeshmanager.hpp | 3 ++ components/detournavigator/version.hpp | 21 ++++++++ 14 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 components/bullethelpers/aabb.hpp create mode 100644 components/detournavigator/oscillatingrecastmeshobject.cpp create mode 100644 components/detournavigator/oscillatingrecastmeshobject.hpp create mode 100644 components/detournavigator/version.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index d44007385..5b89eea03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,6 +127,7 @@ Feature #3983: Wizard: Add link to buy Morrowind Feature #4894: Consider actors as obstacles for pathfinding Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing + Feature #4917: Do not trigger NavMesh update when RecastMesh update should not change NavMesh Feature #4977: Use the "default icon.tga" when an item's icon is not found Feature #5043: Head Bobbing Feature #5199: OpenMW-CS: Improve scene view colors diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index d377aed1e..3b7dc4915 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -835,4 +835,55 @@ namespace ASSERT_THAT(result, Optional(Vec3fEq(mEnd.x(), mEnd.y(), 1.87719))); } + + TEST_F(DetourNavigatorNavigatorTest, update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update) + { + const std::array heightfieldData {{ + 0, 0, 0, 0, 0, + 0, -25, -25, -25, -25, + 0, -25, -100, -100, -100, + 0, -25, -100, -100, -100, + 0, -25, -100, -100, -100, + }}; + btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); + heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); + + const btBoxShape oscillatingBoxShape(btVector3(20, 20, 20)); + const btVector3 oscillatingBoxShapePosition(32, 32, 400); + const btBoxShape boderBoxShape(btVector3(50, 50, 50)); + + mNavigator->addAgent(mAgentHalfExtents); + mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); + mNavigator->addObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, + btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition)); + // add this box to make navmesh bound box independent from oscillatingBoxShape rotations + mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape, + btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + const auto navMeshes = mNavigator->getNavMeshes(); + ASSERT_EQ(navMeshes.size(), 1); + { + const auto navMesh = navMeshes.begin()->second->lockConst(); + ASSERT_EQ(navMesh->getGeneration(), 1); + ASSERT_EQ(navMesh->getNavMeshRevision(), 4); + } + + for (int n = 0; n < 10; ++n) + { + const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10), + oscillatingBoxShapePosition); + mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform); + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + } + + ASSERT_EQ(navMeshes.size(), 1); + { + const auto navMesh = navMeshes.begin()->second->lockConst(); + ASSERT_EQ(navMesh->getGeneration(), 1); + ASSERT_EQ(navMesh->getNavMeshRevision(), 4); + } + } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 970f029b4..2fe09070a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -183,6 +183,7 @@ add_component_dir(detournavigator findrandompointaroundcircle raycast navmeshtileview + oscillatingrecastmeshobject ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui diff --git a/components/bullethelpers/aabb.hpp b/components/bullethelpers/aabb.hpp new file mode 100644 index 000000000..6509b946c --- /dev/null +++ b/components/bullethelpers/aabb.hpp @@ -0,0 +1,29 @@ +#ifndef OPENMW_COMPONENTS_BULLETHELPERS_AABB_H +#define OPENMW_COMPONENTS_BULLETHELPERS_AABB_H + +#include +#include +#include +#include + +inline bool operator==(const btAABB& lhs, const btAABB& rhs) +{ + return lhs.m_min == rhs.m_min && lhs.m_max == rhs.m_max; +} + +inline bool operator!=(const btAABB& lhs, const btAABB& rhs) +{ + return !(lhs == rhs); +} + +namespace BulletHelpers +{ + inline btAABB getAabb(const btCollisionShape& shape, const btTransform& transform) + { + btAABB result; + shape.getAabb(transform, result.m_min, result.m_max); + return result; + } +} + +#endif diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index bb78df764..4855cf0ad 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -2,6 +2,7 @@ #include "debug.hpp" #include "makenavmesh.hpp" #include "settings.hpp" +#include "version.hpp" #include #include @@ -180,6 +181,19 @@ namespace DetourNavigator const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile, offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache); + if (recastMesh != nullptr) + { + Version navMeshVersion; + { + const auto locked = navMeshCacheItem->lockConst(); + navMeshVersion.mGeneration = locked->getGeneration(); + navMeshVersion.mRevision = locked->getNavMeshRevision(); + } + mRecastMeshManager.get().reportNavMeshChange(job.mChangedTile, + Version {recastMesh->getGeneration(), recastMesh->getRevision()}, + navMeshVersion); + } + const auto finish = std::chrono::steady_clock::now(); writeDebugFiles(job, recastMesh.get()); diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index 90b426610..22b047fbd 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -61,4 +61,9 @@ namespace DetourNavigator { return mImpl.isEmpty(); } + + void CachedRecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion) + { + mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion); + } } diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index 54574aa99..a19f017a4 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H #include "recastmeshmanager.hpp" +#include "version.hpp" namespace DetourNavigator { @@ -25,6 +26,8 @@ namespace DetourNavigator bool isEmpty() const; + void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion); + private: RecastMeshManager mImpl; std::shared_ptr mCached; diff --git a/components/detournavigator/oscillatingrecastmeshobject.cpp b/components/detournavigator/oscillatingrecastmeshobject.cpp new file mode 100644 index 000000000..dd0b95600 --- /dev/null +++ b/components/detournavigator/oscillatingrecastmeshobject.cpp @@ -0,0 +1,35 @@ +#include "oscillatingrecastmeshobject.hpp" + +#include + +namespace DetourNavigator +{ + OscillatingRecastMeshObject::OscillatingRecastMeshObject(RecastMeshObject impl, std::size_t lastChangeRevision) + : mImpl(std::move(impl)) + , mLastChangeRevision(lastChangeRevision) + , mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform())) + { + } + + bool OscillatingRecastMeshObject::update(const btTransform& transform, const AreaType areaType, + std::size_t lastChangeRevision) + { + 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)); + return currentAabb != mAabb; + } +} diff --git a/components/detournavigator/oscillatingrecastmeshobject.hpp b/components/detournavigator/oscillatingrecastmeshobject.hpp new file mode 100644 index 000000000..476fd4e0d --- /dev/null +++ b/components/detournavigator/oscillatingrecastmeshobject.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H + +#include "areatype.hpp" +#include "recastmeshobject.hpp" + +#include +#include + +namespace DetourNavigator +{ + class OscillatingRecastMeshObject + { + public: + explicit OscillatingRecastMeshObject(RecastMeshObject impl, std::size_t lastChangeRevision); + + bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision); + + const RecastMeshObject& getImpl() const { return mImpl; } + + private: + RecastMeshObject mImpl; + std::size_t mLastChangeRevision; + btAABB mAabb; + }; +} + +#endif diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 0b0298bc0..146038ae6 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -13,7 +13,8 @@ namespace DetourNavigator bool RecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) { - const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(), RecastMeshObject(shape, transform, areaType)); + const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(), + OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1)); if (!mObjects.emplace(id, iterator).second) { mObjectsOrder.erase(iterator); @@ -28,7 +29,9 @@ namespace DetourNavigator const auto object = mObjects.find(id); if (object == mObjects.end()) return false; - if (!object->second->update(transform, areaType)) + const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value() + ? mLastNavMeshReportedChange->mRevision : mRevision; + if (!object->second->update(transform, areaType, lastChangeRevision)) return false; ++mRevision; return true; @@ -39,7 +42,7 @@ namespace DetourNavigator const auto object = mObjects.find(id); if (object == mObjects.end()) return std::nullopt; - const RemovedRecastMeshObject result {object->second->getShape(), object->second->getTransform()}; + const RemovedRecastMeshObject result {object->second->getImpl().getShape(), object->second->getImpl().getTransform()}; mObjectsOrder.erase(object->second); mObjects.erase(object); ++mRevision; @@ -82,12 +85,27 @@ namespace DetourNavigator return mObjects.empty(); } + void RecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion) + { + if (recastMeshVersion.mGeneration != mGeneration) + return; + if (mLastNavMeshReport.has_value() && navMeshVersion < mLastNavMeshReport->mNavMeshVersion) + return; + mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion}; + if (!mLastNavMeshReportedChange.has_value() + || mLastNavMeshReportedChange->mNavMeshVersion < mLastNavMeshReport->mNavMeshVersion) + mLastNavMeshReportedChange = mLastNavMeshReport; + } + void RecastMeshManager::rebuild() { mMeshBuilder.reset(); for (const auto& v : mWaterOrder) mMeshBuilder.addWater(v.mCellSize, v.mTransform); - for (const auto& v : mObjectsOrder) + for (const auto& object : mObjectsOrder) + { + const RecastMeshObject& v = object.getImpl(); mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType()); + } } } diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index 0ad111b5d..daa123fcb 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -2,8 +2,9 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H #include "recastmeshbuilder.hpp" -#include "recastmeshobject.hpp" +#include "oscillatingrecastmeshobject.hpp" #include "objectid.hpp" +#include "version.hpp" #include @@ -50,14 +51,24 @@ namespace DetourNavigator bool isEmpty() const; + void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion); + private: + struct Report + { + std::size_t mRevision; + Version mNavMeshVersion; + }; + std::size_t mRevision = 0; std::size_t mGeneration; RecastMeshBuilder mMeshBuilder; - std::list mObjectsOrder; - std::unordered_map::iterator> mObjects; + std::list mObjectsOrder; + std::unordered_map::iterator> mObjects; std::list mWaterOrder; std::map::iterator> mWater; + std::optional mLastNavMeshReportedChange; + std::optional mLastNavMeshReport; void rebuild(); }; diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index fddaa88f1..20ebc8fea 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -145,6 +145,15 @@ namespace DetourNavigator return mRevision; } + void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) + { + const auto tiles = mTiles.lock(); + const auto it = tiles->find(tilePosition); + if (it == tiles->end()) + return; + it->second.reportNavMeshChange(recastMeshVersion, navMeshVersion); + } + bool TileCachedRecastMeshManager::addTile(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, float border, std::map& tiles) diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index fa192bd73..68683f410 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -5,6 +5,7 @@ #include "tileposition.hpp" #include "settingsutils.hpp" #include "gettilespositions.hpp" +#include "version.hpp" #include @@ -88,6 +89,8 @@ namespace DetourNavigator std::size_t getRevision() const; + void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion); + private: const Settings& mSettings; Misc::ScopeGuarded> mTiles; diff --git a/components/detournavigator/version.hpp b/components/detournavigator/version.hpp new file mode 100644 index 000000000..c9de98459 --- /dev/null +++ b/components/detournavigator/version.hpp @@ -0,0 +1,21 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_VERSION_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_VERSION_H + +#include +#include + +namespace DetourNavigator +{ + struct Version + { + std::size_t mGeneration; + std::size_t mRevision; + + friend inline bool operator<(const Version& lhs, const Version& rhs) + { + return std::tie(lhs.mGeneration, lhs.mRevision) < std::tie(rhs.mGeneration, rhs.mRevision); + } + }; +} + +#endif