1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00

Merge branch 'navigator_check_agent_bounds' into 'master'

Check agent bounds on adding agent to navigator

See merge request OpenMW/openmw!2629
This commit is contained in:
psi29a 2023-01-18 14:31:22 +00:00
commit fbeacc1e0f
9 changed files with 113 additions and 65 deletions

View File

@ -8,6 +8,7 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/detournavigator/agentbounds.hpp> #include <components/detournavigator/agentbounds.hpp>
#include <components/detournavigator/debug.hpp>
#include <components/detournavigator/heightfieldshape.hpp> #include <components/detournavigator/heightfieldshape.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
#include <components/detournavigator/navigatorimpl.hpp> #include <components/detournavigator/navigatorimpl.hpp>
@ -184,7 +185,9 @@ namespace
} }
else if (physics.getActor(ptr)) else if (physics.getActor(ptr))
{ {
navigator.addAgent(world.getPathfindingAgentBounds(ptr)); const DetourNavigator::AgentBounds agentBounds = world.getPathfindingAgentBounds(ptr);
if (!navigator.addAgent(agentBounds))
Log(Debug::Warning) << "Agent bounds are not supported by navigator: " << agentBounds;
} }
} }

View File

@ -40,6 +40,7 @@
#include <components/sceneutil/workqueue.hpp> #include <components/sceneutil/workqueue.hpp>
#include <components/detournavigator/agentbounds.hpp> #include <components/detournavigator/agentbounds.hpp>
#include <components/detournavigator/debug.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
#include <components/detournavigator/navigatorimpl.hpp> #include <components/detournavigator/navigatorimpl.hpp>
#include <components/detournavigator/settings.hpp> #include <components/detournavigator/settings.hpp>
@ -1269,7 +1270,11 @@ namespace MWWorld
mWorldScene->updateObjectScale(ptr); mWorldScene->updateObjectScale(ptr);
if (mPhysics->getActor(ptr)) if (mPhysics->getActor(ptr))
mNavigator->addAgent(getPathfindingAgentBounds(ptr)); {
const DetourNavigator::AgentBounds agentBounds = getPathfindingAgentBounds(ptr);
if (!mNavigator->addAgent(agentBounds))
Log(Debug::Warning) << "Scaled agent bounds are not supported by navigator: " << agentBounds;
}
else if (const auto object = mPhysics->getObject(ptr)) else if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(*object); updateNavigatorObject(*object);
} }
@ -2435,7 +2440,9 @@ namespace MWWorld
applyLoopingParticles(player); applyLoopingParticles(player);
mNavigator->addAgent(getPathfindingAgentBounds(getPlayerConstPtr())); const DetourNavigator::AgentBounds agentBounds = getPathfindingAgentBounds(getPlayerConstPtr());
if (!mNavigator->addAgent(agentBounds))
Log(Debug::Warning) << "Player agent bounds are not supported by navigator: " << agentBounds;
} }
World::RestPermitted World::canRest() const World::RestPermitted World::canRest() const

View File

