mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-21 05:39:42 +00:00
Merge pull request #2695 from elsid/aiwander_check_destination
Add more destination checks for AiWander without pathgrid
This commit is contained in:
commit
7e6a533a29
11 changed files with 152 additions and 13 deletions
|
@ -313,6 +313,8 @@ namespace MWBase
|
|||
|
||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
|
||||
|
||||
virtual bool castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) = 0;
|
||||
|
||||
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;
|
||||
virtual bool isActorCollisionEnabled(const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
|
@ -623,6 +625,8 @@ namespace MWBase
|
|||
virtual osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,34 @@ namespace MWMechanics
|
|||
rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0));
|
||||
return position + osg::Vec3f(distance, 0.0, 0.0) * rotation;
|
||||
}
|
||||
|
||||
bool isDestinationHidden(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination)
|
||||
{
|
||||
const auto position = actor.getRefData().getPosition().asVec3();
|
||||
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
||||
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
|
||||
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
|
||||
osg::Vec3f direction = destination - position;
|
||||
direction.normalize();
|
||||
const auto visibleDestination = (
|
||||
isWaterCreature || isFlyingCreature
|
||||
? destination
|
||||
: destination + osg::Vec3f(0, 0, halfExtents.z())
|
||||
) + direction * std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
|
||||
const int mask = MWPhysics::CollisionType_World
|
||||
| MWPhysics::CollisionType_HeightMap
|
||||
| MWPhysics::CollisionType_Door
|
||||
| MWPhysics::CollisionType_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);
|
||||
}
|
||||
}
|
||||
|
||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||
|
@ -265,6 +293,11 @@ namespace MWMechanics
|
|||
completeManualWalking(actor, storage);
|
||||
}
|
||||
|
||||
if (wanderState == AiWanderStorage::Wander_Walking
|
||||
&& (isDestinationHidden(actor, mPathFinder.getPath().back())
|
||||
|| isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back())))
|
||||
completeManualWalking(actor, storage);
|
||||
|
||||
return false; // AiWander package not yet completed
|
||||
}
|
||||
|
||||
|
@ -328,7 +361,10 @@ namespace MWMechanics
|
|||
if (!isWaterCreature && destinationIsAtWater(actor, mDestination))
|
||||
continue;
|
||||
|
||||
if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination))
|
||||
if (isDestinationHidden(actor, mDestination))
|
||||
continue;
|
||||
|
||||
if (isAreaOccupiedByOtherActor(actor, mDestination))
|
||||
continue;
|
||||
|
||||
if (isWaterCreature || isFlyingCreature)
|
||||
|
@ -357,16 +393,6 @@ namespace MWMechanics
|
|||
return MWBase::Environment::get().getWorld()->isUnderwater(actor.getCell(), positionBelowSurface);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the start to end point travels through a collision point (land).
|
||||
*/
|
||||
bool AiWander::destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination) {
|
||||
const int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;
|
||||
return MWBase::Environment::get().getWorld()->castRay(startPoint.x(), startPoint.y(), startPoint.z(),
|
||||
destination.x(), destination.y(), destination.z(),
|
||||
mask);
|
||||
}
|
||||
|
||||
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
|
||||
stopWalking(actor, storage);
|
||||
mObstacleCheck.clear();
|
||||
|
|
|
@ -136,7 +136,6 @@ namespace MWMechanics
|
|||
bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
|
||||
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
||||
bool destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination);
|
||||
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
|
||||
|
||||
int mDistance; // how far the actor can wander from the spawn point
|
||||
|
|
|
@ -120,6 +120,7 @@ namespace MWMechanics
|
|||
mWalkState = WalkState::Norm;
|
||||
mStateDuration = 0;
|
||||
mPrev = position;
|
||||
mInitialDistance = (destination - position).length();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -129,10 +130,11 @@ namespace MWMechanics
|
|||
const float prevDistance = (destination - mPrev).length();
|
||||
const float currentDistance = (destination - position).length();
|
||||
const float movedDistance = prevDistance - currentDistance;
|
||||
const float movedFromInitialDistance = mInitialDistance - currentDistance;
|
||||
|
||||
mPrev = position;
|
||||
|
||||
if (movedDistance >= distSameSpot)
|
||||
if (movedDistance >= distSameSpot && movedFromInitialDistance >= distSameSpot)
|
||||
{
|
||||
mWalkState = WalkState::Norm;
|
||||
mStateDuration = 0;
|
||||
|
@ -143,6 +145,7 @@ namespace MWMechanics
|
|||
{
|
||||
mWalkState = WalkState::CheckStuck;
|
||||
mStateDuration = duration;
|
||||
mInitialDistance = (destination - position).length();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace MWMechanics
|
|||
|
||||
float mStateDuration;
|
||||
int mEvadeDirectionIndex;
|
||||
float mInitialDistance = 0;
|
||||
|
||||
void chooseEvasionDirection();
|
||||
};
|
||||
|
|
72
apps/openmw/mwphysics/hasspherecollisioncallback.hpp
Normal file
72
apps/openmw/mwphysics/hasspherecollisioncallback.hpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#ifndef OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H
|
||||
#define OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H
|
||||
|
||||
#include <LinearMath/btVector3.h>
|
||||
#include <BulletCollision/BroadphaseCollision/btBroadphaseInterface.h>
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
// https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_collision_detection
|
||||
bool testAabbAgainstSphere(const btVector3& aabbMin, const btVector3& aabbMax,
|
||||
const btVector3& position, const btScalar radius)
|
||||
{
|
||||
const btVector3 nearest(
|
||||
std::max(aabbMin.x(), std::min(aabbMax.x(), position.x())),
|
||||
std::max(aabbMin.y(), std::min(aabbMax.y(), position.y())),
|
||||
std::max(aabbMin.z(), std::min(aabbMax.z(), position.z()))
|
||||
);
|
||||
return nearest.distance(position) < radius;
|
||||
}
|
||||
|
||||
class HasSphereCollisionCallback final : public btBroadphaseAabbCallback
|
||||
{
|
||||
public:
|
||||
HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object,
|
||||
const int mask, const int group)
|
||||
: mPosition(position),
|
||||
mRadius(radius),
|
||||
mCollisionObject(object),
|
||||
mCollisionFilterMask(mask),
|
||||
mCollisionFilterGroup(group)
|
||||
{
|
||||
}
|
||||
|
||||
bool process(const btBroadphaseProxy* proxy) final
|
||||
{
|
||||
if (mResult)
|
||||
return false;
|
||||
const auto collisionObject = static_cast<btCollisionObject*>(proxy->m_clientObject);
|
||||
if (collisionObject == mCollisionObject)
|
||||
return true;
|
||||
if (needsCollision(*proxy))
|
||||
mResult = testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius);
|
||||
return !mResult;
|
||||
}
|
||||
|
||||
bool getResult() const
|
||||
{
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private:
|
||||
btVector3 mPosition;
|
||||
btScalar mRadius;
|
||||
btCollisionObject* mCollisionObject;
|
||||
int mCollisionFilterMask;
|
||||
int mCollisionFilterGroup;
|
||||
bool mResult = false;
|
||||
|
||||
bool needsCollision(const btBroadphaseProxy& proxy) const
|
||||
{
|
||||
bool collides = (proxy.m_collisionFilterGroup & mCollisionFilterMask) != 0;
|
||||
collides = collides && (mCollisionFilterGroup & proxy.m_collisionFilterMask);
|
||||
return collides;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -45,6 +45,7 @@
|
|||
#include "trace.h"
|
||||
#include "object.hpp"
|
||||
#include "heightfield.hpp"
|
||||
#include "hasspherecollisioncallback.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
|
@ -1444,4 +1445,20 @@ namespace MWPhysics
|
|||
mCollisionWorld->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water,
|
||||
CollisionType_Actor);
|
||||
}
|
||||
|
||||
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
|
||||
{
|
||||
btCollisionObject* object = nullptr;
|
||||
const auto it = mActors.find(ignore);
|
||||
if (it != mActors.end())
|
||||
object = it->second->getCollisionObject();
|
||||
const auto bulletPosition = Misc::Convert::toBullet(position);
|
||||
const auto aabbMin = bulletPosition - btVector3(radius, radius, radius);
|
||||
const auto aabbMax = bulletPosition + btVector3(radius, radius, radius);
|
||||
const int mask = MWPhysics::CollisionType_Actor;
|
||||
const int group = 0xff;
|
||||
HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group);
|
||||
mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback);
|
||||
return callback.getResult();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,6 +187,8 @@ namespace MWPhysics
|
|||
std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function);
|
||||
}
|
||||
|
||||
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const;
|
||||
|
||||
private:
|
||||
|
||||
void updateWater();
|
||||
|
|
|
@ -1254,6 +1254,7 @@ namespace MWScript
|
|||
msg << "[Deleted]" << std::endl;
|
||||
|
||||
msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl;
|
||||
msg << "Memory address: " << ptr.getBase() << std::endl;
|
||||
|
||||
if (ptr.isInCell())
|
||||
{
|
||||
|
|
|
@ -1640,6 +1640,11 @@ namespace MWWorld
|
|||
return result.mHit;
|
||||
}
|
||||
|
||||
bool World::castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore)
|
||||
{
|
||||
return mPhysics->castRay(from, to, ignore, std::vector<MWWorld::Ptr>(), mask).mHit;
|
||||
}
|
||||
|
||||
bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration)
|
||||
{
|
||||
const ESM::Position& objPos = door.getRefData().getPosition();
|
||||
|
@ -3896,4 +3901,9 @@ namespace MWWorld
|
|||
btVector3 hitNormal;
|
||||
return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal);
|
||||
}
|
||||
|
||||
bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
|
||||
{
|
||||
return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -426,6 +426,8 @@ namespace MWWorld
|
|||
|
||||
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override;
|
||||
|
||||
bool castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) override;
|
||||
|
||||
void setActorCollisionMode(const Ptr& ptr, bool internal, bool external) override;
|
||||
bool isActorCollisionEnabled(const Ptr& ptr) override;
|
||||
|
||||
|
@ -726,6 +728,8 @@ namespace MWWorld
|
|||
osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue