1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-25 16:43:33 +00:00

Check AiTravel destination for other actors presence

Use faster aabbTest but without destance filter. To avoid dependency on a
specific constant and correctly handle situations when there is a big
difference between actors sizes.
This commit is contained in:
elsid 2021-09-29 00:42:29 +02:00
parent 9950132a5f
commit c0ef4417c3
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40
12 changed files with 82 additions and 29 deletions

View File

@ -41,6 +41,7 @@
Bug #6283: Avis Dorsey follows you after her death Bug #6283: Avis Dorsey follows you after her death
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
Bug #6302: Teleporting disabled actor breaks its disabled state Bug #6302: Teleporting disabled actor breaks its disabled state
Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken
Feature #890: OpenMW-CS: Column filtering Feature #890: OpenMW-CS: Column filtering
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
Feature #2780: A way to see current OpenMW version in the console Feature #2780: A way to see current OpenMW version in the console

View File

@ -653,7 +653,8 @@ namespace MWBase
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0; virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0; virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors = nullptr) const = 0;
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;

View File

@ -1,5 +1,7 @@
#include "aitravel.hpp" #include "aitravel.hpp"
#include <algorithm>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -23,6 +25,11 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
return (pos1 - pos2).length2() <= 7168*7168; return (pos1 - pos2).length2() <= 7168*7168;
} }
float getActorRadius(const MWWorld::ConstPtr& actor)
{
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
return std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
}
} }
namespace MWMechanics namespace MWMechanics
@ -70,16 +77,24 @@ namespace MWMechanics
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor. // Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
// If we got close to target, check for actors nearby. If they are, finish AI package. // If we got close to target, check for actors nearby. If they are, finish AI package.
int destinationTolerance = 64; if (mDestinationCheck.update(duration) == Misc::TimerStatus::Elapsed)
if (distance(actorPos, targetPos) <= destinationTolerance)
{ {
std::vector<MWWorld::Ptr> targetActors; std::vector<MWWorld::Ptr> occupyingActors;
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors); if (isAreaOccupiedByOtherActor(actor, targetPos, &occupyingActors))
if (!result.first.isEmpty())
{ {
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; const float actorRadius = getActorRadius(actor);
return true; const float distanceToTarget = distance(actorPos, targetPos);
for (const MWWorld::Ptr& other : occupyingActors)
{
const float otherRadius = getActorRadius(other);
const auto [minRadius, maxRadius] = std::minmax(actorRadius, otherRadius);
constexpr float toleranceFactor = 1.25;
if (minRadius * toleranceFactor + maxRadius > distanceToTarget)
{
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
} }
} }

View File

@ -52,6 +52,8 @@ namespace MWMechanics
const float mZ; const float mZ;
const bool mHidden; const bool mHidden;
AiReactionTimer mDestinationCheck;
}; };
struct AiInternalTravel final : public AiTravel struct AiInternalTravel final : public AiTravel

View File

@ -85,14 +85,6 @@ namespace MWMechanics
return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor); return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor);
} }
bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination)
{
const auto world = MWBase::Environment::get().getWorld();
const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor);
const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor);
}
void stopMovement(const MWWorld::Ptr& actor) void stopMovement(const MWWorld::Ptr& actor)
{ {
actor.getClass().getMovementSettings(actor).mPosition[0] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0;

View File

@ -4,6 +4,8 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "movement.hpp" #include "movement.hpp"
@ -72,6 +74,15 @@ namespace MWMechanics
return MWWorld::Ptr(); // none found return MWWorld::Ptr(); // none found
} }
bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination,
std::vector<MWWorld::Ptr>* occupyingActors)
{
const auto world = MWBase::Environment::get().getWorld();
const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor);
const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor, occupyingActors);
}
ObstacleCheck::ObstacleCheck() ObstacleCheck::ObstacleCheck()
: mWalkState(WalkState::Initial) : mWalkState(WalkState::Initial)
, mStateDuration(0) , mStateDuration(0)

View File

