bullet3/Demos/CharacterDemo/DynamicCharacterController.cpp

205 lines
5.2 KiB
C++

#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
#include "LinearMath/btDefaultMotionState.h"
#include "DynamicCharacterController.h"
DynamicCharacterController::DynamicCharacterController ()
{
m_rayLambda[0] = 1.0;
m_rayLambda[1] = 1.0;
m_halfHeight = 1.0;
m_turnAngle = 0.0;
m_maxLinearVelocity = 10.0;
m_walkVelocity = 8.0; // meters/sec
m_turnVelocity = 1.0; // radians/sec
m_shape = NULL;
m_rigidBody = NULL;
}
DynamicCharacterController::~DynamicCharacterController ()
{
}
void DynamicCharacterController::setup (btScalar height, btScalar width, btScalar stepHeight)
{
btVector3 spherePositions[2];
btScalar sphereRadii[2];
sphereRadii[0] = width;
sphereRadii[1] = width;
spherePositions[0] = btVector3 (0.0, (height/btScalar(2.0) - width), 0.0);
spherePositions[1] = btVector3 (0.0, (-height/btScalar(2.0) + width), 0.0);
m_halfHeight = height/btScalar(2.0);
m_shape = new btMultiSphereShape (&spherePositions[0], &sphereRadii[0], 2);
btTransform startTransform;
startTransform.setIdentity ();
startTransform.setOrigin (btVector3(0.0, 2.0, 0.0));
btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo cInfo(1.0, myMotionState, m_shape);
m_rigidBody = new btRigidBody(cInfo);
// kinematic vs. static doesn't work
//m_rigidBody->setCollisionFlags( m_rigidBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
m_rigidBody->setSleepingThresholds (0.0, 0.0);
m_rigidBody->setAngularFactor (0.0);
}
void DynamicCharacterController::destroy ()
{
if (m_shape)
{
delete m_shape;
}
if (m_rigidBody)
{
delete m_rigidBody;
m_rigidBody = 0;
}
}
btCollisionObject* DynamicCharacterController::getCollisionObject ()
{
return m_rigidBody;
}
void DynamicCharacterController::preStep (const btCollisionWorld* collisionWorld)
{
btTransform xform;
m_rigidBody->getMotionState()->getWorldTransform (xform);
btVector3 down = -xform.getBasis()[1];
btVector3 forward = xform.getBasis()[2];
down.normalize ();
forward.normalize();
m_raySource[0] = xform.getOrigin();
m_raySource[1] = xform.getOrigin();
m_rayTarget[0] = m_raySource[0] + down * m_halfHeight * btScalar(1.1);
m_rayTarget[1] = m_raySource[1] + forward * m_halfHeight * btScalar(1.1);
class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback
{
public:
ClosestNotMe (btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
{
m_me = me;
}
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
{
if (rayResult.m_collisionObject == m_me)
return 1.0;
return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace
);
}
protected:
btRigidBody* m_me;
};
ClosestNotMe rayCallback(m_rigidBody);
int i = 0;
for (i = 0; i < 2; i++)
{
rayCallback.m_closestHitFraction = 1.0;
collisionWorld->rayTest (m_raySource[i], m_rayTarget[i], rayCallback);
if (rayCallback.hasHit())
{
m_rayLambda[i] = rayCallback.m_closestHitFraction;
} else {
m_rayLambda[i] = 1.0;
}
}
}
void DynamicCharacterController::playerStep (const btCollisionWorld* dynaWorld,btScalar dt,
int forward,
int backward,
int left,
int right,
int jump)
{
btTransform xform;
m_rigidBody->getMotionState()->getWorldTransform (xform);
/* Handle turning */
if (left)
m_turnAngle -= dt * m_turnVelocity;
if (right)
m_turnAngle += dt * m_turnVelocity;
xform.setRotation (btQuaternion (btVector3(0.0, 1.0, 0.0), m_turnAngle));
btVector3 linearVelocity = m_rigidBody->getLinearVelocity();
btScalar speed = m_rigidBody->getLinearVelocity().length();
btVector3 forwardDir = xform.getBasis()[2];
forwardDir.normalize ();
btVector3 walkDirection = btVector3(0.0, 0.0, 0.0);
btScalar walkSpeed = m_walkVelocity * dt;
if (forward)
walkDirection += forwardDir;
if (backward)
walkDirection -= forwardDir;
if (!forward && !backward && onGround())
{
/* Dampen when on the ground and not being moved by the player */
linearVelocity *= btScalar(0.2);
m_rigidBody->setLinearVelocity (linearVelocity);
} else {
if (speed < m_maxLinearVelocity)
{
btVector3 velocity = linearVelocity + walkDirection * walkSpeed;
m_rigidBody->setLinearVelocity (velocity);
}
}
m_rigidBody->getMotionState()->setWorldTransform (xform);
m_rigidBody->setCenterOfMassTransform (xform);
}
bool DynamicCharacterController::canJump () const
{
return onGround();
}
void DynamicCharacterController::jump ()
{
if (!canJump())
return;
btTransform xform;
m_rigidBody->getMotionState()->getWorldTransform (xform);
btVector3 up = xform.getBasis()[1];
up.normalize ();
btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0);
m_rigidBody->applyCentralImpulse (up * magnitude);
}
bool DynamicCharacterController::onGround () const
{
return m_rayLambda[0] < btScalar(1.0);
}
void DynamicCharacterController::reset ()
{
}
void DynamicCharacterController::warp (const btVector3& origin)
{
}
void DynamicCharacterController::registerPairCacheAndDispatcher (btOverlappingPairCache* pairCache, btCollisionDispatcher* dispatcher)
{
}