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