From 37952c9a790ce33bf24078319060cf62407b4cc9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 14 Jun 2017 12:44:18 +0400 Subject: [PATCH] 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.