From d02beae5a8522b0c0c7ae6f90877d7d016d1d556 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 21 Jul 2018 14:16:19 +0300 Subject: [PATCH] Find path for actors according to their abilities to swim and walk --- apps/openmw/mwmechanics/aipackage.cpp | 16 +++- apps/openmw/mwmechanics/aipackage.hpp | 2 + apps/openmw/mwmechanics/pathfinding.cpp | 4 +- apps/openmw/mwmechanics/pathfinding.hpp | 3 +- .../detournavigator/navigator.cpp | 81 +++++++++++++++---- components/detournavigator/findsmoothpath.hpp | 5 +- components/detournavigator/navigator.hpp | 6 +- 7 files changed, 92 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 16af9c8e7..a14617249 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -141,7 +141,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& || world->isFlying(actor); // Prohibit shortcuts for AiWander, if the actor can not move in 3 dimensions. - if (actorCanMoveByZ || getTypeId() != TypeIdWander) + if (actorCanMoveByZ) mIsShortcutting = shortcutPath(start, dest, actor, &destInLOS, actorCanMoveByZ); // try to shortcut first if (!mIsShortcutting) @@ -400,3 +400,17 @@ bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& act // no points of actor's circle should be farther from the center than destination point return (radius <= distToDest); } + +DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::Ptr& actor) const +{ + const auto& actorClass = actor.getClass(); + DetourNavigator::Flags result = DetourNavigator::Flag_none; + + if (actorClass.isPureWaterCreature(actor) || (getTypeId() != TypeIdWander && actorClass.canSwim(actor))) + result |= DetourNavigator::Flag_swim; + + if (actorClass.canWalk(actor)) + result |= DetourNavigator::Flag_walk; + + return result; +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 59bd6689d..20f47480e 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -132,6 +132,8 @@ namespace MWMechanics const PathgridGraph& getPathGridGraph(const MWWorld::CellStore* cell); + DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; + // TODO: all this does not belong here, move into temporary storage PathFinder mPathFinder; ObstacleCheck mObstacleCheck; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 5a34bb230..2611cb5ed 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -290,14 +290,14 @@ namespace MWMechanics } void PathFinder::buildPathByNavigator(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, - const osg::Vec3f& halfExtents) + const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) { try { mPath.clear(); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); - navigator->findPath(halfExtents, startPoint, endPoint, std::back_inserter(mPath)); + navigator->findPath(halfExtents, startPoint, endPoint, flags, std::back_inserter(mPath)); mConstructed = true; } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index f488e3b14..aaae2e88e 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -176,7 +177,7 @@ namespace MWMechanics } void buildPathByNavigator(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, - const osg::Vec3f& halfExtents); + const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); private: bool mConstructed; diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 84aa34474..52e350572 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -64,20 +64,20 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_throw_exception) { - EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut), InvalidArgument); + EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut), InvalidArgument); } TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) { mNavigator->addAgent(mAgentHalfExtents); - EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut), NavigatorException); + EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut), NavigatorException); } TEST_F(DetourNavigatorNavigatorTest, find_path_for_removed_agent_should_throw_exception) { mNavigator->addAgent(mAgentHalfExtents); mNavigator->removeAgent(mAgentHalfExtents); - EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut), InvalidArgument); + EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut), InvalidArgument); } TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent) @@ -85,7 +85,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents); mNavigator->removeAgent(mAgentHalfExtents); - EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut), NavigatorException); + EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut), NavigatorException); } TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path) @@ -105,7 +105,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.85963428020477294921875), @@ -155,7 +155,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, std::back_inserter(mPath)); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, std::back_inserter(mPath)); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.85963428020477294921875), @@ -188,7 +188,7 @@ namespace mNavigator->wait(); mPath.clear(); - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, std::back_inserter(mPath)); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, std::back_inserter(mPath)); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.87827122211456298828125), @@ -239,7 +239,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, std::back_inserter(mPath)); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, std::back_inserter(mPath)); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.87827122211456298828125), @@ -274,7 +274,7 @@ namespace mNavigator->wait(); mPath.clear(); - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.85963428020477294921875), @@ -331,7 +331,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.96328866481781005859375), @@ -387,7 +387,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.9393787384033203125), @@ -417,7 +417,7 @@ namespace })) << mPath; } - TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_ground_lower_than_water) + TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_ground_lower_than_water_with_only_swim_flag) { std::array heightfieldData {{ -50, -50, -50, -50, 0, @@ -440,7 +440,7 @@ namespace mEnd.x() = 0; mEnd.z() = 300; - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_swim, mOut); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, 185.33331298828125), @@ -463,7 +463,7 @@ namespace })) << mPath; } - TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water) + TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water_with_swim_and_walk_flags) { std::array heightfieldData {{ 0, 0, 0, 0, 0, 0, 0, @@ -486,7 +486,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_swim | Flag_walk, mOut); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, -94.75363922119140625), @@ -509,7 +509,7 @@ namespace })) << mPath; } - TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water_with_max_int_cells_size) + TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water_with_max_int_cells_size_and_swim_and_walk_flags) { std::array heightfieldData {{ 0, 0, 0, 0, 0, 0, 0, @@ -532,7 +532,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_swim | Flag_walk, mOut); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, -94.75363922119140625), @@ -554,4 +554,51 @@ namespace osg::Vec3f(0, -215, -94.753631591796875), })) << mPath; } + + TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_ground_when_ground_cross_water_with_only_walk_flag) + { + std::array heightfieldData {{ + 0, 0, 0, 0, 0, 0, 0, + 0, -100, -100, -100, -100, -100, 0, + 0, -100, -150, -150, -150, -100, 0, + 0, -100, -150, -200, -150, -100, 0, + 0, -100, -150, -150, -150, -100, 0, + 0, -100, -100, -100, -100, -100, 0, + 0, 0, 0, 0, 0, 0, 0, + }}; + btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + shape.setLocalScaling(btVector3(128, 128, 1)); + + mNavigator->addAgent(mAgentHalfExtents); + mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); + mNavigator->addObject(1, shape, btTransform::getIdentity()); + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + mStart.x() = 0; + mEnd.x() = 0; + + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, Flag_walk, mOut); + + EXPECT_EQ(mPath, std::deque({ + osg::Vec3f(0, 215, -94.75363922119140625), + osg::Vec3f(9.8083515167236328125, 188.4185333251953125, -105.19994354248046875), + osg::Vec3f(19.6167049407958984375, 161.837066650390625, -114.25496673583984375), + osg::Vec3f(29.42505645751953125, 135.255615234375, -123.309967041015625), + osg::Vec3f(39.23340606689453125, 108.674163818359375, -132.3649749755859375), + osg::Vec3f(49.04175567626953125, 82.09270477294921875, -137.2874755859375), + osg::Vec3f(58.8501129150390625, 55.5112457275390625, -139.2451171875), + osg::Vec3f(68.6584625244140625, 28.9297885894775390625, -141.2027740478515625), + osg::Vec3f(78.4668121337890625, 2.3483295440673828125, -143.1604156494140625), + osg::Vec3f(88.27516937255859375, -24.233127593994140625, -141.3894805908203125), + osg::Vec3f(83.73651885986328125, -52.2005767822265625, -142.3761444091796875), + osg::Vec3f(79.19786834716796875, -80.16802978515625, -143.114837646484375), + osg::Vec3f(64.8477935791015625, -104.598602294921875, -137.840911865234375), + osg::Vec3f(50.497714996337890625, -129.0291748046875, -131.45831298828125), + osg::Vec3f(36.147632598876953125, -153.459747314453125, -121.42321014404296875), + osg::Vec3f(21.7975559234619140625, -177.8903350830078125, -111.38809967041015625), + osg::Vec3f(7.44747829437255859375, -202.3209075927734375, -101.1938323974609375), + osg::Vec3f(0, -215, -94.753631591796875), + })) << mPath; + } } diff --git a/components/detournavigator/findsmoothpath.hpp b/components/detournavigator/findsmoothpath.hpp index c145e828c..08bfa14da 100644 --- a/components/detournavigator/findsmoothpath.hpp +++ b/components/detournavigator/findsmoothpath.hpp @@ -219,13 +219,14 @@ namespace DetourNavigator template OutputIterator findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, - const osg::Vec3f& start, const osg::Vec3f& end, const Settings& settings, OutputIterator out) + const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, + const Settings& settings, OutputIterator out) { dtNavMeshQuery navMeshQuery; OPENMW_CHECK_DT_STATUS(navMeshQuery.init(&navMesh, settings.mMaxNavMeshQueryNodes)); dtQueryFilter queryFilter; - queryFilter.setIncludeFlags(Flag_swim | Flag_walk); + queryFilter.setIncludeFlags(includeFlags); dtPolyRef startRef = 0; osg::Vec3f startPolygonPosition; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index b06397525..3e60d72e0 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATOR_H #include "findsmoothpath.hpp" +#include "flags.hpp" #include "navmeshmanager.hpp" #include "settings.hpp" #include "settingsutils.hpp" @@ -48,11 +49,12 @@ namespace DetourNavigator template OutputIterator findPath(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, - const osg::Vec3f& end, OutputIterator out) const + const osg::Vec3f& end, const Flags includeFlags, OutputIterator out) const { const auto navMesh = mNavMeshManager.getNavMesh(agentHalfExtents); return findSmoothPath(*navMesh.lock(), toNavMeshCoordinates(mSettings, agentHalfExtents), - toNavMeshCoordinates(mSettings, start), toNavMeshCoordinates(mSettings, end), mSettings, out); + toNavMeshCoordinates(mSettings, start), toNavMeshCoordinates(mSettings, end), includeFlags, + mSettings, out); } std::map> getNavMeshes() const;