mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-06 00:55:50 +00:00
ed73d130f9
Use LRU modification to hold currently used items. Use RecastMesh binary data for item key. Store original pointer of btCollisionShape in user pointer to make available it as an identifier within all duplicates. Use pointer to heights data array for btHeightfieldTerrainShape.
159 lines
5.9 KiB
C++
159 lines
5.9 KiB
C++
#include "navmeshtilescache.hpp"
|
|
#include "exceptions.hpp"
|
|
|
|
namespace DetourNavigator
|
|
{
|
|
namespace
|
|
{
|
|
inline std::string makeNavMeshKey(const RecastMesh& recastMesh,
|
|
const std::vector<OffMeshConnection>& offMeshConnections)
|
|
{
|
|
std::string result;
|
|
result.reserve(
|
|
recastMesh.getIndices().size() * sizeof(int)
|
|
+ recastMesh.getVertices().size() * sizeof(float)
|
|
+ recastMesh.getAreaTypes().size() * sizeof(AreaType)
|
|
+ recastMesh.getWater().size() * sizeof(RecastMesh::Water)
|
|
+ offMeshConnections.size() * sizeof(OffMeshConnection)
|
|
);
|
|
std::copy(
|
|
reinterpret_cast<const char*>(recastMesh.getIndices().data()),
|
|
reinterpret_cast<const char*>(recastMesh.getIndices().data() + recastMesh.getIndices().size()),
|
|
std::back_inserter(result)
|
|
);
|
|
std::copy(
|
|
reinterpret_cast<const char*>(recastMesh.getVertices().data()),
|
|
reinterpret_cast<const char*>(recastMesh.getVertices().data() + recastMesh.getVertices().size()),
|
|
std::back_inserter(result)
|
|
);
|
|
std::copy(
|
|
reinterpret_cast<const char*>(recastMesh.getAreaTypes().data()),
|
|
reinterpret_cast<const char*>(recastMesh.getAreaTypes().data() + recastMesh.getAreaTypes().size()),
|
|
std::back_inserter(result)
|
|
);
|
|
std::copy(
|
|
reinterpret_cast<const char*>(recastMesh.getWater().data()),
|
|
reinterpret_cast<const char*>(recastMesh.getWater().data() + recastMesh.getWater().size()),
|
|
std::back_inserter(result)
|
|
);
|
|
std::copy(
|
|
reinterpret_cast<const char*>(offMeshConnections.data()),
|
|
reinterpret_cast<const char*>(offMeshConnections.data() + offMeshConnections.size()),
|
|
std::back_inserter(result)
|
|
);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
NavMeshTilesCache::NavMeshTilesCache(const std::size_t maxNavMeshDataSize)
|
|
: mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0) {}
|
|
|
|
NavMeshTilesCache::Value NavMeshTilesCache::get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
|
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections)
|
|
{
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
const auto agentValues = mValues.find(agentHalfExtents);
|
|
if (agentValues == mValues.end())
|
|
return Value();
|
|
|
|
const auto tileValues = agentValues->second.find(changedTile);
|
|
if (tileValues == agentValues->second.end())
|
|
return Value();
|
|
|
|
const auto tile = tileValues->second.find(makeNavMeshKey(recastMesh, offMeshConnections));
|
|
if (tile == tileValues->second.end())
|
|
return Value();
|
|
|
|
acquireItemUnsafe(tile->second);
|
|
|
|
return Value(*this, tile->second);
|
|
}
|
|
|
|
NavMeshTilesCache::Value NavMeshTilesCache::set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
|
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections,
|
|
NavMeshData value)
|
|
{
|
|
const auto navMeshSize = static_cast<std::size_t>(value.mSize);
|
|
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
if (navMeshSize > mMaxNavMeshDataSize)
|
|
return Value();
|
|
|
|
if (navMeshSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))
|
|
return Value();
|
|
|
|
while (!mFreeItems.empty() && mUsedNavMeshDataSize + navMeshSize > mMaxNavMeshDataSize)
|
|
removeLeastRecentlyUsed();
|
|
|
|
const auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections);
|
|
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey);
|
|
const auto emplaced = mValues[agentHalfExtents][changedTile].emplace(navMeshKey, iterator);
|
|
|
|
if (!emplaced.second)
|
|
{
|
|
mFreeItems.erase(iterator);
|
|
throw InvalidArgument("Set existing cache value");
|
|
}
|
|
|
|
iterator->mNavMeshData = std::move(value);
|
|
mUsedNavMeshDataSize += navMeshSize;
|
|
mFreeNavMeshDataSize += navMeshSize;
|
|
|
|
acquireItemUnsafe(iterator);
|
|
|
|
return Value(*this, iterator);
|
|
}
|
|
|
|
void NavMeshTilesCache::removeLeastRecentlyUsed()
|
|
{
|
|
const auto& item = mFreeItems.back();
|
|
|
|
const auto agentValues = mValues.find(item.mAgentHalfExtents);
|
|
if (agentValues == mValues.end())
|
|
return;
|
|
|
|
const auto tileValues = agentValues->second.find(item.mChangedTile);
|
|
if (tileValues == agentValues->second.end())
|
|
return;
|
|
|
|
const auto value = tileValues->second.find(item.mNavMeshKey);
|
|
if (value == tileValues->second.end())
|
|
return;
|
|
|
|
mUsedNavMeshDataSize -= static_cast<std::size_t>(item.mNavMeshData.mSize);
|
|
mFreeItems.pop_back();
|
|
|
|
tileValues->second.erase(value);
|
|
if (!tileValues->second.empty())
|
|
return;
|
|
|
|
agentValues->second.erase(tileValues);
|
|
if (!agentValues->second.empty())
|
|
return;
|
|
|
|
mValues.erase(agentValues);
|
|
}
|
|
|
|
void NavMeshTilesCache::acquireItemUnsafe(ItemIterator iterator)
|
|
{
|
|
if (++iterator->mUseCount > 1)
|
|
return;
|
|
|
|
mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator);
|
|
mFreeNavMeshDataSize -= static_cast<std::size_t>(iterator->mNavMeshData.mSize);
|
|
}
|
|
|
|
void NavMeshTilesCache::releaseItem(ItemIterator iterator)
|
|
{
|
|
if (--iterator->mUseCount > 0)
|
|
return;
|
|
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator);
|
|
mFreeNavMeshDataSize += static_cast<std::size_t>(iterator->mNavMeshData.mSize);
|
|
}
|
|
}
|