@ -135,7 +135,7 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception)
{ {
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
EXPECT_EQ( EXPECT_EQ(
findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::StartPolygonNotFound); Status::StartPolygonNotFound);
@ -143,8 +143,8 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent) TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent)
{ {
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->removeAgent(mAgentBounds); mNavigator->removeAgent(mAgentBounds);
EXPECT_EQ( EXPECT_EQ(
findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut), findPath(*mNavigator, mAgentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
@ -163,7 +163,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
auto updateGuard = mNavigator->makeUpdateGuard(); auto updateGuard = mNavigator->makeUpdateGuard();
mNavigator->addHeightfield(mCellPosition, cellSize, surface, updateGuard.get()); mNavigator->addHeightfield(mCellPosition, cellSize, surface, updateGuard.get());
mNavigator->update(mPlayerPosition, updateGuard.get()); mNavigator->update(mPlayerPosition, updateGuard.get());
@ -220,7 +220,7 @@ namespace
compound.shape().addChildShape( compound.shape().addChildShape(
btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
@ -311,7 +311,7 @@ namespace
compound.shape().addChildShape( compound.shape().addChildShape(
btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100))); btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject( mNavigator->addObject(
ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr); ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr);
@ -409,7 +409,7 @@ namespace
CollisionShapeInstance heightfield2(makeSquareHeightfieldTerrainShape(heightfieldData2)); CollisionShapeInstance heightfield2(makeSquareHeightfieldTerrainShape(heightfieldData2));
heightfield2.shape().setLocalScaling(btVector3(128, 128, 1)); heightfield2.shape().setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform), mNavigator->addObject(ObjectId(&heightfield1.shape()), ObjectShapes(heightfield1.instance(), mObjectTransform),
mTransform, nullptr); mTransform, nullptr);
mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform), mNavigator->addObject(ObjectId(&heightfield2.shape()), ObjectShapes(heightfield2.instance(), mObjectTransform),
@ -469,7 +469,7 @@ namespace
const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2); const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2);
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1); const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
@ -512,7 +512,7 @@ namespace
osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape)); osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addObject( mNavigator->addObject(
ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform, nullptr); ObjectId(instance->mCollisionShape.get()), ObjectShapes(instance, mObjectTransform), mTransform, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
@ -561,7 +561,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addWater(mCellPosition, cellSize, 300, nullptr); mNavigator->addWater(mCellPosition, cellSize, 300, nullptr);
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
@ -605,7 +605,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr); mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
@ -648,7 +648,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addWater(mCellPosition, std::numeric_limits<int>::max(), -25, nullptr); mNavigator->addWater(mCellPosition, std::numeric_limits<int>::max(), -25, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
@ -690,7 +690,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr); mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
@ -730,7 +730,7 @@ namespace
CollisionShapeInstance heightfield(makeSquareHeightfieldTerrainShape(heightfieldData)); CollisionShapeInstance heightfield(makeSquareHeightfieldTerrainShape(heightfieldData));
heightfield.shape().setLocalScaling(btVector3(128, 128, 1)); heightfield.shape().setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform), mNavigator->addObject(ObjectId(&heightfield.shape()), ObjectShapes(heightfield.instance(), mObjectTransform),
mTransform, nullptr); mTransform, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
@ -787,7 +787,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
@ -843,7 +843,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
@ -882,7 +882,7 @@ namespace
std::generate_n( std::generate_n(
std::back_inserter(boxes), 100, [] { return std::make_unique<btBoxShape>(btVector3(20, 20, 100)); }); std::back_inserter(boxes), 100, [] { return std::make_unique<btBoxShape>(btVector3(20, 20, 100)); });
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
@ -944,7 +944,7 @@ namespace
std::generate_n( std::generate_n(
std::back_inserter(shapes), 100, [] { return std::make_unique<btBoxShape>(btVector3(64, 64, 64)); }); std::back_inserter(shapes), 100, [] { return std::make_unique<btBoxShape>(btVector3(64, 64, 64)); });
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
@ -992,7 +992,7 @@ namespace
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData); const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1); const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
@ -1022,7 +1022,7 @@ namespace
const btVector3 oscillatingBoxShapePosition(288, 288, 400); const btVector3 oscillatingBoxShapePosition(288, 288, 400);
CollisionShapeInstance borderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50))); CollisionShapeInstance borderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50)));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject(ObjectId(&oscillatingBox.shape()), mNavigator->addObject(ObjectId(&oscillatingBox.shape()),
ObjectShapes(oscillatingBox.instance(), mObjectTransform), ObjectShapes(oscillatingBox.instance(), mObjectTransform),
@ -1058,7 +1058,7 @@ namespace
const HeightfieldPlane plane{ 100 }; const HeightfieldPlane plane{ 100 };
const int cellSize = mHeightfieldTileSize * 4; const int cellSize = mHeightfieldTileSize * 4;
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener); mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
@ -1109,7 +1109,7 @@ namespace
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)), compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
new btBoxShape(btVector3(200, 200, 1000))); new btBoxShape(btVector3(200, 200, 1000)));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject( mNavigator->addObject(
ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr); ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr);
@ -1150,7 +1150,7 @@ namespace
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)), compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
new btBoxShape(btVector3(100, 100, 1000))); new btBoxShape(btVector3(100, 100, 1000)));
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr); mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
mNavigator->addObject( mNavigator->addObject(
ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr); ObjectId(&compound.shape()), ObjectShapes(compound.instance(), mObjectTransform), mTransform, nullptr);
@ -1192,7 +1192,7 @@ namespace
const int cellSize2 = 200; const int cellSize2 = 200;
const float level2 = 2; const float level2 = 2;
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addWater(mCellPosition, cellSize1, level1, nullptr); mNavigator->addWater(mCellPosition, cellSize1, level1, nullptr);
mNavigator->update(mPlayerPosition, nullptr); mNavigator->update(mPlayerPosition, nullptr);
mNavigator->wait(WaitConditionType::allJobsDone, &mListener); mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
@ -1206,31 +1206,6 @@ namespace
EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version); EXPECT_EQ(mNavigator->getNavMesh(mAgentBounds)->lockConst()->getVersion(), version);
} }
TEST_F(DetourNavigatorNavigatorTest, add_agent_with_zero_coordinate_should_not_have_nav_mesh)
{
constexpr std::array<float, 5 * 5> heightfieldData{ {
0, 0, 0, 0, 0, // row 0
0, -25, -25, -25, -25, // row 1
0, -25, -100, -100, -100, // row 2
0, -25, -100, -100, -100, // row 3
0, -25, -100, -100, -100, // row 4
} };
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
const AgentBounds agentBounds{ CollisionShapeType::RotatingBox, { 0, 1, 1 } };
mNavigator->addAgent(agentBounds);
auto updateGuard = mNavigator->makeUpdateGuard();
mNavigator->addHeightfield(mCellPosition, cellSize, surface, updateGuard.get());
mNavigator->update(mPlayerPosition, updateGuard.get());
updateGuard.reset();
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
EXPECT_EQ(
findPath(*mNavigator, agentBounds, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
Status::NavMeshNotFound);
}
TEST_F(DetourNavigatorNavigatorTest, update_for_very_big_object_should_be_limited) TEST_F(DetourNavigatorNavigatorTest, update_for_very_big_object_should_be_limited)
{ {
const float size = static_cast<float>(2 * static_cast<std::int64_t>(std::numeric_limits<int>::max()) - 1); const float size = static_cast<float>(2 * static_cast<std::int64_t>(std::numeric_limits<int>::max()) - 1);
@ -1241,7 +1216,7 @@ namespace
}; };
mNavigator->updateBounds(mPlayerPosition, nullptr); mNavigator->updateBounds(mPlayerPosition, nullptr);
mNavigator->addAgent(mAgentBounds); ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
mNavigator->addObject(ObjectId(&bigBox.shape()), ObjectShapes(bigBox.instance(), objectTransform), mNavigator->addObject(ObjectId(&bigBox.shape()), ObjectShapes(bigBox.instance(), objectTransform),
btTransform::getIdentity(), nullptr); btTransform::getIdentity(), nullptr);
@ -1275,4 +1250,36 @@ namespace
navMesh->lockConst()->forEachUsedTile([&](const auto&...) { ++usedNavMeshTiles; }); navMesh->lockConst()->forEachUsedTile([&](const auto&...) { ++usedNavMeshTiles; });
EXPECT_EQ(usedNavMeshTiles, 509); EXPECT_EQ(usedNavMeshTiles, 509);
} }
struct DetourNavigatorNavigatorNotSupportedAgentBoundsTest : TestWithParam<AgentBounds>
{
};
TEST_P(DetourNavigatorNavigatorNotSupportedAgentBoundsTest, on_add_agent)
{
const Settings settings = makeSettings();
NavigatorImpl navigator(settings, nullptr);
EXPECT_FALSE(navigator.addAgent(GetParam()));
}
const std::array notSupportedAgentBounds = {
AgentBounds{ .mShapeType = CollisionShapeType::Aabb, .mHalfExtents = osg::Vec3f(0, 0, 0) },
AgentBounds{ .mShapeType = CollisionShapeType::RotatingBox, .mHalfExtents = osg::Vec3f(0, 0, 0) },
AgentBounds{ .mShapeType = CollisionShapeType::Cylinder, .mHalfExtents = osg::Vec3f(0, 0, 0) },
AgentBounds{ .mShapeType = CollisionShapeType::Aabb, .mHalfExtents = osg::Vec3f(0, 0, 11.34f) },
AgentBounds{ .mShapeType = CollisionShapeType::RotatingBox, .mHalfExtents = osg::Vec3f(0, 11.34f, 11.34f) },
AgentBounds{ .mShapeType = CollisionShapeType::Cylinder, .mHalfExtents = osg::Vec3f(0, 0, 11.34f) },
AgentBounds{ .mShapeType = CollisionShapeType::Aabb, .mHalfExtents = osg::Vec3f(1, 1, 0) },
AgentBounds{ .mShapeType = CollisionShapeType::RotatingBox, .mHalfExtents = osg::Vec3f(1, 1, 0) },
AgentBounds{ .mShapeType = CollisionShapeType::Cylinder, .mHalfExtents = osg::Vec3f(1, 1, 0) },
AgentBounds{ .mShapeType = CollisionShapeType::Aabb, .mHalfExtents = osg::Vec3f(1, 1, 11.33f) },
AgentBounds{ .mShapeType = CollisionShapeType::RotatingBox, .mHalfExtents = osg::Vec3f(1, 1, 11.33f) },
AgentBounds{ .mShapeType = CollisionShapeType::Cylinder, .mHalfExtents = osg::Vec3f(1, 1, 11.33f) },
AgentBounds{ .mShapeType = CollisionShapeType::Aabb, .mHalfExtents = osg::Vec3f(2043.54f, 2043.54f, 11.34f) },
AgentBounds{ .mShapeType = CollisionShapeType::RotatingBox, .mHalfExtents = osg::Vec3f(2890, 1, 11.34f) },
AgentBounds{ .mShapeType = CollisionShapeType::Cylinder, .mHalfExtents = osg::Vec3f(2890, 2890, 11.34f) },
};
INSTANTIATE_TEST_SUITE_P(NotSupportedAgentBounds, DetourNavigatorNavigatorNotSupportedAgentBoundsTest,
ValuesIn(notSupportedAgentBounds));
} }

View File

@ -31,6 +31,8 @@ namespace DetourNavigator
{ {
namespace namespace
{ {
constexpr int walkableRadiusUpperLimit = 255;
struct Rectangle struct Rectangle
{ {
TileBounds mBounds; TileBounds mBounds;
@ -114,6 +116,16 @@ namespace DetourNavigator
return waterLevel - settings.mSwimHeightScale * agentHalfExtentsZ - agentHalfExtentsZ; return waterLevel - settings.mSwimHeightScale * agentHalfExtentsZ - agentHalfExtentsZ;
} }
int getWalkableHeight(const RecastSettings& settings, const AgentBounds& agentBounds)
{
return static_cast<int>(std::ceil(getHeight(settings, agentBounds) / settings.mCellHeight));
}
int getWalkableRadius(const RecastSettings& settings, const AgentBounds& agentBounds)
{
return static_cast<int>(std::ceil(getRadius(settings, agentBounds) / settings.mCellSize));
}
struct RecastParams struct RecastParams
{ {
float mSampleDist = 0; float mSampleDist = 0;
@ -128,10 +140,9 @@ namespace DetourNavigator
{ {
RecastParams result; RecastParams result;
result.mWalkableHeight result.mWalkableHeight = getWalkableHeight(settings, agentBounds);
= static_cast<int>(std::ceil(getHeight(settings, agentBounds) / settings.mCellHeight));
result.mWalkableClimb = static_cast<int>(std::floor(getMaxClimb(settings) / settings.mCellHeight)); result.mWalkableClimb = static_cast<int>(std::floor(getMaxClimb(settings) / settings.mCellHeight));
result.mWalkableRadius = static_cast<int>(std::ceil(getRadius(settings, agentBounds) / settings.mCellSize)); result.mWalkableRadius = getWalkableRadius(settings, agentBounds);
result.mMaxEdgeLen result.mMaxEdgeLen
= static_cast<int>(std::round(static_cast<float>(settings.mMaxEdgeLen) / settings.mCellSize)); = static_cast<int>(std::round(static_cast<float>(settings.mMaxEdgeLen) / settings.mCellSize));
result.mSampleDist result.mSampleDist
@ -288,10 +299,15 @@ namespace DetourNavigator
context, realTileBounds, recastMesh.getFlatHeightfields(), settings, params, solid); context, realTileBounds, recastMesh.getFlatHeightfields(), settings, params, solid);
} }
bool isValidWalkableHeight(int value)
{
return value >= 3;
}
[[nodiscard]] bool buildCompactHeightfield(RecastContext& context, const int walkableHeight, [[nodiscard]] bool buildCompactHeightfield(RecastContext& context, const int walkableHeight,
const int walkableClimb, rcHeightfield& solid, rcCompactHeightfield& compact) const int walkableClimb, rcHeightfield& solid, rcCompactHeightfield& compact)
{ {
if (walkableHeight < 3) if (!isValidWalkableHeight(walkableHeight))
{ {
Log(Debug::Warning) << context.getPrefix() Log(Debug::Warning) << context.getPrefix()
<< "Invalid walkableHeight to build compact heightfield: " << walkableHeight; << "Invalid walkableHeight to build compact heightfield: " << walkableHeight;
@ -308,9 +324,14 @@ namespace DetourNavigator
return rcBuildCompactHeightfield(&context, walkableHeight, walkableClimb, solid, compact); return rcBuildCompactHeightfield(&context, walkableHeight, walkableClimb, solid, compact);
} }
bool isValidWalkableRadius(int value)
{
return 0 < value && value < walkableRadiusUpperLimit;
}
[[nodiscard]] bool erodeWalkableArea(RecastContext& context, int walkableRadius, rcCompactHeightfield& compact) [[nodiscard]] bool erodeWalkableArea(RecastContext& context, int walkableRadius, rcCompactHeightfield& compact)
{ {
if (walkableRadius <= 0 || 255 <= walkableRadius) if (!isValidWalkableRadius(walkableRadius))
{ {
Log(Debug::Warning) << context.getPrefix() Log(Debug::Warning) << context.getPrefix()
<< "Invalid walkableRadius to erode walkable area: " << walkableRadius; << "Invalid walkableRadius to erode walkable area: " << walkableRadius;
@ -614,4 +635,10 @@ namespace DetourNavigator
return navMesh; return navMesh;
} }
bool isSupportedAgentBounds(const RecastSettings& settings, const AgentBounds& agentBounds)
{
return isValidWalkableHeight(getWalkableHeight(settings, agentBounds))
&& isValidWalkableRadius(getWalkableRadius(settings, agentBounds));
}
} }

View File

@ -50,6 +50,8 @@ namespace DetourNavigator
const TilePosition& tile, const RecastSettings& settings); const TilePosition& tile, const RecastSettings& settings);
NavMeshPtr makeEmptyNavMesh(const Settings& settings); NavMeshPtr makeEmptyNavMesh(const Settings& settings);
bool isSupportedAgentBounds(const RecastSettings& settings, const AgentBounds& agentBounds);
} }
#endif #endif

View File

@ -77,8 +77,9 @@ namespace DetourNavigator
* @brief addAgent should be called for each agent even if all of them has same half extents. * @brief addAgent should be called for each agent even if all of them has same half extents.
* @param agentBounds allows to setup bounding cylinder for each agent, for each different half extents * @param agentBounds allows to setup bounding cylinder for each agent, for each different half extents
* there is different navmesh. * there is different navmesh.
* @return true if agent is successfully added or false if agent bounds are not supported.
*/ */
virtual void addAgent(const AgentBounds& agentBounds) = 0; virtual bool addAgent(const AgentBounds& agentBounds) = 0;
/** /**
* @brief removeAgent should be called for each agent even if all of them has same half extents * @brief removeAgent should be called for each agent even if all of them has same half extents

View File

@ -1,4 +1,5 @@
#include "navigatorimpl.hpp" #include "navigatorimpl.hpp"
#include "makenavmesh.hpp"
#include "settingsutils.hpp" #include "settingsutils.hpp"
#include "stats.hpp" #include "stats.hpp"
@ -15,13 +16,13 @@ namespace DetourNavigator
{ {
} }
void NavigatorImpl::addAgent(const AgentBounds& agentBounds) bool NavigatorImpl::addAgent(const AgentBounds& agentBounds)
{ {
if (agentBounds.mHalfExtents.x() == 0.f || agentBounds.mHalfExtents.y() == 0.f if (!isSupportedAgentBounds(mSettings.mRecast, agentBounds))
|| agentBounds.mHalfExtents.z() == 0.f) return false;
return;
++mAgents[agentBounds]; ++mAgents[agentBounds];
mNavMeshManager.addAgent(agentBounds); mNavMeshManager.addAgent(agentBounds);
return true;
} }
void NavigatorImpl::removeAgent(const AgentBounds& agentBounds) void NavigatorImpl::removeAgent(const AgentBounds& agentBounds)

View File

@ -23,7 +23,7 @@ namespace DetourNavigator
return std::make_unique<const UpdateGuard>(*this); return std::make_unique<const UpdateGuard>(*this);
} }
void addAgent(const AgentBounds& agentBounds) override; bool addAgent(const AgentBounds& agentBounds) override;
void removeAgent(const AgentBounds& agentBounds) override; void removeAgent(const AgentBounds& agentBounds) override;

View File

@ -19,7 +19,7 @@ namespace DetourNavigator
std::unique_ptr<const UpdateGuard> makeUpdateGuard() override { return nullptr; } std::unique_ptr<const UpdateGuard> makeUpdateGuard() override { return nullptr; }
void addAgent(const AgentBounds& /*agentBounds*/) override {} bool addAgent(const AgentBounds& /*agentBounds*/) override { return true; }
void removeAgent(const AgentBounds& /*agentBounds*/) override {} void removeAgent(const AgentBounds& /*agentBounds*/) override {}