From 52945921a7b8a65bc6484cd700d2677841a2e09b Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 2 Feb 2020 20:49:39 +0100 Subject: [PATCH 1/5] Print ptr by betacomment --- apps/openmw/mwscript/miscextensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 06676d006..7d779e62f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -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()) { From 0c92a567afef73a34c83c18ecbb3787b782b03b4 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 6 Feb 2020 00:20:55 +0100 Subject: [PATCH 2/5] Use distance to position since last normal state in obstacle checker --- apps/openmw/mwmechanics/obstacle.cpp | 5 ++++- apps/openmw/mwmechanics/obstacle.hpp | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 6268eaddf..e30a2947f 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -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; } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 8314031ea..6c2197d81 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -54,6 +54,7 @@ namespace MWMechanics float mStateDuration; int mEvadeDirectionIndex; + float mInitialDistance = 0; void chooseEvasionDirection(); }; From 85414e23539b22f10dbc2fe1b6c09acad3e2d964 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 2 Feb 2020 20:02:25 +0100 Subject: [PATCH 3/5] Check for line of sight for wander destination --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/aiwander.cpp | 32 ++++++++++++++++++---------- apps/openmw/mwmechanics/aiwander.hpp | 1 - apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 0529140f4..dc92e26a4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 442ba0499..f7023ed89 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -61,6 +61,26 @@ 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); + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): @@ -328,7 +348,7 @@ namespace MWMechanics if (!isWaterCreature && destinationIsAtWater(actor, mDestination)) continue; - if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination)) + if (isDestinationHidden(actor, mDestination)) continue; if (isWaterCreature || isFlyingCreature) @@ -357,16 +377,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(); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 38123a970..a6c2563fc 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -126,7 +126,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 diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 71948119a..e76580465 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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(), mask).mHit; + } + bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration) { const ESM::Position& objPos = door.getRefData().getPosition(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ed622b5b8..0fcf02891 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -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; From 4a0c056489a3adb0a48d251e77b0820a0a2d591e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 9 Feb 2020 18:24:08 +0100 Subject: [PATCH 4/5] Do not wander to occupied area by other actor --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwmechanics/aiwander.cpp | 11 +++ .../mwphysics/hasspherecollisioncallback.hpp | 72 +++++++++++++++++++ apps/openmw/mwphysics/physicssystem.cpp | 17 +++++ apps/openmw/mwphysics/physicssystem.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 5 ++ apps/openmw/mwworld/worldimp.hpp | 2 + 7 files changed, 111 insertions(+) create mode 100644 apps/openmw/mwphysics/hasspherecollisioncallback.hpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index dc92e26a4..31d4a1b3c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -625,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; }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f7023ed89..f08c19bbd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -81,6 +81,14 @@ namespace MWMechanics | 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& idle, bool repeat): @@ -351,6 +359,9 @@ namespace MWMechanics if (isDestinationHidden(actor, mDestination)) continue; + if (isAreaOccupiedByOtherActor(actor, mDestination)) + continue; + if (isWaterCreature || isFlyingCreature) mPathFinder.buildStraightPath(mDestination); else diff --git a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp new file mode 100644 index 000000000..58e7373e5 --- /dev/null +++ b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp @@ -0,0 +1,72 @@ +#ifndef OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H +#define OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H + +#include +#include +#include +#include + +#include + +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(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 diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 31325cf21..69177d95d 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -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(); + } } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 364a59ab1..d74e2de16 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -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(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e76580465..29d1fd696 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3914,4 +3914,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); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0fcf02891..fd36e5ba2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -728,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; }; } From 9404b1dd7218f8bf77ec562fc458dd96c9198fce Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 9 Feb 2020 18:37:24 +0100 Subject: [PATCH 5/5] Stop wandering when destination is hidden or occupied by other actor --- apps/openmw/mwmechanics/aiwander.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f08c19bbd..ff213b219 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -293,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 }