mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 07:39:41 +00:00
Use R-tree for objects to be used for navmesh generation
Instead of storing a set of objects per tile.
This commit is contained in:
parent
1859c6eded
commit
d15e1dca84
19 changed files with 526 additions and 856 deletions
|
@ -43,14 +43,6 @@ namespace
|
|||
EXPECT_EQ(manager.getRevision(), 0);
|
||||
}
|
||||
|
||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, for_each_tile_position_for_empty_should_call_none)
|
||||
{
|
||||
TileCachedRecastMeshManager manager(mSettings);
|
||||
std::size_t calls = 0;
|
||||
manager.forEachTile([&] (const TilePosition&, const CachedRecastMeshManager&) { ++calls; });
|
||||
EXPECT_EQ(calls, 0);
|
||||
}
|
||||
|
||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_new_object_should_return_true)
|
||||
{
|
||||
TileCachedRecastMeshManager manager(mSettings);
|
||||
|
@ -105,7 +97,7 @@ namespace
|
|||
manager.setBounds(bounds);
|
||||
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
|
||||
manager.takeChangedTiles();
|
||||
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
||||
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground));
|
||||
EXPECT_THAT(manager.takeChangedTiles(), ElementsAre(
|
||||
std::pair(TilePosition(-1, -1), ChangeType::add),
|
||||
std::pair(TilePosition(-1, 0), ChangeType::add),
|
||||
|
@ -123,7 +115,7 @@ namespace
|
|||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
manager.takeChangedTiles();
|
||||
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
||||
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground));
|
||||
EXPECT_THAT(manager.takeChangedTiles(), IsEmpty());
|
||||
}
|
||||
|
||||
|
@ -184,7 +176,7 @@ namespace
|
|||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
|
||||
|
||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||
|
@ -204,7 +196,7 @@ namespace
|
|||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||
|
||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
|
||||
}
|
||||
|
@ -236,7 +228,7 @@ namespace
|
|||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
|
||||
|
||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||
|
@ -272,7 +264,7 @@ namespace
|
|||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
|
||||
const auto beforeUpdateRevision = manager.getRevision();
|
||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1);
|
||||
}
|
||||
|
||||
|
@ -284,7 +276,7 @@ namespace
|
|||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
const auto beforeUpdateRevision = manager.getRevision();
|
||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
manager.updateObject(ObjectId(&boxShape), btTransform::getIdentity(), AreaType::AreaType_ground);
|
||||
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision);
|
||||
}
|
||||
|
||||
|
|
|
@ -290,8 +290,6 @@ add_component_dir(detournavigator
|
|||
makenavmesh
|
||||
findsmoothpath
|
||||
recastmeshbuilder
|
||||
recastmeshmanager
|
||||
cachedrecastmeshmanager
|
||||
navmeshmanager
|
||||
navigatorimpl
|
||||
asyncnavmeshupdater
|
||||
|
@ -304,7 +302,6 @@ add_component_dir(detournavigator
|
|||
findrandompointaroundcircle
|
||||
raycast
|
||||
navmeshtileview
|
||||
oscillatingrecastmeshobject
|
||||
offmeshconnectionsmanager
|
||||
preparednavmeshdata
|
||||
navmeshcacheitem
|
||||
|
@ -317,6 +314,7 @@ add_component_dir(detournavigator
|
|||
gettilespositions
|
||||
collisionshapetype
|
||||
stats
|
||||
commulativeaabb
|
||||
)
|
||||
|
||||
add_component_dir(loadinglistener
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
#include "cachedrecastmeshmanager.hpp"
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
CachedRecastMeshManager::CachedRecastMeshManager(const TileBounds& bounds, std::size_t generation)
|
||||
: mImpl(bounds, generation)
|
||||
{}
|
||||
|
||||
bool CachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,
|
||||
const btTransform& transform, const AreaType areaType)
|
||||
{
|
||||
if (!mImpl.addObject(id, shape, transform, areaType))
|
||||
return false;
|
||||
mOutdatedCache = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachedRecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType)
|
||||
{
|
||||
if (!mImpl.updateObject(id, transform, areaType))
|
||||
return false;
|
||||
mOutdatedCache = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachedRecastMeshManager::removeObject(const ObjectId id)
|
||||
{
|
||||
const bool result = mImpl.removeObject(id);
|
||||
if (result)
|
||||
mOutdatedCache = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
|
||||
{
|
||||
if (!mImpl.addWater(cellPosition, cellSize, level))
|
||||
return false;
|
||||
mOutdatedCache = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition)
|
||||
{
|
||||
const bool result = mImpl.removeWater(cellPosition);
|
||||
if (result)
|
||||
mOutdatedCache = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize,
|
||||
const HeightfieldShape& shape)
|
||||
{
|
||||
if (!mImpl.addHeightfield(cellPosition, cellSize, shape))
|
||||
return false;
|
||||
mOutdatedCache = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition)
|
||||
{
|
||||
const bool result = mImpl.removeHeightfield(cellPosition);
|
||||
if (result)
|
||||
mOutdatedCache = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> CachedRecastMeshManager::getMesh()
|
||||
{
|
||||
bool outdated = true;
|
||||
if (!mOutdatedCache.compare_exchange_strong(outdated, false))
|
||||
{
|
||||
std::shared_ptr<RecastMesh> cached = getCachedMesh();
|
||||
if (cached != nullptr)
|
||||
return cached;
|
||||
}
|
||||
std::shared_ptr<RecastMesh> mesh = mImpl.getMesh();
|
||||
*mCached.lock() = mesh;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> CachedRecastMeshManager::getCachedMesh() const
|
||||
{
|
||||
return *mCached.lockConst();
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> CachedRecastMeshManager::getNewMesh() const
|
||||
{
|
||||
return mImpl.getMesh();
|
||||
}
|
||||
|
||||
bool CachedRecastMeshManager::isEmpty() const
|
||||
{
|
||||
return mImpl.isEmpty();
|
||||
}
|
||||
|
||||
void CachedRecastMeshManager::reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion)
|
||||
{
|
||||
mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion);
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H
|
||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H
|
||||
|
||||
#include "recastmeshmanager.hpp"
|
||||
#include "version.hpp"
|
||||
#include "heightfieldshape.hpp"
|
||||
|
||||
#include <components/misc/guarded.hpp>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
class CachedRecastMeshManager
|
||||
{
|
||||
public:
|
||||
explicit CachedRecastMeshManager(const TileBounds& bounds, std::size_t generation);
|
||||
|
||||
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
const AreaType areaType);
|
||||
|
||||
bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType);
|
||||
|
||||
bool removeObject(const ObjectId id);
|
||||
|
||||
bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level);
|
||||
|
||||
bool removeWater(const osg::Vec2i& cellPosition);
|
||||
|
||||
bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape);
|
||||
|
||||
bool removeHeightfield(const osg::Vec2i& cellPosition);
|
||||
|
||||
std::shared_ptr<RecastMesh> getMesh();
|
||||
|
||||
std::shared_ptr<RecastMesh> getCachedMesh() const;
|
||||
|
||||
std::shared_ptr<RecastMesh> getNewMesh() const;
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
void reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion);
|
||||
|
||||
Version getVersion() const { return mImpl.getVersion(); }
|
||||
|
||||
private:
|
||||
RecastMeshManager mImpl;
|
||||
Misc::ScopeGuarded<std::shared_ptr<RecastMesh>> mCached;
|
||||
std::atomic_bool mOutdatedCache {true};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
27
components/detournavigator/commulativeaabb.cpp
Normal file
27
components/detournavigator/commulativeaabb.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "commulativeaabb.hpp"
|
||||
|
||||
#include <components/bullethelpers/aabb.hpp>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
CommulativeAabb::CommulativeAabb(std::size_t lastChangeRevision, const btAABB& aabb)
|
||||
: mLastChangeRevision(lastChangeRevision)
|
||||
, mAabb(aabb)
|
||||
{
|
||||
}
|
||||
|
||||
bool CommulativeAabb::update(std::size_t lastChangeRevision, const btAABB& aabb)
|
||||
{
|
||||
if (mLastChangeRevision != lastChangeRevision)
|
||||
{
|
||||
mLastChangeRevision = lastChangeRevision;
|
||||
// btAABB doesn't have copy-assignment operator
|
||||
mAabb.m_min = aabb.m_min;
|
||||
mAabb.m_max = aabb.m_max;
|
||||
return true;
|
||||
}
|
||||
const btAABB currentAabb = mAabb;
|
||||
mAabb.merge(aabb);
|
||||
return currentAabb != mAabb;
|
||||
}
|
||||
}
|
24
components/detournavigator/commulativeaabb.hpp
Normal file
24
components/detournavigator/commulativeaabb.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_COMMULATIVEAABB_H
|
||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_COMMULATIVEAABB_H
|
||||
|
||||
#include <BulletCollision/Gimpact/btBoxCollision.h>
|
||||
#include <LinearMath/btTransform.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
class CommulativeAabb
|
||||
{
|
||||
public:
|
||||
explicit CommulativeAabb(std::size_t lastChangeRevision, const btAABB& aabb);
|
||||
|
||||
bool update(std::size_t lastChangeRevision, const btAABB& aabb);
|
||||
|
||||
private:
|
||||
std::size_t mLastChangeRevision;
|
||||
btAABB mAabb;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -4,6 +4,7 @@
|
|||
#include "settings.hpp"
|
||||
#include "settingsutils.hpp"
|
||||
#include "version.hpp"
|
||||
#include "tilespositionsrange.hpp"
|
||||
|
||||
#include <components/bullethelpers/operators.hpp>
|
||||
|
||||
|
@ -196,6 +197,11 @@ namespace DetourNavigator
|
|||
return stream << "Version {" << value.mGeneration << ", " << value.mRevision << "}";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const TilesPositionsRange& value)
|
||||
{
|
||||
return stream << "TilesPositionsRange {" << value.mBegin << ", " << value.mEnd << "}";
|
||||
}
|
||||
|
||||
void writeToFile(const RecastMesh& recastMesh, const std::string& pathPrefix,
|
||||
const std::string& revision, const RecastSettings& settings)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ class dtNavMesh;
|
|||
namespace DetourNavigator
|
||||
{
|
||||
struct Version;
|
||||
struct TilesPositionsRange;
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const TileBounds& value);
|
||||
|
||||
|
@ -58,6 +59,8 @@ namespace DetourNavigator
|
|||
|
||||
std::ostream& operator<<(std::ostream& stream, const Version& value);
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const TilesPositionsRange& value);
|
||||
|
||||
class RecastMesh;
|
||||
struct RecastSettings;
|
||||
|
||||
|
|
|
@ -77,13 +77,11 @@ namespace DetourNavigator
|
|||
|
||||
void NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
|
||||
{
|
||||
const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);
|
||||
mNavMeshManager.updateObject(id, collisionShape, transform, AreaType_ground);
|
||||
mNavMeshManager.updateObject(id, transform, AreaType_ground);
|
||||
if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->mAvoidCollisionShape.get())
|
||||
{
|
||||
const ObjectId avoidId(avoidShape);
|
||||
const CollisionShape avoidCollisionShape(shapes.mShapeInstance, *avoidShape, shapes.mTransform);
|
||||
if (mNavMeshManager.updateObject(avoidId, avoidCollisionShape, transform, AreaType_null))
|
||||
if (mNavMeshManager.updateObject(avoidId, transform, AreaType_null))
|
||||
updateAvoidShapeId(id, avoidId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "settings.hpp"
|
||||
#include "waitconditiontype.hpp"
|
||||
#include "settingsutils.hpp"
|
||||
#include "cachedrecastmeshmanager.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/bullethelpers/heightfield.hpp>
|
||||
|
@ -81,10 +80,9 @@ namespace DetourNavigator
|
|||
return mRecastMeshManager.addObject(id, shape, transform, areaType);
|
||||
}
|
||||
|
||||
bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
const AreaType areaType)
|
||||
bool NavMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType)
|
||||
{
|
||||
return mRecastMeshManager.updateObject(id, shape, transform, areaType);
|
||||
return mRecastMeshManager.updateObject(id, transform, areaType);
|
||||
}
|
||||
|
||||
void NavMeshManager::removeObject(const ObjectId id)
|
||||
|
@ -165,36 +163,33 @@ namespace DetourNavigator
|
|||
mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision();
|
||||
mPlayerTile = playerTile;
|
||||
const auto changedTiles = mRecastMeshManager.takeChangedTiles();
|
||||
const TilesPositionsRange range = mRecastMeshManager.getRange();
|
||||
for (const auto& [agentBounds, cached] : mCache)
|
||||
update(agentBounds, playerTile, cached, changedTiles);
|
||||
update(agentBounds, playerTile, range, cached, changedTiles);
|
||||
}
|
||||
|
||||
void NavMeshManager::update(const AgentBounds& agentBounds, const TilePosition& playerTile,
|
||||
const SharedNavMeshCacheItem& cached, const std::map<osg::Vec2i, ChangeType>& changedTiles)
|
||||
const TilesPositionsRange& range, const SharedNavMeshCacheItem& cached,
|
||||
const std::map<osg::Vec2i, ChangeType>& changedTiles)
|
||||
{
|
||||
std::map<TilePosition, ChangeType> tilesToPost;
|
||||
std::map<osg::Vec2i, ChangeType> tilesToPost = changedTiles;
|
||||
{
|
||||
const auto locked = cached->lockConst();
|
||||
const auto& navMesh = locked->getImpl();
|
||||
for (const auto& [tilePosition, changeType] : changedTiles)
|
||||
if (navMesh.getTileAt(tilePosition.x(), tilePosition.y(), 0))
|
||||
tilesToPost.emplace(tilePosition, changeType);
|
||||
const auto maxTiles = std::min(mSettings.mMaxTilesNumber, navMesh.getParams()->maxTiles);
|
||||
mRecastMeshManager.forEachTile([&] (const TilePosition& tile, CachedRecastMeshManager& recastMeshManager)
|
||||
getTilesPositions(range, [&] (const TilePosition& tile)
|
||||
{
|
||||
if (tilesToPost.count(tile))
|
||||
if (changedTiles.find(tile) != changedTiles.end())
|
||||
return;
|
||||
const auto shouldAdd = shouldAddTile(tile, playerTile, maxTiles);
|
||||
const auto presentInNavMesh = bool(navMesh.getTileAt(tile.x(), tile.y(), 0));
|
||||
const bool shouldAdd = shouldAddTile(tile, playerTile, maxTiles);
|
||||
const bool presentInNavMesh = navMesh.getTileAt(tile.x(), tile.y(), 0) != nullptr;
|
||||
if (shouldAdd && !presentInNavMesh)
|
||||
tilesToPost.emplace(tile, locked->isEmptyTile(tile) ? ChangeType::update : ChangeType::add);
|
||||
else if (!shouldAdd && presentInNavMesh)
|
||||
tilesToPost.emplace(tile, ChangeType::mixed);
|
||||
else
|
||||
recastMeshManager.reportNavMeshChange(recastMeshManager.getVersion(), Version {0, 0});
|
||||
});
|
||||
}
|
||||
mAsyncNavMeshUpdater.post(agentBounds, cached, playerTile, mRecastMeshManager.getWorldspace(), tilesToPost);
|
||||
mAsyncNavMeshUpdater.post(agentBounds, cached, playerTile, mWorldspace, tilesToPost);
|
||||
Log(Debug::Debug) << "Cache update posted for agent=" << agentBounds <<
|
||||
" playerTile=" << playerTile << " recastMeshManagerRevision=" << mLastRecastMeshManagerRevision;
|
||||
}
|
||||
|
@ -221,14 +216,12 @@ namespace DetourNavigator
|
|||
|
||||
RecastMeshTiles NavMeshManager::getRecastMeshTiles() const
|
||||
{
|
||||
std::vector<TilePosition> tiles;
|
||||
mRecastMeshManager.forEachTile(
|
||||
[&tiles] (const TilePosition& tile, const CachedRecastMeshManager&) { tiles.push_back(tile); });
|
||||
const std::string worldspace = mRecastMeshManager.getWorldspace();
|
||||
RecastMeshTiles result;
|
||||
for (const TilePosition& tile : tiles)
|
||||
if (auto mesh = mRecastMeshManager.getCachedMesh(worldspace, tile))
|
||||
result.emplace(tile, std::move(mesh));
|
||||
getTilesPositions(mRecastMeshManager.getRange(), [&] (const TilePosition& v)
|
||||
{
|
||||
if (auto mesh = mRecastMeshManager.getCachedMesh(mWorldspace, v))
|
||||
result.emplace(v, std::move(mesh));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,7 @@ namespace DetourNavigator
|
|||
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
const AreaType areaType);
|
||||
|
||||
bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
const AreaType areaType);
|
||||
bool updateObject(ObjectId id, const btTransform& transform, AreaType areaType);
|
||||
|
||||
void removeObject(const ObjectId id);
|
||||
|
||||
|
@ -76,7 +75,8 @@ namespace DetourNavigator
|
|||
inline SharedNavMeshCacheItem getCached(const AgentBounds& agentBounds) const;
|
||||
|
||||
inline void update(const AgentBounds& agentBounds, const TilePosition& playerTile,
|
||||
const SharedNavMeshCacheItem& cached, const std::map<osg::Vec2i, ChangeType>& changedTiles);
|
||||
const TilesPositionsRange& range, const SharedNavMeshCacheItem& cached,
|
||||
const std::map<osg::Vec2i, ChangeType>& changedTiles);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
#include "oscillatingrecastmeshobject.hpp"
|
||||
#include "tilebounds.hpp"
|
||||
|
||||
#include <components/bullethelpers/aabb.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void limitBy(btAABB& aabb, const TileBounds& bounds)
|
||||
{
|
||||
aabb.m_min.setX(std::max(aabb.m_min.x(), static_cast<btScalar>(bounds.mMin.x())));
|
||||
aabb.m_min.setY(std::max(aabb.m_min.y(), static_cast<btScalar>(bounds.mMin.y())));
|
||||
aabb.m_max.setX(std::min(aabb.m_max.x(), static_cast<btScalar>(bounds.mMax.x())));
|
||||
aabb.m_max.setY(std::min(aabb.m_max.y(), static_cast<btScalar>(bounds.mMax.y())));
|
||||
}
|
||||
}
|
||||
|
||||
OscillatingRecastMeshObject::OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision)
|
||||
: mImpl(std::move(impl))
|
||||
, mLastChangeRevision(lastChangeRevision)
|
||||
, mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform()))
|
||||
{
|
||||
}
|
||||
|
||||
OscillatingRecastMeshObject::OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision)
|
||||
: mImpl(impl)
|
||||
, mLastChangeRevision(lastChangeRevision)
|
||||
, mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform()))
|
||||
{
|
||||
}
|
||||
|
||||
bool OscillatingRecastMeshObject::update(const btTransform& transform, const AreaType areaType,
|
||||
std::size_t lastChangeRevision, const TileBounds& bounds)
|
||||
{
|
||||
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));
|
||||
limitBy(mAabb, bounds);
|
||||
return currentAabb != mAabb;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H
|
||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H
|
||||
|
||||
#include "areatype.hpp"
|
||||
#include "recastmeshobject.hpp"
|
||||
#include "tilebounds.hpp"
|
||||
|
||||
#include <LinearMath/btTransform.h>
|
||||
#include <BulletCollision/Gimpact/btBoxCollision.h>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
class OscillatingRecastMeshObject
|
||||
{
|
||||
public:
|
||||
explicit OscillatingRecastMeshObject(RecastMeshObject&& impl, std::size_t lastChangeRevision);
|
||||
explicit OscillatingRecastMeshObject(const RecastMeshObject& impl, std::size_t lastChangeRevision);
|
||||
|
||||
bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision,
|
||||
const TileBounds& bounds);
|
||||
|
||||
const RecastMeshObject& getImpl() const { return mImpl; }
|
||||
|
||||
private:
|
||||
RecastMeshObject mImpl;
|
||||
std::size_t mLastChangeRevision;
|
||||
btAABB mAabb;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -277,6 +277,8 @@ namespace DetourNavigator
|
|||
mTriangles.erase(std::remove_if(mTriangles.begin(), mTriangles.end(), isNan), mTriangles.end());
|
||||
std::sort(mTriangles.begin(), mTriangles.end());
|
||||
std::sort(mWater.begin(), mWater.end());
|
||||
std::sort(mHeightfields.begin(), mHeightfields.end());
|
||||
std::sort(mFlatHeightfields.begin(), mFlatHeightfields.end());
|
||||
Mesh mesh = makeMesh(std::move(mTriangles));
|
||||
return std::make_shared<RecastMesh>(version, std::move(mesh), std::move(mWater),
|
||||
std::move(mHeightfields), std::move(mFlatHeightfields),
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
#include "recastmeshmanager.hpp"
|
||||
#include "recastmeshbuilder.hpp"
|
||||
#include "heightfieldshape.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/convert.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct AddHeightfield
|
||||
{
|
||||
osg::Vec2i mCellPosition;
|
||||
int mCellSize;
|
||||
DetourNavigator::RecastMeshBuilder& mBuilder;
|
||||
|
||||
void operator()(const DetourNavigator::HeightfieldSurface& v)
|
||||
{
|
||||
mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeights, v.mSize, v.mMinHeight, v.mMaxHeight);
|
||||
}
|
||||
|
||||
void operator()(DetourNavigator::HeightfieldPlane v)
|
||||
{
|
||||
mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeight);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
RecastMeshManager::RecastMeshManager(const TileBounds& bounds, std::size_t generation)
|
||||
: mGeneration(generation)
|
||||
, mTileBounds(bounds)
|
||||
{
|
||||
}
|
||||
|
||||
bool RecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
const AreaType areaType)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto object = mObjects.lower_bound(id);
|
||||
if (object != mObjects.end() && object->first == id)
|
||||
return false;
|
||||
mObjects.emplace_hint(object, id,
|
||||
OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1));
|
||||
++mRevision;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto object = mObjects.find(id);
|
||||
if (object == mObjects.end())
|
||||
return false;
|
||||
const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value()
|
||||
? mLastNavMeshReportedChange->mRevision : mRevision;
|
||||
if (!object->second.update(transform, areaType, lastChangeRevision, mTileBounds))
|
||||
return false;
|
||||
++mRevision;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecastMeshManager::removeObject(const ObjectId id)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto object = mObjects.find(id);
|
||||
if (object == mObjects.end())
|
||||
return false;
|
||||
mObjects.erase(object);
|
||||
++mRevision;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
if (!mWater.emplace(cellPosition, Water {cellSize, level}).second)
|
||||
return false;
|
||||
++mRevision;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecastMeshManager::removeWater(const osg::Vec2i& cellPosition)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto water = mWater.find(cellPosition);
|
||||
if (water == mWater.end())
|
||||
return false;
|
||||
++mRevision;
|
||||
mWater.erase(water);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize,
|
||||
const HeightfieldShape& shape)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
if (!mHeightfields.emplace(cellPosition, SizedHeightfieldShape {cellSize, shape}).second)
|
||||
return false;
|
||||
++mRevision;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto it = mHeightfields.find(cellPosition);
|
||||
if (it == mHeightfields.end())
|
||||
return false;
|
||||
++mRevision;
|
||||
mHeightfields.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> RecastMeshManager::getMesh() const
|
||||
{
|
||||
RecastMeshBuilder builder(mTileBounds);
|
||||
using Object = std::tuple<
|
||||
osg::ref_ptr<const Resource::BulletShapeInstance>,
|
||||
ObjectTransform,
|
||||
std::reference_wrapper<const btCollisionShape>,
|
||||
btTransform,
|
||||
AreaType
|
||||
>;
|
||||
std::vector<Object> objects;
|
||||
std::size_t revision;
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
for (const auto& [k, v] : mWater)
|
||||
builder.addWater(k, v);
|
||||
for (const auto& [cellPosition, v] : mHeightfields)
|
||||
std::visit(AddHeightfield {cellPosition, v.mCellSize, builder}, v.mShape);
|
||||
objects.reserve(mObjects.size());
|
||||
for (const auto& [k, object] : mObjects)
|
||||
{
|
||||
const RecastMeshObject& impl = object.getImpl();
|
||||
objects.emplace_back(impl.getInstance(), impl.getObjectTransform(), impl.getShape(),
|
||||
impl.getTransform(), impl.getAreaType());
|
||||
}
|
||||
revision = mRevision;
|
||||
}
|
||||
for (const auto& [instance, objectTransform, shape, transform, areaType] : objects)
|
||||
builder.addObject(shape, transform, areaType, instance->getSource(), objectTransform);
|
||||
return std::move(builder).create(Version {.mGeneration = mGeneration, .mRevision = revision});
|
||||
}
|
||||
|
||||
bool RecastMeshManager::isEmpty() const
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
return mObjects.empty() && mWater.empty() && mHeightfields.empty();
|
||||
}
|
||||
|
||||
void RecastMeshManager::reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion)
|
||||
{
|
||||
if (recastMeshVersion.mGeneration != mGeneration)
|
||||
return;
|
||||
const std::lock_guard lock(mMutex);
|
||||
if (mLastNavMeshReport.has_value() && navMeshVersion < mLastNavMeshReport->mNavMeshVersion)
|
||||
return;
|
||||
mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion};
|
||||
if (!mLastNavMeshReportedChange.has_value()
|
||||
|| mLastNavMeshReportedChange->mNavMeshVersion < mLastNavMeshReport->mNavMeshVersion)
|
||||
mLastNavMeshReportedChange = mLastNavMeshReport;
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H
|
||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H
|
||||
|
||||
#include "oscillatingrecastmeshobject.hpp"
|
||||
#include "objectid.hpp"
|
||||
#include "version.hpp"
|
||||
#include "recastmesh.hpp"
|
||||
#include "heightfieldshape.hpp"
|
||||
|
||||
#include <LinearMath/btTransform.h>
|
||||
|
||||
#include <osg/Vec2i>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class btCollisionShape;
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
struct Settings;
|
||||
class RecastMesh;
|
||||
|
||||
struct SizedHeightfieldShape
|
||||
{
|
||||
int mCellSize;
|
||||
HeightfieldShape mShape;
|
||||
};
|
||||
|
||||
class RecastMeshManager
|
||||
{
|
||||
public:
|
||||
explicit RecastMeshManager(const TileBounds& bounds, std::size_t generation);
|
||||
|
||||
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
const AreaType areaType);
|
||||
|
||||
bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType);
|
||||
|
||||
bool removeObject(const ObjectId id);
|
||||
|
||||
bool addWater(const osg::Vec2i& cellPosition, int cellSize, float level);
|
||||
|
||||
bool removeWater(const osg::Vec2i& cellPosition);
|
||||
|
||||
bool addHeightfield(const osg::Vec2i& cellPosition, int cellSize, const HeightfieldShape& shape);
|
||||
|
||||
bool removeHeightfield(const osg::Vec2i& cellPosition);
|
||||
|
||||
std::shared_ptr<RecastMesh> getMesh() const;
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
void reportNavMeshChange(const Version& recastMeshVersion, const Version& navMeshVersion);
|
||||
|
||||
Version getVersion() const { return Version {mGeneration, mRevision}; }
|
||||
|
||||
private:
|
||||
struct Report
|
||||
{
|
||||
std::size_t mRevision;
|
||||
Version mNavMeshVersion;
|
||||
};
|
||||
|
||||
const std::size_t mGeneration;
|
||||
const TileBounds mTileBounds;
|
||||
mutable std::mutex mMutex;
|
||||
std::size_t mRevision = 0;
|
||||
std::map<ObjectId, OscillatingRecastMeshObject> mObjects;
|
||||
std::map<osg::Vec2i, Water> mWater;
|
||||
std::map<osg::Vec2i, SizedHeightfieldShape> mHeightfields;
|
||||
std::optional<Report> mLastNavMeshReportedChange;
|
||||
std::optional<Report> mLastNavMeshReport;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -2,10 +2,12 @@
|
|||
#include "gettilespositions.hpp"
|
||||
#include "settingsutils.hpp"
|
||||
#include "changetype.hpp"
|
||||
#include "cachedrecastmeshmanager.hpp"
|
||||
#include "recastmeshbuilder.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/convert.hpp>
|
||||
#include <components/bullethelpers/aabb.hpp>
|
||||
|
||||
#include <boost/geometry/geometry.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
@ -20,11 +22,26 @@ namespace DetourNavigator
|
|||
osg::Vec2f(std::numeric_limits<float>::max(), std::numeric_limits<float>::max())
|
||||
};
|
||||
|
||||
bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType,
|
||||
const TilePosition& tilePosition, std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>& tiles)
|
||||
struct AddHeightfield
|
||||
{
|
||||
const auto tile = tiles.find(tilePosition);
|
||||
return tile != tiles.end() && tile->second->updateObject(id, transform, areaType);
|
||||
osg::Vec2i mCellPosition;
|
||||
int mCellSize;
|
||||
RecastMeshBuilder& mBuilder;
|
||||
|
||||
void operator()(const HeightfieldSurface& v)
|
||||
{
|
||||
mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeights, v.mSize, v.mMinHeight, v.mMaxHeight);
|
||||
}
|
||||
|
||||
void operator()(HeightfieldPlane v)
|
||||
{
|
||||
mBuilder.addHeightfield(mCellPosition, mCellSize, v.mHeight);
|
||||
}
|
||||
};
|
||||
|
||||
TilePosition makeTilePosition(const boost::geometry::model::point<int, 2, boost::geometry::cs::cartesian>& v)
|
||||
{
|
||||
return TilePosition(v.get<0>(), v.get<1>());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,345 +58,318 @@ namespace DetourNavigator
|
|||
|
||||
bool changed = false;
|
||||
const auto newRange = makeTilesPositionsRange(bounds.mMin, bounds.mMax, mSettings);
|
||||
|
||||
if (mBounds != infiniteTileBounds)
|
||||
{
|
||||
const auto locked = mWorldspaceTiles.lock();
|
||||
for (auto& object : mObjects)
|
||||
for (const auto& [id, data] : mObjects)
|
||||
{
|
||||
const ObjectId id = object.first;
|
||||
ObjectData& data = object.second;
|
||||
const TilesPositionsRange objectRange = makeTilesPositionsRange(data.mShape.getShape(), data.mTransform, mSettings);
|
||||
const TilesPositionsRange objectRange = makeTilesPositionsRange(data->mObject.getShape(),
|
||||
data->mObject.getTransform(), mSettings);
|
||||
|
||||
const auto onOldTilePosition = [&] (const TilePosition& position)
|
||||
getTilesPositions(getIntersection(mRange, objectRange), [&] (const TilePosition& v)
|
||||
{
|
||||
if (isInTilesPositionsRange(newRange, position))
|
||||
return;
|
||||
const auto it = data.mTiles.find(position);
|
||||
if (it == data.mTiles.end())
|
||||
return;
|
||||
data.mTiles.erase(it);
|
||||
if (removeTile(id, position, locked->mTiles))
|
||||
if (!isInTilesPositionsRange(newRange, v))
|
||||
{
|
||||
addChangedTile(position, ChangeType::remove);
|
||||
addChangedTile(v, ChangeType::remove);
|
||||
changed = true;
|
||||
}
|
||||
};
|
||||
getTilesPositions(getIntersection(mRange, objectRange), onOldTilePosition);
|
||||
});
|
||||
|
||||
const auto onNewTilePosition = [&] (const TilePosition& position)
|
||||
getTilesPositions(getIntersection(newRange, objectRange), [&] (const TilePosition& v)
|
||||
{
|
||||
if (data.mTiles.find(position) != data.mTiles.end())
|
||||
return;
|
||||
if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, locked->mTiles))
|
||||
if (!isInTilesPositionsRange(mRange, v))
|
||||
{
|
||||
data.mTiles.insert(position);
|
||||
addChangedTile(position, ChangeType::add);
|
||||
addChangedTile(v, ChangeType::add);
|
||||
changed = true;
|
||||
}
|
||||
};
|
||||
getTilesPositions(getIntersection(newRange, objectRange), onNewTilePosition);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
++mRevision;
|
||||
}
|
||||
|
||||
mBounds = bounds;
|
||||
mRange = newRange;
|
||||
}
|
||||
|
||||
std::string TileCachedRecastMeshManager::getWorldspace() const
|
||||
TilesPositionsRange TileCachedRecastMeshManager::getRange() const
|
||||
{
|
||||
return mWorldspaceTiles.lockConst()->mWorldspace;
|
||||
const auto bounds = mObjectIndex.bounds();
|
||||
return TilesPositionsRange {
|
||||
.mBegin = makeTilePosition(bounds.min_corner()),
|
||||
.mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1),
|
||||
};
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace)
|
||||
{
|
||||
const auto locked = mWorldspaceTiles.lock();
|
||||
if (locked->mWorldspace == worldspace)
|
||||
const std::lock_guard lock(mMutex);
|
||||
if (mWorldspace == worldspace)
|
||||
return;
|
||||
locked->mTiles.clear();
|
||||
locked->mWorldspace = worldspace;
|
||||
mWorldspace = worldspace;
|
||||
++mGeneration;
|
||||
++mRevision;
|
||||
mObjectIndex.clear();
|
||||
mObjects.clear();
|
||||
mWater.clear();
|
||||
mHeightfields.clear();
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,
|
||||
const btTransform& transform, const AreaType areaType)
|
||||
bool TileCachedRecastMeshManager::addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
const AreaType areaType)
|
||||
{
|
||||
auto it = mObjects.find(id);
|
||||
if (it != mObjects.end())
|
||||
return false;
|
||||
const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings);
|
||||
const TilesPositionsRange range = getIntersection(mRange, objectRange);
|
||||
std::set<TilePosition> tilesPositions;
|
||||
if (range.mBegin != range.mEnd)
|
||||
const TilesPositionsRange range = makeTilesPositionsRange(shape.getShape(), transform, mSettings);
|
||||
{
|
||||
const auto locked = mWorldspaceTiles.lock();
|
||||
getTilesPositions(range,
|
||||
[&] (const TilePosition& tilePosition)
|
||||
{
|
||||
if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles))
|
||||
{
|
||||
tilesPositions.insert(tilePosition);
|
||||
addChangedTile(tilePosition, ChangeType::add);
|
||||
}
|
||||
});
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto it = mObjects.find(id);
|
||||
if (it != mObjects.end())
|
||||
return false;
|
||||
const std::size_t revision = mRevision + 1;
|
||||
ObjectData* const dataPtr = mObjects.emplace_hint(it, id, std::unique_ptr<ObjectData>(new ObjectData {
|
||||
.mObject = RecastMeshObject(shape, transform, areaType),
|
||||
.mRange = range,
|
||||
.mAabb = CommulativeAabb(revision, BulletHelpers::getAabb(shape.getShape(), transform)),
|
||||
.mGeneration = mGeneration,
|
||||
.mRevision = revision,
|
||||
.mLastNavMeshReportedChange = {},
|
||||
.mLastNavMeshReport = {},
|
||||
}))->second.get();
|
||||
assert(range.mBegin != range.mEnd);
|
||||
mObjectIndex.insert(makeObjectIndexValue(range, dataPtr));
|
||||
mRevision = revision;
|
||||
}
|
||||
mObjects.emplace_hint(it, id, ObjectData {shape, transform, areaType, std::move(tilesPositions)});
|
||||
++mRevision;
|
||||
getTilesPositions(getIntersection(range, mRange),
|
||||
[&] (const TilePosition& v) { addChangedTile(v, ChangeType::add); });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileCachedRecastMeshManager::updateObject(const ObjectId id, const CollisionShape& shape,
|
||||
const btTransform& transform, AreaType areaType)
|
||||
bool TileCachedRecastMeshManager::updateObject(ObjectId id, const btTransform& transform, const AreaType areaType)
|
||||
{
|
||||
const auto object = mObjects.find(id);
|
||||
if (object == mObjects.end())
|
||||
return false;
|
||||
auto& data = object->second;
|
||||
bool changed = false;
|
||||
std::set<TilePosition> newTiles;
|
||||
TilesPositionsRange newRange;
|
||||
TilesPositionsRange oldRange;
|
||||
{
|
||||
const TilesPositionsRange objectRange = makeTilesPositionsRange(shape.getShape(), transform, mSettings);
|
||||
const TilesPositionsRange range = getIntersection(mRange, objectRange);
|
||||
const auto locked = mWorldspaceTiles.lock();
|
||||
const auto onTilePosition = [&] (const TilePosition& tilePosition)
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto it = mObjects.find(id);
|
||||
if (it == mObjects.end())
|
||||
return false;
|
||||
if (!it->second->mObject.update(transform, areaType))
|
||||
return false;
|
||||
const std::size_t lastChangeRevision = it->second->mLastNavMeshReportedChange.has_value()
|
||||
? it->second->mLastNavMeshReportedChange->mRevision : mRevision;
|
||||
const btCollisionShape& shape = it->second->mObject.getShape();
|
||||
if (!it->second->mAabb.update(lastChangeRevision, BulletHelpers::getAabb(shape, transform)))
|
||||
return false;
|
||||
newRange = makeTilesPositionsRange(shape, transform, mSettings);
|
||||
oldRange = it->second->mRange;
|
||||
if (newRange != oldRange)
|
||||
{
|
||||
if (data.mTiles.find(tilePosition) != data.mTiles.end())
|
||||
{
|
||||
newTiles.insert(tilePosition);
|
||||
if (updateTile(id, transform, areaType, tilePosition, locked->mTiles))
|
||||
{
|
||||
addChangedTile(tilePosition, ChangeType::update);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles))
|
||||
{
|
||||
newTiles.insert(tilePosition);
|
||||
addChangedTile(tilePosition, ChangeType::add);
|
||||
changed = true;
|
||||
}
|
||||
};
|
||||
getTilesPositions(range, onTilePosition);
|
||||
for (const auto& tile : data.mTiles)
|
||||
{
|
||||
if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, locked->mTiles))
|
||||
{
|
||||
addChangedTile(tile, ChangeType::remove);
|
||||
changed = true;
|
||||
}
|
||||
mObjectIndex.remove(makeObjectIndexValue(oldRange, it->second.get()));
|
||||
mObjectIndex.insert(makeObjectIndexValue(newRange, it->second.get()));
|
||||
it->second->mRange = newRange;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
data.mTiles = std::move(newTiles);
|
||||
++mRevision;
|
||||
it->second->mRevision = mRevision;
|
||||
}
|
||||
return changed;
|
||||
if (newRange == oldRange)
|
||||
{
|
||||
getTilesPositions(getIntersection(newRange, mRange),
|
||||
[&] (const TilePosition& v) { addChangedTile(v, ChangeType::update); });
|
||||
}
|
||||
else
|
||||
{
|
||||
getTilesPositions(getIntersection(newRange, mRange), [&] (const TilePosition& v)
|
||||
{
|
||||
const ChangeType changeType = isInTilesPositionsRange(oldRange, v)
|
||||
? ChangeType::update : ChangeType::add;
|
||||
addChangedTile(v, changeType);
|
||||
});
|
||||
getTilesPositions(getIntersection(oldRange, mRange), [&] (const TilePosition& v)
|
||||
{
|
||||
if (!isInTilesPositionsRange(newRange, v))
|
||||
addChangedTile(v, ChangeType::remove);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::removeObject(const ObjectId id)
|
||||
void TileCachedRecastMeshManager::removeObject(ObjectId id)
|
||||
{
|
||||
const auto object = mObjects.find(id);
|
||||
if (object == mObjects.end())
|
||||
return;
|
||||
bool changed = false;
|
||||
TilesPositionsRange range;
|
||||
{
|
||||
const auto locked = mWorldspaceTiles.lock();
|
||||
for (const auto& tilePosition : object->second.mTiles)
|
||||
{
|
||||
if (removeTile(id, tilePosition, locked->mTiles))
|
||||
{
|
||||
addChangedTile(tilePosition, ChangeType::remove);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
mObjects.erase(object);
|
||||
if (changed)
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto it = mObjects.find(id);
|
||||
if (it == mObjects.end())
|
||||
return;
|
||||
range = it->second->mRange;
|
||||
mObjectIndex.remove(makeObjectIndexValue(range, it->second.get()));
|
||||
mObjects.erase(it);
|
||||
++mRevision;
|
||||
}
|
||||
getTilesPositions(getIntersection(range, mRange),
|
||||
[&] (const TilePosition& v) { addChangedTile(v, ChangeType::remove); });
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const float level)
|
||||
{
|
||||
const auto it = mWaterTilesPositions.find(cellPosition);
|
||||
if (it != mWaterTilesPositions.end())
|
||||
return;
|
||||
|
||||
std::vector<TilePosition>& tilesPositions = mWaterTilesPositions.emplace_hint(
|
||||
it, cellPosition, std::vector<TilePosition>())->second;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (cellSize == std::numeric_limits<int>::max())
|
||||
const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level));
|
||||
const std::optional<TilesPositionsRange> range = cellSize == std::numeric_limits<int>::max()
|
||||
? std::optional<TilesPositionsRange>()
|
||||
: makeTilesPositionsRange(cellSize, shift, mSettings);
|
||||
{
|
||||
const auto locked = mWorldspaceTiles.lock();
|
||||
for (auto& [tilePosition, data] : locked->mTiles)
|
||||
{
|
||||
if (data->addWater(cellPosition, cellSize, level))
|
||||
{
|
||||
tilesPositions.push_back(tilePosition);
|
||||
addChangedTile(tilePosition, ChangeType::add);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
const std::lock_guard lock(mMutex);
|
||||
auto it = mWater.find(cellPosition);
|
||||
if (it != mWater.end())
|
||||
return;
|
||||
const std::size_t revision = mRevision + 1;
|
||||
it = mWater.emplace_hint(it, cellPosition, WaterData {
|
||||
.mWater = Water {.mCellSize = cellSize, .mLevel = level},
|
||||
.mRange = range,
|
||||
.mRevision = revision,
|
||||
});
|
||||
if (range.has_value())
|
||||
mWaterIndex.insert(makeWaterIndexValue(*range, it));
|
||||
else
|
||||
mInfiniteWater = it;
|
||||
mRevision = revision;
|
||||
}
|
||||
else
|
||||
{
|
||||
const btVector3 shift = Misc::Convert::toBullet(getWaterShift3d(cellPosition, cellSize, level));
|
||||
const auto worldspaceTiles = mWorldspaceTiles.lock();
|
||||
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
|
||||
[&] (const TilePosition& tilePosition)
|
||||
{
|
||||
auto tile = worldspaceTiles->mTiles.find(tilePosition);
|
||||
if (tile == worldspaceTiles->mTiles.end())
|
||||
{
|
||||
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
|
||||
tile = worldspaceTiles->mTiles.emplace_hint(tile, tilePosition,
|
||||
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
|
||||
}
|
||||
if (tile->second->addWater(cellPosition, cellSize, level))
|
||||
{
|
||||
tilesPositions.push_back(tilePosition);
|
||||
addChangedTile(tilePosition, ChangeType::add);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (changed)
|
||||
++mRevision;
|
||||
addChangedTiles(range, ChangeType::add);
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition)
|
||||
{
|
||||
const auto object = mWaterTilesPositions.find(cellPosition);
|
||||
if (object == mWaterTilesPositions.end())
|
||||
return;
|
||||
bool changed = false;
|
||||
std::optional<TilesPositionsRange> range;
|
||||
{
|
||||
const auto worldspaceTiles = mWorldspaceTiles.lock();
|
||||
for (const auto& tilePosition : object->second)
|
||||
{
|
||||
const auto tile = worldspaceTiles->mTiles.find(tilePosition);
|
||||
if (tile == worldspaceTiles->mTiles.end())
|
||||
continue;
|
||||
if (tile->second->removeWater(cellPosition))
|
||||
{
|
||||
addChangedTile(tilePosition, ChangeType::remove);
|
||||
changed = true;
|
||||
}
|
||||
if (tile->second->isEmpty())
|
||||
{
|
||||
worldspaceTiles->mTiles.erase(tile);
|
||||
++mTilesGeneration;
|
||||
}
|
||||
}
|
||||
}
|
||||
mWaterTilesPositions.erase(object);
|
||||
if (changed)
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto it = mWater.find(cellPosition);
|
||||
if (it == mWater.end())
|
||||
return;
|
||||
range = it->second.mRange;
|
||||
if (range.has_value())
|
||||
mWaterIndex.remove(makeWaterIndexValue(*range, it));
|
||||
else
|
||||
mInfiniteWater = mWater.end();
|
||||
mWater.erase(it);
|
||||
++mRevision;
|
||||
}
|
||||
addChangedTiles(range, ChangeType::remove);
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, const int cellSize,
|
||||
const HeightfieldShape& shape)
|
||||
{
|
||||
const auto it = mHeightfieldTilesPositions.find(cellPosition);
|
||||
if (it != mHeightfieldTilesPositions.end())
|
||||
return;
|
||||
|
||||
std::vector<TilePosition>& tilesPositions = mHeightfieldTilesPositions.emplace_hint(
|
||||
it, cellPosition, std::vector<TilePosition>())->second;
|
||||
const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
const std::optional<TilesPositionsRange> range = cellSize == std::numeric_limits<int>::max()
|
||||
? std::optional<TilesPositionsRange>()
|
||||
: makeTilesPositionsRange(cellSize, shift, mSettings);
|
||||
{
|
||||
const auto worldspaceTiles = mWorldspaceTiles.lock();
|
||||
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
|
||||
[&] (const TilePosition& tilePosition)
|
||||
{
|
||||
auto tile = worldspaceTiles->mTiles.find(tilePosition);
|
||||
if (tile == worldspaceTiles->mTiles.end())
|
||||
{
|
||||
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
|
||||
tile = worldspaceTiles->mTiles.emplace_hint(tile, tilePosition,
|
||||
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
|
||||
}
|
||||
if (tile->second->addHeightfield(cellPosition, cellSize, shape))
|
||||
{
|
||||
tilesPositions.push_back(tilePosition);
|
||||
addChangedTile(tilePosition, ChangeType::add);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
const std::lock_guard lock(mMutex);
|
||||
auto it = mHeightfields.find(cellPosition);
|
||||
if (it != mHeightfields.end())
|
||||
return;
|
||||
const std::size_t revision = mRevision + 1;
|
||||
it = mHeightfields.emplace_hint(it, cellPosition, HeightfieldData {
|
||||
.mCellSize = cellSize,
|
||||
.mShape = shape,
|
||||
.mRange = range,
|
||||
.mRevision = revision,
|
||||
});
|
||||
if (range.has_value())
|
||||
mHeightfieldIndex.insert(makeHeightfieldIndexValue(*range, it));
|
||||
else
|
||||
mInfiniteHeightfield = it;
|
||||
mRevision = revision;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
++mRevision;
|
||||
addChangedTiles(range, ChangeType::add);
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::removeHeightfield(const osg::Vec2i& cellPosition)
|
||||
{
|
||||
const auto object = mHeightfieldTilesPositions.find(cellPosition);
|
||||
if (object == mHeightfieldTilesPositions.end())
|
||||
return;
|
||||
bool changed = false;
|
||||
std::optional<TilesPositionsRange> range;
|
||||
{
|
||||
const auto worldspaceTiles = mWorldspaceTiles.lock();
|
||||
for (const auto& tilePosition : object->second)
|
||||
{
|
||||
const auto tile = worldspaceTiles->mTiles.find(tilePosition);
|
||||
if (tile == worldspaceTiles->mTiles.end())
|
||||
continue;
|
||||
if (tile->second->removeHeightfield(cellPosition))
|
||||
{
|
||||
addChangedTile(tilePosition, ChangeType::remove);
|
||||
changed = true;
|
||||
}
|
||||
if (tile->second->isEmpty())
|
||||
{
|
||||
worldspaceTiles->mTiles.erase(tile);
|
||||
++mTilesGeneration;
|
||||
}
|
||||
}
|
||||
}
|
||||
mHeightfieldTilesPositions.erase(object);
|
||||
if (changed)
|
||||
const std::lock_guard lock(mMutex);
|
||||
const auto it = mHeightfields.find(cellPosition);
|
||||
if (it == mHeightfields.end())
|
||||
return;
|
||||
range = it->second.mRange;
|
||||
if (range.has_value())
|
||||
mHeightfieldIndex.remove(makeHeightfieldIndexValue(*range, it));
|
||||
else
|
||||
mInfiniteHeightfield = mHeightfields.end();
|
||||
mHeightfields.erase(it);
|
||||
++mRevision;
|
||||
}
|
||||
addChangedTiles(range, ChangeType::remove);
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(std::string_view worldspace, const TilePosition& tilePosition) const
|
||||
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(std::string_view worldspace,
|
||||
const TilePosition& tilePosition)
|
||||
{
|
||||
if (const auto manager = getManager(worldspace, tilePosition))
|
||||
return manager->getMesh();
|
||||
return nullptr;
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
if (mWorldspace != worldspace)
|
||||
return nullptr;
|
||||
const auto it = mCache.find(tilePosition);
|
||||
if (it != mCache.end() && it->second.mRecastMesh->getVersion() == it->second.mVersion)
|
||||
return it->second.mRecastMesh;
|
||||
}
|
||||
auto result = makeMesh(tilePosition);
|
||||
if (result != nullptr)
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
mCache.insert_or_assign(tilePosition, CachedTile {
|
||||
.mVersion = result->getVersion(),
|
||||
.mRecastMesh = result,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getCachedMesh(std::string_view worldspace, const TilePosition& tilePosition) const
|
||||
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getCachedMesh(std::string_view worldspace,
|
||||
const TilePosition& tilePosition) const
|
||||
{
|
||||
if (const auto manager = getManager(worldspace, tilePosition))
|
||||
return manager->getCachedMesh();
|
||||
return nullptr;
|
||||
const std::lock_guard lock(mMutex);
|
||||
if (mWorldspace != worldspace)
|
||||
return nullptr;
|
||||
const auto it = mCache.find(tilePosition);
|
||||
if (it == mCache.end())
|
||||
return nullptr;
|
||||
return it->second.mRecastMesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getNewMesh(std::string_view worldspace, const TilePosition& tilePosition) const
|
||||
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getNewMesh(std::string_view worldspace,
|
||||
const TilePosition& tilePosition) const
|
||||
{
|
||||
if (const auto manager = getManager(worldspace, tilePosition))
|
||||
return manager->getNewMesh();
|
||||
return nullptr;
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
if (mWorldspace != worldspace)
|
||||
return nullptr;
|
||||
}
|
||||
return makeMesh(tilePosition);
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const
|
||||
void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition,
|
||||
Version recastMeshVersion, Version navMeshVersion)
|
||||
{
|
||||
const auto locked = mWorldspaceTiles.lockConst();
|
||||
const auto it = locked->mTiles.find(tilePosition);
|
||||
if (it == locked->mTiles.end())
|
||||
return;
|
||||
it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion);
|
||||
const std::lock_guard lock(mMutex);
|
||||
for (auto it = mObjectIndex.qbegin(makeIndexQuery(tilePosition)); it != mObjectIndex.qend(); ++it)
|
||||
{
|
||||
ObjectData& object = *it->second;
|
||||
if (recastMeshVersion.mGeneration != object.mGeneration)
|
||||
continue;
|
||||
if (object.mLastNavMeshReport.has_value() && navMeshVersion < object.mLastNavMeshReport->mNavMeshVersion)
|
||||
continue;
|
||||
object.mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion};
|
||||
if (!object.mLastNavMeshReportedChange.has_value()
|
||||
|| object.mLastNavMeshReportedChange->mNavMeshVersion < object.mLastNavMeshReport->mNavMeshVersion)
|
||||
object.mLastNavMeshReportedChange = object.mLastNavMeshReport;
|
||||
}
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::addChangedTile(const TilePosition& tilePosition, ChangeType changeType)
|
||||
void TileCachedRecastMeshManager::addChangedTile(const TilePosition& tilePosition, const ChangeType changeType)
|
||||
{
|
||||
auto tile = mChangedTiles.find(tilePosition);
|
||||
if (tile == mChangedTiles.end())
|
||||
|
@ -388,44 +378,112 @@ namespace DetourNavigator
|
|||
tile->second = addChangeType(tile->second, changeType);
|
||||
}
|
||||
|
||||
bool TileCachedRecastMeshManager::addTile(const ObjectId id, const CollisionShape& shape,
|
||||
const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition,
|
||||
TilesMap& tiles)
|
||||
std::map<osg::Vec2i, ChangeType> TileCachedRecastMeshManager::takeChangedTiles()
|
||||
{
|
||||
auto tile = tiles.find(tilePosition);
|
||||
if (tile == tiles.end())
|
||||
{
|
||||
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
|
||||
tile = tiles.emplace_hint(tile, tilePosition,
|
||||
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
|
||||
const std::lock_guard lock(mMutex);
|
||||
for (const auto& [tilePosition, changeType] : mChangedTiles)
|
||||
if (const auto it = mCache.find(tilePosition); it != mCache.end())
|
||||
++it->second.mVersion.mRevision;
|
||||
}
|
||||
return tile->second->addObject(id, shape, transform, areaType);
|
||||
return std::move(mChangedTiles);
|
||||
}
|
||||
|
||||
bool TileCachedRecastMeshManager::removeTile(const ObjectId id,
|
||||
const TilePosition& tilePosition, TilesMap& tiles)
|
||||
TileCachedRecastMeshManager::IndexPoint TileCachedRecastMeshManager::makeIndexPoint(const TilePosition& tilePosition)
|
||||
{
|
||||
const auto tile = tiles.find(tilePosition);
|
||||
if (tile == tiles.end())
|
||||
return false;
|
||||
const bool result = tile->second->removeObject(id);
|
||||
if (tile->second->isEmpty())
|
||||
{
|
||||
tiles.erase(tile);
|
||||
++mTilesGeneration;
|
||||
}
|
||||
return result;
|
||||
return IndexPoint(tilePosition.x(), tilePosition.y());
|
||||
}
|
||||
|
||||
std::shared_ptr<CachedRecastMeshManager> TileCachedRecastMeshManager::getManager(std::string_view worldspace,
|
||||
const TilePosition& tilePosition) const
|
||||
TileCachedRecastMeshManager::IndexBox TileCachedRecastMeshManager::makeIndexBox(const TilesPositionsRange& range)
|
||||
{
|
||||
const auto locked = mWorldspaceTiles.lockConst();
|
||||
if (locked->mWorldspace != worldspace)
|
||||
assert(range.mBegin != range.mEnd);
|
||||
return IndexBox(makeIndexPoint(range.mBegin), makeIndexPoint(range.mEnd - TilePosition(1, 1)));
|
||||
}
|
||||
|
||||
TileCachedRecastMeshManager::ObjectIndexValue TileCachedRecastMeshManager::makeObjectIndexValue(
|
||||
const TilesPositionsRange& range, ObjectData* id)
|
||||
{
|
||||
return {makeIndexBox(range), id};
|
||||
}
|
||||
|
||||
TileCachedRecastMeshManager::WaterIndexValue TileCachedRecastMeshManager::makeWaterIndexValue(
|
||||
const TilesPositionsRange& range, std::map<osg::Vec2i, WaterData>::const_iterator it)
|
||||
{
|
||||
return {makeIndexBox(range), it};
|
||||
}
|
||||
|
||||
TileCachedRecastMeshManager::HeightfieldIndexValue TileCachedRecastMeshManager::makeHeightfieldIndexValue(
|
||||
const TilesPositionsRange& range, std::map<osg::Vec2i, HeightfieldData>::const_iterator it)
|
||||
{
|
||||
return {makeIndexBox(range), it};
|
||||
}
|
||||
|
||||
auto TileCachedRecastMeshManager::makeIndexQuery(const TilePosition& tilePosition)
|
||||
-> decltype(boost::geometry::index::intersects(IndexBox()))
|
||||
{
|
||||
const IndexPoint point = makeIndexPoint(tilePosition);
|
||||
return boost::geometry::index::intersects(IndexBox(point, point));
|
||||
}
|
||||
|
||||
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::makeMesh(const TilePosition& tilePosition) const
|
||||
{
|
||||
RecastMeshBuilder builder(makeRealTileBoundsWithBorder(mSettings, tilePosition));
|
||||
using Object = std::tuple<
|
||||
osg::ref_ptr<const Resource::BulletShapeInstance>,
|
||||
ObjectTransform,
|
||||
std::reference_wrapper<const btCollisionShape>,
|
||||
btTransform,
|
||||
AreaType
|
||||
>;
|
||||
std::vector<Object> objects;
|
||||
Version version;
|
||||
bool hasInput = false;
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
for (auto it = mWaterIndex.qbegin(makeIndexQuery(tilePosition)); it != mWaterIndex.qend(); ++it)
|
||||
{
|
||||
const auto& [cellPosition, data] = *it->second;
|
||||
builder.addWater(cellPosition, data.mWater);
|
||||
hasInput = true;
|
||||
}
|
||||
for (auto it = mHeightfieldIndex.qbegin(makeIndexQuery(tilePosition)); it != mHeightfieldIndex.qend(); ++it)
|
||||
{
|
||||
const auto& [cellPosition, data] = *it->second;
|
||||
std::visit(AddHeightfield {cellPosition, data.mCellSize, builder}, data.mShape);
|
||||
hasInput = true;
|
||||
}
|
||||
objects.reserve(mObjects.size());
|
||||
for (auto it = mObjectIndex.qbegin(makeIndexQuery(tilePosition)); it != mObjectIndex.qend(); ++it)
|
||||
{
|
||||
const auto& object = it->second->mObject;
|
||||
objects.emplace_back(object.getInstance(), object.getObjectTransform(), object.getShape(),
|
||||
object.getTransform(), object.getAreaType());
|
||||
hasInput = true;
|
||||
}
|
||||
if (hasInput)
|
||||
{
|
||||
if (mInfiniteWater != mWater.end())
|
||||
builder.addWater(mInfiniteWater->first, mInfiniteWater->second.mWater);
|
||||
if (mInfiniteHeightfield != mHeightfields.end())
|
||||
std::visit(
|
||||
AddHeightfield {mInfiniteHeightfield->first, mInfiniteHeightfield->second.mCellSize, builder},
|
||||
mInfiniteHeightfield->second.mShape
|
||||
);
|
||||
version.mGeneration = mGeneration;
|
||||
version.mRevision = mRevision;
|
||||
}
|
||||
}
|
||||
if (!hasInput)
|
||||
return nullptr;
|
||||
const auto it = locked->mTiles.find(tilePosition);
|
||||
if (it == locked->mTiles.end())
|
||||
return nullptr;
|
||||
return it->second;
|
||||
for (const auto& [instance, objectTransform, shape, transform, areaType] : objects)
|
||||
builder.addObject(shape, transform, areaType, instance->getSource(), objectTransform);
|
||||
return std::move(builder).create(version);
|
||||
}
|
||||
|
||||
void TileCachedRecastMeshManager::addChangedTiles(const std::optional<TilesPositionsRange>& range,
|
||||
ChangeType changeType)
|
||||
{
|
||||
if (range.has_value())
|
||||
getTilesPositions(*range, [&] (const TilePosition& v) { addChangedTile(v, changeType); });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,23 @@
|
|||
#include "objectid.hpp"
|
||||
#include "areatype.hpp"
|
||||
#include "recastmeshobject.hpp"
|
||||
#include "commulativeaabb.hpp"
|
||||
#include "version.hpp"
|
||||
#include "recastmesh.hpp"
|
||||
|
||||
#include <components/misc/guarded.hpp>
|
||||
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
#include <boost/geometry/geometries/point.hpp>
|
||||
#include <boost/geometry/index/rtree.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
class CachedRecastMeshManager;
|
||||
class RecastMesh;
|
||||
|
||||
class TileCachedRecastMeshManager
|
||||
|
@ -29,13 +35,13 @@ namespace DetourNavigator
|
|||
|
||||
void setBounds(const TileBounds& bounds);
|
||||
|
||||
std::string getWorldspace() const;
|
||||
TilesPositionsRange getRange() const;
|
||||
|
||||
void setWorldspace(std::string_view worldspace);
|
||||
|
||||
bool addObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType);
|
||||
|
||||
bool updateObject(ObjectId id, const CollisionShape& shape, const btTransform& transform, AreaType areaType);
|
||||
bool updateObject(ObjectId id, const btTransform& transform, AreaType areaType);
|
||||
|
||||
void removeObject(ObjectId id);
|
||||
|
||||
|
@ -47,63 +53,101 @@ namespace DetourNavigator
|
|||
|
||||
void removeHeightfield(const osg::Vec2i& cellPosition);
|
||||
|
||||
std::shared_ptr<RecastMesh> getMesh(std::string_view worldspace, const TilePosition& tilePosition) const;
|
||||
std::shared_ptr<RecastMesh> getMesh(std::string_view worldspace, const TilePosition& tilePosition);
|
||||
|
||||
std::shared_ptr<RecastMesh> getCachedMesh(std::string_view worldspace, const TilePosition& tilePosition) const;
|
||||
|
||||
std::shared_ptr<RecastMesh> getNewMesh(std::string_view worldspace, const TilePosition& tilePosition) const;
|
||||
|
||||
template <class Function>
|
||||
void forEachTile(Function&& function) const
|
||||
{
|
||||
const auto& locked = mWorldspaceTiles.lockConst();
|
||||
for (const auto& [tilePosition, recastMeshManager] : locked->mTiles)
|
||||
function(tilePosition, *recastMeshManager);
|
||||
}
|
||||
|
||||
std::size_t getRevision() const { return mRevision; }
|
||||
|
||||
void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const;
|
||||
void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion);
|
||||
|
||||
void addChangedTile(const TilePosition& tilePosition, ChangeType changeType);
|
||||
|
||||
std::map<osg::Vec2i, ChangeType> takeChangedTiles() { return std::move(mChangedTiles); }
|
||||
std::map<osg::Vec2i, ChangeType> takeChangedTiles();
|
||||
|
||||
private:
|
||||
using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>;
|
||||
struct Report
|
||||
{
|
||||
std::size_t mRevision;
|
||||
Version mNavMeshVersion;
|
||||
};
|
||||
|
||||
struct ObjectData
|
||||
{
|
||||
const CollisionShape mShape;
|
||||
const btTransform mTransform;
|
||||
const AreaType mAreaType;
|
||||
std::set<TilePosition> mTiles;
|
||||
RecastMeshObject mObject;
|
||||
TilesPositionsRange mRange;
|
||||
CommulativeAabb mAabb;
|
||||
std::size_t mGeneration = 0;
|
||||
std::size_t mRevision = 0;
|
||||
std::optional<Report> mLastNavMeshReportedChange;
|
||||
std::optional<Report> mLastNavMeshReport;
|
||||
};
|
||||
|
||||
struct WorldspaceTiles
|
||||
struct WaterData
|
||||
{
|
||||
std::string mWorldspace;
|
||||
TilesMap mTiles;
|
||||
Water mWater;
|
||||
std::optional<TilesPositionsRange> mRange;
|
||||
std::size_t mRevision;
|
||||
};
|
||||
|
||||
struct HeightfieldData
|
||||
{
|
||||
int mCellSize;
|
||||
HeightfieldShape mShape;
|
||||
std::optional<TilesPositionsRange> mRange;
|
||||
std::size_t mRevision;
|
||||
};
|
||||
|
||||
struct CachedTile
|
||||
{
|
||||
Version mVersion;
|
||||
std::shared_ptr<RecastMesh> mRecastMesh;
|
||||
};
|
||||
|
||||
using IndexPoint = boost::geometry::model::point<int, 2, boost::geometry::cs::cartesian>;
|
||||
using IndexBox = boost::geometry::model::box<IndexPoint>;
|
||||
using ObjectIndexValue = std::pair<IndexBox, ObjectData*>;
|
||||
using WaterIndexValue = std::pair<IndexBox, std::map<osg::Vec2i, WaterData>::const_iterator>;
|
||||
using HeightfieldIndexValue = std::pair<IndexBox, std::map<osg::Vec2i, HeightfieldData>::const_iterator>;
|
||||
|
||||
const RecastSettings& mSettings;
|
||||
TileBounds mBounds;
|
||||
TilesPositionsRange mRange;
|
||||
Misc::ScopeGuarded<WorldspaceTiles> mWorldspaceTiles;
|
||||
std::unordered_map<ObjectId, ObjectData> mObjects;
|
||||
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
|
||||
std::map<osg::Vec2i, std::vector<TilePosition>> mHeightfieldTilesPositions;
|
||||
std::string mWorldspace;
|
||||
std::unordered_map<ObjectId, std::unique_ptr<ObjectData>> mObjects;
|
||||
boost::geometry::index::rtree<ObjectIndexValue, boost::geometry::index::quadratic<16>> mObjectIndex;
|
||||
std::map<osg::Vec2i, WaterData> mWater;
|
||||
std::map<osg::Vec2i, WaterData>::const_iterator mInfiniteWater = mWater.end();
|
||||
boost::geometry::index::rtree<WaterIndexValue, boost::geometry::index::linear<4>> mWaterIndex;
|
||||
std::map<osg::Vec2i, HeightfieldData> mHeightfields;
|
||||
std::map<osg::Vec2i, HeightfieldData>::const_iterator mInfiniteHeightfield = mHeightfields.end();
|
||||
boost::geometry::index::rtree<HeightfieldIndexValue, boost::geometry::index::linear<4>> mHeightfieldIndex;
|
||||
std::map<osg::Vec2i, ChangeType> mChangedTiles;
|
||||
std::map<TilePosition, CachedTile> mCache;
|
||||
std::size_t mGeneration = 0;
|
||||
std::size_t mRevision = 0;
|
||||
std::size_t mTilesGeneration = 0;
|
||||
mutable std::mutex mMutex;
|
||||
|
||||
inline bool addTile(ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||
AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles);
|
||||
inline static IndexPoint makeIndexPoint(const TilePosition& tilePosition);
|
||||
|
||||
inline bool removeTile(ObjectId id, const TilePosition& tilePosition, TilesMap& tiles);
|
||||
inline static IndexBox makeIndexBox(const TilesPositionsRange& range);
|
||||
|
||||
inline std::shared_ptr<CachedRecastMeshManager> getManager(std::string_view worldspace,
|
||||
const TilePosition& tilePosition) const;
|
||||
inline static ObjectIndexValue makeObjectIndexValue(const TilesPositionsRange& range, ObjectData* data);
|
||||
|
||||
inline static WaterIndexValue makeWaterIndexValue(const TilesPositionsRange& range,
|
||||
std::map<osg::Vec2i, WaterData>::const_iterator it);
|
||||
|
||||
inline static HeightfieldIndexValue makeHeightfieldIndexValue(const TilesPositionsRange& range,
|
||||
std::map<osg::Vec2i, HeightfieldData>::const_iterator it);
|
||||
|
||||
inline static auto makeIndexQuery(const TilePosition& tilePosition)
|
||||
-> decltype(boost::geometry::index::intersects(IndexBox()));
|
||||
|
||||
inline std::shared_ptr<RecastMesh> makeMesh(const TilePosition& tilePosition) const;
|
||||
|
||||
inline void addChangedTiles(const std::optional<TilesPositionsRange>& range, ChangeType changeType);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "tileposition.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
struct TilesPositionsRange
|
||||
|
@ -10,6 +12,16 @@ namespace DetourNavigator
|
|||
TilePosition mBegin;
|
||||
TilePosition mEnd;
|
||||
};
|
||||
|
||||
inline auto tie(const TilesPositionsRange& value)
|
||||
{
|
||||
return std::tie(value.mBegin, value.mEnd);
|
||||
}
|
||||
|
||||
inline bool operator==(const TilesPositionsRange& lhs, const TilesPositionsRange& rhs)
|
||||
{
|
||||
return tie(lhs) == tie(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue