Use a proper cone shape with a contact test to check for melee hits

actorid
Chris Robinson 12 years ago
parent f5d03a16c1
commit 3fa65f21dd

@ -312,28 +312,31 @@ namespace MWWorld
}
std::pair<std::string,Ogre::Vector3> 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<std::string,btVector3> 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<ESM::GameSetting>().find("fCombatAngleXY")->getFloat();
float fCombatAngleZ = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().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<ESM::GameSetting> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
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<const OEngine::Physic::RigidBody*,btVector3> 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]));
}

@ -543,61 +543,63 @@ namespace Physic
#endif
};
struct AabbResultCallback : public btBroadphaseAabbCallback {
std::vector<RigidBody*> hits;
//AabbResultCallback(){}
virtual bool process(const btBroadphaseProxy* proxy) {
RigidBody* collisionObject = static_cast<RigidBody*>(proxy->m_clientObject);
if(proxy->m_collisionFilterGroup == CollisionType_Actor && (collisionObject->mName != "player"))
this->hits.push_back(collisionObject);
return true;
}
};
std::pair<std::string,btVector3> 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<float>::max())
{ }
broadphase->aabbTest(aabbMin,aabbMax,callback);
for(int i=0;i<static_cast<int> (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<radius)
const RigidBody* body = dynamic_cast<const RigidBody*>(col1Wrap->m_collisionObject);
if(body && body->mName != mFilter)
{
std::pair<std::string,float> 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.size();i++)
#else
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObject* col0, int partId0, int index0,
const btCollisionObject* col1, int partId1, int index1)
{
//TODO: raycasting
if(callback.mResultName[i] != "hitDetectionShpere__")
return std::pair<std::string,btVector3>(callback.mResultName[i],callback.mResultContact[i]);
*/
return std::make_pair(std::string(""),btVector3(0,0,0));
}
const RigidBody* body = dynamic_cast<const RigidBody*>(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<std::string> PhysicEngine::getCollisions(const std::string& name)
{
@ -607,6 +609,17 @@ namespace Physic
return callback.mResult;
}
std::pair<const RigidBody*,btVector3> 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

@ -321,10 +321,14 @@ public:
std::pair<bool, float> sphereCast (float radius, btVector3& from, btVector3& to);
///< @return (hit, relative distance)
std::pair<std::string,btVector3> sphereTest(float radius,btVector3& pos);
std::vector<std::string> getCollisions(const std::string& name);
// Get the nearest object that's inside the given object, filtering out objects of the
// provided name
std::pair<const RigidBody*,btVector3> getFilteredContact(const std::string &filter,
const btVector3 &origin,
btCollisionObject *object);
//event list of non player object
std::list<PhysicEvent> NPEventList;

Loading…
Cancel
Save