mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 21:49:55 +00:00
Use a better method to do actor physics traces
This commit is contained in:
parent
76b812f75f
commit
14acacf401
4 changed files with 124 additions and 45 deletions
|
@ -41,24 +41,21 @@ namespace MWWorld
|
|||
return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees();
|
||||
}
|
||||
|
||||
static bool stepMove(Ogre::Vector3& position, const Ogre::Quaternion& orient,
|
||||
static bool stepMove(btCollisionObject *colobj, Ogre::Vector3 &position,
|
||||
const Ogre::Vector3 &velocity, float &remainingTime,
|
||||
const Ogre::Vector3 &halfExtents, bool isInterior,
|
||||
OEngine::Physic::PhysicEngine *engine)
|
||||
{
|
||||
traceResults trace;
|
||||
newtrace(&trace, orient, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize),
|
||||
halfExtents, isInterior, engine);
|
||||
actortrace(&trace, colobj, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), engine);
|
||||
if(trace.fraction == 0.0f)
|
||||
return false;
|
||||
|
||||
newtrace(&trace, orient, trace.endpos, trace.endpos + velocity*remainingTime,
|
||||
halfExtents, isInterior, engine);
|
||||
actortrace(&trace, colobj, trace.endpos, trace.endpos + velocity*remainingTime, engine);
|
||||
if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope))
|
||||
return false;
|
||||
float movefrac = trace.fraction;
|
||||
|
||||
newtrace(&trace, orient, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine);
|
||||
actortrace(&trace, colobj, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), engine);
|
||||
if(getSlope(trace.planenormal) <= sMaxSlope)
|
||||
{
|
||||
// only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
|
||||
|
@ -100,7 +97,7 @@ namespace MWWorld
|
|||
bool wasCollisionMode = physicActor->getCollisionMode();
|
||||
physicActor->enableCollisions(false);
|
||||
|
||||
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();// + Vector3(1,1,1);
|
||||
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();
|
||||
halfExtents.z = 1; // we trace the feet only, so we use a very thin box
|
||||
|
||||
Ogre::Vector3 newPosition = position;
|
||||
|
@ -133,15 +130,15 @@ namespace MWWorld
|
|||
if(!physicActor || !physicActor->getCollisionMode())
|
||||
{
|
||||
// FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why?
|
||||
return position + (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
|
||||
Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
return position + (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
|
||||
Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
movement;
|
||||
}
|
||||
|
||||
bool isInterior = !ptr.getCell()->isExterior();
|
||||
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();// + Vector3(1,1,1);
|
||||
physicActor->enableCollisions(false);
|
||||
btCollisionObject *colobj = physicActor->getCollisionBody();
|
||||
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();
|
||||
position.z += halfExtents.z;
|
||||
|
||||
traceResults trace;
|
||||
bool onground = false;
|
||||
|
@ -158,7 +155,7 @@ namespace MWWorld
|
|||
{
|
||||
if(!(movement.z > 0.0f))
|
||||
{
|
||||
newtrace(&trace, orient, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine);
|
||||
actortrace(&trace, colobj, position, position-Ogre::Vector3(0,0,4), engine);
|
||||
if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope)
|
||||
onground = true;
|
||||
}
|
||||
|
@ -178,7 +175,7 @@ namespace MWWorld
|
|||
for(int iterations = 0;iterations < sMaxIterations && remainingTime > 0.01f;++iterations)
|
||||
{
|
||||
// trace to where character would go if there were no obstructions
|
||||
newtrace(&trace, orient, newPosition, newPosition+velocity*remainingTime, halfExtents, isInterior, engine);
|
||||
actortrace(&trace, colobj, newPosition, newPosition+velocity*remainingTime, engine);
|
||||
|
||||
// check for obstructions
|
||||
if(trace.fraction >= 1.0f)
|
||||
|
@ -190,7 +187,7 @@ namespace MWWorld
|
|||
|
||||
//std::cout<<"angle: "<<getSlope(trace.planenormal)<<"\n";
|
||||
// We hit something. Try to step up onto it.
|
||||
if(stepMove(newPosition, orient, velocity, remainingTime, halfExtents, isInterior, engine))
|
||||
if(stepMove(colobj, newPosition, velocity, remainingTime, engine))
|
||||
onground = gravity;
|
||||
else
|
||||
{
|
||||
|
@ -209,7 +206,7 @@ namespace MWWorld
|
|||
|
||||
if(onground)
|
||||
{
|
||||
newtrace(&trace, orient, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), halfExtents, isInterior, engine);
|
||||
actortrace(&trace, colobj, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), engine);
|
||||
if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope)
|
||||
newPosition.z = trace.endpos.z + 2.0f;
|
||||
else
|
||||
|
@ -218,8 +215,8 @@ namespace MWWorld
|
|||
|
||||
physicActor->setOnGround(onground);
|
||||
physicActor->setVerticalForce(!onground ? velocity.z - time*627.2f : 0.0f);
|
||||
physicActor->enableCollisions(true);
|
||||
|
||||
newPosition.z -= halfExtents.z;
|
||||
return newPosition;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -66,6 +66,20 @@ namespace Physic
|
|||
std::string mName;
|
||||
};
|
||||
|
||||
/**
|
||||
*This class is just an extension of normal btRigidBody in order to add extra info.
|
||||
*When bullet give back a btRigidBody, you can just do a static_cast to RigidBody,
|
||||
*so one never should use btRigidBody directly!
|
||||
*/
|
||||
class RigidBody: public btRigidBody
|
||||
{
|
||||
public:
|
||||
RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name);
|
||||
virtual ~RigidBody();
|
||||
std::string mName;
|
||||
bool mPlaceable;
|
||||
};
|
||||
|
||||
/**
|
||||
* A physic actor uses a rigid body based on box shapes.
|
||||
* Pmove is used to move the physic actor around the dynamic world.
|
||||
|
@ -129,47 +143,41 @@ namespace Physic
|
|||
|
||||
bool getOnGround() const;
|
||||
|
||||
btCollisionObject *getCollisionBody() const
|
||||
{
|
||||
return mBody;
|
||||
}
|
||||
|
||||
private:
|
||||
void disableCollisionBody();
|
||||
void enableCollisionBody();
|
||||
public:
|
||||
//HACK: in Visual Studio 2010 and presumably above, this structures alignment
|
||||
// must be 16, but the built in operator new & delete don't properly
|
||||
// perform this alignment.
|
||||
// must be 16, but the built in operator new & delete don't properly
|
||||
// perform this alignment.
|
||||
#if _MSC_VER >= 1600
|
||||
void * operator new (size_t Size) { return _aligned_malloc (Size, 16); }
|
||||
void operator delete (void * Data) { _aligned_free (Data); }
|
||||
void * operator new (size_t Size) { return _aligned_malloc (Size, 16); }
|
||||
void operator delete (void * Data) { _aligned_free (Data); }
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
|
||||
OEngine::Physic::RigidBody* mBody;
|
||||
OEngine::Physic::RigidBody* mRaycastingBody;
|
||||
|
||||
Ogre::Vector3 mBoxScaledTranslation;
|
||||
btQuaternion mBoxRotationInverse;
|
||||
Ogre::Quaternion mBoxRotation;
|
||||
btQuaternion mBoxRotationInverse;
|
||||
|
||||
float verticalForce;
|
||||
bool onGround;
|
||||
bool collisionMode;
|
||||
|
||||
std::string mMesh;
|
||||
PhysicEngine* mEngine;
|
||||
std::string mName;
|
||||
PhysicEngine *mEngine;
|
||||
};
|
||||
|
||||
/**
|
||||
*This class is just an extension of normal btRigidBody in order to add extra info.
|
||||
*When bullet give back a btRigidBody, you can just do a static_cast to RigidBody,
|
||||
*so one never should use btRigidBody directly!
|
||||
*/
|
||||
class RigidBody: public btRigidBody
|
||||
{
|
||||
public:
|
||||
RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name);
|
||||
virtual ~RigidBody();
|
||||
std::string mName;
|
||||
bool mPlaceable;
|
||||
};
|
||||
|
||||
struct HeightField
|
||||
{
|
||||
|
|
|
@ -8,12 +8,6 @@
|
|||
|
||||
#include "physic.hpp"
|
||||
|
||||
enum traceWorldType
|
||||
{
|
||||
collisionWorldTrace = 1,
|
||||
pickWorldTrace = 2,
|
||||
bothWorldTrace = collisionWorldTrace | pickWorldTrace
|
||||
};
|
||||
|
||||
void newtrace(traceResults *results, const Ogre::Quaternion& orient, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine *enginePass) //Traceobj was a Aedra Object
|
||||
{
|
||||
|
@ -46,3 +40,79 @@ void newtrace(traceResults *results, const Ogre::Quaternion& orient, const Ogre:
|
|||
results->fraction = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||
{
|
||||
public:
|
||||
ClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot)
|
||||
: btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),
|
||||
mMe(me), mUp(up), mMinSlopeDot(minSlopeDot)
|
||||
{
|
||||
}
|
||||
|
||||
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
|
||||
{
|
||||
if(convexResult.m_hitCollisionObject == mMe)
|
||||
return btScalar( 1 );
|
||||
|
||||
btVector3 hitNormalWorld;
|
||||
if(normalInWorldSpace)
|
||||
hitNormalWorld = convexResult.m_hitNormalLocal;
|
||||
else
|
||||
{
|
||||
///need to transform normal into worldspace
|
||||
hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
|
||||
}
|
||||
|
||||
// NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box...
|
||||
|
||||
btScalar dotUp = mUp.dot(hitNormalWorld);
|
||||
if(dotUp < mMinSlopeDot)
|
||||
return btScalar(1);
|
||||
|
||||
return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
|
||||
}
|
||||
|
||||
protected:
|
||||
btCollisionObject *mMe;
|
||||
const btVector3 mUp;
|
||||
const btScalar mMinSlopeDot;
|
||||
};
|
||||
|
||||
void actortrace(traceResults *results, btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, OEngine::Physic::PhysicEngine *enginePass)
|
||||
{
|
||||
const btVector3 btstart(start.x, start.y, start.z);
|
||||
const btVector3 btend(end.x, end.y, end.z);
|
||||
|
||||
const btTransform &trans = actor->getWorldTransform();
|
||||
btTransform from(trans);
|
||||
btTransform to(trans);
|
||||
from.setOrigin(btstart);
|
||||
to.setOrigin(btend);
|
||||
|
||||
ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0));
|
||||
newTraceCallback.m_collisionFilterMask = OEngine::Physic::CollisionType_World |
|
||||
OEngine::Physic::CollisionType_HeightMap |
|
||||
OEngine::Physic::CollisionType_Actor;
|
||||
|
||||
btCollisionShape *shape = actor->getCollisionShape();
|
||||
assert(shape->isConvex());
|
||||
enginePass->dynamicsWorld->convexSweepTest(static_cast<btConvexShape*>(shape),
|
||||
from, to, newTraceCallback);
|
||||
|
||||
// Copy the hit data over to our trace results struct:
|
||||
if(newTraceCallback.hasHit())
|
||||
{
|
||||
const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;
|
||||
results->fraction = newTraceCallback.m_closestHitFraction;
|
||||
results->planenormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
|
||||
results->endpos = (end-start)*results->fraction + start;
|
||||
}
|
||||
else
|
||||
{
|
||||
results->endpos = end;
|
||||
results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f);
|
||||
results->fraction = 1.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ namespace OEngine
|
|||
}
|
||||
|
||||
|
||||
class btCollisionObject;
|
||||
|
||||
|
||||
struct traceResults
|
||||
{
|
||||
Ogre::Vector3 endpos;
|
||||
|
@ -22,5 +25,6 @@ struct traceResults
|
|||
};
|
||||
|
||||
void newtrace(traceResults *results, const Ogre::Quaternion& orient, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
|
||||
void actortrace(traceResults *results, btCollisionObject *actor, const Ogre::Vector3& start, const Ogre::Vector3& end, OEngine::Physic::PhysicEngine *enginePass);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue