1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 07:09:41 +00:00

Check agent bounds on adding agent to navigator

Do not add agent bounds which are not supported by recastnavigation with given
settings and log such events.

To avoid reaching navmesh tile generation to find out it can't be generated for
such agent bounds.
This commit is contained in:
elsid 2023-01-17 23:31:17 +01:00
parent a7e37509de
commit cf1d8544e3
No known key found for this signature in database
GPG key ID: 4DE04C198CBA7625
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 {}