From 37952c9a790ce33bf24078319060cf62407b4cc9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 14 Jun 2017 12:44:18 +0400 Subject: [PATCH 1/2] Added door detection based by ray casting --- apps/openmw/mwbase/world.hpp | 4 +++ apps/openmw/mwmechanics/aipackage.cpp | 4 ++- apps/openmw/mwmechanics/aiwander.cpp | 9 +++-- apps/openmw/mwmechanics/obstacle.cpp | 52 ++++++++++----------------- apps/openmw/mwmechanics/obstacle.hpp | 9 ++--- apps/openmw/mwworld/worldimp.cpp | 9 +++-- apps/openmw/mwworld/worldimp.hpp | 3 ++ 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 86d26d3a7..ce6cf38eb 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -10,6 +10,7 @@ #include "../mwworld/ptr.hpp" #include "../mwrender/rendermode.hpp" +#include "../mwphysics/physicssystem.hpp" namespace osg { @@ -299,6 +300,9 @@ namespace MWBase virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; ///< cast a Ray and return true if there is an object in the ray path. + virtual MWPhysics::PhysicsSystem::RayResult castRayTest (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + ///< cast a rendering ray and return ray result. + virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 529e7ca41..f837ad2ee 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -176,7 +176,9 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur if (!mObstacleCheck.check(actor, duration)) return; // first check if obstacle is a door - MWWorld::Ptr door = getNearbyDoor(actor); // NOTE: checks interior cells only + static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); + + MWWorld::Ptr door = getNearbyDoor(actor, distance); if (door != MWWorld::Ptr()) { // note: AiWander currently does not open doors diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index a992bc8d4..3888aaf6d 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -440,11 +440,14 @@ namespace MWMechanics { // Check if an idle actor is too close to a door - if so start walking storage.mDoorCheckDuration += duration; + + static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); + if (storage.mDoorCheckDuration >= DOOR_CHECK_INTERVAL) { storage.mDoorCheckDuration = 0; // restart timer if (mDistance && // actor is not intended to be stationary - proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6f*1.6f)) // NOTE: checks interior cells only + proximityToDoor(actor, distance*1.6f)) { storage.setState(Wander_MoveNow); storage.mTrimCurrentNode = false; // just in case @@ -516,10 +519,12 @@ namespace MWMechanics void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos) { + static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); + if (mObstacleCheck.isEvading()) { // first check if we're walking into a door - if (proximityToDoor(actor)) // NOTE: checks interior cells only + if (proximityToDoor(actor, distance)) { // remove allowed points then select another random destination storage.mTrimCurrentNode = true; diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 5d99fe723..8e932f351 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -1,8 +1,11 @@ #include "obstacle.hpp" +#include + #include #include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -23,51 +26,34 @@ namespace MWMechanics { -1.0f, -1.0f } // move to side and backwards }; - // Proximity check function for interior doors. Given that most interior cells - // do not have many doors performance shouldn't be too much of an issue. - // - // Limitation: there can be false detections, and does not test whether the - // actor is facing the door. - bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr) + bool proximityToDoor(const MWWorld::Ptr& actor, float minDist) { - if(getNearbyDoor(actor, minSqr)!=MWWorld::Ptr()) + if(getNearbyDoor(actor, minDist)!=MWWorld::Ptr()) return true; else return false; } - MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr) + MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist) { - MWWorld::CellStore *cell = actor.getCell(); + osg::Vec3f origin = MWBase::Environment::get().getWorld()->getActorHeadTransform(actor).getTrans(); + + osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) + * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); - if(cell->getCell()->isExterior()) - return MWWorld::Ptr(); // check interior cells only + osg::Vec3f direction = orient * osg::Vec3f(0,1,0); + osg::Vec3f dest = origin + direction * minDist; - // Check all the doors in this cell - const MWWorld::CellRefList& doors = cell->getReadOnlyDoors(); - const MWWorld::CellRefList::List& refList = doors.mList; - MWWorld::CellRefList::List::const_iterator it = refList.begin(); osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); + MWPhysics::PhysicsSystem::RayResult result = MWBase::Environment::get().getWorld()->castRayTest(pos.x(), pos.y(), pos.z(), dest.x(), dest.y(), dest.z()); - /// TODO: How to check whether the actor is facing a door? Below code is for - /// the player, perhaps it can be adapted. - //MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); - //if(!ptr.isEmpty()) - //std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl; + if (!result.mHit || result.mHitObject.isEmpty()) + return MWWorld::Ptr(); // none found - /// TODO: The in-game observation of rot[2] value seems to be the - /// opposite of the code in World::activateDoor() ::confused:: - for (; it != refList.end(); ++it) - { - const MWWorld::LiveCellRef& ref = *it; - if((pos - ref.mData.getPosition().asVec3()).length2() < minSqr - && ref.mData.getPosition().rot[2] == ref.mRef.getPosition().rot[2]) - { - // FIXME cast - return MWWorld::Ptr(&const_cast &>(ref), actor.getCell()); // found, stop searching - } - } - return MWWorld::Ptr(); // none found + if (result.mHitObject.getClass().getTypeName() == typeid(ESM::Door).name() && !result.mHitObject.getCellRef().getTeleport()) + return result.mHitObject; + + return MWWorld::Ptr(); } ObstacleCheck::ObstacleCheck(): diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 1d7cf1e0e..f71207346 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -10,19 +10,14 @@ namespace MWMechanics { struct Movement; - /// NOTE: determined empirically based on in-game behaviour - static const float MIN_DIST_TO_DOOR_SQUARED = 128*128; - static const int NUM_EVADE_DIRECTIONS = 4; /// tests actor's proximity to a closed door by default - bool proximityToDoor(const MWWorld::Ptr& actor, - float minSqr = MIN_DIST_TO_DOOR_SQUARED); + bool proximityToDoor(const MWWorld::Ptr& actor, float minDist); /// Returns door pointer within range. No guarantee is given as to which one /** \return Pointer to the door, or NULL if none exists **/ - MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, - float minSqr = MIN_DIST_TO_DOOR_SQUARED); + MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist); class ObstacleCheck { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0421ecb24..86943a7e7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1451,11 +1451,16 @@ namespace MWWorld } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) + { + MWPhysics::PhysicsSystem::RayResult result = castRayTest(x1, y1, z1, x2, y2, z2); + return result.mHit; + } + + MWPhysics::PhysicsSystem::RayResult World::castRayTest (float x1, float y1, float z1, float x2, float y2, float z2) { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); - return result.mHit; + return mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); } void World::processDoors(float duration) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ce6e27672..951a837da 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -407,6 +407,9 @@ namespace MWWorld virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2); ///< cast a Ray and return true if there is an object in the ray path. + virtual MWPhysics::PhysicsSystem::RayResult castRayTest (float x1, float y1, float z1, float x2, float y2, float z2); + ///< cast a rendering ray and return ray result. + virtual bool toggleCollisionMode(); ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. From 10eb6ec75f9eec6df9c6fee9b40f4b99732b1dd4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 31 Jul 2017 19:28:02 +0400 Subject: [PATCH 2/2] AI: Check angle between actor and door --- apps/openmw/mwbase/world.hpp | 4 --- apps/openmw/mwmechanics/obstacle.cpp | 43 ++++++++++++++++++---------- apps/openmw/mwworld/worldimp.cpp | 10 ++----- apps/openmw/mwworld/worldimp.hpp | 3 -- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index ce6cf38eb..86d26d3a7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -10,7 +10,6 @@ #include "../mwworld/ptr.hpp" #include "../mwrender/rendermode.hpp" -#include "../mwphysics/physicssystem.hpp" namespace osg { @@ -300,9 +299,6 @@ namespace MWBase virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; ///< cast a Ray and return true if there is an object in the ray path. - virtual MWPhysics::PhysicsSystem::RayResult castRayTest (float x1, float y1, float z1, float x2, float y2, float z2) = 0; - ///< cast a rendering ray and return ray result. - virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 8e932f351..3c6f14bfd 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -1,11 +1,9 @@ #include "obstacle.hpp" -#include - #include +#include #include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -36,24 +34,39 @@ namespace MWMechanics MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist) { - osg::Vec3f origin = MWBase::Environment::get().getWorld()->getActorHeadTransform(actor).getTrans(); + MWWorld::CellStore *cell = actor.getCell(); - osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)) - * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1)); + // Check all the doors in this cell + const MWWorld::CellRefList& doors = cell->getReadOnlyDoors(); + const MWWorld::CellRefList::List& refList = doors.mList; + MWWorld::CellRefList::List::const_iterator it = refList.begin(); + osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); + pos.z() = 0; - osg::Vec3f direction = orient * osg::Vec3f(0,1,0); - osg::Vec3f dest = origin + direction * minDist; + osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0)); - osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); - MWPhysics::PhysicsSystem::RayResult result = MWBase::Environment::get().getWorld()->castRayTest(pos.x(), pos.y(), pos.z(), dest.x(), dest.y(), dest.z()); + for (; it != refList.end(); ++it) + { + const MWWorld::LiveCellRef& ref = *it; - if (!result.mHit || result.mHitObject.isEmpty()) - return MWWorld::Ptr(); // none found + osg::Vec3f doorPos(ref.mData.getPosition().asVec3()); + doorPos.z() = 0; - if (result.mHitObject.getClass().getTypeName() == typeid(ESM::Door).name() && !result.mHitObject.getCellRef().getTeleport()) - return result.mHitObject; + float angle = std::acos(actorDir * (doorPos - pos) / (actorDir.length() * (doorPos - pos).length())); + + // Allow 60 degrees angle between actor and door + if (angle < -osg::PI / 3 || angle > osg::PI / 3) + continue; + + // Door is not close enough + if ((pos - doorPos).length2() > minDist*minDist) + continue; + + // FIXME cast + return MWWorld::Ptr(&const_cast &>(ref), actor.getCell()); // found, stop searching + } - return MWWorld::Ptr(); + return MWWorld::Ptr(); // none found } ObstacleCheck::ObstacleCheck(): diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 86943a7e7..91a3c4a8f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1451,16 +1451,12 @@ namespace MWWorld } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) - { - MWPhysics::PhysicsSystem::RayResult result = castRayTest(x1, y1, z1, x2, y2, z2); - return result.mHit; - } - - MWPhysics::PhysicsSystem::RayResult World::castRayTest (float x1, float y1, float z1, float x2, float y2, float z2) { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); - return mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); + + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); + return result.mHit; } void World::processDoors(float duration) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 951a837da..ce6e27672 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -407,9 +407,6 @@ namespace MWWorld virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2); ///< cast a Ray and return true if there is an object in the ray path. - virtual MWPhysics::PhysicsSystem::RayResult castRayTest (float x1, float y1, float z1, float x2, float y2, float z2); - ///< cast a rendering ray and return ray result. - virtual bool toggleCollisionMode(); ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity.