mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-19 17:11:33 +00:00
Merge pull request #2324 from elsid/aiwander_navmesh
Use navmesh for AiWander
This commit is contained in:
commit
b3fe85fb6d
7 changed files with 69 additions and 9 deletions
|
@ -301,7 +301,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const float actorSpeed = actor.getClass().getSpeed(actor);
|
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 distToTarget = osg::Vec2f(endPoint.x(), endPoint.y()).length();
|
||||||
|
|
||||||
const float offsetXY = distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2;
|
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
|
// get actor's shortest radius for moving in circle
|
||||||
float speed = actor.getClass().getSpeed(actor);
|
float speed = actor.getClass().getSpeed(actor);
|
||||||
speed += speed * 0.1f; // 10% real speed inaccuracy
|
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
|
// get radius direction to the center
|
||||||
const float* rot = actor.getRefData().getPosition().rot;
|
const float* rot = actor.getRefData().getPosition().rot;
|
||||||
|
|
|
@ -448,7 +448,7 @@ namespace MWMechanics
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// have not yet reached the destination
|
// have not yet reached the destination
|
||||||
evadeObstacles(actor, storage);
|
evadeObstacles(actor, duration, storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,8 +479,17 @@ namespace MWMechanics
|
||||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
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())
|
if (mObstacleCheck.isEvading())
|
||||||
{
|
{
|
||||||
// first check if we're walking into a door
|
// first check if we're walking into a door
|
||||||
|
|
|
@ -137,7 +137,7 @@ namespace MWMechanics
|
||||||
short unsigned getRandomIdle();
|
short unsigned getRandomIdle();
|
||||||
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
||||||
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
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 turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
||||||
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
|
|
|
@ -73,6 +73,13 @@ namespace
|
||||||
{
|
{
|
||||||
return sqrDistance(osg::Vec2f(lhs.x(), lhs.y()), osg::Vec2f(rhs.x(), rhs.y()));
|
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
|
namespace MWMechanics
|
||||||
|
@ -320,8 +327,7 @@ namespace MWMechanics
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
const auto world = MWBase::Environment::get().getWorld();
|
||||||
const auto realHalfExtents = world->getHalfExtents(actor); // This may differ from halfExtents argument
|
const auto stepSize = getPathStepSize(actor);
|
||||||
const auto stepSize = 2 * std::max(realHalfExtents.x(), realHalfExtents.y());
|
|
||||||
const auto navigator = world->getNavigator();
|
const auto navigator = world->getNavigator();
|
||||||
navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out);
|
navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out);
|
||||||
}
|
}
|
||||||
|
@ -333,4 +339,39 @@ namespace MWMechanics
|
||||||
<< DetourNavigator::WriteFlags {flags} << ")";
|
<< 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<osg::Vec3f> 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} << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,9 @@ namespace MWMechanics
|
||||||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
||||||
const DetourNavigator::Flags flags);
|
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
|
/// Remove front point if exist and within tolerance
|
||||||
void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance);
|
void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance);
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f
|
||||||
if (absDiff < epsilonRadians)
|
if (absDiff < epsilonRadians)
|
||||||
return true;
|
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)
|
if (absDiff > limit)
|
||||||
diff = osg::sign(diff) * limit;
|
diff = osg::sign(diff) * limit;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <osg/Math>
|
#include <osg/Math>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class Ptr;
|
class Ptr;
|
||||||
|
@ -12,7 +14,12 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
// Max rotating speed, radian/sec
|
// 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)
|
/// configure rotation settings for an actor to reach this target angle (eventually)
|
||||||
/// @return have we reached the target angle?
|
/// @return have we reached the target angle?
|
||||||
|
|
Loading…
Reference in a new issue