Abstracted character controller interface
Renamed old character controller to DynamicCharacterController First start at KinematicCharacterController. Still has bugs.
This commit is contained in:
32
Demos/CharacterDemo/CharacterControllerInterface.h
Normal file
32
Demos/CharacterDemo/CharacterControllerInterface.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef CHARACTER_CONTROLLER_INTERFACE_H
|
||||
#define CHARACTER_CONTROLLER_INTERFACE_H
|
||||
|
||||
#include "LinearMath/btVector3.h"
|
||||
|
||||
class btCollisionShape;
|
||||
class btRigidBody;
|
||||
class btDynamicsWorld;
|
||||
|
||||
class CharacterControllerInterface
|
||||
{
|
||||
public:
|
||||
CharacterControllerInterface () {};
|
||||
virtual ~CharacterControllerInterface () {};
|
||||
virtual void setup (btDynamicsWorld* dynamicsWorld, btScalar height = 2.0, btScalar width = 0.25, btScalar stepHeight = 0.25) = 0;
|
||||
virtual void destroy (btDynamicsWorld* dynamicsWorld) = 0;
|
||||
|
||||
virtual btRigidBody* getRigidBody () = 0;
|
||||
|
||||
virtual void preStep (btDynamicsWorld* dynamicsWorld) = 0;
|
||||
virtual void playerStep (btDynamicsWorld* dynamicsWorld, btScalar dt,
|
||||
int forward,
|
||||
int backward,
|
||||
int left,
|
||||
int right) = 0;
|
||||
virtual bool canJump () const = 0;
|
||||
virtual void jump () = 0;
|
||||
|
||||
virtual bool onGround () const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -28,7 +28,11 @@ subject to the following restrictions:
|
||||
|
||||
#include "GlutStuff.h"
|
||||
#include "CharacterDemo.h"
|
||||
#include "CharacterController.h"
|
||||
#ifdef DYNAMIC_CHARACTER_CONTROLLER
|
||||
#include "DynamicCharacterController.h"
|
||||
#else
|
||||
#include "KinematicCharacterController.h"
|
||||
#endif
|
||||
|
||||
const int maxProxies = 32766;
|
||||
const int maxOverlap = 65535;
|
||||
@@ -39,6 +43,55 @@ static int gLeft = 0;
|
||||
static int gRight = 0;
|
||||
static int gJump = 0;
|
||||
|
||||
#define QUAKE_BSP_IMPORTING 1
|
||||
|
||||
#ifdef QUAKE_BSP_IMPORTING
|
||||
#include "Demos/BspDemo/BspLoader.h"
|
||||
#include "Demos/BspDemo/BspConverter.h"
|
||||
#endif //QUAKE_BSP_IMPORTING
|
||||
|
||||
|
||||
class BspToBulletConverter : public BspConverter
|
||||
{
|
||||
CharacterDemo* m_demoApp;
|
||||
|
||||
public:
|
||||
|
||||
BspToBulletConverter(CharacterDemo* demoApp)
|
||||
:m_demoApp(demoApp)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void addConvexVerticesCollider(btAlignedObjectArray<btVector3>& vertices, bool isEntity, const btVector3& entityTargetLocation)
|
||||
{
|
||||
///perhaps we can do something special with entities (isEntity)
|
||||
///like adding a collision Triggering (as example)
|
||||
|
||||
if (vertices.size() > 0)
|
||||
{
|
||||
float mass = 0.f;
|
||||
btTransform startTransform;
|
||||
//can use a shift
|
||||
startTransform.setIdentity();
|
||||
startTransform.setOrigin(btVector3(0,-10.0f,0.0f));
|
||||
//this create an internal copy of the vertices
|
||||
for (int i = 0; i < vertices.size(); i++)
|
||||
{
|
||||
vertices[i] *= btScalar(0.5);
|
||||
float t = vertices[i].getZ() * btScalar(0.75);
|
||||
vertices[i].setZ(-vertices[i].getY());
|
||||
vertices[i].setY(t);
|
||||
}
|
||||
|
||||
btCollisionShape* shape = new btConvexHullShape(&(vertices[0].getX()),vertices.size());
|
||||
m_demoApp->m_collisionShapes.push_back(shape);
|
||||
|
||||
//btRigidBody* body = m_demoApp->localCreateRigidBody(mass, startTransform,shape);
|
||||
m_demoApp->localCreateRigidBody(mass, startTransform,shape);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CharacterDemo::CharacterDemo()
|
||||
:
|
||||
m_cameraHeight(4.f),
|
||||
@@ -57,6 +110,7 @@ CharacterDemo::~CharacterDemo()
|
||||
if (m_character)
|
||||
m_character->destroy (m_dynamicsWorld);
|
||||
|
||||
|
||||
//remove the rigidbodies from the dynamics world and delete them
|
||||
int i;
|
||||
for (i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
|
||||
@@ -153,11 +207,15 @@ public:
|
||||
return m_hashPairCache->getOverlappingPairArray();
|
||||
}
|
||||
|
||||
btOverlappingPairCache* getOverlappingPairCache()
|
||||
{
|
||||
return m_hashPairCache;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void CharacterDemo::initPhysics()
|
||||
{
|
||||
|
||||
btCollisionShape* groundShape = new btBoxShape(btVector3(50,3,50));
|
||||
m_collisionShapes.push_back(groundShape);
|
||||
m_collisionConfiguration = new btDefaultCollisionConfiguration();
|
||||
@@ -173,7 +231,53 @@ void CharacterDemo::initPhysics()
|
||||
btTransform tr;
|
||||
tr.setIdentity();
|
||||
|
||||
//either use heightfield or triangle mesh
|
||||
#define USE_BSP_STAGE
|
||||
#ifdef USE_BSP_STAGE
|
||||
#ifdef QUAKE_BSP_IMPORTING
|
||||
char* bspfilename = "BspDemo.bsp";
|
||||
void* memoryBuffer = 0;
|
||||
|
||||
FILE* file = fopen(bspfilename,"r");
|
||||
if (!file)
|
||||
{
|
||||
//try again other path,
|
||||
//sight... visual studio leaves the current working directory in the projectfiles folder
|
||||
//instead of executable folder. who wants this default behaviour?!?
|
||||
bspfilename = "../../BspDemo.bsp";
|
||||
file = fopen(bspfilename,"r");
|
||||
}
|
||||
if (!file)
|
||||
{
|
||||
//try again other path,
|
||||
//sight... visual studio leaves the current working directory in the projectfiles folder
|
||||
//instead of executable folder. who wants this default behaviour?!?
|
||||
bspfilename = "BspDemo.bsp";
|
||||
file = fopen(bspfilename,"r");
|
||||
}
|
||||
|
||||
if (file)
|
||||
{
|
||||
BspLoader bspLoader;
|
||||
int size=0;
|
||||
if (fseek(file, 0, SEEK_END) || (size = ftell(file)) == EOF || fseek(file, 0, SEEK_SET)) { /* File operations denied? ok, just close and return failure */
|
||||
printf("Error: cannot get filesize from %s\n", bspfilename);
|
||||
} else
|
||||
{
|
||||
//how to detect file size?
|
||||
memoryBuffer = malloc(size+1);
|
||||
fread(memoryBuffer,1,size,file);
|
||||
bspLoader.loadBSPFile( memoryBuffer);
|
||||
|
||||
BspToBulletConverter bsp2bullet(this);
|
||||
float bspScaling = 0.1f;
|
||||
bsp2bullet.convertBsp(bspLoader,bspScaling);
|
||||
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
#define USE_TRIMESH_GROUND 1
|
||||
#ifdef USE_TRIMESH_GROUND
|
||||
int i;
|
||||
@@ -297,15 +401,6 @@ const float TRIANGLE_SIZE=20.f;
|
||||
//create ground object
|
||||
localCreateRigidBody(0,tr,groundShape);
|
||||
|
||||
m_character = new CharacterController ();
|
||||
m_character->setup (m_dynamicsWorld);
|
||||
|
||||
//we need to remove the rigid body from the broadphase in order to register all collisions
|
||||
m_dynamicsWorld->removeRigidBody(m_character->getRigidBody());
|
||||
//some custom callback sample
|
||||
m_customPairCallback = new MyCustomOverlappingPairCallback(this,m_character->getRigidBody());
|
||||
sweepBP->setOverlappingPairUserCallback(m_customPairCallback);
|
||||
m_dynamicsWorld->addRigidBody(m_character->getRigidBody());
|
||||
|
||||
|
||||
#define CUBE_HALF_EXTENTS 0.5
|
||||
@@ -365,6 +460,22 @@ const float TRIANGLE_SIZE=20.f;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DYNAMIC_CHARACTER_CONTROLLER
|
||||
m_character = new DynamicCharacterController ();
|
||||
#else
|
||||
m_character = new KinematicCharacterController ();
|
||||
#endif
|
||||
m_character->setup (m_dynamicsWorld);
|
||||
|
||||
//we need to remove the rigid body from the broadphase in order to register all collisions
|
||||
m_dynamicsWorld->removeRigidBody(m_character->getRigidBody());
|
||||
//some custom callback sample
|
||||
m_customPairCallback = new MyCustomOverlappingPairCallback(this,m_character->getRigidBody());
|
||||
sweepBP->setOverlappingPairUserCallback(m_customPairCallback);
|
||||
m_dynamicsWorld->addRigidBody(m_character->getRigidBody());
|
||||
m_character->registerPairCache (m_customPairCallback->getOverlappingPairCache());
|
||||
clientResetScene();
|
||||
|
||||
setCameraDistance(26.f);
|
||||
@@ -391,7 +502,7 @@ void CharacterDemo::clientMoveAndDisplay()
|
||||
if (m_character)
|
||||
{
|
||||
m_character->preStep (m_dynamicsWorld);
|
||||
m_character->playerStep (dt, gForward, gBackward, gLeft, gRight);
|
||||
m_character->playerStep (m_dynamicsWorld, dt, gForward, gBackward, gLeft, gRight);
|
||||
if (gJump)
|
||||
{
|
||||
gJump = 0;
|
||||
@@ -616,8 +727,8 @@ void CharacterDemo::updateCamera()
|
||||
//update OpenGL camera settings
|
||||
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10000.0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
gluLookAt(m_cameraPosition[0],m_cameraPosition[1],m_cameraPosition[2],
|
||||
m_cameraTargetPosition[0],m_cameraTargetPosition[1], m_cameraTargetPosition[2],
|
||||
|
||||
@@ -16,6 +16,7 @@ subject to the following restrictions:
|
||||
#define CHARACTER_DEMO_H
|
||||
|
||||
class CharacterController;
|
||||
class KinematicCharacterController;
|
||||
|
||||
class btCollisionShape;
|
||||
|
||||
@@ -27,7 +28,11 @@ class CharacterDemo : public DemoApplication
|
||||
{
|
||||
public:
|
||||
|
||||
#ifdef DYNAMIC_CHARACTER_CONTROLLER
|
||||
CharacterController* m_character;
|
||||
#else
|
||||
KinematicCharacterController* m_character;
|
||||
#endif
|
||||
|
||||
btAlignedObjectArray<btCollisionShape*> m_collisionShapes;
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
#include "BulletDynamics/Dynamics/btRigidBody.h"
|
||||
#include "BulletDynamics/Dynamics/btDynamicsWorld.h"
|
||||
#include "LinearMath/btDefaultMotionState.h"
|
||||
#include "CharacterController.h"
|
||||
#include "DynamicCharacterController.h"
|
||||
|
||||
CharacterController::CharacterController ()
|
||||
DynamicCharacterController::DynamicCharacterController ()
|
||||
{
|
||||
m_rayLambda[0] = 1.0;
|
||||
m_rayLambda[1] = 1.0;
|
||||
@@ -17,11 +17,11 @@ CharacterController::CharacterController ()
|
||||
m_rigidBody = NULL;
|
||||
}
|
||||
|
||||
CharacterController::~CharacterController ()
|
||||
DynamicCharacterController::~DynamicCharacterController ()
|
||||
{
|
||||
}
|
||||
|
||||
void CharacterController::setup (btDynamicsWorld* dynamicsWorld, btScalar height, btScalar width)
|
||||
void DynamicCharacterController::setup (btDynamicsWorld* dynamicsWorld, btScalar height, btScalar width, btScalar stepHeight)
|
||||
{
|
||||
btVector3 spherePositions[2];
|
||||
btScalar sphereRadii[2];
|
||||
@@ -48,7 +48,7 @@ void CharacterController::setup (btDynamicsWorld* dynamicsWorld, btScalar height
|
||||
dynamicsWorld->addRigidBody (m_rigidBody);
|
||||
}
|
||||
|
||||
void CharacterController::destroy (btDynamicsWorld* dynamicsWorld)
|
||||
void DynamicCharacterController::destroy (btDynamicsWorld* dynamicsWorld)
|
||||
{
|
||||
if (m_shape)
|
||||
{
|
||||
@@ -62,12 +62,12 @@ void CharacterController::destroy (btDynamicsWorld* dynamicsWorld)
|
||||
}
|
||||
}
|
||||
|
||||
btRigidBody* CharacterController::getRigidBody ()
|
||||
btRigidBody* DynamicCharacterController::getRigidBody ()
|
||||
{
|
||||
return m_rigidBody;
|
||||
}
|
||||
|
||||
void CharacterController::preStep (btDynamicsWorld* dynamicsWorld)
|
||||
void DynamicCharacterController::preStep (btDynamicsWorld* dynamicsWorld)
|
||||
{
|
||||
btTransform xform;
|
||||
m_rigidBody->getMotionState()->getWorldTransform (xform);
|
||||
@@ -118,7 +118,7 @@ void CharacterController::preStep (btDynamicsWorld* dynamicsWorld)
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::playerStep (btScalar dt,
|
||||
void DynamicCharacterController::playerStep (btScalar dt,
|
||||
int forward,
|
||||
int backward,
|
||||
int left,
|
||||
@@ -167,12 +167,12 @@ void CharacterController::playerStep (btScalar dt,
|
||||
m_rigidBody->setCenterOfMassTransform (xform);
|
||||
}
|
||||
|
||||
bool CharacterController::canJump () const
|
||||
bool DynamicCharacterController::canJump () const
|
||||
{
|
||||
return onGround();
|
||||
}
|
||||
|
||||
void CharacterController::jump ()
|
||||
void DynamicCharacterController::jump ()
|
||||
{
|
||||
if (!canJump())
|
||||
return;
|
||||
@@ -185,7 +185,7 @@ void CharacterController::jump ()
|
||||
m_rigidBody->applyCentralImpulse (up * magnitude);
|
||||
}
|
||||
|
||||
bool CharacterController::onGround () const
|
||||
bool DynamicCharacterController::onGround () const
|
||||
{
|
||||
return m_rayLambda[0] < btScalar(1.0);
|
||||
}
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
#include "LinearMath/btVector3.h"
|
||||
|
||||
#include "CharacterControllerInterface.h"
|
||||
|
||||
class btCollisionShape;
|
||||
class btRigidBody;
|
||||
class btDynamicsWorld;
|
||||
|
||||
class CharacterController
|
||||
class DynamicCharacterController : public CharacterControllerInterface
|
||||
{
|
||||
protected:
|
||||
btScalar m_halfHeight;
|
||||
@@ -25,9 +27,9 @@ protected:
|
||||
btScalar m_walkVelocity;
|
||||
btScalar m_turnVelocity;
|
||||
public:
|
||||
CharacterController ();
|
||||
~CharacterController ();
|
||||
void setup (btDynamicsWorld* dynamicsWorld, btScalar height = 2.0, btScalar width = 0.25);
|
||||
DynamicCharacterController ();
|
||||
~DynamicCharacterController ();
|
||||
void setup (btDynamicsWorld* dynamicsWorld, btScalar height = 2.0, btScalar width = 0.25, btScalar stepHeight = 0.25);
|
||||
void destroy (btDynamicsWorld* dynamicsWorld);
|
||||
|
||||
btRigidBody* getRigidBody ();
|
||||
384
Demos/CharacterDemo/KinematicCharacterController.cpp
Normal file
384
Demos/CharacterDemo/KinematicCharacterController.cpp
Normal file
@@ -0,0 +1,384 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "GLDebugDrawer.h"
|
||||
|
||||
#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
|
||||
#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
|
||||
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
|
||||
#include "BulletDynamics/Dynamics/btRigidBody.h"
|
||||
#include "BulletDynamics/Dynamics/btDynamicsWorld.h"
|
||||
#include "LinearMath/btDefaultMotionState.h"
|
||||
#include "KinematicCharacterController.h"
|
||||
|
||||
/* TODO:
|
||||
* Handle projecting/slide along surfaces
|
||||
* Deal with starting in penetration
|
||||
* Interact with dynamic objects
|
||||
* Ride kinematicly animated platforms properly
|
||||
* Step climbing
|
||||
*/
|
||||
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
||||
{
|
||||
public:
|
||||
ClosestNotMeRayResultCallback (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;
|
||||
};
|
||||
|
||||
class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||
{
|
||||
public:
|
||||
ClosestNotMeConvexResultCallback (btRigidBody* me) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
|
||||
{
|
||||
m_me = me;
|
||||
}
|
||||
|
||||
virtual btScalar AddSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
|
||||
{
|
||||
if (convexResult.m_hitCollisionObject == m_me)
|
||||
return 1.0;
|
||||
|
||||
return ClosestConvexResultCallback::AddSingleResult (convexResult, normalInWorldSpace);
|
||||
}
|
||||
protected:
|
||||
btRigidBody* m_me;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal'
|
||||
*
|
||||
* from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
|
||||
*/
|
||||
btVector3 computeReflectionDirection (const btVector3& direction, const btVector3& normal)
|
||||
{
|
||||
return direction - (btScalar(2.0) * direction.dot(normal)) * normal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the portion of 'direction' that is parallel to 'normal'
|
||||
*/
|
||||
btVector3 parallelComponent (const btVector3& direction, const btVector3& normal)
|
||||
{
|
||||
btScalar magnitude = direction.dot(normal);
|
||||
return normal * magnitude;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the portion of 'direction' that is perpindicular to 'normal'
|
||||
*/
|
||||
btVector3 perpindicularComponent (const btVector3& direction, const btVector3& normal)
|
||||
{
|
||||
return direction - parallelComponent(direction, normal);
|
||||
}
|
||||
|
||||
KinematicCharacterController::KinematicCharacterController ()
|
||||
{
|
||||
m_turnAngle = btScalar(0.0);
|
||||
m_walkVelocity = btScalar(1.1) * 4.0; // 4 km/h -> 1.1 m/s
|
||||
m_shape = NULL;
|
||||
m_pairCache = NULL;
|
||||
m_rigidBody = NULL;
|
||||
}
|
||||
|
||||
KinematicCharacterController::~KinematicCharacterController ()
|
||||
{
|
||||
}
|
||||
|
||||
void KinematicCharacterController::setup (btDynamicsWorld* dynamicsWorld, 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 (btVector3(width/btScalar(2.0), height/btScalar(2.0), width/btScalar(2.0)), &spherePositions[0], &sphereRadii[0], 2);
|
||||
m_stepHeight = stepHeight;
|
||||
m_height = height;
|
||||
m_width = width;
|
||||
btTransform startTransform;
|
||||
startTransform.setIdentity ();
|
||||
startTransform.setOrigin (btVector3(0.0, 4.0, 0.0));
|
||||
btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
|
||||
btRigidBody::btRigidBodyConstructionInfo cInfo(1.0, myMotionState, m_shape);
|
||||
m_rigidBody = new btRigidBody(cInfo);
|
||||
m_rigidBody->setCollisionFlags( m_rigidBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
||||
m_rigidBody->setSleepingThresholds (0.0, 0.0);
|
||||
m_rigidBody->setAngularFactor (0.0);
|
||||
dynamicsWorld->addRigidBody (m_rigidBody);
|
||||
}
|
||||
|
||||
void KinematicCharacterController::destroy (btDynamicsWorld* dynamicsWorld)
|
||||
{
|
||||
if (m_rigidBody)
|
||||
{
|
||||
dynamicsWorld->removeRigidBody (m_rigidBody);
|
||||
delete m_rigidBody;
|
||||
}
|
||||
|
||||
if (m_shape)
|
||||
{
|
||||
delete m_shape;
|
||||
}
|
||||
}
|
||||
|
||||
btRigidBody* KinematicCharacterController::getRigidBody ()
|
||||
{
|
||||
return m_rigidBody;
|
||||
}
|
||||
|
||||
void KinematicCharacterController::recoverFromPenetration (btDynamicsWorld* dynamicsWorld)
|
||||
{
|
||||
if (m_pairCache == NULL)
|
||||
return;
|
||||
|
||||
printf("%d\n", m_pairCache->getNumOverlappingPairs());
|
||||
dynamicsWorld->getDispatcher()->dispatchAllCollisionPairs (m_pairCache, dynamicsWorld->getDispatchInfo(), dynamicsWorld->getDispatcher());
|
||||
|
||||
btManifoldArray manifoldArray;
|
||||
for (int i = 0; i < m_pairCache->getNumOverlappingPairs(); i++)
|
||||
{
|
||||
printf("%d\n",i);
|
||||
manifoldArray.clear();
|
||||
|
||||
btBroadphasePair* collisionPair = &m_pairCache->getOverlappingPairArray()[i];
|
||||
|
||||
if (collisionPair->m_algorithm)
|
||||
collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);
|
||||
|
||||
for (int j=0;j<manifoldArray.size();j++)
|
||||
{
|
||||
btPersistentManifold* manifold = manifoldArray[j];
|
||||
for (int p=0;p<manifold->getNumContacts();p++)
|
||||
{
|
||||
const btManifoldPoint&pt = manifold->getContactPoint(p);
|
||||
|
||||
if (pt.getDistance() < 0.0)
|
||||
{
|
||||
printf("penetration %f\n", pt.getDistance());
|
||||
} else {
|
||||
printf("touching %f\n", pt.getDistance());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KinematicCharacterController::stepUp (btDynamicsWorld* dynamicsWorld)
|
||||
{
|
||||
// phase 1: up
|
||||
btTransform start, end;
|
||||
m_targetPosition = m_currentPosition + btVector3 (btScalar(0.0), m_stepHeight, btScalar(0.0));
|
||||
|
||||
start.setIdentity ();
|
||||
end.setIdentity ();
|
||||
|
||||
/* FIXME: Handle penetration properly */
|
||||
start.setOrigin (m_currentPosition + btVector3(btScalar(0.0), btScalar(0.1), btScalar(0.0)));
|
||||
end.setOrigin (m_targetPosition);
|
||||
|
||||
ClosestNotMeConvexResultCallback callback (m_rigidBody);
|
||||
|
||||
dynamicsWorld->convexSweepTest (m_shape, start, end, callback);
|
||||
|
||||
if (callback.HasHit())
|
||||
{
|
||||
// we moved up only a fraction of the step height
|
||||
m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
|
||||
m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
|
||||
} else {
|
||||
m_currentStepOffset = m_stepHeight;
|
||||
m_currentPosition = m_targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
void KinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag)
|
||||
{
|
||||
btVector3 movementDirection = m_targetPosition - m_currentPosition;
|
||||
btScalar movementLength = movementDirection.length();
|
||||
movementDirection.normalize();
|
||||
|
||||
btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal);
|
||||
reflectDir.normalize();
|
||||
|
||||
btVector3 parallelDir, perpindicularDir;
|
||||
|
||||
parallelDir = parallelComponent (reflectDir, hitNormal);
|
||||
perpindicularDir = perpindicularComponent (reflectDir, hitNormal);
|
||||
|
||||
m_targetPosition = m_currentPosition;
|
||||
if (tangentMag != 0.0)
|
||||
{
|
||||
m_targetPosition += parallelDir * btScalar (tangentMag*movementLength);
|
||||
}
|
||||
|
||||
if (normalMag != 0.0)
|
||||
{
|
||||
m_targetPosition += perpindicularDir * btScalar (normalMag*movementLength);
|
||||
}
|
||||
}
|
||||
|
||||
void KinematicCharacterController::stepForwardAndStrafe (btDynamicsWorld* dynamicsWorld, const btVector3& walkMove)
|
||||
{
|
||||
// phase 2: forward and strafe
|
||||
btTransform start, end;
|
||||
m_targetPosition = m_currentPosition + walkMove;
|
||||
start.setIdentity ();
|
||||
end.setIdentity ();
|
||||
|
||||
btScalar fraction = 1.0;
|
||||
btScalar distance2 = (m_currentPosition-m_targetPosition).length2();
|
||||
|
||||
while (fraction > btScalar(0.01))
|
||||
{
|
||||
start.setOrigin (m_currentPosition);
|
||||
end.setOrigin (m_targetPosition);
|
||||
|
||||
ClosestNotMeConvexResultCallback callback (m_rigidBody);
|
||||
dynamicsWorld->convexSweepTest (m_shape, start, end, callback);
|
||||
|
||||
fraction -= callback.m_closestHitFraction;
|
||||
|
||||
if (callback.HasHit())
|
||||
{
|
||||
// we moved only a fraction
|
||||
m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
|
||||
updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld);
|
||||
distance2 = (m_currentPosition-m_targetPosition).length2();
|
||||
} else {
|
||||
// we moved whole way
|
||||
m_currentPosition = m_targetPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KinematicCharacterController::stepDown (btDynamicsWorld* dynamicsWorld, btScalar dt)
|
||||
{
|
||||
btTransform start, end;
|
||||
|
||||
// phase 3: down
|
||||
btVector3 step_drop = btVector3(btScalar(0.0), m_currentStepOffset, btScalar(0.0));
|
||||
btVector3 gravity_drop = btVector3(btScalar(0.0), m_stepHeight, btScalar(0.0));
|
||||
m_targetPosition -= (step_drop + gravity_drop);
|
||||
|
||||
start.setIdentity ();
|
||||
end.setIdentity ();
|
||||
|
||||
start.setOrigin (m_currentPosition);
|
||||
end.setOrigin (m_targetPosition);
|
||||
|
||||
ClosestNotMeConvexResultCallback callback (m_rigidBody);
|
||||
|
||||
dynamicsWorld->convexSweepTest (m_shape, start, end, callback);
|
||||
|
||||
if (callback.HasHit())
|
||||
{
|
||||
// we dropped a fraction of the height -> hit floor
|
||||
m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
|
||||
} else {
|
||||
// we dropped the full height
|
||||
|
||||
m_currentPosition = m_targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
void KinematicCharacterController::registerPairCache (btOverlappingPairCache* pairCache)
|
||||
{
|
||||
m_pairCache = pairCache;
|
||||
}
|
||||
|
||||
void KinematicCharacterController::preStep (btDynamicsWorld* dynamicsWorld)
|
||||
{
|
||||
btTransform xform;
|
||||
m_rigidBody->getMotionState()->getWorldTransform (xform);
|
||||
|
||||
btVector3 forwardDir = xform.getBasis()[2];
|
||||
btVector3 upDir = xform.getBasis()[1];
|
||||
btVector3 strafeDir = xform.getBasis()[0];
|
||||
forwardDir.normalize ();
|
||||
upDir.normalize ();
|
||||
strafeDir.normalize ();
|
||||
|
||||
m_upDirection = upDir;
|
||||
m_forwardDirection = forwardDir;
|
||||
m_strafeDirection = strafeDir;
|
||||
|
||||
m_currentPosition = xform.getOrigin();
|
||||
m_targetPosition = m_currentPosition;
|
||||
recoverFromPenetration (dynamicsWorld);
|
||||
}
|
||||
|
||||
void KinematicCharacterController::playerStep (btDynamicsWorld* dynamicsWorld,
|
||||
btScalar dt,
|
||||
int forward,
|
||||
int backward,
|
||||
int left,
|
||||
int right)
|
||||
{
|
||||
btVector3 walkDirection = btVector3(0.0, 0.0, 0.0);
|
||||
btScalar walkSpeed = m_walkVelocity * dt;
|
||||
|
||||
if (left)
|
||||
walkDirection += m_strafeDirection;
|
||||
|
||||
if (right)
|
||||
walkDirection -= m_strafeDirection;
|
||||
|
||||
if (forward)
|
||||
walkDirection += m_forwardDirection;
|
||||
|
||||
if (backward)
|
||||
walkDirection -= m_forwardDirection;
|
||||
|
||||
btTransform xform;
|
||||
m_rigidBody->getMotionState()->getWorldTransform (xform);
|
||||
|
||||
stepUp (dynamicsWorld);
|
||||
stepForwardAndStrafe (dynamicsWorld, walkDirection * walkSpeed);
|
||||
stepDown (dynamicsWorld, dt);
|
||||
|
||||
xform.setOrigin (m_currentPosition);
|
||||
m_rigidBody->getMotionState()->setWorldTransform (xform);
|
||||
m_rigidBody->setCenterOfMassTransform (xform);
|
||||
}
|
||||
|
||||
bool KinematicCharacterController::canJump () const
|
||||
{
|
||||
return onGround();
|
||||
}
|
||||
|
||||
void KinematicCharacterController::jump ()
|
||||
{
|
||||
if (!canJump())
|
||||
return;
|
||||
|
||||
#if 0
|
||||
currently no jumping.
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KinematicCharacterController::onGround () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
61
Demos/CharacterDemo/KinematicCharacterController.h
Normal file
61
Demos/CharacterDemo/KinematicCharacterController.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef KINEMATIC_CHARACTER_CONTROLLER_H
|
||||
#define KINEMATIC_CHARACTER_CONTROLLER_H
|
||||
|
||||
#include "LinearMath/btVector3.h"
|
||||
|
||||
#include "CharacterControllerInterface.h"
|
||||
|
||||
class btCollisionShape;
|
||||
class btRigidBody;
|
||||
class btDynamicsWorld;
|
||||
|
||||
class KinematicCharacterController : public CharacterControllerInterface
|
||||
{
|
||||
protected:
|
||||
btScalar m_halfHeight;
|
||||
btConvexShape* m_shape;
|
||||
btRigidBody* m_rigidBody;
|
||||
btOverlappingPairCache* m_pairCache;
|
||||
|
||||
btScalar m_turnAngle;
|
||||
btScalar m_walkVelocity;
|
||||
|
||||
btScalar m_height;
|
||||
btScalar m_width;
|
||||
btScalar m_stepHeight;
|
||||
|
||||
btVector3 m_upDirection;
|
||||
btVector3 m_forwardDirection;
|
||||
btVector3 m_strafeDirection;
|
||||
|
||||
btVector3 m_currentPosition;
|
||||
btScalar m_currentStepOffset;
|
||||
btVector3 m_targetPosition;
|
||||
|
||||
void recoverFromPenetration (btDynamicsWorld* dynamicsWorld);
|
||||
void stepUp (btDynamicsWorld* dynamicsWorld);
|
||||
void updateTargetPositionBasedOnCollision (const btVector3& hit_normal, btScalar tangentMag = btScalar(1.0), btScalar normalMag = btScalar(0.0));
|
||||
void stepForwardAndStrafe (btDynamicsWorld* dynamicsWorld, const btVector3& walkMove);
|
||||
void stepDown (btDynamicsWorld* dynamicsWorld, btScalar dt);
|
||||
public:
|
||||
KinematicCharacterController ();
|
||||
~KinematicCharacterController ();
|
||||
void setup (btDynamicsWorld* dynamicsWorld, btScalar height = btScalar(1.75), btScalar width = btScalar(0.4), btScalar stepHeight = btScalar(0.35));
|
||||
void destroy (btDynamicsWorld* dynamicsWorld);
|
||||
|
||||
btRigidBody* getRigidBody ();
|
||||
|
||||
void registerPairCache (btOverlappingPairCache* pairCache);
|
||||
void preStep (btDynamicsWorld* dynamicsWorld);
|
||||
void playerStep (btDynamicsWorld* dynamicsWorld, btScalar dt,
|
||||
int forward,
|
||||
int backward,
|
||||
int left,
|
||||
int right);
|
||||
bool canJump () const;
|
||||
void jump ();
|
||||
|
||||
bool onGround () const;
|
||||
};
|
||||
|
||||
#endif // KINEMATIC_CHARACTER_CONTROLLER_H
|
||||
Reference in New Issue
Block a user