diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 9fa8db782..761e5111c 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -312,28 +312,31 @@ namespace MWWorld } std::pair PhysicsSystem::getHitContact(const std::string &name, - const Ogre::Vector3 &origin_, - const Ogre::Quaternion &orient_, + const Ogre::Vector3 &origin, + const Ogre::Quaternion &orient, float queryDistance) { - btVector3 origin(origin_.x, origin_.y, origin_.z); - - std::pair result = mEngine->sphereTest(queryDistance,origin); - if(result.first == "") return std::make_pair("",0); - btVector3 a = result.second - origin; - Ogre::Vector3 a_ = Ogre::Vector3(a.x(),a.y(),a.z()); - a_ = orient_.Inverse()*a_; - Ogre::Vector2 a_xy = Ogre::Vector2(a_.x,a_.y); - Ogre::Vector2 a_yz = Ogre::Vector2(a_xy.length(),a_.z); - float axy = a_xy.angleBetween(Ogre::Vector2::UNIT_Y).valueDegrees(); - float az = a_yz.angleBetween(Ogre::Vector2::UNIT_X).valueDegrees(); - - float fCombatAngleXY = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatAngleXY")->getFloat(); - float fCombatAngleZ = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatAngleZ")->getFloat(); - if(abs(axy) < fCombatAngleXY && abs(az) < fCombatAngleZ) - return std::make_pair (result.first,result.second.length()); - else - return std::make_pair("",0); + const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); + + btConeShape shape(Ogre::Degree(store.find("fCombatAngleXY")->getFloat()/2.0f).valueRadians(), + queryDistance); + shape.setLocalScaling(btVector3(1, 1, Ogre::Degree(store.find("fCombatAngleZ")->getFloat()/2.0f).valueRadians() / + shape.getRadius())); + + // The shape origin is its center, so we have to move it forward by half the length. The + // real origin will be provided to getFilteredContact to find the closest. + Ogre::Vector3 center = origin + (orient * Ogre::Vector3(0.0f, queryDistance*0.5f, 0.0f)); + + btCollisionObject object; + object.setCollisionShape(&shape); + object.setWorldTransform(btTransform(btQuaternion(orient.x, orient.y, orient.z, orient.w), + btVector3(center.x, center.y, center.z))); + + std::pair result = mEngine->getFilteredContact( + name, btVector3(origin.x, origin.y, origin.z), &object); + if(!result.first) + return std::make_pair(std::string(), Ogre::Vector3(&result.second[0])); + return std::make_pair(result.first->mName, Ogre::Vector3(&result.second[0])); } diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index e488d6e99..e33edda18 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -543,61 +543,63 @@ namespace Physic #endif }; - struct AabbResultCallback : public btBroadphaseAabbCallback { - std::vector hits; - //AabbResultCallback(){} - virtual bool process(const btBroadphaseProxy* proxy) { - RigidBody* collisionObject = static_cast(proxy->m_clientObject); - if(proxy->m_collisionFilterGroup == CollisionType_Actor && (collisionObject->mName != "player")) - this->hits.push_back(collisionObject); - return true; - } - }; - - - std::pair PhysicEngine::sphereTest(float radius,btVector3& pos) + class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback { - AabbResultCallback callback; - /*btDefaultMotionState* newMotionState = - new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),pos)); - btCollisionShape * shape = new btSphereShape(radius); - btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo - (0,newMotionState, shape); - RigidBody* body = new RigidBody(CI,"hitDetectionShpere__"); - btTransform tr = body->getWorldTransform(); - tr.setOrigin(pos); - body->setWorldTransform(tr); - dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_World); - body->setWorldTransform(tr);*/ + const std::string &mFilter; + // Store the real origin, since the shape's origin is its center + btVector3 mOrigin; + + public: + const RigidBody *mObject; + btVector3 mContactPoint; + btScalar mLeastDistSqr; - btVector3 aabbMin = pos - radius*btVector3(1.0f, 1.0f, 1.0f); - btVector3 aabbMax = pos + radius*btVector3(1.0f, 1.0f, 1.0f); + DeepestNotMeContactTestResultCallback(const std::string &filter, const btVector3 &origin) + : mFilter(filter), mOrigin(origin), mObject(0), mContactPoint(0,0,0), + mLeastDistSqr(std::numeric_limits::max()) + { } - broadphase->aabbTest(aabbMin,aabbMax,callback); - for(int i=0;i (callback.hits.size()); ++i) +#if defined(BT_COLLISION_OBJECT_WRAPPER_H) + virtual btScalar addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) { - float d = (callback.hits[i]->getWorldTransform().getOrigin()-pos).length(); - if(d(col1Wrap->m_collisionObject); + if(body && body->mName != mFilter) { - std::pair rayResult = this->rayTest(pos,callback.hits[i]->getWorldTransform().getOrigin()); - if(rayResult.second>d || rayResult.first == callback.hits[i]->mName) - return std::make_pair(callback.hits[i]->mName,callback.hits[i]->getWorldTransform().getOrigin()); + btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); + if(!mObject || distsqr < mLeastDistSqr) + { + mObject = body; + mLeastDistSqr = distsqr; + mContactPoint = cp.getPositionWorldOnA(); + } } + + return 0.f; } - //ContactTestResultCallback callback; - //dynamicsWorld->contactTest(body, callback); - //dynamicsWorld->removeRigidBody(body); - //delete body; - //delete shape; - //if(callback.mResultName.empty()) return std::make_pair(std::string(""),btVector3(0,0,0)); - /*for(int i=0;i(callback.mResultName[i],callback.mResultContact[i]); - */ - return std::make_pair(std::string(""),btVector3(0,0,0)); - } + const RigidBody* body = dynamic_cast(col1); + if(body && body->mName != mFilter) + { + btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); + if(!mObject || distsqr < mLeastDistSqr) + { + mObject = body; + mLeastDistSqr = distsqr; + mContactPoint = cp.getPositionWorldOnA(); + } + } + + return 0.f; + } +#endif + }; + std::vector PhysicEngine::getCollisions(const std::string& name) { @@ -607,6 +609,17 @@ namespace Physic return callback.mResult; } + + std::pair PhysicEngine::getFilteredContact(const std::string &filter, + const btVector3 &origin, + btCollisionObject *object) + { + DeepestNotMeContactTestResultCallback callback(filter, origin); + dynamicsWorld->contactTest(object, callback); + return std::make_pair(callback.mObject, callback.mContactPoint); + } + + void PhysicEngine::stepSimulation(double deltaT) { // This seems to be needed for character controller objects diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 46dafda76..f28f95ccb 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -321,10 +321,14 @@ public: std::pair sphereCast (float radius, btVector3& from, btVector3& to); ///< @return (hit, relative distance) - std::pair sphereTest(float radius,btVector3& pos); - std::vector getCollisions(const std::string& name); + // Get the nearest object that's inside the given object, filtering out objects of the + // provided name + std::pair getFilteredContact(const std::string &filter, + const btVector3 &origin, + btCollisionObject *object); + //event list of non player object std::list NPEventList;