diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fd6f4ff6f..733b961a7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -230,6 +230,7 @@ namespace MWWorld if (Settings::Manager::getBool("enable log", "Navigator")) DetourNavigator::Log::instance().setSink(std::unique_ptr( new DetourNavigator::FileSink(Settings::Manager::getString("log path", "Navigator")))); + DetourNavigator::RecastGlobalAllocator::init(); mNavigator.reset(new DetourNavigator::Navigator(navigatorSettings)); mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath, *mNavigator)); diff --git a/components/detournavigator/chunkytrimesh.hpp b/components/detournavigator/chunkytrimesh.hpp index c9d35f086..9f6275ec8 100644 --- a/components/detournavigator/chunkytrimesh.hpp +++ b/components/detournavigator/chunkytrimesh.hpp @@ -50,8 +50,8 @@ namespace DetourNavigator ChunkyTriMesh& operator=(const ChunkyTriMesh&) = delete; /// Returns the chunk indices which overlap the input rectable. - template - void getChunksOverlappingRect(const Rect& rect, OutputIterator out) const + template + void forEachChunksOverlappingRect(const Rect& rect, Function&& function) const { // Traverse tree for (std::size_t i = 0; i < mNodes.size(); ) @@ -61,7 +61,7 @@ namespace DetourNavigator const bool isLeafNode = node->mOffset >= 0; if (isLeafNode && overlap) - *out++ = i; + function(i); if (overlap || isLeafNode) i++; diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 7072f2a23..3ee730386 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -149,62 +149,61 @@ namespace std::vector areas(chunkyMesh.getMaxTrisPerChunk(), AreaType_null); const osg::Vec2f tileBoundsMin(config.bmin[0], config.bmin[2]); const osg::Vec2f tileBoundsMax(config.bmax[0], config.bmax[2]); - std::vector cids; - chunkyMesh.getChunksOverlappingRect(Rect {tileBoundsMin, tileBoundsMax}, std::back_inserter(cids)); + bool result = false; - if (cids.empty()) - return false; + chunkyMesh.forEachChunksOverlappingRect(Rect {tileBoundsMin, tileBoundsMax}, + [&] (const std::size_t cid) + { + const auto chunk = chunkyMesh.getChunk(cid); - for (const auto cid : cids) - { - const auto chunk = chunkyMesh.getChunk(cid); + std::fill( + areas.begin(), + std::min(areas.begin() + static_cast(chunk.mSize), + areas.end()), + AreaType_null + ); - std::fill( - areas.begin(), - std::min(areas.begin() + static_cast(chunk.mSize), - areas.end()), - AreaType_null - ); + rcMarkWalkableTriangles( + &context, + config.walkableSlopeAngle, + recastMesh.getVertices().data(), + static_cast(recastMesh.getVerticesCount()), + chunk.mIndices, + static_cast(chunk.mSize), + areas.data() + ); - rcMarkWalkableTriangles( - &context, - config.walkableSlopeAngle, - recastMesh.getVertices().data(), - static_cast(recastMesh.getVerticesCount()), - chunk.mIndices, - static_cast(chunk.mSize), - areas.data() - ); + for (std::size_t i = 0; i < chunk.mSize; ++i) + areas[i] = chunk.mAreaTypes[i]; - for (std::size_t i = 0; i < chunk.mSize; ++i) - areas[i] = chunk.mAreaTypes[i]; + rcClearUnwalkableTriangles( + &context, + config.walkableSlopeAngle, + recastMesh.getVertices().data(), + static_cast(recastMesh.getVerticesCount()), + chunk.mIndices, + static_cast(chunk.mSize), + areas.data() + ); - rcClearUnwalkableTriangles( - &context, - config.walkableSlopeAngle, - recastMesh.getVertices().data(), - static_cast(recastMesh.getVerticesCount()), - chunk.mIndices, - static_cast(chunk.mSize), - areas.data() - ); + const auto trianglesRasterized = rcRasterizeTriangles( + &context, + recastMesh.getVertices().data(), + static_cast(recastMesh.getVerticesCount()), + chunk.mIndices, + areas.data(), + static_cast(chunk.mSize), + solid, + config.walkableClimb + ); - const auto trianglesRasterized = rcRasterizeTriangles( - &context, - recastMesh.getVertices().data(), - static_cast(recastMesh.getVerticesCount()), - chunk.mIndices, - areas.data(), - static_cast(chunk.mSize), - solid, - config.walkableClimb - ); + if (!trianglesRasterized) + throw NavigatorException("Failed to create rasterize triangles from recast mesh for navmesh"); - if (!trianglesRasterized) - throw NavigatorException("Failed to create rasterize triangles from recast mesh for navmesh"); - } + result = true; + }); - return true; + return result; } void rasterizeWaterTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, diff --git a/components/detournavigator/recastallocutils.hpp b/components/detournavigator/recastallocutils.hpp new file mode 100644 index 000000000..7b083d139 --- /dev/null +++ b/components/detournavigator/recastallocutils.hpp @@ -0,0 +1,102 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTALLOCUTILS_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTALLOCUTILS_H + +#include + +#include + +namespace DetourNavigator +{ + static_assert(sizeof(std::size_t) == sizeof(void*), ""); + + enum BufferType : std::size_t + { + BufferType_perm, + BufferType_temp, + BufferType_unused, + }; + + inline BufferType* tempPtrBufferType(void* ptr) + { + return reinterpret_cast(static_cast(ptr) + 1); + } + + inline BufferType getTempPtrBufferType(void* ptr) + { + return *tempPtrBufferType(ptr); + } + + inline void setTempPtrBufferType(void* ptr, BufferType value) + { + *tempPtrBufferType(ptr) = value; + } + + inline void** tempPtrPrev(void* ptr) + { + return static_cast(ptr); + } + + inline void* getTempPtrPrev(void* ptr) + { + return *tempPtrPrev(ptr); + } + + inline void setTempPtrPrev(void* ptr, void* value) + { + *tempPtrPrev(ptr) = value; + } + + inline void* getTempPtrDataPtr(void* ptr) + { + return reinterpret_cast(static_cast(ptr) + 2); + } + + inline BufferType* dataPtrBufferType(void* dataPtr) + { + return reinterpret_cast(static_cast(dataPtr) - 1); + } + + inline BufferType getDataPtrBufferType(void* dataPtr) + { + return *dataPtrBufferType(dataPtr); + } + + inline void setDataPtrBufferType(void* dataPtr, BufferType value) + { + *dataPtrBufferType(dataPtr) = value; + } + + inline void* getTempDataPtrStackPtr(void* dataPtr) + { + return static_cast(dataPtr) - 2; + } + + inline void* getPermDataPtrHeapPtr(void* dataPtr) + { + return static_cast(dataPtr) - 1; + } + + inline void setPermPtrBufferType(void* ptr, BufferType value) + { + *static_cast(ptr) = value; + } + + inline void* getPermPtrDataPtr(void* ptr) + { + return static_cast(ptr) + 1; + } + + // TODO: use std::align + inline void* align(std::size_t align, std::size_t size, void*& ptr, std::size_t& space) noexcept + { + const auto intptr = reinterpret_cast(ptr); + const auto aligned = (intptr - 1u + align) & - align; + const auto diff = aligned - intptr; + if ((size + diff) > space) + return nullptr; + space -= diff; + return ptr = reinterpret_cast(aligned); + } +} + +#endif diff --git a/components/detournavigator/recastglobalallocator.hpp b/components/detournavigator/recastglobalallocator.hpp new file mode 100644 index 000000000..7c4b2c534 --- /dev/null +++ b/components/detournavigator/recastglobalallocator.hpp @@ -0,0 +1,68 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTGLOBALALLOCATOR_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTGLOBALALLOCATOR_H + +#include "recasttempallocator.hpp" + +namespace DetourNavigator +{ + class RecastGlobalAllocator + { + public: + static void init() + { + instance(); + } + + static void* alloc(size_t size, rcAllocHint hint) + { + void* result = nullptr; + if (rcLikely(hint == RC_ALLOC_TEMP)) + result = tempAllocator().alloc(size); + if (rcUnlikely(!result)) + result = allocPerm(size); + return result; + } + + static void free(void* ptr) + { + if (rcUnlikely(!ptr)) + return; + if (rcLikely(BufferType_temp == getDataPtrBufferType(ptr))) + tempAllocator().free(ptr); + else + { + assert(BufferType_perm == getDataPtrBufferType(ptr)); + ::free(getPermDataPtrHeapPtr(ptr)); + } + } + + private: + RecastGlobalAllocator() + { + rcAllocSetCustom(&RecastGlobalAllocator::alloc, &RecastGlobalAllocator::free); + } + + static RecastGlobalAllocator& instance() + { + static RecastGlobalAllocator value; + return value; + } + + static RecastTempAllocator& tempAllocator() + { + static thread_local RecastTempAllocator value(1024ul * 1024ul); + return value; + } + + static void* allocPerm(size_t size) + { + const auto ptr = ::malloc(size + sizeof(std::size_t)); + if (rcUnlikely(!ptr)) + return ptr; + setPermPtrBufferType(ptr, BufferType_perm); + return getPermPtrDataPtr(ptr); + } + }; +} + +#endif diff --git a/components/detournavigator/recasttempallocator.hpp b/components/detournavigator/recasttempallocator.hpp new file mode 100644 index 000000000..e369b4224 --- /dev/null +++ b/components/detournavigator/recasttempallocator.hpp @@ -0,0 +1,65 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTTEMPALLOCATOR_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTTEMPALLOCATOR_H + +#include "recastallocutils.hpp" + +#include +#include +#include + +namespace DetourNavigator +{ + class RecastTempAllocator + { + public: + RecastTempAllocator(std::size_t capacity) + : mStack(capacity), mTop(mStack.data()), mPrev(nullptr) + {} + + void* alloc(std::size_t size) + { + std::size_t space = mStack.size() - getUsedSize(); + void* top = mTop; + const auto itemSize = 2 * sizeof(std::size_t) + size; + if (rcUnlikely(!align(sizeof(std::size_t), itemSize, top, space))) + return nullptr; + setTempPtrBufferType(top, BufferType_temp); + setTempPtrPrev(top, mPrev); + mTop = static_cast(top) + itemSize; + mPrev = static_cast(top); + return getTempPtrDataPtr(top); + } + + void free(void* ptr) + { + if (rcUnlikely(!ptr)) + return; + assert(BufferType_temp == getDataPtrBufferType(ptr)); + if (!mPrev || getTempDataPtrStackPtr(ptr) != mPrev) + { + setDataPtrBufferType(ptr, BufferType_unused); + return; + } + mTop = getTempDataPtrStackPtr(ptr); + mPrev = getTempPtrPrev(mTop); + while (mPrev && BufferType_unused == getTempPtrBufferType(mPrev)) + { + mTop = mPrev; + mPrev = getTempPtrPrev(mTop); + } + return; + } + + private: + std::vector mStack; + void* mTop; + void* mPrev; + + std::size_t getUsedSize() const + { + return static_cast(static_cast(mTop) - mStack.data()); + } + }; +} + +#endif