diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 94a502544..598292fc3 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -301,7 +301,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin return true; const float actorSpeed = actor.getClass().getSpeed(actor); - const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / MAX_VEL_ANGULAR_RADIANS * 2; // *2 - for reliability + const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / getAngularVelocity(actorSpeed) * 2; // *2 - for reliability const float distToTarget = osg::Vec2f(endPoint.x(), endPoint.y()).length(); const float offsetXY = distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2; @@ -363,7 +363,7 @@ bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& act // get actor's shortest radius for moving in circle float speed = actor.getClass().getSpeed(actor); speed += speed * 0.1f; // 10% real speed inaccuracy - float radius = speed / MAX_VEL_ANGULAR_RADIANS; + float radius = speed / getAngularVelocity(speed); // get radius direction to the center const float* rot = actor.getRefData().getPosition().rot; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index d00bbeb5f..cd13b1820 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -448,7 +448,7 @@ namespace MWMechanics else { // have not yet reached the destination - evadeObstacles(actor, storage); + evadeObstacles(actor, duration, storage); } } @@ -479,8 +479,17 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_IdleNow); } - void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage) + void AiWander::evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage) { + if (mUsePathgrid) + { + const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + const float actorTolerance = 2 * actor.getClass().getSpeed(actor) * duration + + 1.2 * std::max(halfExtents.x(), halfExtents.y()); + const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance); + mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), pointTolerance); + } + if (mObstacleCheck.isEvading()) { // first check if we're walking into a door diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index d586bb0bc..0b967983f 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -137,7 +137,7 @@ namespace MWMechanics short unsigned getRandomIdle(); void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage); - void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage); + void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 4ff28e01a..900f97a67 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -73,6 +73,13 @@ namespace { return sqrDistance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y())); } + + float getPathStepSize(const MWWorld::ConstPtr& actor) + { + const auto world = MWBase::Environment::get().getWorld(); + const auto realHalfExtents = world->getHalfExtents(actor); + return 2 * std::max(realHalfExtents.x(), realHalfExtents.y()); + } } namespace MWMechanics @@ -320,8 +327,7 @@ namespace MWMechanics try { const auto world = MWBase::Environment::get().getWorld(); - const auto realHalfExtents = world->getHalfExtents(actor); // This may differ from halfExtents argument - const auto stepSize = 2 * std::max(realHalfExtents.x(), realHalfExtents.y()); + const auto stepSize = getPathStepSize(actor); const auto navigator = world->getNavigator(); navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out); } @@ -333,4 +339,39 @@ namespace MWMechanics << DetourNavigator::WriteFlags {flags} << ")"; } } + + void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, + const DetourNavigator::Flags flags, const float pointTolerance) + { + if (mPath.empty()) + return; + + const auto stepSize = getPathStepSize(actor); + const auto startPoint = actor.getRefData().getPosition().asVec3(); + + if (sqrDistanceIgnoreZ(mPath.front(), startPoint) <= 4 * stepSize * stepSize) + return; + + try + { + const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); + std::deque prePath; + navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, std::back_inserter(prePath)); + + while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.front(), startPoint) < stepSize * stepSize) + prePath.pop_front(); + + while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.back(), mPath.front()) < stepSize * stepSize) + prePath.pop_back(); + + std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath)); + } + catch (const DetourNavigator::NavigatorException& exception) + { + Log(Debug::Debug) << "Build path by navigator exception: \"" << exception.what() + << "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase() + << ") from " << startPoint << " to " << mPath.front() << " with flags (" + << DetourNavigator::WriteFlags {flags} << ")"; + } + } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 1dc85c5e5..f762b6f18 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -84,6 +84,9 @@ namespace MWMechanics const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); + void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, + const DetourNavigator::Flags flags, const float pointTolerance); + /// Remove front point if exist and within tolerance void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); diff --git a/apps/openmw/mwmechanics/steering.cpp b/apps/openmw/mwmechanics/steering.cpp index 1ef46e1ae..b08a90220 100644 --- a/apps/openmw/mwmechanics/steering.cpp +++ b/apps/openmw/mwmechanics/steering.cpp @@ -32,7 +32,7 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f if (absDiff < epsilonRadians) return true; - float limit = MAX_VEL_ANGULAR_RADIANS * MWBase::Environment::get().getFrameDuration(); + float limit = getAngularVelocity(actor.getClass().getSpeed(actor)) * MWBase::Environment::get().getFrameDuration(); if (absDiff > limit) diff = osg::sign(diff) * limit; diff --git a/apps/openmw/mwmechanics/steering.hpp b/apps/openmw/mwmechanics/steering.hpp index 632ab3611..f305a6961 100644 --- a/apps/openmw/mwmechanics/steering.hpp +++ b/apps/openmw/mwmechanics/steering.hpp @@ -3,6 +3,8 @@ #include +#include + namespace MWWorld { class Ptr; @@ -12,7 +14,12 @@ namespace MWMechanics { // Max rotating speed, radian/sec -const float MAX_VEL_ANGULAR_RADIANS(10); +inline float getAngularVelocity(const float actorSpeed) +{ + const float baseAngluarVelocity = 10; + const float baseSpeed = 200; + return baseAngluarVelocity * std::max(actorSpeed / baseSpeed, 1.0f); +} /// configure rotation settings for an actor to reach this target angle (eventually) /// @return have we reached the target angle?