mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 18:19:55 +00:00
Made a working Bullet collision example
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@47 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
parent
1cfaeeb8fc
commit
5fad8ea459
6 changed files with 537 additions and 167 deletions
|
@ -32,12 +32,23 @@ extern(C):
|
||||||
|
|
||||||
// Initialize the dynamic world. Returns non-zero if an error occurs.
|
// Initialize the dynamic world. Returns non-zero if an error occurs.
|
||||||
int cpp_initBullet();
|
int cpp_initBullet();
|
||||||
void cpp_timeStep(float delta);
|
|
||||||
void cpp_cleanupBullet();
|
// Warp the player to a specific location.
|
||||||
|
void cpp_movePlayer(float x, float y, float z);
|
||||||
|
|
||||||
|
// Request that the player moves in this direction
|
||||||
|
void cpp_setPlayerDir(float x, float y, float z);
|
||||||
|
|
||||||
|
// Get the current player position, after physics and collision have
|
||||||
|
// been applied.
|
||||||
|
void cpp_getPlayerPos(float *x, float *y, float *z);
|
||||||
|
|
||||||
// Insert a debug collision object
|
// Insert a debug collision object
|
||||||
void cpp_insertBox(float x, float y, float z);
|
void cpp_insertBox(float x, float y, float z);
|
||||||
|
|
||||||
// Move the player's collision shape
|
// Move the physics simulation 'delta' seconds forward in time
|
||||||
int cpp_movePlayerCollision(float x, float y, float z,
|
void cpp_timeStep(float delta);
|
||||||
float dx, float dy, float dz);
|
|
||||||
|
// Deallocate objects
|
||||||
|
void cpp_cleanupBullet();
|
||||||
|
|
||||||
|
|
|
@ -1,196 +1,229 @@
|
||||||
#include "btBulletCollisionCommon.h"
|
#include "btBulletDynamicsCommon.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
btDefaultCollisionConfiguration* m_collisionConfiguration;
|
class CustomOverlappingPairCallback;
|
||||||
btCollisionDispatcher *m_dispatcher;
|
|
||||||
btBroadphaseInterface *m_broadphase;
|
|
||||||
//btSequentialImpulseConstraintSolver* m_solver;
|
|
||||||
//btDynamicsWorld *m_dynamicsWorld;
|
|
||||||
btCollisionWorld *m_collisionWorld;
|
|
||||||
|
|
||||||
//btCollisionObject* m_playerObject;
|
// System variables
|
||||||
btConvexShape *playerShape;
|
btDefaultCollisionConfiguration* g_collisionConfiguration;
|
||||||
|
btCollisionDispatcher *g_dispatcher;
|
||||||
|
//btBroadphaseInterface *g_broadphase;
|
||||||
|
btAxisSweep3 *g_broadphase;
|
||||||
|
btSequentialImpulseConstraintSolver* g_solver;
|
||||||
|
btDynamicsWorld *g_dynamicsWorld;
|
||||||
|
|
||||||
|
// Player variables
|
||||||
|
btCollisionObject* g_playerObject;
|
||||||
|
btConvexShape *g_playerShape;
|
||||||
|
|
||||||
|
// Player position. This is updated automatically by the physics
|
||||||
|
// system based on g_walkDirection and collisions. It is read by D
|
||||||
|
// code through cpp_getPlayerPos().
|
||||||
|
btVector3 g_playerPosition;
|
||||||
|
|
||||||
|
// Walking vector - defines direction and speed that the player
|
||||||
|
// intends to move right now. This is updated from D code each frame
|
||||||
|
// through cpp_setPlayerDir(), based on player input (and later, AI
|
||||||
|
// decisions.) The units of the vector are points per second.
|
||||||
|
btVector3 g_walkDirection;
|
||||||
|
|
||||||
|
// These variables and the class below are used in player collision
|
||||||
|
// detection. The callback is injected into the broadphase and keeps a
|
||||||
|
// continuously updated list of what objects are colliding with the
|
||||||
|
// player (in g_pairCache). This list is used in the function called
|
||||||
|
// recoverFromPenetration() below.
|
||||||
|
btHashedOverlappingPairCache* g_pairCache;
|
||||||
|
CustomOverlappingPairCallback *g_customPairCallback;
|
||||||
|
|
||||||
|
// Include the player physics
|
||||||
|
#include "cpp_player.cpp"
|
||||||
|
|
||||||
|
class CustomOverlappingPairCallback : public btOverlappingPairCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,
|
||||||
|
btBroadphaseProxy* proxy1)
|
||||||
|
{
|
||||||
|
if (proxy0->m_clientObject==g_playerObject ||
|
||||||
|
proxy1->m_clientObject==g_playerObject)
|
||||||
|
return g_pairCache->addOverlappingPair(proxy0,proxy1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void* removeOverlappingPair(btBroadphaseProxy* proxy0,
|
||||||
|
btBroadphaseProxy* proxy1,
|
||||||
|
btDispatcher* dispatcher)
|
||||||
|
{
|
||||||
|
if (proxy0->m_clientObject==g_playerObject ||
|
||||||
|
proxy1->m_clientObject==g_playerObject)
|
||||||
|
return g_pairCache->removeOverlappingPair(proxy0,proxy1,dispatcher);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy0,
|
||||||
|
btDispatcher* dispatcher)
|
||||||
|
{ if (proxy0->m_clientObject==g_playerObject)
|
||||||
|
g_pairCache->removeOverlappingPairsContainingProxy(proxy0,dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KILLME
|
||||||
|
btBroadphasePairArray& getOverlappingPairArray()
|
||||||
|
{ return g_pairCache->getOverlappingPairArray(); }
|
||||||
|
btOverlappingPairCache* getOverlappingPairCache()
|
||||||
|
{ return g_pairCache; }
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
extern "C" int32_t cpp_initBullet()
|
extern "C" int32_t cpp_initBullet()
|
||||||
{
|
{
|
||||||
|
// ------- SET UP THE WORLD -------
|
||||||
|
|
||||||
// Set up basic objects
|
// Set up basic objects
|
||||||
m_collisionConfiguration = new btDefaultCollisionConfiguration();
|
g_collisionConfiguration = new btDefaultCollisionConfiguration();
|
||||||
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
|
g_dispatcher = new btCollisionDispatcher(g_collisionConfiguration);
|
||||||
m_broadphase = new btDbvtBroadphase();
|
//g_broadphase = new btDbvtBroadphase();
|
||||||
m_collisionWorld =
|
g_solver = new btSequentialImpulseConstraintSolver;
|
||||||
new btCollisionWorld(m_dispatcher, m_broadphase,
|
|
||||||
m_collisionConfiguration);
|
|
||||||
|
|
||||||
/*m_solver = new btSequentialImpulseConstraintSolver;
|
// TODO: Figure out what to do with this. We need the user callback
|
||||||
m_dynamicsWorld =
|
// function used below (I think), but this is only offered by this
|
||||||
new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase,
|
// broadphase implementation (as far as I can see.)
|
||||||
m_solver, m_collisionConfiguration);
|
btVector3 worldMin(-100000,-100000,-100000);
|
||||||
m_dynamicsWorld->setGravity(btVector3(0,-10,0));
|
btVector3 worldMax(100000,100000,100000);
|
||||||
*/
|
g_broadphase = new btAxisSweep3(worldMin,worldMax);
|
||||||
|
|
||||||
// Create player collision object
|
g_dynamicsWorld =
|
||||||
//playerShape = new btCylinderShape(btVector3(50, 50, 50));
|
new btDiscreteDynamicsWorld(g_dispatcher,
|
||||||
playerShape = new btSphereShape(50);
|
g_broadphase,
|
||||||
|
g_solver,
|
||||||
|
g_collisionConfiguration);
|
||||||
|
|
||||||
|
//g_dynamicsWorld->setGravity(btVector3(0,-10,0));
|
||||||
|
|
||||||
|
|
||||||
|
// ------- SET UP THE PLAYER -------
|
||||||
|
|
||||||
|
// Create the player collision shape.
|
||||||
|
float width = 50;
|
||||||
|
//float height = 50;
|
||||||
/*
|
/*
|
||||||
m_playerObject = new btCollisionObject ();
|
// One possible shape is the convex hull around two spheres
|
||||||
m_playerObject->setCollisionShape (m_shape);
|
btVector3 spherePositions[2];
|
||||||
m_playerObject->setCollisionFlags (btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
btScalar sphereRadii[2];
|
||||||
|
sphereRadii[0] = width;
|
||||||
|
sphereRadii[1] = width;
|
||||||
|
spherePositions[0] = btVector3 (0.0, height/2.0, 0.0);
|
||||||
|
spherePositions[1] = btVector3 (0.0, -height/2.0, 0.0);
|
||||||
|
m_shape = new btMultiSphereShape(btVector3(width/2.0, height/2.0, width/2.0),
|
||||||
|
&spherePositions[0], &sphereRadii[0], 2);
|
||||||
*/
|
*/
|
||||||
|
//g_playerShape = new btCylinderShape(btVector3(50, 50, 50));
|
||||||
|
g_playerShape = new btSphereShape(width);
|
||||||
|
|
||||||
/*
|
// Create the collision object
|
||||||
// Dynamic shapes:
|
g_playerObject = new btCollisionObject ();
|
||||||
|
g_playerObject->setCollisionShape (g_playerShape);
|
||||||
|
g_playerObject->setCollisionFlags (btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
||||||
|
|
||||||
// Re-using the same collision is better for memory usage and
|
|
||||||
// performance.
|
|
||||||
|
|
||||||
btCollisionShape* colShape = new btBoxShape(btVector3(1,1,1));
|
// ------- OTHER STUFF -------
|
||||||
//btCollisionShape* colShape = new btSphereShape(btScalar(1.));
|
|
||||||
//m_collisionShapes.push_back(colShape);
|
|
||||||
|
|
||||||
// Create Dynamic Objects
|
// Create a custom callback to pick out all the objects colliding
|
||||||
btTransform startTransform;
|
// with the player. We use this in the collision recovery phase.
|
||||||
startTransform.setIdentity();
|
g_pairCache = new btHashedOverlappingPairCache();
|
||||||
|
g_customPairCallback = new CustomOverlappingPairCallback();
|
||||||
|
g_broadphase->setOverlappingPairUserCallback(g_customPairCallback);
|
||||||
|
|
||||||
mass = 1.f;
|
// Set up the callback that moves the player at the end of each
|
||||||
|
// simulation step.
|
||||||
|
g_dynamicsWorld->setInternalTickCallback(playerStepCallback);
|
||||||
|
|
||||||
colShape->calculateLocalInertia(mass,localInertia);
|
// Add the character collision object to the world.
|
||||||
|
g_dynamicsWorld->addCollisionObject(g_playerObject
|
||||||
|
,btBroadphaseProxy::DebrisFilter
|
||||||
|
//,btBroadphaseProxy::StaticFilter
|
||||||
|
);
|
||||||
|
|
||||||
///create 125 (5x5x5) dynamic objects
|
// Success!
|
||||||
#define ARRAY_SIZE_X 5
|
|
||||||
#define ARRAY_SIZE_Y 5
|
|
||||||
#define ARRAY_SIZE_Z 5
|
|
||||||
|
|
||||||
#define START_POS_X -5
|
|
||||||
#define START_POS_Y -5
|
|
||||||
#define START_POS_Z -3
|
|
||||||
|
|
||||||
float start_x = START_POS_X - ARRAY_SIZE_X/2;
|
|
||||||
float start_y = START_POS_Y;
|
|
||||||
float start_z = START_POS_Z - ARRAY_SIZE_Z/2;
|
|
||||||
|
|
||||||
for (int k=0;k<ARRAY_SIZE_Y;k++)
|
|
||||||
for (int i=0;i<ARRAY_SIZE_X;i++)
|
|
||||||
for(int j = 0;j<ARRAY_SIZE_Z;j++)
|
|
||||||
{
|
|
||||||
startTransform.setOrigin(btVector3(2.0*i + start_x,
|
|
||||||
10+2.0*k + start_y,
|
|
||||||
2.0*j + start_z));
|
|
||||||
|
|
||||||
btDefaultMotionState* myMotionState =
|
|
||||||
new btDefaultMotionState(startTransform);
|
|
||||||
|
|
||||||
btRigidBody::btRigidBodyConstructionInfo
|
|
||||||
rbInfo(mass,myMotionState,colShape,localInertia);
|
|
||||||
|
|
||||||
btRigidBody* body = new btRigidBody(rbInfo);
|
|
||||||
body->setActivationState(ISLAND_SLEEPING);
|
|
||||||
|
|
||||||
m_dynamicsWorld->addRigidBody(body);
|
|
||||||
body->setActivationState(ISLAND_SLEEPING);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int32_t cpp_movePlayerCollision(float x, float y, float z,
|
// Warp the player to a specific location. We do not bother setting
|
||||||
float dx, float dy, float dz)
|
// rotation, since it's completely irrelevant for collision detection.
|
||||||
|
extern "C" void cpp_movePlayer(float x, float y, float z)
|
||||||
{
|
{
|
||||||
btTransform start, end;
|
btTransform tr;
|
||||||
start.setIdentity();
|
tr.setIdentity();
|
||||||
end.setIdentity();
|
tr.setOrigin(btVector3(x,y,z));
|
||||||
|
g_playerObject->setWorldTransform(tr);
|
||||||
// The sweep test moves the shape from the old position to the
|
|
||||||
// new. The 0.1 offset in one of the coordinates is to make sure a
|
|
||||||
// sweep is performed even when the player does not move.
|
|
||||||
start.setOrigin(btVector3(x, y, z));
|
|
||||||
end.setOrigin(btVector3(x+dx,y+dy,z+dz));
|
|
||||||
|
|
||||||
btCollisionWorld::ClosestConvexResultCallback cb(btVector3(0,0,0),
|
|
||||||
btVector3(0,0,0));
|
|
||||||
|
|
||||||
m_collisionWorld->convexSweepTest(playerShape, start, end, cb);
|
|
||||||
|
|
||||||
if(cb.hasHit()) return 1;
|
|
||||||
else return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Request that the player moves in this direction
|
||||||
|
extern "C" void cpp_setPlayerDir(float x, float y, float z)
|
||||||
|
{ g_walkDirection.setValue(x,y,z); }
|
||||||
|
|
||||||
|
// Get the current player position, after physics and collision have
|
||||||
|
// been applied.
|
||||||
|
extern "C" void cpp_getPlayerPos(float *x, float *y, float *z)
|
||||||
|
{
|
||||||
|
*x = g_playerPosition.getX();
|
||||||
|
*y = g_playerPosition.getY();
|
||||||
|
*z = g_playerPosition.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert a debug collision shape
|
||||||
extern "C" void cpp_insertBox(float x, float y, float z)
|
extern "C" void cpp_insertBox(float x, float y, float z)
|
||||||
{
|
{
|
||||||
btCollisionShape* groundShape =
|
btCollisionShape* groundShape =
|
||||||
new btSphereShape(50);
|
new btSphereShape(50);
|
||||||
//new btBoxShape(btVector3(100,100,100));
|
//new btBoxShape(btVector3(100,100,100));
|
||||||
|
|
||||||
|
// Create a motion state to hold the object (not sure why yet.)
|
||||||
btTransform groundTransform;
|
btTransform groundTransform;
|
||||||
groundTransform.setIdentity();
|
groundTransform.setIdentity();
|
||||||
groundTransform.setOrigin(btVector3(x,y,z));
|
groundTransform.setOrigin(btVector3(x,y,z));
|
||||||
|
|
||||||
btCollisionObject *obj = new btCollisionObject;
|
|
||||||
obj->setCollisionShape(groundShape);
|
|
||||||
obj->setWorldTransform(groundTransform);
|
|
||||||
|
|
||||||
m_collisionWorld->addCollisionObject(obj);
|
|
||||||
|
|
||||||
/*
|
|
||||||
m_collisionWorld->addCollisionObject(obj,
|
|
||||||
btBroadphaseProxy::DebrisFilter, // ??
|
|
||||||
btBroadphaseProxy::StaticFilter); // Only static objects
|
|
||||||
|
|
||||||
/*
|
|
||||||
btDefaultMotionState* myMotionState =
|
btDefaultMotionState* myMotionState =
|
||||||
new btDefaultMotionState(groundTransform);
|
new btDefaultMotionState(groundTransform);
|
||||||
|
|
||||||
|
// Create a rigid body from the motion state. Give it zero mass and
|
||||||
|
// inertia.
|
||||||
btRigidBody::btRigidBodyConstructionInfo
|
btRigidBody::btRigidBodyConstructionInfo
|
||||||
rbInfo(0, myMotionState, groundShape, btVector3(0,0,0));
|
rbInfo(0, myMotionState, groundShape, btVector3(0,0,0));
|
||||||
|
|
||||||
btRigidBody* body = new btRigidBody(rbInfo);
|
btRigidBody* body = new btRigidBody(rbInfo);
|
||||||
|
|
||||||
// Add the body to the dynamics world
|
// Add the body to the world
|
||||||
m_dynamicsWorld->addRigidBody(body);
|
g_dynamicsWorld->addRigidBody(body);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Move the physics simulation 'delta' seconds forward in time
|
||||||
extern "C" void cpp_timeStep(float delta)
|
extern "C" void cpp_timeStep(float delta)
|
||||||
{
|
{
|
||||||
// TODO: Find what unit Bullet uses here
|
// TODO: We might experiment with the number of time steps. Remember
|
||||||
m_dynamicsWorld->stepSimulation(delta / 1000.f);
|
// that the function also returns the number of steps performed.
|
||||||
|
g_dynamicsWorld->stepSimulation(delta,2);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Cleanup in the reverse order of creation/initialization
|
// Cleanup in the reverse order of creation/initialization
|
||||||
extern "C" void cpp_cleanupBullet()
|
extern "C" void cpp_cleanupBullet()
|
||||||
{
|
{
|
||||||
// Remove the rigidbodies from the dynamics world and delete them
|
// Remove the rigidbodies from the dynamics world and delete them
|
||||||
for (int i=m_collisionWorld->getNumCollisionObjects()-1; i>=0 ;i--)
|
for (int i=g_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
|
||||||
{
|
{
|
||||||
btCollisionObject* obj = m_collisionWorld->getCollisionObjectArray()[i];
|
btCollisionObject* obj = g_dynamicsWorld->getCollisionObjectArray()[i];
|
||||||
/*
|
|
||||||
btRigidBody* body = btRigidBody::upcast(obj);
|
btRigidBody* body = btRigidBody::upcast(obj);
|
||||||
|
|
||||||
if (body && body->getMotionState())
|
if (body && body->getMotionState())
|
||||||
delete body->getMotionState();
|
delete body->getMotionState();
|
||||||
*/
|
|
||||||
|
|
||||||
m_collisionWorld->removeCollisionObject( obj );
|
g_dynamicsWorld->removeCollisionObject( obj );
|
||||||
delete obj;
|
delete obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete collision shapes
|
delete g_dynamicsWorld;
|
||||||
/*
|
delete g_solver;
|
||||||
for (int j=0;j<m_collisionShapes.size();j++)
|
delete g_broadphase;
|
||||||
{
|
delete g_dispatcher;
|
||||||
btCollisionShape* shape = m_collisionShapes[j];
|
delete g_collisionConfiguration;
|
||||||
delete shape;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
delete m_collisionWorld;
|
|
||||||
//delete m_solver;
|
|
||||||
delete m_broadphase;
|
|
||||||
delete m_dispatcher;
|
|
||||||
delete m_collisionConfiguration;
|
|
||||||
}
|
}
|
||||||
|
|
303
bullet/cpp_player.cpp
Normal file
303
bullet/cpp_player.cpp
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
// This file handles player-specific physics and collision detection
|
||||||
|
|
||||||
|
// TODO: Later we might handle various physics modes, eg. dynamic
|
||||||
|
// (full physics), player_walk, player_fall, player_swim, player_float,
|
||||||
|
// player_levitate, player_ghost. These would be applicable to any
|
||||||
|
// object (through Monster script), allowing player physics to be used
|
||||||
|
// on NPCs and creatures.
|
||||||
|
|
||||||
|
// Variables used internally in this file. Once we make per-object
|
||||||
|
// player collision, these will be member variables.
|
||||||
|
bool g_touchingContact;
|
||||||
|
btVector3 g_touchingNormal;
|
||||||
|
|
||||||
|
// Returns the reflection direction of a ray going 'direction' hitting
|
||||||
|
// a surface with normal 'normal'
|
||||||
|
btVector3 reflect (const btVector3& direction, const btVector3& normal)
|
||||||
|
{ return direction - (btScalar(2.0) * direction.dot(normal)) * normal; }
|
||||||
|
|
||||||
|
// Returns the portion of 'direction' that is perpendicular to
|
||||||
|
// 'normal'
|
||||||
|
btVector3 perpComponent (const btVector3& direction, const btVector3& normal)
|
||||||
|
{ return direction - normal * direction.dot(normal); }
|
||||||
|
|
||||||
|
btManifoldArray manifoldArray;
|
||||||
|
|
||||||
|
// Callback used for collision detection sweep tests. It prevents self
|
||||||
|
// collision and is used in calls to convexSweepTest().
|
||||||
|
class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClosestNotMeConvexResultCallback()
|
||||||
|
: btCollisionWorld::ClosestConvexResultCallback
|
||||||
|
(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
|
||||||
|
{
|
||||||
|
m_collisionFilterGroup = g_playerObject->
|
||||||
|
getBroadphaseHandle()->m_collisionFilterGroup;
|
||||||
|
|
||||||
|
m_collisionFilterMask = g_playerObject->
|
||||||
|
getBroadphaseHandle()->m_collisionFilterMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
btScalar addSingleResult(btCollisionWorld::LocalConvexResult&
|
||||||
|
convexResult, bool normalInWorldSpace)
|
||||||
|
{
|
||||||
|
if (convexResult.m_hitCollisionObject == g_playerObject) return 1.0;
|
||||||
|
|
||||||
|
return ClosestConvexResultCallback::addSingleResult
|
||||||
|
(convexResult, normalInWorldSpace);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used to step up small steps and slopes. Not done.
|
||||||
|
void KinematicCharacterController::stepUp (const btCollisionWorld* world)
|
||||||
|
{
|
||||||
|
// phase 1: up
|
||||||
|
btVector3 targetPosition = g_playerPosition + btVector3 (btScalar(0.0), m_stepHeight, btScalar(0.0));
|
||||||
|
btTransform start, end;
|
||||||
|
|
||||||
|
start.setIdentity ();
|
||||||
|
end.setIdentity ();
|
||||||
|
|
||||||
|
// FIXME: Handle penetration properly
|
||||||
|
start.setOrigin (g_playerPosition + btVector3(0.0, 0.1, 0.0));
|
||||||
|
end.setOrigin (targetPosition);
|
||||||
|
|
||||||
|
ClosestNotMeConvexResultCallback callback (g_playerObject);
|
||||||
|
world->convexSweepTest (g_playerShape, start, end, callback);
|
||||||
|
|
||||||
|
if (callback.hasHit())
|
||||||
|
{
|
||||||
|
// we moved up only a fraction of the step height
|
||||||
|
m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
|
||||||
|
g_playerPosition.setInterpolate3(g_playerPosition, targetPosition,
|
||||||
|
callback.m_closestHitFraction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_currentStepOffset = m_stepHeight;
|
||||||
|
g_playerPosition = targetPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void updateTargetPositionBasedOnCollision (const btVector3& hitNormal,
|
||||||
|
btVector3 &targetPosition)
|
||||||
|
{
|
||||||
|
btVector3 movementDirection = targetPosition - g_playerPosition;
|
||||||
|
btScalar movementLength = movementDirection.length();
|
||||||
|
|
||||||
|
if (movementLength <= SIMD_EPSILON)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Is this needed?
|
||||||
|
movementDirection.normalize();
|
||||||
|
|
||||||
|
btVector3 reflectDir = reflect(movementDirection, hitNormal);
|
||||||
|
reflectDir.normalize();
|
||||||
|
|
||||||
|
btVector3 perpendicularDir = perpComponent (reflectDir, hitNormal);
|
||||||
|
|
||||||
|
targetPosition = g_playerPosition;
|
||||||
|
targetPosition += perpendicularDir * movementLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This covers all normal forward movement and collision, including
|
||||||
|
// walking sideways when hitting a wall at an angle. It does NOT
|
||||||
|
// handle walking up slopes and steps, or falling/gravity.
|
||||||
|
void stepForward(btVector3& walkMove)
|
||||||
|
{
|
||||||
|
btVector3 originalDir = walkMove.normalized();
|
||||||
|
|
||||||
|
// If no walking direction is given, we still run the function. This
|
||||||
|
// allows moving forces to push the player around even if she is
|
||||||
|
// standing still.
|
||||||
|
if (walkMove.length() < SIMD_EPSILON)
|
||||||
|
originalDir.setValue(0.f,0.f,0.f);
|
||||||
|
|
||||||
|
btTransform start, end;
|
||||||
|
btVector3 targetPosition = g_playerPosition + walkMove;
|
||||||
|
start.setIdentity ();
|
||||||
|
end.setIdentity ();
|
||||||
|
|
||||||
|
btScalar fraction = 1.0;
|
||||||
|
btScalar distance2 = (g_playerPosition-targetPosition).length2();
|
||||||
|
|
||||||
|
if (g_touchingContact)
|
||||||
|
if (originalDir.dot(g_touchingNormal) > btScalar(0.0))
|
||||||
|
updateTargetPositionBasedOnCollision (g_touchingNormal, targetPosition);
|
||||||
|
|
||||||
|
int maxIter = 10;
|
||||||
|
|
||||||
|
while (fraction > btScalar(0.01) && maxIter-- > 0)
|
||||||
|
{
|
||||||
|
start.setOrigin (g_playerPosition);
|
||||||
|
end.setOrigin (targetPosition);
|
||||||
|
|
||||||
|
ClosestNotMeConvexResultCallback callback;
|
||||||
|
g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback);
|
||||||
|
|
||||||
|
fraction -= callback.m_closestHitFraction;
|
||||||
|
|
||||||
|
if (callback.hasHit())
|
||||||
|
{
|
||||||
|
// We moved only a fraction
|
||||||
|
btScalar hitDistance = (callback.m_hitPointWorld - g_playerPosition).length();
|
||||||
|
// If the distance is further than the collision margin,
|
||||||
|
// move
|
||||||
|
if (hitDistance > 0.05)
|
||||||
|
g_playerPosition.setInterpolate3(g_playerPosition, targetPosition,
|
||||||
|
callback.m_closestHitFraction);
|
||||||
|
|
||||||
|
updateTargetPositionBasedOnCollision(callback.m_hitNormalWorld,
|
||||||
|
targetPosition);
|
||||||
|
btVector3 currentDir = targetPosition - g_playerPosition;
|
||||||
|
distance2 = currentDir.length2();
|
||||||
|
|
||||||
|
if (distance2 <= SIMD_EPSILON)
|
||||||
|
break;
|
||||||
|
|
||||||
|
currentDir.normalize();
|
||||||
|
|
||||||
|
if (currentDir.dot(originalDir) <= btScalar(0.0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// we moved the whole way
|
||||||
|
g_playerPosition = targetPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not done. Will handle gravity, falling, sliding, etc.
|
||||||
|
void KinematicCharacterController::stepDown (const btCollisionWorld* g_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));
|
||||||
|
targetPosition -= (step_drop + gravity_drop);
|
||||||
|
|
||||||
|
start.setIdentity ();
|
||||||
|
end.setIdentity ();
|
||||||
|
|
||||||
|
start.setOrigin (g_playerPosition);
|
||||||
|
end.setOrigin (targetPosition);
|
||||||
|
|
||||||
|
ClosestNotMeConvexResultCallback callback (g_playerObject);
|
||||||
|
g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback);
|
||||||
|
|
||||||
|
if (callback.hasHit())
|
||||||
|
{
|
||||||
|
// we dropped a fraction of the height -> hit floor
|
||||||
|
g_playerPosition.setInterpolate3 (g_playerPosition, targetPosition, callback.m_closestHitFraction);
|
||||||
|
} else {
|
||||||
|
// we dropped the full height
|
||||||
|
|
||||||
|
g_playerPosition = targetPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check if the player currently collides with anything, and adjust
|
||||||
|
// its position accordingly. Returns true if collisions were found.
|
||||||
|
bool recoverFromPenetration()
|
||||||
|
{
|
||||||
|
bool penetration = false;
|
||||||
|
|
||||||
|
// Update the collision pair cache
|
||||||
|
g_dispatcher->dispatchAllCollisionPairs(g_pairCache,
|
||||||
|
g_dynamicsWorld->getDispatchInfo(),
|
||||||
|
g_dispatcher);
|
||||||
|
|
||||||
|
g_playerPosition = g_playerObject->getWorldTransform().getOrigin();
|
||||||
|
|
||||||
|
btScalar maxPen = 0.0;
|
||||||
|
for (int i = 0; i < g_pairCache->getNumOverlappingPairs(); i++)
|
||||||
|
{
|
||||||
|
manifoldArray.resize(0);
|
||||||
|
|
||||||
|
btBroadphasePair* collisionPair = &g_pairCache->getOverlappingPairArray()[i];
|
||||||
|
// Get the contact points
|
||||||
|
if (collisionPair->m_algorithm)
|
||||||
|
collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);
|
||||||
|
|
||||||
|
// And handle them
|
||||||
|
for (int j=0;j<manifoldArray.size();j++)
|
||||||
|
{
|
||||||
|
btPersistentManifold* manifold = manifoldArray[j];
|
||||||
|
btScalar directionSign = manifold->getBody0() ==
|
||||||
|
g_playerObject ? btScalar(-1.0) : btScalar(1.0);
|
||||||
|
|
||||||
|
for (int p=0;p<manifold->getNumContacts();p++)
|
||||||
|
{
|
||||||
|
const btManifoldPoint &pt = manifold->getContactPoint(p);
|
||||||
|
|
||||||
|
if (pt.getDistance() < 0.0)
|
||||||
|
{
|
||||||
|
// Pick out the maximum penetration normal and store
|
||||||
|
// it
|
||||||
|
if (pt.getDistance() < maxPen)
|
||||||
|
{
|
||||||
|
maxPen = pt.getDistance();
|
||||||
|
g_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
|
||||||
|
|
||||||
|
}
|
||||||
|
g_playerPosition += pt.m_normalWorldOnB * directionSign *
|
||||||
|
pt.getDistance() * btScalar(0.2);
|
||||||
|
|
||||||
|
penetration = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
btTransform newTrans = g_playerObject->getWorldTransform();
|
||||||
|
newTrans.setOrigin(g_playerPosition);
|
||||||
|
g_playerObject->setWorldTransform(newTrans);
|
||||||
|
|
||||||
|
return penetration;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback called at the end of each simulation cycle. This is the
|
||||||
|
// main function is responsible for player movement.
|
||||||
|
void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep)
|
||||||
|
{
|
||||||
|
// Before moving, recover from current penetrations
|
||||||
|
|
||||||
|
int numPenetrationLoops = 0;
|
||||||
|
g_touchingContact = false;
|
||||||
|
while (recoverFromPenetration())
|
||||||
|
{
|
||||||
|
numPenetrationLoops++;
|
||||||
|
g_touchingContact = true;
|
||||||
|
|
||||||
|
// Make sure we don't stay here indefinitely
|
||||||
|
if (numPenetrationLoops > 4)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the player position
|
||||||
|
btTransform xform;
|
||||||
|
xform = g_playerObject->getWorldTransform ();
|
||||||
|
g_playerPosition = xform.getOrigin();
|
||||||
|
|
||||||
|
// Next, do the walk.
|
||||||
|
|
||||||
|
// TODO: Walking direction should be set from D code, and the final
|
||||||
|
// position should be retrieved back from C++. The walking direction
|
||||||
|
// always reflects the intentional action of an agent (ie. the
|
||||||
|
// player or the AI), but the end result comes from collision and
|
||||||
|
// physics.
|
||||||
|
|
||||||
|
btVector3 walkStep = g_walkDirection * timeStep;
|
||||||
|
|
||||||
|
//stepUp();
|
||||||
|
stepForward(walkStep);
|
||||||
|
//stepDown(dt);
|
||||||
|
|
||||||
|
// Move the player (but keep rotation)
|
||||||
|
xform = g_playerObject->getWorldTransform ();
|
||||||
|
xform.setOrigin (g_playerPosition);
|
||||||
|
g_playerObject->setWorldTransform (xform);
|
||||||
|
}
|
|
@ -243,13 +243,18 @@ float musCumTime = 0;
|
||||||
|
|
||||||
void initializeInput()
|
void initializeInput()
|
||||||
{
|
{
|
||||||
// Move the camera in place
|
// Move the player into place. TODO: This isn't really input-related
|
||||||
|
// at all, and should be moved.
|
||||||
with(playerData.position)
|
with(playerData.position)
|
||||||
{
|
{
|
||||||
cpp_moveCamera(position[0], position[1], position[2],
|
// TODO: Think about renaming these functions to ogre_moveCamera
|
||||||
rotation[0], rotation[1], rotation[2]);
|
// and bullet_movePlayer etc.
|
||||||
|
cpp_moveCamera(position[0], position[1], position[2]);
|
||||||
|
cpp_setCameraRotation(rotation[0], rotation[1], rotation[2]);
|
||||||
|
|
||||||
// Insert a collision box close to the player
|
cpp_movePlayer(position[0], position[1], position[2]);
|
||||||
|
|
||||||
|
// Insert a collision shape close to the player
|
||||||
cpp_insertBox(position[0], position[1]+500, position[2]);
|
cpp_insertBox(position[0], position[1]+500, position[2]);
|
||||||
cpp_drawBox(position[0], position[1]+500, position[2]);
|
cpp_drawBox(position[0], position[1]+500, position[2]);
|
||||||
}
|
}
|
||||||
|
@ -299,12 +304,12 @@ extern(C) int d_frameStarted(float time)
|
||||||
// The rest is ignored in pause mode
|
// The rest is ignored in pause mode
|
||||||
if(pause) return 1;
|
if(pause) return 1;
|
||||||
|
|
||||||
const float moveSpeed = 900;
|
// Walking / floating speed, in points per second.
|
||||||
float speed = moveSpeed * time;
|
const float speed = 300;
|
||||||
|
|
||||||
// Check if the movement keys are pressed
|
// Check if the movement keys are pressed
|
||||||
float moveX = 0, moveY = 0, moveZ = 0;
|
float moveX = 0, moveY = 0, moveZ = 0;
|
||||||
float x, y, z;
|
float x, y, z, ox, oy, oz;
|
||||||
|
|
||||||
if(isPressed(Keys.MoveLeft)) moveX -= speed;
|
if(isPressed(Keys.MoveLeft)) moveX -= speed;
|
||||||
if(isPressed(Keys.MoveRight)) moveX += speed;
|
if(isPressed(Keys.MoveRight)) moveX += speed;
|
||||||
|
@ -313,21 +318,34 @@ extern(C) int d_frameStarted(float time)
|
||||||
if(isPressed(Keys.MoveUp)) moveY += speed;
|
if(isPressed(Keys.MoveUp)) moveY += speed;
|
||||||
if(isPressed(Keys.MoveDown)) moveY -= speed;
|
if(isPressed(Keys.MoveDown)) moveY -= speed;
|
||||||
|
|
||||||
// Move the player. This is a temporary hack, we should do this more
|
// This isn't very elegant, but it's simple and it works.
|
||||||
// efficiently in C++.
|
|
||||||
if(moveX != 0 || moveY !=0 || moveZ != 0)
|
|
||||||
{
|
|
||||||
cpp_moveCameraRel(moveX, moveY, moveZ);
|
|
||||||
cpp_getCameraPos(&x, &y, &z);
|
|
||||||
bool nw = cpp_movePlayerCollision(x, y, z, moveX, moveY, moveZ) != 0;
|
|
||||||
|
|
||||||
if(nw != collides)
|
// Get the current coordinates
|
||||||
{
|
cpp_getCameraPos(&ox, &oy, &oz);
|
||||||
if(nw) writefln("Entered shape");
|
|
||||||
else writefln("Left shape");
|
// Move camera using relative coordinates. TODO: We won't really
|
||||||
collides = nw;
|
// need to move the camera here (since it's moved below anyway), we
|
||||||
}
|
// only want the transformation from camera space to world
|
||||||
}
|
// space. This can likely be done more efficiently.
|
||||||
|
cpp_moveCameraRel(moveX, moveY, moveZ);
|
||||||
|
|
||||||
|
// Get the result
|
||||||
|
cpp_getCameraPos(&x, &y, &z);
|
||||||
|
|
||||||
|
// The result is the real movement direction, in world coordinates
|
||||||
|
moveX = x-ox;
|
||||||
|
moveY = y-oy;
|
||||||
|
moveZ = z-oz;
|
||||||
|
|
||||||
|
// Tell Bullet that this is where we want to go
|
||||||
|
cpp_setPlayerDir(moveX, moveY, moveZ);
|
||||||
|
|
||||||
|
// Perform a Bullet time step
|
||||||
|
cpp_timeStep(time);
|
||||||
|
|
||||||
|
// Get the final (actual) player position and update the camera
|
||||||
|
cpp_getPlayerPos(&x, &y, &z);
|
||||||
|
cpp_moveCamera(x,y,z);
|
||||||
|
|
||||||
sndCumTime += time;
|
sndCumTime += time;
|
||||||
if(sndCumTime > sndRefresh)
|
if(sndCumTime > sndRefresh)
|
||||||
|
|
|
@ -142,7 +142,8 @@ void cpp_screenshot(char *filename);
|
||||||
|
|
||||||
// Camera control and information
|
// Camera control and information
|
||||||
void cpp_rotateCamera(float x, float y);
|
void cpp_rotateCamera(float x, float y);
|
||||||
void cpp_moveCamera(float x, float y, float z, float r1, float r2, float r3);
|
void cpp_moveCamera(float x, float y, float z);
|
||||||
|
void cpp_setCameraRotation(float r1, float r2, float r3);
|
||||||
void cpp_getCameraPos(float *x, float *y, float *z);
|
void cpp_getCameraPos(float *x, float *y, float *z);
|
||||||
void cpp_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz);
|
void cpp_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz);
|
||||||
void cpp_moveCameraRel(float x, float y, float z);
|
void cpp_moveCameraRel(float x, float y, float z);
|
||||||
|
|
|
@ -132,7 +132,8 @@ extern "C" void cpp_getCameraPos(float *x, float *y, float *z)
|
||||||
*z = pos[1];
|
*z = pos[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current camera orientation
|
// Get current camera orientation, in the form of 'front' and 'up'
|
||||||
|
// vectors.
|
||||||
extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz,
|
extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz,
|
||||||
float *ux, float *uy, float *uz)
|
float *ux, float *uy, float *uz)
|
||||||
{
|
{
|
||||||
|
@ -146,18 +147,21 @@ extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz,
|
||||||
*uz = up[1];
|
*uz = up[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move and rotate camera in place.
|
// Move camera
|
||||||
extern "C" void cpp_moveCamera(float x, float y, float z,
|
extern "C" void cpp_moveCamera(float x, float y, float z)
|
||||||
float r1, float r2, float r3)
|
|
||||||
{
|
{
|
||||||
// Transforms Morrowind coordinates to OGRE coordinates. The camera
|
// Transforms Morrowind coordinates to OGRE coordinates. The camera
|
||||||
// is not affected by the rotation of the root node, so we must
|
// is not affected by the rotation of the root node, so we must
|
||||||
// transform this manually.
|
// transform this manually.
|
||||||
mCamera->setPosition(Vector3(x,z,-y));
|
mCamera->setPosition(Vector3(x,z,-y));
|
||||||
|
}
|
||||||
|
|
||||||
// Rotation is probably not correct, but for now I have no reference
|
// Rotate camera using Morrowind rotation specifiers
|
||||||
// point. Fix it later when we teleport from one cell to another, so
|
extern "C" void cpp_setCameraRotation(float r1, float r2, float r3)
|
||||||
// we have something to compare against.
|
{
|
||||||
|
// TODO: This translation is probably not correct, but for now I
|
||||||
|
// have no reference point. Fix it later when we teleport from one
|
||||||
|
// cell to another, so we have something to compare against.
|
||||||
|
|
||||||
// Rotate around X axis
|
// Rotate around X axis
|
||||||
Quaternion xr(Radian(-r1), Vector3::UNIT_X);
|
Quaternion xr(Radian(-r1), Vector3::UNIT_X);
|
||||||
|
@ -170,7 +174,7 @@ extern "C" void cpp_moveCamera(float x, float y, float z,
|
||||||
mCamera->setOrientation(xr*yr*zr);
|
mCamera->setOrientation(xr*yr*zr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move camera relative to its own axis set
|
// Move camera relative to its own axis set.
|
||||||
extern "C" void cpp_moveCameraRel(float x, float y, float z)
|
extern "C" void cpp_moveCameraRel(float x, float y, float z)
|
||||||
{
|
{
|
||||||
mCamera->moveRelative(Vector3(x,y,z));
|
mCamera->moveRelative(Vector3(x,y,z));
|
||||||
|
|
Loading…
Reference in a new issue