From d1d6ba3ed0728e87289bd479d105a0b240e6a6cd Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 7 Oct 2019 20:19:12 +0200 Subject: [PATCH 1/5] Fix rebuild path for walking actors Ignore z coordinate for not swimming nor flying actors to calculate distance from actor destination to last path point. If walking actor destination point is floating above the ground then a point on navmesh may be too far away when z coordinate is included. In this case path will be rebuild on each AI_REACTION_TIME. --- apps/openmw/mwmechanics/aipackage.cpp | 29 +++++++++++++++++++++++---- apps/openmw/mwmechanics/aipackage.hpp | 2 +- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 646b376698..8336ca9719 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -22,6 +22,27 @@ #include +namespace MWMechanics +{ + static float distance(const osg::Vec2f& lhs, const osg::Vec2f& rhs) + { + return (lhs - rhs).length2(); + } + + static float distanceIgnoreZ(const osg::Vec3f& lhs, const osg::Vec3f& rhs) + { + return distance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y())); + } + + static float distance(const osg::Vec3f& lhs, const osg::Vec3f& rhs, const MWWorld::Ptr& actor) + { + const auto world = MWBase::Environment::get().getWorld(); + if (world->isSwimming(actor) || world->isFlying(actor)) + return distance(lhs, rhs); + return distanceIgnoreZ(lhs, rhs); + } +} + MWMechanics::AiPackage::~AiPackage() {} MWMechanics::AiPackage::AiPackage() : @@ -133,7 +154,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& if (!mIsShortcutting) { - if (wasShortcutting || doesPathNeedRecalc(dest, actor.getCell())) // if need to rebuild path + if (wasShortcutting || doesPathNeedRecalc(dest, actor)) // if need to rebuild path { const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), @@ -328,11 +349,11 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin return false; } -bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::CellStore* currentCell) +bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::Ptr& actor) const { return mPathFinder.getPath().empty() - || (distance(mPathFinder.getPath().back(), newDest) > 10) - || mPathFinder.getPathCell() != currentCell; + || distance(mPathFinder.getPath().back(), newDest, actor) > 10 + || mPathFinder.getPathCell() != actor.getCell(); } bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position) diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 6bb12342a0..20b4c390ea 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -120,7 +120,7 @@ namespace MWMechanics /// Check if the way to the destination is clear, taking into account actor speed bool checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor); - bool doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::CellStore* currentCell); + bool doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::Ptr& actor) const; void evadeObstacles(const MWWorld::Ptr& actor); From cbf8deeb31acd4572d7e00198effa3087f2ba686 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 7 Oct 2019 21:05:41 +0200 Subject: [PATCH 2/5] Remove unused default settings --- files/settings-default.cfg | 6 ------ 1 file changed, 6 deletions(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index f8c31eed7b..6ae183ad6e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -711,12 +711,6 @@ max smooth path size = 1024 # Maximum number of triangles in each node of mesh AABB tree (value > 0) triangles per chunk = 256 -# Enable debug log (true, false) -enable log = false - -# Write debug log to this file -log path = detournavigator.log - # Write recast mesh to file in .obj format for each use to update nav mesh (true, false) enable write recast mesh to file = false From bfe6005ed484923fa72b183793f51062ce53cbbb Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 7 Oct 2019 21:29:41 +0200 Subject: [PATCH 3/5] Increase default recast scale factor To make 4 voxels fit into sStepSizeUp = 34: "recast scale factor" = 4 * "cell size" / sStepSizeUp --- docs/source/reference/modding/settings/navigator.rst | 4 ++-- files/settings-default.cfg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index 42494a4760..db2ad66cfa 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -176,12 +176,12 @@ recast scale factor :Type: floating point :Range: > 0.0 -:Default: 0.017647058823529415 +:Default: 0.023529411764705882 Scale of nav mesh coordinates to world coordinates. Recastnavigation builds voxels for world geometry. Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size "recast scale factor" / "cell size". Default value calculates by this equation: -sStepSizeUp * "recast scale factor" / "cell size" = 3 (max climb height should be equal to 3 voxels). +sStepSizeUp * "recast scale factor" / "cell size" = 4 (max climb height should be equal to 4 voxels). Changing this value will change generated nav mesh. Some locations may become unavailable for NPC and creatures. Pay attention to slopes and roofs when change it. Increasing this value will reduce nav mesh update latency. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6ae183ad6e..f999cbb01c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -642,8 +642,8 @@ enable = true # Scale of NavMesh coordinates to world coordinates (value > 0.0). Recastnavigation builds voxels for world geometry. # Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size # "recast scale factor" / "cell size". Default value calculates by this equation: -# sStepSizeUp * "recast scale factor" / "cell size" = 3 (max climb height should be equal to 3 voxels) -recast scale factor = 0.017647058823529415 +# sStepSizeUp * "recast scale factor" / "cell size" = 4 (max climb height should be equal to 4 voxels) +recast scale factor = 0.023529411764705882 # The z-axis cell size to use for fields. (value > 0.0) # Defines voxel/grid/cell size. So their values have significant From 81832f8e17cf4d90cb52e3811c457d9d42d1d3c6 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 19 Oct 2019 12:40:34 +0200 Subject: [PATCH 4/5] Reuse distance functions --- apps/openmw/mwmechanics/aipackage.cpp | 23 +---------------------- apps/openmw/mwmechanics/combat.cpp | 15 +++++++++------ apps/openmw/mwmechanics/combat.hpp | 3 +++ apps/openmw/mwmechanics/pathfinding.cpp | 8 ++++++++ apps/openmw/mwmechanics/pathfinding.hpp | 14 +++++++++++++- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 8336ca9719..37f5f5bf76 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -22,27 +22,6 @@ #include -namespace MWMechanics -{ - static float distance(const osg::Vec2f& lhs, const osg::Vec2f& rhs) - { - return (lhs - rhs).length2(); - } - - static float distanceIgnoreZ(const osg::Vec3f& lhs, const osg::Vec3f& rhs) - { - return distance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y())); - } - - static float distance(const osg::Vec3f& lhs, const osg::Vec3f& rhs, const MWWorld::Ptr& actor) - { - const auto world = MWBase::Environment::get().getWorld(); - if (world->isSwimming(actor) || world->isFlying(actor)) - return distance(lhs, rhs); - return distanceIgnoreZ(lhs, rhs); - } -} - MWMechanics::AiPackage::~AiPackage() {} MWMechanics::AiPackage::AiPackage() : @@ -352,7 +331,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::Ptr& actor) const { return mPathFinder.getPath().empty() - || distance(mPathFinder.getPath().back(), newDest, actor) > 10 + || getPathDistance(actor, mPathFinder.getPath().back(), newDest) > 10 || mPathFinder.getPathCell() != actor.getCell(); } diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9258c6b2de..27dc0b473e 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -20,6 +20,7 @@ #include "spellcasting.hpp" #include "difficultyscaling.hpp" #include "actorutil.hpp" +#include "pathfinding.hpp" namespace { @@ -467,13 +468,8 @@ namespace MWMechanics { osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); - if (canActorMoveByZAxis(actor2)) - { - pos1.z() = 0.f; - pos2.z() = 0.f; - } - float d = (pos1 - pos2).length(); + float d = getAggroDistance(actor2, pos1, pos2); static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( "iFightDistanceBase")->mValue.getInteger(); @@ -489,4 +485,11 @@ namespace MWMechanics return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0) || (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75); } + + float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs) + { + if (canActorMoveByZAxis(actor)) + return distanceIgnoreZ(lhs, rhs); + return distance(lhs, rhs); + } } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index fd2717b191..3d4a1bd77c 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -55,6 +55,9 @@ void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2); bool isTargetMagicallyHidden(const MWWorld::Ptr& target); + +float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs); + } #endif diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 900f97a674..97c29680c6 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -18,6 +18,7 @@ #include "pathgrid.hpp" #include "coordinateconverter.hpp" +#include "actorutil.hpp" namespace { @@ -84,6 +85,13 @@ namespace namespace MWMechanics { + float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs) + { + if (canActorMoveByZAxis(actor)) + return distance(lhs, rhs); + return distanceIgnoreZ(lhs, rhs); + } + bool checkWayIsClear(const osg::Vec3f& from, const osg::Vec3f& to, float offsetXY) { osg::Vec3f dir = to - from; diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index f762b6f185..b413810f40 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -13,17 +13,29 @@ namespace MWWorld { class CellStore; class ConstPtr; + class Ptr; } namespace MWMechanics { class PathgridGraph; - inline float distance(const osg::Vec3f& lhs, const osg::Vec3f& rhs) + template + inline float distance(const T& lhs, const T& rhs) { + static_assert(std::is_same::value + || std::is_same::value, + "T is not a position"); return (lhs - rhs).length(); } + inline float distanceIgnoreZ(const osg::Vec3f& lhs, const osg::Vec3f& rhs) + { + return distance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y())); + } + + float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs); + inline float getZAngleToDir(const osg::Vec3f& dir) { return std::atan2(dir.x(), dir.y()); From e4d0af6a6de7d30cf8cd687ab808f4d5e5f7e20d Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 19 Oct 2019 22:01:32 +0200 Subject: [PATCH 5/5] Use z coordinate for path distance when diff by z is greater then actor height --- apps/openmw/mwmechanics/pathfinding.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 97c29680c6..f7e7c277c0 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -81,13 +81,20 @@ namespace const auto realHalfExtents = world->getHalfExtents(actor); return 2 * std::max(realHalfExtents.x(), realHalfExtents.y()); } + + float getHeight(const MWWorld::ConstPtr& actor) + { + const auto world = MWBase::Environment::get().getWorld(); + const auto halfExtents = world->getHalfExtents(actor); + return 2.0 * halfExtents.z(); + } } namespace MWMechanics { float getPathDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs) { - if (canActorMoveByZAxis(actor)) + if (std::abs(lhs.z() - rhs.z()) > getHeight(actor) || canActorMoveByZAxis(actor)) return distance(lhs, rhs); return distanceIgnoreZ(lhs, rhs); }