@ -3,9 +3,12 @@
#include <osg/Vec3f> #include <osg/Vec3f>
#include <vector>
namespace MWWorld namespace MWWorld
{ {
class Ptr; class Ptr;
class ConstPtr;
} }
namespace MWMechanics namespace MWMechanics
@ -21,6 +24,9 @@ namespace MWMechanics
/** \return Pointer to the door, or empty pointer if none exists **/ /** \return Pointer to the door, or empty pointer if none exists **/
const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist); const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist);
bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination,
std::vector<MWWorld::Ptr>* occupyingActors = nullptr);
class ObstacleCheck class ObstacleCheck
{ {
public: public:

View File

@ -22,28 +22,36 @@ namespace MWPhysics
return nearest.distance(position) < radius; return nearest.distance(position) < radius;
} }
template <class OnCollision>
class HasSphereCollisionCallback final : public btBroadphaseAabbCallback class HasSphereCollisionCallback final : public btBroadphaseAabbCallback
{ {
public: public:
HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object, HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object,
const int mask, const int group) const int mask, const int group, OnCollision* onCollision)
: mPosition(position), : mPosition(position),
mRadius(radius), mRadius(radius),
mCollisionObject(object), mCollisionObject(object),
mCollisionFilterMask(mask), mCollisionFilterMask(mask),
mCollisionFilterGroup(group) mCollisionFilterGroup(group),
mOnCollision(onCollision)
{ {
} }
bool process(const btBroadphaseProxy* proxy) override bool process(const btBroadphaseProxy* proxy) override
{ {
if (mResult) if (mResult && mOnCollision == nullptr)
return false; return false;
const auto collisionObject = static_cast<btCollisionObject*>(proxy->m_clientObject); const auto collisionObject = static_cast<btCollisionObject*>(proxy->m_clientObject);
if (collisionObject == mCollisionObject) if (collisionObject == mCollisionObject
|| !needsCollision(*proxy)
|| !testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius))
return true; return true;
if (needsCollision(*proxy)) mResult = true;
mResult = testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius); if (mOnCollision != nullptr)
{
(*mOnCollision)(collisionObject);
return true;
}
return !mResult; return !mResult;
} }
@ -58,6 +66,7 @@ namespace MWPhysics
btCollisionObject* mCollisionObject; btCollisionObject* mCollisionObject;
int mCollisionFilterMask; int mCollisionFilterMask;
int mCollisionFilterGroup; int mCollisionFilterGroup;
OnCollision* mOnCollision;
bool mResult = false; bool mResult = false;
bool needsCollision(const btBroadphaseProxy& proxy) const bool needsCollision(const btBroadphaseProxy& proxy) const

View File

@ -923,7 +923,8 @@ namespace MWPhysics
CollisionType_Actor|CollisionType_Projectile); CollisionType_Actor|CollisionType_Projectile);
} }
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const
{ {
btCollisionObject* object = nullptr; btCollisionObject* object = nullptr;
const auto it = mActors.find(ignore.mRef); const auto it = mActors.find(ignore.mRef);
@ -934,7 +935,19 @@ namespace MWPhysics
const auto aabbMax = bulletPosition + btVector3(radius, radius, radius); const auto aabbMax = bulletPosition + btVector3(radius, radius, radius);
const int mask = MWPhysics::CollisionType_Actor; const int mask = MWPhysics::CollisionType_Actor;
const int group = 0xff; const int group = 0xff;
HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group); if (occupyingActors == nullptr)
{
HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group,
static_cast<void (*)(const btCollisionObject*)>(nullptr));
mTaskScheduler->aabbTest(aabbMin, aabbMax, callback);
return callback.getResult();
}
const auto onCollision = [&] (const btCollisionObject* object)
{
if (PtrHolder* holder = static_cast<PtrHolder*>(object->getUserPointer()))
occupyingActors->push_back(holder->getPtr());
};
HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group, &onCollision);
mTaskScheduler->aabbTest(aabbMin, aabbMax, callback); mTaskScheduler->aabbTest(aabbMin, aabbMax, callback);
return callback.getResult(); return callback.getResult();
} }

View File

@ -252,7 +252,8 @@ namespace MWPhysics
std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function); std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function);
} }
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const; bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const; void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
void reportCollision(const btVector3& position, const btVector3& normal); void reportCollision(const btVector3& position, const btVector3& normal);

View File

@ -3935,9 +3935,10 @@ namespace MWWorld
return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal); return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal);
} }
bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const
{ {
return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore); return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore, occupyingActors);
} }
void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const

View File

@ -735,7 +735,8 @@ namespace MWWorld
bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override; bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override;
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override; bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const override;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;