mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-13 10:13:06 +00:00
Merge branch 'cull_navmesh_objects' into 'master'
Cull navmesh objects by scene bounds (#5858) Closes #5858 See merge request OpenMW/openmw!1625
This commit is contained in:
commit
becffef142
28 changed files with 539 additions and 211 deletions
|
|
@ -177,8 +177,8 @@ namespace NavMeshTool
|
||||||
|
|
||||||
DetourNavigator::getTilesPositions(
|
DetourNavigator::getTilesPositions(
|
||||||
DetourNavigator::makeTilesPositionsRange(
|
DetourNavigator::makeTilesPositionsRange(
|
||||||
Misc::Convert::toOsg(input->mAabb.m_min),
|
Misc::Convert::toOsgXY(input->mAabb.m_min),
|
||||||
Misc::Convert::toOsg(input->mAabb.m_max),
|
Misc::Convert::toOsgXY(input->mAabb.m_max),
|
||||||
settings.mRecast
|
settings.mRecast
|
||||||
),
|
),
|
||||||
[&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); }
|
[&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); }
|
||||||
|
|
|
||||||
|
|
@ -298,12 +298,14 @@ namespace NavMeshTool
|
||||||
const ObjectId objectId(++objectsCounter);
|
const ObjectId objectId(++objectsCounter);
|
||||||
const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform());
|
const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform());
|
||||||
|
|
||||||
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, DetourNavigator::AreaType_ground);
|
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform,
|
||||||
|
DetourNavigator::AreaType_ground, [] (const auto&) {});
|
||||||
|
|
||||||
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
||||||
{
|
{
|
||||||
const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform());
|
const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform());
|
||||||
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform, DetourNavigator::AreaType_null);
|
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform,
|
||||||
|
DetourNavigator::AreaType_null, [] (const auto&) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
data.mObjects.emplace_back(std::move(object));
|
data.mObjects.emplace_back(std::move(object));
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ namespace MWPhysics
|
||||||
assert (mShapeInstance->mCollisionShape->isCompound());
|
assert (mShapeInstance->mCollisionShape->isCompound());
|
||||||
|
|
||||||
btCompoundShape* compound = static_cast<btCompoundShape*>(mShapeInstance->mCollisionShape.get());
|
btCompoundShape* compound = static_cast<btCompoundShape*>(mShapeInstance->mCollisionShape.get());
|
||||||
|
bool result = false;
|
||||||
for (const auto& [recIndex, shapeIndex] : mShapeInstance->mAnimatedShapes)
|
for (const auto& [recIndex, shapeIndex] : mShapeInstance->mAnimatedShapes)
|
||||||
{
|
{
|
||||||
auto nodePathFound = mRecIndexToNodePath.find(recIndex);
|
auto nodePathFound = mRecIndexToNodePath.find(recIndex);
|
||||||
|
|
@ -145,8 +146,11 @@ namespace MWPhysics
|
||||||
// Note: we can not apply scaling here for now since we treat scaled shapes
|
// Note: we can not apply scaling here for now since we treat scaled shapes
|
||||||
// as new shapes (btScaledBvhTriangleMeshShape) with 1.0 scale for now
|
// as new shapes (btScaledBvhTriangleMeshShape) with 1.0 scale for now
|
||||||
if (!(transform == compound->getChildTransform(shapeIndex)))
|
if (!(transform == compound->getChildTransform(shapeIndex)))
|
||||||
|
{
|
||||||
compound->updateChildTransform(shapeIndex, transform);
|
compound->updateChildTransform(shapeIndex, transform);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -490,7 +490,7 @@ namespace MWPhysics
|
||||||
mObjects.emplace(ptr.mRef, obj);
|
mObjects.emplace(ptr.mRef, obj);
|
||||||
|
|
||||||
if (obj->isAnimated())
|
if (obj->isAnimated())
|
||||||
mAnimatedObjects.insert(obj.get());
|
mAnimatedObjects.emplace(obj.get(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSystem::remove(const MWWorld::Ptr &ptr)
|
void PhysicsSystem::remove(const MWWorld::Ptr &ptr)
|
||||||
|
|
@ -739,13 +739,18 @@ namespace MWPhysics
|
||||||
|
|
||||||
void PhysicsSystem::stepSimulation(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
void PhysicsSystem::stepSimulation(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||||
{
|
{
|
||||||
for (Object* animatedObject : mAnimatedObjects)
|
for (auto& [animatedObject, changed] : mAnimatedObjects)
|
||||||
{
|
{
|
||||||
if (animatedObject->animateCollisionShapes())
|
if (animatedObject->animateCollisionShapes())
|
||||||
{
|
{
|
||||||
auto obj = mObjects.find(animatedObject->getPtr().mRef);
|
auto obj = mObjects.find(animatedObject->getPtr().mRef);
|
||||||
assert(obj != mObjects.end());
|
assert(obj != mObjects.end());
|
||||||
mTaskScheduler->updateSingleAabb(obj->second);
|
mTaskScheduler->updateSingleAabb(obj->second);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
changed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ namespace MWPhysics
|
||||||
using ObjectMap = std::unordered_map<const MWWorld::LiveCellRefBase*, std::shared_ptr<Object>>;
|
using ObjectMap = std::unordered_map<const MWWorld::LiveCellRefBase*, std::shared_ptr<Object>>;
|
||||||
ObjectMap mObjects;
|
ObjectMap mObjects;
|
||||||
|
|
||||||
std::set<Object*> mAnimatedObjects; // stores pointers to elements in mObjects
|
std::map<Object*, bool> mAnimatedObjects; // stores pointers to elements in mObjects
|
||||||
|
|
||||||
ActorMap mActors;
|
ActorMap mActors;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,7 @@ namespace MWWorld
|
||||||
mActiveCells.erase(cell);
|
mActiveCells.erase(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn)
|
void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position)
|
||||||
{
|
{
|
||||||
using DetourNavigator::HeightfieldShape;
|
using DetourNavigator::HeightfieldShape;
|
||||||
|
|
||||||
|
|
@ -461,7 +461,7 @@ namespace MWWorld
|
||||||
mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f);
|
mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
mNavigator.update(player.getRefData().getPosition().asVec3());
|
mNavigator.update(position);
|
||||||
|
|
||||||
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
|
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
|
||||||
mRendering.configureAmbient(cell->getCell());
|
mRendering.configureAmbient(cell->getCell());
|
||||||
|
|
@ -534,6 +534,8 @@ namespace MWWorld
|
||||||
unloadCell (cell);
|
unloadCell (cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mNavigator.updateBounds(pos);
|
||||||
|
|
||||||
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
|
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
|
||||||
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
|
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
|
||||||
mRendering.setActiveGrid(newGrid);
|
mRendering.setActiveGrid(newGrid);
|
||||||
|
|
@ -608,7 +610,7 @@ namespace MWWorld
|
||||||
if (!isCellInCollection(x, y, mActiveCells))
|
if (!isCellInCollection(x, y, mActiveCells))
|
||||||
{
|
{
|
||||||
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);
|
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);
|
||||||
loadCell (cell, loadingListener, changeEvent);
|
loadCell(cell, loadingListener, changeEvent, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,7 +643,7 @@ namespace MWWorld
|
||||||
loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")...");
|
loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")...");
|
||||||
|
|
||||||
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY);
|
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY);
|
||||||
loadCell(cell, nullptr, false);
|
loadCell(cell, nullptr, false, osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits);
|
||||||
|
|
||||||
auto iter = mActiveCells.begin();
|
auto iter = mActiveCells.begin();
|
||||||
while (iter != mActiveCells.end())
|
while (iter != mActiveCells.end())
|
||||||
|
|
@ -686,7 +688,9 @@ namespace MWWorld
|
||||||
loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")...");
|
loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")...");
|
||||||
|
|
||||||
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName);
|
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName);
|
||||||
loadCell(cell, nullptr, false);
|
ESM::Position position;
|
||||||
|
MWBase::Environment::get().getWorld()->findInteriorPosition(it->mName, position);
|
||||||
|
loadCell(cell, nullptr, false, position.asVec3());
|
||||||
|
|
||||||
auto iter = mActiveCells.begin();
|
auto iter = mActiveCells.begin();
|
||||||
while (iter != mActiveCells.end())
|
while (iter != mActiveCells.end())
|
||||||
|
|
@ -819,9 +823,11 @@ namespace MWWorld
|
||||||
|
|
||||||
loadingListener->setProgressRange(cell->count());
|
loadingListener->setProgressRange(cell->count());
|
||||||
|
|
||||||
|
mNavigator.updateBounds(position.asVec3());
|
||||||
|
|
||||||
// Load cell.
|
// Load cell.
|
||||||
mPagedRefs.clear();
|
mPagedRefs.clear();
|
||||||
loadCell(cell, loadingListener, changeEvent);
|
loadCell(cell, loadingListener, changeEvent, position.asVec3());
|
||||||
|
|
||||||
changePlayerCell(cell, position, adjustPlayerPos);
|
changePlayerCell(cell, position, adjustPlayerPos);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ namespace MWWorld
|
||||||
osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const;
|
osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const;
|
||||||
|
|
||||||
void unloadCell(CellStore* cell);
|
void unloadCell(CellStore* cell);
|
||||||
void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn);
|
void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn, const osg::Vec3f& position);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1525,7 +1525,12 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::updateNavigator()
|
void World::updateNavigator()
|
||||||
{
|
{
|
||||||
mPhysics->forEachAnimatedObject([&] (const MWPhysics::Object* object) { updateNavigatorObject(*object); });
|
mPhysics->forEachAnimatedObject([&] (const auto& pair)
|
||||||
|
{
|
||||||
|
const auto [object, changed] = pair;
|
||||||
|
if (changed)
|
||||||
|
updateNavigatorObject(*object);
|
||||||
|
});
|
||||||
|
|
||||||
for (const auto& door : mDoorStates)
|
for (const auto& door : mDoorStates)
|
||||||
if (const auto object = mPhysics->getObject(door.first))
|
if (const auto object = mPhysics->getObject(door.first))
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace
|
||||||
osg::ref_ptr<Resource::BulletShapeInstance>(new Resource::BulletShapeInstance(bulletShape)),
|
osg::ref_ptr<Resource::BulletShapeInstance>(new Resource::BulletShapeInstance(bulletShape)),
|
||||||
shape, objectTransform
|
shape, objectTransform
|
||||||
);
|
);
|
||||||
recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground);
|
recastMeshManager.addObject(id, collisionShape, btTransform::getIdentity(), AreaType_ground, [] (auto) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DetourNavigatorAsyncNavMeshUpdaterTest : Test
|
struct DetourNavigatorAsyncNavMeshUpdaterTest : Test
|
||||||
|
|
|
||||||
|
|
@ -37,35 +37,35 @@ namespace
|
||||||
|
|
||||||
TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_in_single_tile_should_return_one_tile)
|
TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_in_single_tile_should_return_one_tile)
|
||||||
{
|
{
|
||||||
getTilesPositions(makeTilesPositionsRange(osg::Vec3f(2, 2, 0), osg::Vec3f(31, 31, 1), mSettings), mCollect);
|
getTilesPositions(makeTilesPositionsRange(osg::Vec2f(2, 2), osg::Vec2f(31, 31), mSettings), mCollect);
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_x_bounds_in_two_tiles_should_return_two_tiles)
|
TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_x_bounds_in_two_tiles_should_return_two_tiles)
|
||||||
{
|
{
|
||||||
getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 31, 1), mSettings), mCollect);
|
getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(32, 31), mSettings), mCollect);
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(1, 0)));
|
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(1, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_y_bounds_in_two_tiles_should_return_two_tiles)
|
TEST_F(DetourNavigatorGetTilesPositionsTest, for_object_with_y_bounds_in_two_tiles_should_return_two_tiles)
|
||||||
{
|
{
|
||||||
getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 32, 1), mSettings), mCollect);
|
getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(31, 32), mSettings), mCollect);
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(0, 1)));
|
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0), TilePosition(0, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_works_only_for_x_and_y_coordinates)
|
TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_works_only_for_x_and_y_coordinates)
|
||||||
{
|
{
|
||||||
getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31, 31, 32), mSettings), mCollect);
|
getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(31, 31), mSettings), mCollect);
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_should_work_with_negative_coordinates)
|
TEST_F(DetourNavigatorGetTilesPositionsTest, tiling_should_work_with_negative_coordinates)
|
||||||
{
|
{
|
||||||
getTilesPositions(makeTilesPositionsRange(osg::Vec3f(-31, -31, 0), osg::Vec3f(31, 31, 1), mSettings), mCollect);
|
getTilesPositions(makeTilesPositionsRange(osg::Vec2f(-31, -31), osg::Vec2f(31, 31), mSettings), mCollect);
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(
|
EXPECT_THAT(mTilesPositions, ElementsAre(
|
||||||
TilePosition(-1, -1),
|
TilePosition(-1, -1),
|
||||||
|
|
@ -79,7 +79,7 @@ namespace
|
||||||
{
|
{
|
||||||
mSettings.mBorderSize = 1;
|
mSettings.mBorderSize = 1;
|
||||||
|
|
||||||
getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(31.5, 31.5, 1), mSettings), mCollect);
|
getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(31.5, 31.5), mSettings), mCollect);
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(
|
EXPECT_THAT(mTilesPositions, ElementsAre(
|
||||||
TilePosition(-1, -1),
|
TilePosition(-1, -1),
|
||||||
|
|
@ -98,7 +98,7 @@ namespace
|
||||||
{
|
{
|
||||||
mSettings.mRecastScaleFactor = 0.5;
|
mSettings.mRecastScaleFactor = 0.5;
|
||||||
|
|
||||||
getTilesPositions(makeTilesPositionsRange(osg::Vec3f(0, 0, 0), osg::Vec3f(32, 32, 1), mSettings), mCollect);
|
getTilesPositions(makeTilesPositionsRange(osg::Vec2f(0, 0), osg::Vec2f(32, 32), mSettings), mCollect);
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1132,4 +1132,16 @@ namespace
|
||||||
Vec3fEq(306, 56.66666412353515625, -2.6667339801788330078125)
|
Vec3fEq(306, 56.66666412353515625, -2.6667339801788330078125)
|
||||||
)) << mPath;
|
)) << mPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, only_one_water_per_cell_is_allowed)
|
||||||
|
{
|
||||||
|
const int cellSize1 = 100;
|
||||||
|
const float level1 = 1;
|
||||||
|
const int cellSize2 = 200;
|
||||||
|
const float level2 = 2;
|
||||||
|
|
||||||
|
mNavigator->addAgent(mAgentHalfExtents);
|
||||||
|
EXPECT_TRUE(mNavigator->addWater(mCellPosition, cellSize1, level1));
|
||||||
|
EXPECT_FALSE(mNavigator->addWater(mCellPosition, cellSize2, level2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,6 @@
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
namespace DetourNavigator
|
|
||||||
{
|
|
||||||
static inline bool operator ==(const TileBounds& lhs, const TileBounds& rhs)
|
|
||||||
{
|
|
||||||
return lhs.mMin == rhs.mMin && lhs.mMax == rhs.mMax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template <class T>
|
template <class T>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ namespace
|
||||||
struct DetourNavigatorTileCachedRecastMeshManagerTest : Test
|
struct DetourNavigatorTileCachedRecastMeshManagerTest : Test
|
||||||
{
|
{
|
||||||
RecastSettings mSettings;
|
RecastSettings mSettings;
|
||||||
std::vector<TilePosition> mChangedTiles;
|
std::vector<TilePosition> mAddedTiles;
|
||||||
|
std::vector<std::pair<TilePosition, ChangeType>> mChangedTiles;
|
||||||
const ObjectTransform mObjectTransform {ESM::Position {{0, 0, 0}, {0, 0, 0}}, 0.0f};
|
const ObjectTransform mObjectTransform {ESM::Position {{0, 0, 0}, {0, 0, 0}}, 0.0f};
|
||||||
const osg::ref_ptr<const Resource::BulletShape> mShape = new Resource::BulletShape;
|
const osg::ref_ptr<const Resource::BulletShape> mShape = new Resource::BulletShape;
|
||||||
const osg::ref_ptr<const Resource::BulletShapeInstance> mInstance = new Resource::BulletShapeInstance(mShape);
|
const osg::ref_ptr<const Resource::BulletShapeInstance> mInstance = new Resource::BulletShapeInstance(mShape);
|
||||||
|
|
@ -29,9 +30,14 @@ namespace
|
||||||
mSettings.mTileSize = 64;
|
mSettings.mTileSize = 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onChangedTile(const TilePosition& tilePosition)
|
void onAddedTile(const TilePosition& tilePosition)
|
||||||
{
|
{
|
||||||
mChangedTiles.push_back(tilePosition);
|
mAddedTiles.push_back(tilePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChangedTile(const TilePosition& tilePosition, ChangeType changeType)
|
||||||
|
{
|
||||||
|
mChangedTiles.emplace_back(tilePosition, changeType);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -60,7 +66,7 @@ namespace
|
||||||
TileCachedRecastMeshManager manager(mSettings);
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false)
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false)
|
||||||
|
|
@ -68,8 +74,8 @@ namespace
|
||||||
TileCachedRecastMeshManager manager(mSettings);
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)
|
||||||
|
|
@ -78,26 +84,47 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
for (int x = -1; x < 1; ++x)
|
for (int x = -1; x < 1; ++x)
|
||||||
for (int y = -1; y < 1; ++y)
|
for (int y = -1; y < 1; ++y)
|
||||||
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
|
ASSERT_NE(manager.getMesh("worldspace", TilePosition(x, y)), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_return_added_tiles)
|
||||||
|
{
|
||||||
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
|
TileBounds bounds;
|
||||||
|
bounds.mMin = osg::Vec2f(182, 182);
|
||||||
|
bounds.mMax = osg::Vec2f(1000, 1000);
|
||||||
|
manager.setBounds(bounds);
|
||||||
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
|
||||||
|
[&] (const auto& v) { onAddedTile(v); });
|
||||||
|
EXPECT_THAT(mAddedTiles, ElementsAre(TilePosition(0, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles)
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_changed_object_should_return_changed_tiles)
|
||||||
{
|
{
|
||||||
TileCachedRecastMeshManager manager(mSettings);
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
|
TileBounds bounds;
|
||||||
|
bounds.mMin = osg::Vec2f(-1000, -1000);
|
||||||
|
bounds.mMax = osg::Vec2f(1000, 1000);
|
||||||
|
manager.setBounds(bounds);
|
||||||
|
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
|
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
|
||||||
[&] (const auto& v) { onChangedTile(v); }));
|
[&] (const auto& ... v) { onChangedTile(v ...); }));
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(mChangedTiles, ElementsAre(
|
||||||
mChangedTiles,
|
std::pair(TilePosition(-1, -1), ChangeType::add),
|
||||||
ElementsAre(TilePosition(-1, -1), TilePosition(-1, 0), TilePosition(0, -1), TilePosition(0, 0),
|
std::pair(TilePosition(-1, 0), ChangeType::add),
|
||||||
TilePosition(1, -1), TilePosition(1, 0))
|
std::pair(TilePosition(0, -1), ChangeType::update),
|
||||||
);
|
std::pair(TilePosition(0, 0), ChangeType::update),
|
||||||
|
std::pair(TilePosition(1, -1), ChangeType::remove),
|
||||||
|
std::pair(TilePosition(1, 0), ChangeType::remove)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_return_empty)
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, update_object_for_not_changed_object_should_return_empty)
|
||||||
|
|
@ -105,10 +132,10 @@ namespace
|
||||||
TileCachedRecastMeshManager manager(mSettings);
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
|
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
|
||||||
[&] (const auto& v) { onChangedTile(v); }));
|
[&] (const auto& ... v) { onChangedTile(v ...); }));
|
||||||
EXPECT_EQ(mChangedTiles, std::vector<TilePosition>());
|
EXPECT_THAT(mChangedTiles, IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile)
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_after_add_object_should_return_recast_mesh_for_each_used_tile)
|
||||||
|
|
@ -117,7 +144,7 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||||
|
|
@ -130,26 +157,30 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile)
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, get_mesh_for_moved_object_should_return_recast_mesh_for_each_used_tile)
|
||||||
{
|
{
|
||||||
TileCachedRecastMeshManager manager(mSettings);
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
|
TileBounds bounds;
|
||||||
|
bounds.mMin = osg::Vec2f(-1000, -1000);
|
||||||
|
bounds.mMax = osg::Vec2f(1000, 1000);
|
||||||
|
manager.setBounds(bounds);
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
|
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
|
|
||||||
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
|
||||||
|
|
||||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||||
|
|
@ -165,11 +196,11 @@ namespace
|
||||||
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
|
|
||||||
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||||
|
|
||||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
|
||||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, 0)), nullptr);
|
||||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
|
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(1, -1)), nullptr);
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +211,7 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
manager.removeObject(ObjectId(&boxShape));
|
manager.removeObject(ObjectId(&boxShape));
|
||||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||||
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
EXPECT_EQ(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||||
|
|
@ -195,13 +226,13 @@ namespace
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
|
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, 0)), nullptr);
|
||||||
|
|
||||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, -1)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(-1, 0)), nullptr);
|
||||||
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
EXPECT_NE(manager.getMesh("worldspace", TilePosition(0, -1)), nullptr);
|
||||||
|
|
@ -214,7 +245,7 @@ namespace
|
||||||
const auto initialRevision = manager.getRevision();
|
const auto initialRevision = manager.getRevision();
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
EXPECT_EQ(manager.getRevision(), initialRevision + 1);
|
EXPECT_EQ(manager.getRevision(), initialRevision + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,9 +254,9 @@ namespace
|
||||||
TileCachedRecastMeshManager manager(mSettings);
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
const auto beforeAddRevision = manager.getRevision();
|
const auto beforeAddRevision = manager.getRevision();
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
EXPECT_EQ(manager.getRevision(), beforeAddRevision);
|
EXPECT_EQ(manager.getRevision(), beforeAddRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,9 +266,9 @@ namespace
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground, [] (auto) {});
|
||||||
const auto beforeUpdateRevision = manager.getRevision();
|
const auto beforeUpdateRevision = manager.getRevision();
|
||||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
|
||||||
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1);
|
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,9 +278,9 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
const auto beforeUpdateRevision = manager.getRevision();
|
const auto beforeUpdateRevision = manager.getRevision();
|
||||||
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto, auto) {});
|
||||||
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision);
|
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,7 +289,7 @@ namespace
|
||||||
TileCachedRecastMeshManager manager(mSettings);
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
const auto beforeRemoveRevision = manager.getRevision();
|
const auto beforeRemoveRevision = manager.getRevision();
|
||||||
manager.removeObject(ObjectId(&boxShape));
|
manager.removeObject(ObjectId(&boxShape));
|
||||||
EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1);
|
EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1);
|
||||||
|
|
@ -298,7 +329,7 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
const osg::Vec2i cellPosition(0, 0);
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
const int cellSize = std::numeric_limits<int>::max();
|
const int cellSize = std::numeric_limits<int>::max();
|
||||||
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
|
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
|
||||||
|
|
@ -343,7 +374,7 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
const osg::Vec2i cellPosition(0, 0);
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
const int cellSize = 8192;
|
const int cellSize = 8192;
|
||||||
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
|
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
|
||||||
|
|
@ -361,7 +392,7 @@ namespace
|
||||||
const int cellSize = 8192;
|
const int cellSize = 8192;
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
|
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, 0.0f));
|
||||||
ASSERT_TRUE(manager.removeObject(ObjectId(&boxShape)));
|
ASSERT_TRUE(manager.removeObject(ObjectId(&boxShape)));
|
||||||
for (int x = -1; x < 12; ++x)
|
for (int x = -1; x < 12; ++x)
|
||||||
|
|
@ -375,10 +406,28 @@ namespace
|
||||||
manager.setWorldspace("worldspace");
|
manager.setWorldspace("worldspace");
|
||||||
const btBoxShape boxShape(btVector3(20, 20, 100));
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
const CollisionShape shape(nullptr, boxShape, mObjectTransform);
|
const CollisionShape shape(nullptr, boxShape, mObjectTransform);
|
||||||
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
|
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}));
|
||||||
manager.setWorldspace("other");
|
manager.setWorldspace("other");
|
||||||
for (int x = -1; x < 1; ++x)
|
for (int x = -1; x < 1; ++x)
|
||||||
for (int y = -1; y < 1; ++y)
|
for (int y = -1; y < 1; ++y)
|
||||||
ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr);
|
ASSERT_EQ(manager.getMesh("other", TilePosition(x, y)), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, set_bounds_should_return_changed_tiles)
|
||||||
|
{
|
||||||
|
TileCachedRecastMeshManager manager(mSettings);
|
||||||
|
const btBoxShape boxShape(btVector3(20, 20, 100));
|
||||||
|
const CollisionShape shape(mInstance, boxShape, mObjectTransform);
|
||||||
|
TileBounds bounds;
|
||||||
|
bounds.mMin = osg::Vec2f(182, 0);
|
||||||
|
bounds.mMax = osg::Vec2f(1000, 1000);
|
||||||
|
manager.setBounds(bounds);
|
||||||
|
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
|
||||||
|
bounds.mMin = osg::Vec2f(-1000, -1000);
|
||||||
|
bounds.mMax = osg::Vec2f(0, -182);
|
||||||
|
EXPECT_THAT(manager.setBounds(bounds), ElementsAre(
|
||||||
|
std::pair(TilePosition(-1, -1), ChangeType::add),
|
||||||
|
std::pair(TilePosition(0, 0), ChangeType::remove)
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "navmeshtilescache.hpp"
|
#include "navmeshtilescache.hpp"
|
||||||
#include "waitconditiontype.hpp"
|
#include "waitconditiontype.hpp"
|
||||||
#include "navmeshdb.hpp"
|
#include "navmeshdb.hpp"
|
||||||
|
#include "changetype.hpp"
|
||||||
|
|
||||||
#include <osg/Vec3f>
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
|
|
@ -32,29 +33,6 @@ namespace Loading
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
enum class ChangeType
|
|
||||||
{
|
|
||||||
remove = 0,
|
|
||||||
mixed = 1,
|
|
||||||
add = 2,
|
|
||||||
update = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::ostream& operator <<(std::ostream& stream, ChangeType value)
|
|
||||||
{
|
|
||||||
switch (value) {
|
|
||||||
case ChangeType::remove:
|
|
||||||
return stream << "ChangeType::remove";
|
|
||||||
case ChangeType::mixed:
|
|
||||||
return stream << "ChangeType::mixed";
|
|
||||||
case ChangeType::add:
|
|
||||||
return stream << "ChangeType::add";
|
|
||||||
case ChangeType::update:
|
|
||||||
return stream << "ChangeType::update";
|
|
||||||
}
|
|
||||||
return stream << "ChangeType::" << static_cast<int>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class JobState
|
enum class JobState
|
||||||
{
|
{
|
||||||
Initial,
|
Initial,
|
||||||
|
|
|
||||||
33
components/detournavigator/changetype.hpp
Normal file
33
components/detournavigator/changetype.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CHANGETYPE_H
|
||||||
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CHANGETYPE_H
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
enum class ChangeType
|
||||||
|
{
|
||||||
|
remove = 0,
|
||||||
|
mixed = 1,
|
||||||
|
add = 2,
|
||||||
|
update = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::ostream& operator <<(std::ostream& stream, ChangeType value)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case ChangeType::remove:
|
||||||
|
return stream << "ChangeType::remove";
|
||||||
|
case ChangeType::mixed:
|
||||||
|
return stream << "ChangeType::mixed";
|
||||||
|
case ChangeType::add:
|
||||||
|
return stream << "ChangeType::add";
|
||||||
|
case ChangeType::update:
|
||||||
|
return stream << "ChangeType::update";
|
||||||
|
}
|
||||||
|
return stream << "ChangeType::" << static_cast<int>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "settingsutils.hpp"
|
#include "settingsutils.hpp"
|
||||||
#include "tileposition.hpp"
|
#include "tileposition.hpp"
|
||||||
|
#include "tilebounds.hpp"
|
||||||
|
|
||||||
#include <components/misc/convert.hpp>
|
#include <components/misc/convert.hpp>
|
||||||
|
|
||||||
|
|
@ -9,15 +10,15 @@
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
TilesPositionsRange makeTilesPositionsRange(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax,
|
TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin, const osg::Vec2f& aabbMax,
|
||||||
const RecastSettings& settings)
|
const RecastSettings& settings)
|
||||||
{
|
{
|
||||||
osg::Vec3f min = toNavMeshCoordinates(settings, aabbMin);
|
osg::Vec2f min = toNavMeshCoordinates(settings, aabbMin);
|
||||||
osg::Vec3f max = toNavMeshCoordinates(settings, aabbMax);
|
osg::Vec2f max = toNavMeshCoordinates(settings, aabbMax);
|
||||||
|
|
||||||
const float border = getBorderSize(settings);
|
const float border = getBorderSize(settings);
|
||||||
min -= osg::Vec3f(border, border, border);
|
min -= osg::Vec2f(border, border);
|
||||||
max += osg::Vec3f(border, border, border);
|
max += osg::Vec2f(border, border);
|
||||||
|
|
||||||
TilePosition minTile = getTilePosition(settings, min);
|
TilePosition minTile = getTilePosition(settings, min);
|
||||||
TilePosition maxTile = getTilePosition(settings, max);
|
TilePosition maxTile = getTilePosition(settings, max);
|
||||||
|
|
@ -28,24 +29,27 @@ namespace DetourNavigator
|
||||||
if (minTile.y() > maxTile.y())
|
if (minTile.y() > maxTile.y())
|
||||||
std::swap(minTile.y(), maxTile.y());
|
std::swap(minTile.y(), maxTile.y());
|
||||||
|
|
||||||
return {minTile, maxTile};
|
return {minTile, maxTile + osg::Vec2i(1, 1)};
|
||||||
}
|
}
|
||||||
|
|
||||||
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform,
|
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform,
|
||||||
const RecastSettings& settings)
|
const RecastSettings& settings)
|
||||||
{
|
{
|
||||||
btVector3 aabbMin;
|
const TileBounds bounds = makeObjectTileBounds(shape, transform);
|
||||||
btVector3 aabbMax;
|
return makeTilesPositionsRange(bounds.mMin, bounds.mMax, settings);
|
||||||
shape.getAabb(transform, aabbMin, aabbMax);
|
}
|
||||||
|
|
||||||
return makeTilesPositionsRange(Misc::Convert::toOsg(aabbMin), Misc::Convert::toOsg(aabbMax), settings);
|
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape, const btTransform& transform,
|
||||||
|
const TileBounds& bounds, const RecastSettings& settings)
|
||||||
|
{
|
||||||
|
if (const auto intersection = getIntersection(bounds, makeObjectTileBounds(shape, transform)))
|
||||||
|
return makeTilesPositionsRange(intersection->mMin, intersection->mMax, settings);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift,
|
TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift,
|
||||||
const RecastSettings& settings)
|
const RecastSettings& settings)
|
||||||
{
|
{
|
||||||
using Misc::Convert::toOsg;
|
|
||||||
|
|
||||||
const int halfCellSize = cellSize / 2;
|
const int halfCellSize = cellSize / 2;
|
||||||
const btTransform transform(btMatrix3x3::getIdentity(), shift);
|
const btTransform transform(btMatrix3x3::getIdentity(), shift);
|
||||||
btVector3 aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0));
|
btVector3 aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0));
|
||||||
|
|
@ -57,6 +61,19 @@ namespace DetourNavigator
|
||||||
aabbMax.setX(std::max(aabbMin.x(), aabbMax.x()));
|
aabbMax.setX(std::max(aabbMin.x(), aabbMax.x()));
|
||||||
aabbMax.setY(std::max(aabbMin.y(), aabbMax.y()));
|
aabbMax.setY(std::max(aabbMin.y(), aabbMax.y()));
|
||||||
|
|
||||||
return makeTilesPositionsRange(toOsg(aabbMin), toOsg(aabbMax), settings);
|
return makeTilesPositionsRange(Misc::Convert::toOsgXY(aabbMin), Misc::Convert::toOsgXY(aabbMax), settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept
|
||||||
|
{
|
||||||
|
const int beginX = std::max(a.mBegin.x(), b.mBegin.x());
|
||||||
|
const int endX = std::min(a.mEnd.x(), b.mEnd.x());
|
||||||
|
if (beginX > endX)
|
||||||
|
return {};
|
||||||
|
const int beginY = std::max(a.mBegin.y(), b.mBegin.y());
|
||||||
|
const int endY = std::min(a.mEnd.y(), b.mEnd.y());
|
||||||
|
if (beginY > endY)
|
||||||
|
return {};
|
||||||
|
return TilesPositionsRange {TilePosition(beginX, beginY), TilePosition(endX, endY)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H
|
||||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_GETTILESPOSITIONS_H
|
||||||
|
|
||||||
|
#include "tilebounds.hpp"
|
||||||
#include "tileposition.hpp"
|
#include "tileposition.hpp"
|
||||||
|
|
||||||
class btVector3;
|
class btVector3;
|
||||||
|
|
@ -9,7 +10,7 @@ class btCollisionShape;
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class Vec3f;
|
class Vec2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
|
|
@ -18,26 +19,42 @@ namespace DetourNavigator
|
||||||
|
|
||||||
struct TilesPositionsRange
|
struct TilesPositionsRange
|
||||||
{
|
{
|
||||||
TilePosition mMin;
|
TilePosition mBegin;
|
||||||
TilePosition mMax;
|
TilePosition mEnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
TilesPositionsRange makeTilesPositionsRange(const osg::Vec3f& aabbMin,
|
TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin,
|
||||||
const osg::Vec3f& aabbMax, const RecastSettings& settings);
|
const osg::Vec2f& aabbMax, const RecastSettings& settings);
|
||||||
|
|
||||||
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape,
|
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape,
|
||||||
const btTransform& transform, const RecastSettings& settings);
|
const btTransform& transform, const RecastSettings& settings);
|
||||||
|
|
||||||
|
TilesPositionsRange makeTilesPositionsRange(const btCollisionShape& shape,
|
||||||
|
const btTransform& transform, const TileBounds& bounds, const RecastSettings& settings);
|
||||||
|
|
||||||
TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift,
|
TilesPositionsRange makeTilesPositionsRange(const int cellSize, const btVector3& shift,
|
||||||
const RecastSettings& settings);
|
const RecastSettings& settings);
|
||||||
|
|
||||||
template <class Callback>
|
template <class Callback>
|
||||||
void getTilesPositions(const TilesPositionsRange& range, Callback&& callback)
|
inline void getTilesPositions(const TilesPositionsRange& range, Callback&& callback)
|
||||||
{
|
{
|
||||||
for (int tileX = range.mMin.x(); tileX <= range.mMax.x(); ++tileX)
|
for (int tileX = range.mBegin.x(); tileX < range.mEnd.x(); ++tileX)
|
||||||
for (int tileY = range.mMin.y(); tileY <= range.mMax.y(); ++tileY)
|
for (int tileY = range.mBegin.y(); tileY < range.mEnd.y(); ++tileY)
|
||||||
callback(TilePosition {tileX, tileY});
|
callback(TilePosition {tileX, tileY});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isInTilesPositionsRange(int begin, int end, int coordinate)
|
||||||
|
{
|
||||||
|
return begin <= coordinate && coordinate < end;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isInTilesPositionsRange(const TilesPositionsRange& range, const TilePosition& position)
|
||||||
|
{
|
||||||
|
return isInTilesPositionsRange(range.mBegin.x(), range.mEnd.x(), position.x())
|
||||||
|
&& isInTilesPositionsRange(range.mBegin.y(), range.mEnd.y(), position.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,12 @@ namespace DetourNavigator
|
||||||
*/
|
*/
|
||||||
virtual void setWorldspace(std::string_view worldspace) = 0;
|
virtual void setWorldspace(std::string_view worldspace) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief updateBounds should be called before adding object from loading cell
|
||||||
|
* @param playerPosition corresponds to the bounds center
|
||||||
|
*/
|
||||||
|
virtual void updateBounds(const osg::Vec3f& playerPosition) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes
|
* @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes
|
||||||
* @param id is used to distinguish different objects
|
* @param id is used to distinguish different objects
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,11 @@ namespace DetourNavigator
|
||||||
mNavMeshManager.setWorldspace(worldspace);
|
mNavMeshManager.setWorldspace(worldspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavigatorImpl::updateBounds(const osg::Vec3f& playerPosition)
|
||||||
|
{
|
||||||
|
mNavMeshManager.updateBounds(playerPosition);
|
||||||
|
}
|
||||||
|
|
||||||
bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
|
bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
|
||||||
{
|
{
|
||||||
const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);
|
const CollisionShape collisionShape(shapes.mShapeInstance, *shapes.mShapeInstance->mCollisionShape, shapes.mTransform);
|
||||||
|
|
@ -158,6 +163,7 @@ namespace DetourNavigator
|
||||||
const TilePosition tilePosition = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition));
|
const TilePosition tilePosition = getTilePosition(mSettings.mRecast, toNavMeshCoordinates(mSettings.mRecast, playerPosition));
|
||||||
if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition)
|
if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition)
|
||||||
return;
|
return;
|
||||||
|
mNavMeshManager.updateBounds(playerPosition);
|
||||||
update(playerPosition);
|
update(playerPosition);
|
||||||
mLastPlayerPosition = tilePosition;
|
mLastPlayerPosition = tilePosition;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ namespace DetourNavigator
|
||||||
|
|
||||||
void setWorldspace(std::string_view worldspace) override;
|
void setWorldspace(std::string_view worldspace) override;
|
||||||
|
|
||||||
|
void updateBounds(const osg::Vec3f& playerPosition) override;
|
||||||
|
|
||||||
bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
|
bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
|
||||||
|
|
||||||
bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;
|
bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ namespace DetourNavigator
|
||||||
|
|
||||||
void update(const osg::Vec3f& /*playerPosition*/) override {}
|
void update(const osg::Vec3f& /*playerPosition*/) override {}
|
||||||
|
|
||||||
|
void updateBounds(const osg::Vec3f& /*playerPosition*/) override {}
|
||||||
|
|
||||||
void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {};
|
void updatePlayerPosition(const osg::Vec3f& /*playerPosition*/) override {};
|
||||||
|
|
||||||
void setUpdatesEnabled(bool /*enabled*/) override {}
|
void setUpdatesEnabled(bool /*enabled*/) override {}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,18 @@ namespace
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
TileBounds makeBounds(const RecastSettings& settings, const osg::Vec2f& center, int maxTiles)
|
||||||
|
{
|
||||||
|
const float radius = fromNavMeshCoordinates(settings, std::ceil(std::sqrt(static_cast<float>(maxTiles) / osg::PIf) + 1) * getTileSize(settings));
|
||||||
|
TileBounds result;
|
||||||
|
result.mMin = center - osg::Vec2f(radius, radius);
|
||||||
|
result.mMax = center + osg::Vec2f(radius, radius);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
|
NavMeshManager::NavMeshManager(const Settings& settings, std::unique_ptr<NavMeshDb>&& db)
|
||||||
: mSettings(settings)
|
: mSettings(settings)
|
||||||
, mRecastMeshManager(settings.mRecast)
|
, mRecastMeshManager(settings.mRecast)
|
||||||
|
|
@ -59,21 +71,37 @@ namespace DetourNavigator
|
||||||
mWorldspace = worldspace;
|
mWorldspace = worldspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavMeshManager::updateBounds(const osg::Vec3f& playerPosition)
|
||||||
|
{
|
||||||
|
const TileBounds bounds = makeBounds(mSettings.mRecast, osg::Vec2f(playerPosition.x(), playerPosition.y()),
|
||||||
|
mSettings.mMaxTilesNumber);
|
||||||
|
const auto changedTiles = mRecastMeshManager.setBounds(bounds);
|
||||||
|
for (const auto& [agent, cache] : mCache)
|
||||||
|
{
|
||||||
|
auto& tiles = mChangedTiles[agent];
|
||||||
|
for (const auto& [tilePosition, changeType] : changedTiles)
|
||||||
|
{
|
||||||
|
auto tile = tiles.find(tilePosition);
|
||||||
|
if (tile == tiles.end())
|
||||||
|
tiles.emplace_hint(tile, tilePosition, changeType);
|
||||||
|
else
|
||||||
|
tile->second = addChangeType(tile->second, changeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||||
const AreaType areaType)
|
const AreaType areaType)
|
||||||
{
|
{
|
||||||
const btCollisionShape& collisionShape = shape.getShape();
|
return mRecastMeshManager.addObject(id, shape, transform, areaType,
|
||||||
if (!mRecastMeshManager.addObject(id, shape, transform, areaType))
|
[&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::add); });
|
||||||
return false;
|
|
||||||
addChangedTiles(collisionShape, transform, ChangeType::add);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||||
const AreaType areaType)
|
const AreaType areaType)
|
||||||
{
|
{
|
||||||
return mRecastMeshManager.updateObject(id, shape, transform, areaType,
|
return mRecastMeshManager.updateObject(id, shape, transform, areaType,
|
||||||
[&] (const TilePosition& tile) { addChangedTile(tile, ChangeType::update); });
|
[&] (const TilePosition& tile, ChangeType changeType) { addChangedTile(tile, changeType); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavMeshManager::removeObject(const ObjectId id)
|
bool NavMeshManager::removeObject(const ObjectId id)
|
||||||
|
|
@ -263,7 +291,8 @@ namespace DetourNavigator
|
||||||
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
|
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
|
||||||
const ChangeType changeType)
|
const ChangeType changeType)
|
||||||
{
|
{
|
||||||
getTilesPositions(makeTilesPositionsRange(shape, transform, mSettings.mRecast),
|
const auto bounds = mRecastMeshManager.getBounds();
|
||||||
|
getTilesPositions(makeTilesPositionsRange(shape, transform, bounds, mSettings.mRecast),
|
||||||
[&] (const TilePosition& v) { addChangedTile(v, changeType); });
|
[&] (const TilePosition& v) { addChangedTile(v, changeType); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ namespace DetourNavigator
|
||||||
|
|
||||||
void setWorldspace(std::string_view worldspace);
|
void setWorldspace(std::string_view worldspace);
|
||||||
|
|
||||||
|
void updateBounds(const osg::Vec3f& playerPosition);
|
||||||
|
|
||||||
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||||
const AreaType areaType);
|
const AreaType areaType);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ namespace DetourNavigator
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline float fromNavMeshCoordinates(const RecastSettings& settings, float value)
|
||||||
|
{
|
||||||
|
return value / settings.mRecastScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
inline osg::Vec3f fromNavMeshCoordinates(const RecastSettings& settings, osg::Vec3f position)
|
inline osg::Vec3f fromNavMeshCoordinates(const RecastSettings& settings, osg::Vec3f position)
|
||||||
{
|
{
|
||||||
const auto factor = 1.0f / settings.mRecastScaleFactor;
|
const auto factor = 1.0f / settings.mRecastScaleFactor;
|
||||||
|
|
@ -50,12 +55,24 @@ namespace DetourNavigator
|
||||||
return static_cast<float>(settings.mTileSize) * settings.mCellSize;
|
return static_cast<float>(settings.mTileSize) * settings.mCellSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int getTilePosition(const RecastSettings& settings, float position)
|
||||||
|
{
|
||||||
|
const float v = std::floor(position / getTileSize(settings));
|
||||||
|
if (v < static_cast<float>(std::numeric_limits<int>::min()))
|
||||||
|
return std::numeric_limits<int>::min();
|
||||||
|
if (v > static_cast<float>(std::numeric_limits<int>::max() - 1))
|
||||||
|
return std::numeric_limits<int>::max() - 1;
|
||||||
|
return static_cast<int>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline TilePosition getTilePosition(const RecastSettings& settings, const osg::Vec2f& position)
|
||||||
|
{
|
||||||
|
return TilePosition(getTilePosition(settings, position.x()), getTilePosition(settings, position.y()));
|
||||||
|
}
|
||||||
|
|
||||||
inline TilePosition getTilePosition(const RecastSettings& settings, const osg::Vec3f& position)
|
inline TilePosition getTilePosition(const RecastSettings& settings, const osg::Vec3f& position)
|
||||||
{
|
{
|
||||||
return TilePosition(
|
return getTilePosition(settings, osg::Vec2f(position.x(), position.z()));
|
||||||
static_cast<int>(std::floor(position.x() / getTileSize(settings))),
|
|
||||||
static_cast<int>(std::floor(position.z() / getTileSize(settings)))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline TileBounds makeTileBounds(const RecastSettings& settings, const TilePosition& tilePosition)
|
inline TileBounds makeTileBounds(const RecastSettings& settings, const TilePosition& tilePosition)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H
|
||||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILEBOUNDS_H
|
||||||
|
|
||||||
|
#include <components/misc/convert.hpp>
|
||||||
|
|
||||||
#include <osg/Vec2f>
|
#include <osg/Vec2f>
|
||||||
#include <osg/Vec2i>
|
#include <osg/Vec2i>
|
||||||
|
|
||||||
|
#include <BulletCollision/CollisionShapes/btCollisionShape.h>
|
||||||
|
#include <LinearMath/btTransform.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
@ -16,9 +21,24 @@ namespace DetourNavigator
|
||||||
osg::Vec2f mMax;
|
osg::Vec2f mMax;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline auto tie(const TileBounds& value) noexcept
|
||||||
|
{
|
||||||
|
return std::tie(value.mMin, value.mMax);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator<(const TileBounds& lhs, const TileBounds& rhs) noexcept
|
inline bool operator<(const TileBounds& lhs, const TileBounds& rhs) noexcept
|
||||||
{
|
{
|
||||||
return std::tie(lhs.mMin, lhs.mMax) < std::tie(rhs.mMin, rhs.mMax);
|
return tie(lhs) < tie(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator ==(const TileBounds& lhs, const TileBounds& rhs) noexcept
|
||||||
|
{
|
||||||
|
return tie(lhs) == tie(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator !=(const TileBounds& lhs, const TileBounds& rhs) noexcept
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::optional<TileBounds> getIntersection(const TileBounds& a, const TileBounds& b) noexcept
|
inline std::optional<TileBounds> getIntersection(const TileBounds& a, const TileBounds& b) noexcept
|
||||||
|
|
@ -41,6 +61,14 @@ namespace DetourNavigator
|
||||||
osg::Vec2f(position.x() + 1, position.y() + 1) * size
|
osg::Vec2f(position.x() + 1, position.y() + 1) * size
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline TileBounds makeObjectTileBounds(const btCollisionShape& shape, const btTransform& transform)
|
||||||
|
{
|
||||||
|
btVector3 aabbMin;
|
||||||
|
btVector3 aabbMax;
|
||||||
|
shape.getAabb(transform, aabbMin, aabbMax);
|
||||||
|
return TileBounds {Misc::Convert::toOsgXY(aabbMin), Misc::Convert::toOsgXY(aabbMax)};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2,70 +2,123 @@
|
||||||
#include "makenavmesh.hpp"
|
#include "makenavmesh.hpp"
|
||||||
#include "gettilespositions.hpp"
|
#include "gettilespositions.hpp"
|
||||||
#include "settingsutils.hpp"
|
#include "settingsutils.hpp"
|
||||||
|
#include "changetype.hpp"
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/misc/convert.hpp>
|
#include <components/misc/convert.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const TileBounds infiniteTileBounds {
|
||||||
|
osg::Vec2f(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max()),
|
||||||
|
osg::Vec2f(std::numeric_limits<float>::max(), std::numeric_limits<float>::max())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
|
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
|
||||||
: mSettings(settings)
|
: mSettings(settings)
|
||||||
|
, mBounds(infiniteTileBounds)
|
||||||
|
, mRange(makeTilesPositionsRange(mBounds.mMin, mBounds.mMax, mSettings))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
TileBounds TileCachedRecastMeshManager::getBounds() const
|
||||||
|
{
|
||||||
|
return mBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<TilePosition, ChangeType>> TileCachedRecastMeshManager::setBounds(const TileBounds& bounds)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<TilePosition, ChangeType>> changedTiles;
|
||||||
|
|
||||||
|
if (mBounds == bounds)
|
||||||
|
return changedTiles;
|
||||||
|
|
||||||
|
const auto newRange = makeTilesPositionsRange(bounds.mMin, bounds.mMax, mSettings);
|
||||||
|
|
||||||
|
if (mBounds != infiniteTileBounds)
|
||||||
|
{
|
||||||
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
|
for (auto& object : mObjects)
|
||||||
|
{
|
||||||
|
const ObjectId id = object.first;
|
||||||
|
ObjectData& data = object.second;
|
||||||
|
const TilesPositionsRange objectRange = makeTilesPositionsRange(data.mShape.getShape(), data.mTransform, mSettings);
|
||||||
|
|
||||||
|
const auto onOldTilePosition = [&] (const TilePosition& position)
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
changedTiles.emplace_back(position, ChangeType::remove);
|
||||||
|
};
|
||||||
|
getTilesPositions(getIntersection(mRange, objectRange), onOldTilePosition);
|
||||||
|
|
||||||
|
const auto onNewTilePosition = [&] (const TilePosition& position)
|
||||||
|
{
|
||||||
|
if (data.mTiles.find(position) != data.mTiles.end())
|
||||||
|
return;
|
||||||
|
if (addTile(id, data.mShape, data.mTransform, data.mAreaType, position, locked->mTiles))
|
||||||
|
{
|
||||||
|
data.mTiles.insert(position);
|
||||||
|
changedTiles.emplace_back(position, ChangeType::add);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getTilesPositions(getIntersection(newRange, objectRange), onNewTilePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(changedTiles.begin(), changedTiles.end());
|
||||||
|
changedTiles.erase(std::unique(changedTiles.begin(), changedTiles.end()), changedTiles.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changedTiles.empty())
|
||||||
|
++mRevision;
|
||||||
|
|
||||||
|
mBounds = bounds;
|
||||||
|
mRange = newRange;
|
||||||
|
|
||||||
|
return changedTiles;
|
||||||
|
}
|
||||||
|
|
||||||
std::string TileCachedRecastMeshManager::getWorldspace() const
|
std::string TileCachedRecastMeshManager::getWorldspace() const
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
return mWorldspaceTiles.lockConst()->mWorldspace;
|
||||||
return mWorldspace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace)
|
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
if (mWorldspace == worldspace)
|
if (locked->mWorldspace == worldspace)
|
||||||
return;
|
return;
|
||||||
mTiles.clear();
|
locked->mTiles.clear();
|
||||||
mWorldspace = worldspace;
|
locked->mWorldspace = worldspace;
|
||||||
}
|
|
||||||
|
|
||||||
bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,
|
|
||||||
const btTransform& transform, const AreaType areaType)
|
|
||||||
{
|
|
||||||
std::vector<TilePosition> tilesPositions;
|
|
||||||
{
|
|
||||||
const std::lock_guard lock(mMutex);
|
|
||||||
getTilesPositions(makeTilesPositionsRange(shape.getShape(), transform, mSettings),
|
|
||||||
[&] (const TilePosition& tilePosition)
|
|
||||||
{
|
|
||||||
if (addTile(id, shape, transform, areaType, tilePosition, mTiles))
|
|
||||||
tilesPositions.push_back(tilePosition);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (tilesPositions.empty())
|
|
||||||
return false;
|
|
||||||
std::sort(tilesPositions.begin(), tilesPositions.end());
|
|
||||||
mObjectsTilesPositions.insert_or_assign(id, std::move(tilesPositions));
|
|
||||||
++mRevision;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeObject(const ObjectId id)
|
std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeObject(const ObjectId id)
|
||||||
{
|
{
|
||||||
const auto object = mObjectsTilesPositions.find(id);
|
const auto object = mObjects.find(id);
|
||||||
if (object == mObjectsTilesPositions.end())
|
if (object == mObjects.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
std::optional<RemovedRecastMeshObject> result;
|
std::optional<RemovedRecastMeshObject> result;
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
for (const auto& tilePosition : object->second)
|
for (const auto& tilePosition : object->second.mTiles)
|
||||||
{
|
{
|
||||||
const auto removed = removeTile(id, tilePosition, mTiles);
|
const auto removed = removeTile(id, tilePosition, locked->mTiles);
|
||||||
if (removed && !result)
|
if (removed && !result)
|
||||||
result = removed;
|
result = removed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mObjects.erase(object);
|
||||||
if (result)
|
if (result)
|
||||||
++mRevision;
|
++mRevision;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -73,14 +126,19 @@ namespace DetourNavigator
|
||||||
|
|
||||||
bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
|
bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, int cellSize, float level)
|
||||||
{
|
{
|
||||||
auto& tilesPositions = mWaterTilesPositions[cellPosition];
|
const auto it = mWaterTilesPositions.find(cellPosition);
|
||||||
|
if (it != mWaterTilesPositions.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<TilePosition>& tilesPositions = mWaterTilesPositions.emplace_hint(
|
||||||
|
it, cellPosition, std::vector<TilePosition>())->second;
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (cellSize == std::numeric_limits<int>::max())
|
if (cellSize == std::numeric_limits<int>::max())
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
for (auto& tile : mTiles)
|
for (auto& tile : locked->mTiles)
|
||||||
{
|
{
|
||||||
if (tile.second->addWater(cellPosition, cellSize, level))
|
if (tile.second->addWater(cellPosition, cellSize, level))
|
||||||
{
|
{
|
||||||
|
|
@ -95,12 +153,12 @@ namespace DetourNavigator
|
||||||
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
|
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
|
||||||
[&] (const TilePosition& tilePosition)
|
[&] (const TilePosition& tilePosition)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
auto tile = mTiles.find(tilePosition);
|
auto tile = locked->mTiles.find(tilePosition);
|
||||||
if (tile == mTiles.end())
|
if (tile == locked->mTiles.end())
|
||||||
{
|
{
|
||||||
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
|
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
|
||||||
tile = mTiles.emplace_hint(tile, tilePosition,
|
tile = locked->mTiles.emplace_hint(tile, tilePosition,
|
||||||
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
|
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
|
||||||
}
|
}
|
||||||
if (tile->second->addWater(cellPosition, cellSize, level))
|
if (tile->second->addWater(cellPosition, cellSize, level))
|
||||||
|
|
@ -125,19 +183,20 @@ namespace DetourNavigator
|
||||||
std::optional<Water> result;
|
std::optional<Water> result;
|
||||||
for (const auto& tilePosition : object->second)
|
for (const auto& tilePosition : object->second)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
const auto tile = mTiles.find(tilePosition);
|
const auto tile = locked->mTiles.find(tilePosition);
|
||||||
if (tile == mTiles.end())
|
if (tile == locked->mTiles.end())
|
||||||
continue;
|
continue;
|
||||||
const auto tileResult = tile->second->removeWater(cellPosition);
|
const auto tileResult = tile->second->removeWater(cellPosition);
|
||||||
if (tile->second->isEmpty())
|
if (tile->second->isEmpty())
|
||||||
{
|
{
|
||||||
mTiles.erase(tile);
|
locked->mTiles.erase(tile);
|
||||||
++mTilesGeneration;
|
++mTilesGeneration;
|
||||||
}
|
}
|
||||||
if (tileResult && !result)
|
if (tileResult && !result)
|
||||||
result = tileResult;
|
result = tileResult;
|
||||||
}
|
}
|
||||||
|
mWaterTilesPositions.erase(object);
|
||||||
if (result)
|
if (result)
|
||||||
++mRevision;
|
++mRevision;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -146,20 +205,25 @@ namespace DetourNavigator
|
||||||
bool TileCachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize,
|
bool TileCachedRecastMeshManager::addHeightfield(const osg::Vec2i& cellPosition, int cellSize,
|
||||||
const HeightfieldShape& shape)
|
const HeightfieldShape& shape)
|
||||||
{
|
{
|
||||||
|
const auto it = mHeightfieldTilesPositions.find(cellPosition);
|
||||||
|
if (it != mHeightfieldTilesPositions.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<TilePosition>& tilesPositions = mHeightfieldTilesPositions.emplace_hint(
|
||||||
|
it, cellPosition, std::vector<TilePosition>())->second;
|
||||||
const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize);
|
const btVector3 shift = getHeightfieldShift(shape, cellPosition, cellSize);
|
||||||
auto& tilesPositions = mHeightfieldTilesPositions[cellPosition];
|
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
|
getTilesPositions(makeTilesPositionsRange(cellSize, shift, mSettings),
|
||||||
[&] (const TilePosition& tilePosition)
|
[&] (const TilePosition& tilePosition)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
auto tile = mTiles.find(tilePosition);
|
auto tile = locked->mTiles.find(tilePosition);
|
||||||
if (tile == mTiles.end())
|
if (tile == locked->mTiles.end())
|
||||||
{
|
{
|
||||||
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
|
const TileBounds tileBounds = makeRealTileBoundsWithBorder(mSettings, tilePosition);
|
||||||
tile = mTiles.emplace_hint(tile, tilePosition,
|
tile = locked->mTiles.emplace_hint(tile, tilePosition,
|
||||||
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
|
std::make_shared<CachedRecastMeshManager>(tileBounds, mTilesGeneration));
|
||||||
}
|
}
|
||||||
if (tile->second->addHeightfield(cellPosition, cellSize, shape))
|
if (tile->second->addHeightfield(cellPosition, cellSize, shape))
|
||||||
|
|
@ -183,19 +247,20 @@ namespace DetourNavigator
|
||||||
std::optional<SizedHeightfieldShape> result;
|
std::optional<SizedHeightfieldShape> result;
|
||||||
for (const auto& tilePosition : object->second)
|
for (const auto& tilePosition : object->second)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lock();
|
||||||
const auto tile = mTiles.find(tilePosition);
|
const auto tile = locked->mTiles.find(tilePosition);
|
||||||
if (tile == mTiles.end())
|
if (tile == locked->mTiles.end())
|
||||||
continue;
|
continue;
|
||||||
const auto tileResult = tile->second->removeHeightfield(cellPosition);
|
const auto tileResult = tile->second->removeHeightfield(cellPosition);
|
||||||
if (tile->second->isEmpty())
|
if (tile->second->isEmpty())
|
||||||
{
|
{
|
||||||
mTiles.erase(tile);
|
locked->mTiles.erase(tile);
|
||||||
++mTilesGeneration;
|
++mTilesGeneration;
|
||||||
}
|
}
|
||||||
if (tileResult && !result)
|
if (tileResult && !result)
|
||||||
result = tileResult;
|
result = tileResult;
|
||||||
}
|
}
|
||||||
|
mHeightfieldTilesPositions.erase(object);
|
||||||
if (result)
|
if (result)
|
||||||
++mRevision;
|
++mRevision;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -229,9 +294,9 @@ namespace DetourNavigator
|
||||||
|
|
||||||
void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const
|
void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion) const
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lockConst();
|
||||||
const auto it = mTiles.find(tilePosition);
|
const auto it = locked->mTiles.find(tilePosition);
|
||||||
if (it == mTiles.end())
|
if (it == locked->mTiles.end())
|
||||||
return;
|
return;
|
||||||
it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion);
|
it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion);
|
||||||
}
|
}
|
||||||
|
|
@ -275,11 +340,11 @@ namespace DetourNavigator
|
||||||
std::shared_ptr<CachedRecastMeshManager> TileCachedRecastMeshManager::getManager(std::string_view worldspace,
|
std::shared_ptr<CachedRecastMeshManager> TileCachedRecastMeshManager::getManager(std::string_view worldspace,
|
||||||
const TilePosition& tilePosition) const
|
const TilePosition& tilePosition) const
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto locked = mWorldspaceTiles.lockConst();
|
||||||
if (mWorldspace != worldspace)
|
if (locked->mWorldspace != worldspace)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
const auto it = mTiles.find(tilePosition);
|
const auto it = locked->mTiles.find(tilePosition);
|
||||||
if (it == mTiles.end())
|
if (it == locked->mTiles.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,15 @@
|
||||||
#include "gettilespositions.hpp"
|
#include "gettilespositions.hpp"
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
#include "heightfieldshape.hpp"
|
#include "heightfieldshape.hpp"
|
||||||
|
#include "changetype.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/guarded.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
|
|
@ -20,57 +24,85 @@ namespace DetourNavigator
|
||||||
public:
|
public:
|
||||||
explicit TileCachedRecastMeshManager(const RecastSettings& settings);
|
explicit TileCachedRecastMeshManager(const RecastSettings& settings);
|
||||||
|
|
||||||
|
TileBounds getBounds() const;
|
||||||
|
|
||||||
|
std::vector<std::pair<TilePosition, ChangeType>> setBounds(const TileBounds& bounds);
|
||||||
|
|
||||||
std::string getWorldspace() const;
|
std::string getWorldspace() const;
|
||||||
|
|
||||||
void setWorldspace(std::string_view worldspace);
|
void setWorldspace(std::string_view worldspace);
|
||||||
|
|
||||||
|
template <class OnChangedTile>
|
||||||
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||||
const AreaType areaType);
|
const AreaType areaType, OnChangedTile&& onChangedTile)
|
||||||
|
{
|
||||||
|
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 auto locked = mWorldspaceTiles.lock();
|
||||||
|
getTilesPositions(range,
|
||||||
|
[&] (const TilePosition& tilePosition)
|
||||||
|
{
|
||||||
|
if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles))
|
||||||
|
tilesPositions.insert(tilePosition);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
it = mObjects.emplace_hint(it, id, ObjectData {shape, transform, areaType, std::move(tilesPositions)});
|
||||||
|
std::for_each(it->second.mTiles.begin(), it->second.mTiles.end(), std::forward<OnChangedTile>(onChangedTile));
|
||||||
|
++mRevision;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <class OnChangedTile>
|
template <class OnChangedTile>
|
||||||
bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
|
||||||
const AreaType areaType, OnChangedTile&& onChangedTile)
|
const AreaType areaType, OnChangedTile&& onChangedTile)
|
||||||
{
|
{
|
||||||
const auto object = mObjectsTilesPositions.find(id);
|
const auto object = mObjects.find(id);
|
||||||
if (object == mObjectsTilesPositions.end())
|
if (object == mObjects.end())
|
||||||
return false;
|
return false;
|
||||||
auto& currentTiles = object->second;
|
auto& data = object->second;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
std::vector<TilePosition> newTiles;
|
std::set<TilePosition> newTiles;
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
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 auto onTilePosition = [&] (const TilePosition& tilePosition)
|
||||||
{
|
{
|
||||||
if (std::binary_search(currentTiles.begin(), currentTiles.end(), tilePosition))
|
if (data.mTiles.find(tilePosition) != data.mTiles.end())
|
||||||
{
|
{
|
||||||
newTiles.push_back(tilePosition);
|
newTiles.insert(tilePosition);
|
||||||
if (updateTile(id, transform, areaType, tilePosition, mTiles))
|
if (updateTile(id, transform, areaType, tilePosition, locked->mTiles))
|
||||||
{
|
{
|
||||||
onChangedTile(tilePosition);
|
onChangedTile(tilePosition, ChangeType::update);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (addTile(id, shape, transform, areaType, tilePosition, mTiles))
|
else if (addTile(id, shape, transform, areaType, tilePosition, locked->mTiles))
|
||||||
{
|
{
|
||||||
newTiles.push_back(tilePosition);
|
newTiles.insert(tilePosition);
|
||||||
onChangedTile(tilePosition);
|
onChangedTile(tilePosition, ChangeType::add);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
getTilesPositions(makeTilesPositionsRange(shape.getShape(), transform, mSettings), onTilePosition);
|
getTilesPositions(range, onTilePosition);
|
||||||
std::sort(newTiles.begin(), newTiles.end());
|
for (const auto& tile : data.mTiles)
|
||||||
for (const auto& tile : currentTiles)
|
|
||||||
{
|
{
|
||||||
if (!std::binary_search(newTiles.begin(), newTiles.end(), tile) && removeTile(id, tile, mTiles))
|
if (newTiles.find(tile) == newTiles.end() && removeTile(id, tile, locked->mTiles))
|
||||||
{
|
{
|
||||||
onChangedTile(tile);
|
onChangedTile(tile, ChangeType::remove);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
currentTiles = std::move(newTiles);
|
data.mTiles = std::move(newTiles);
|
||||||
++mRevision;
|
++mRevision;
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
|
|
@ -95,8 +127,8 @@ namespace DetourNavigator
|
||||||
template <class Function>
|
template <class Function>
|
||||||
void forEachTile(Function&& function) const
|
void forEachTile(Function&& function) const
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mMutex);
|
const auto& locked = mWorldspaceTiles.lockConst();
|
||||||
for (auto& [tilePosition, recastMeshManager] : mTiles)
|
for (const auto& [tilePosition, recastMeshManager] : locked->mTiles)
|
||||||
function(tilePosition, *recastMeshManager);
|
function(tilePosition, *recastMeshManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,11 +139,25 @@ namespace DetourNavigator
|
||||||
private:
|
private:
|
||||||
using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>;
|
using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>;
|
||||||
|
|
||||||
|
struct ObjectData
|
||||||
|
{
|
||||||
|
const CollisionShape mShape;
|
||||||
|
const btTransform mTransform;
|
||||||
|
const AreaType mAreaType;
|
||||||
|
std::set<TilePosition> mTiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WorldspaceTiles
|
||||||
|
{
|
||||||
|
std::string mWorldspace;
|
||||||
|
TilesMap mTiles;
|
||||||
|
};
|
||||||
|
|
||||||
const RecastSettings& mSettings;
|
const RecastSettings& mSettings;
|
||||||
mutable std::mutex mMutex;
|
TileBounds mBounds;
|
||||||
std::string mWorldspace;
|
TilesPositionsRange mRange;
|
||||||
TilesMap mTiles;
|
Misc::ScopeGuarded<WorldspaceTiles> mWorldspaceTiles;
|
||||||
std::unordered_map<ObjectId, std::vector<TilePosition>> mObjectsTilesPositions;
|
std::unordered_map<ObjectId, ObjectData> mObjects;
|
||||||
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
|
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
|
||||||
std::map<osg::Vec2i, std::vector<TilePosition>> mHeightfieldTilesPositions;
|
std::map<osg::Vec2i, std::vector<TilePosition>> mHeightfieldTilesPositions;
|
||||||
std::size_t mRevision = 0;
|
std::size_t mRevision = 0;
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,11 @@ namespace Misc::Convert
|
||||||
{
|
{
|
||||||
return btTransform(makeBulletQuaternion(position), toBullet(position.asVec3()));
|
return btTransform(makeBulletQuaternion(position), toBullet(position.asVec3()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline osg::Vec2f toOsgXY(const btVector3& value)
|
||||||
|
{
|
||||||
|
return osg::Vec2f(static_cast<float>(value.x()), static_cast<float>(value.y()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Loading…
Reference in a new issue