mirror of
https://github.com/OpenMW/openmw.git
synced 2025-07-04 14:11:36 +00:00
Added door detection based by ray casting
This commit is contained in:
parent
e9d0bf7c12
commit
37952c9a79
7 changed files with 45 additions and 45 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include "obstacle.hpp"
|
||||
|
||||
#include <osg/Group>
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
#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();
|
||||
|
||||
if(cell->getCell()->isExterior())
|
||||
return MWWorld::Ptr(); // check interior cells only
|
||||
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));
|
||||
|
||||
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<ESM::Door>& doors = cell->getReadOnlyDoors();
|
||||
const MWWorld::CellRefList<ESM::Door>::List& refList = doors.mList;
|
||||
MWWorld::CellRefList<ESM::Door>::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<ESM::Door>& 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<MWWorld::LiveCellRef<ESM::Door> &>(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():
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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<MWWorld::Ptr>(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door);
|
||||
return result.mHit;
|
||||
return mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door);
|
||||
}
|
||||
|
||||
void World::processDoors(float duration)
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue