add physic support

This commit is contained in:
gugus 2011-02-22 14:02:50 +01:00
parent c60a48b397
commit adc1fa8e2c
6 changed files with 1355 additions and 0 deletions

45
bullet/CMotionState.cpp Normal file
View file

@ -0,0 +1,45 @@
#include "CMotionState.h"
#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <components\nifbullet\bullet_nif_loader.hpp>
//#include <apps\openmw\mwworld\world.hpp>
namespace OEngine {
namespace Physic
{
CMotionState::CMotionState(PhysicEngine* eng,std::string name)
{
pEng = eng;
tr.setIdentity();
pName = name;
};
void CMotionState::getWorldTransform(btTransform &worldTrans) const
{
worldTrans = tr;
}
void CMotionState::setWorldTransform(const btTransform &worldTrans)
{
tr = worldTrans;
PhysicEvent evt;
evt.isNPC = isNPC;
evt.isPC = isPC;
evt.newTransform = tr;
evt.RigidBodyName = pName;
if(isPC)
{
pEng->PEventList.push_back(evt);
}
else
{
pEng->NPEventList.push_back(evt);
}
}
}}

52
bullet/CMotionState.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef OENGINE_CMOTIONSTATE_H
#define OENGINE_CMOTIONSTATE_H
#include <BulletDynamics\Dynamics\btRigidBody.h>
#include <string>
namespace OEngine {
namespace Physic
{
class PhysicEngine;
/**
*A CMotionState is associated with a single RigidBody.
*When the RigidBody is moved by bullet, bullet will call the function setWorldTransform.
*for more info, see the bullet Wiki at btMotionState.
*/
class CMotionState:public btMotionState
{
public:
CMotionState(PhysicEngine* eng,std::string name);
/**
*Return the position of the RigidBody.
*/
virtual void getWorldTransform(btTransform &worldTrans) const;
/**
*Function called by bullet when the RigidBody is moved.
*It add an event to the EventList of the PhysicEngine class.
*/
virtual void setWorldTransform(const btTransform &worldTrans);
protected:
PhysicEngine* pEng;
btTransform tr;
bool isNPC;
bool isPC;
std::string pName;
};
struct PhysicEvent
{
bool isNPC;
bool isPC;
btTransform newTransform;
std::string RigidBodyName;
};
}}
#endif

View file

@ -0,0 +1,629 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "LinearMath/btIDebugDraw.h"
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
#include "LinearMath/btDefaultMotionState.h"
#include "btKinematicCharacterController.h"
#include <iostream>
///@todo Interact with dynamic objects,
///Ride kinematicly animated platforms properly
///Support ducking
class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
{
public:
btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
{
m_me[0] = me;
count = 1;
}
btKinematicClosestNotMeRayResultCallback (btCollisionObject* me[], int count_) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
{
count = count_;
for(int i = 0; i < count; i++)
m_me[i] = me[i];
}
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
{
for(int i = 0; i < count; i++)
if (rayResult.m_collisionObject == m_me[i])
return 1.0;
return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
}
protected:
btCollisionObject* m_me[10];
int count;
};
class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
{
public:
btKinematicClosestNotMeConvexResultCallback( btCollisionObject* me, const btVector3& up, btScalar minSlopeDot )
: btCollisionWorld::ClosestConvexResultCallback( btVector3( 0.0, 0.0, 0.0 ), btVector3( 0.0, 0.0, 0.0 ) ),
m_me( me ), m_up( up ), m_minSlopeDot( minSlopeDot )
{
}
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
{
if( convexResult.m_hitCollisionObject == m_me )
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 = m_up.dot(hitNormalWorld);
if( dotUp < m_minSlopeDot )
return btScalar( 1 );
return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
}
protected:
btCollisionObject* m_me;
const btVector3 m_up;
btScalar m_minSlopeDot;
};
btKinematicCharacterController::btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject_,
btPairCachingGhostObject* internalGhostObject_,
btScalar stepHeight,
btScalar constantScale,
btScalar gravity,
btScalar fallVelocity,
btScalar jumpVelocity,
btScalar recoveringFactor )
{
m_upAxis = btKinematicCharacterController::Y_AXIS;
m_walkDirection.setValue( btScalar( 0 ), btScalar( 0 ), btScalar( 0 ) );
m_useGhostObjectSweepTest = true;
externalGhostObject = externalGhostObject_;
internalGhostObject = internalGhostObject_;
m_recoveringFactor = recoveringFactor;
m_stepHeight = stepHeight;
m_useWalkDirection = true; // use walk direction by default, legacy behavior
m_velocityTimeInterval = btScalar( 0 );
m_verticalVelocity = btScalar( 0 );
m_verticalOffset = btScalar( 0 );
m_gravity = constantScale * gravity;
m_fallSpeed = constantScale * fallVelocity; // Terminal velocity of a sky diver in m/s.
m_jumpSpeed = constantScale * jumpVelocity; // ?
m_wasJumping = false;
setMaxSlope( btRadians( 45.0 ) );
}
btKinematicCharacterController::~btKinematicCharacterController ()
{
}
bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld )
{
//std::cout << "recover!!!!";
bool penetration = false;
collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(),
collisionWorld->getDispatchInfo(),
collisionWorld->getDispatcher() );
btVector3 currentPosition = internalGhostObject->getWorldTransform().getOrigin();
btScalar maxPen = btScalar( 0 );
for( int i = 0; i < internalGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ )
{
m_manifoldArray.resize(0);
btBroadphasePair* collisionPair = &internalGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
if( collisionPair->m_algorithm )
collisionPair->m_algorithm->getAllContactManifolds( m_manifoldArray );
for( int j = 0; j < m_manifoldArray.size(); j++ )
{
btPersistentManifold* manifold = m_manifoldArray[j];
btScalar directionSign = manifold->getBody0() == internalGhostObject ? btScalar( -1.0 ) : btScalar( 1.0 );
for( int p = 0; p < manifold->getNumContacts(); p++ )
{
const btManifoldPoint&pt = manifold->getContactPoint( p );
if(manifold->getBody1() == externalGhostObject) std::cout << "external!!";
if(manifold->getBody0() == externalGhostObject) std::cout << "external!!";
if(manifold->getBody1() == internalGhostObject) std::cout << "internal!!";
if(manifold->getBody0() == internalGhostObject) std::cout << "internal!!";
if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject)
||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) )
{
}
else
{
btScalar dist = pt.getDistance();
if( dist < 0.0 )
{
if( dist < maxPen )
maxPen = dist;
// NOTE : btScalar affects the stairs but the parkinson...
// 0.0 , the capsule can break the walls...
currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor;
penetration = true;
std::cout << "recover!!!!";
}
}
}
// ???
//manifold->clearManifold();
}
}
btTransform transform = internalGhostObject->getWorldTransform();
transform.setOrigin( currentPosition );
internalGhostObject->setWorldTransform( transform );
externalGhostObject->setWorldTransform( transform );
return penetration;
}
btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const btVector3& currentPosition, btScalar& currentStepOffset )
{
btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) );
// Retrieve the collision shape
//
btCollisionShape* collisionShape = externalGhostObject->getCollisionShape();
btAssert( collisionShape->isConvex() );
btConvexShape* convexShape = ( btConvexShape* )collisionShape;
// FIXME: Handle penetration properly
//
btTransform start;
start.setIdentity();
start.setOrigin( currentPosition + getUpAxisDirections()[ m_upAxis ] * ( convexShape->getMargin() ) );
btTransform end;
end.setIdentity();
end.setOrigin( targetPosition );
btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, -getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine );
callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
// Sweep test
//
if( m_useGhostObjectSweepTest )
externalGhostObject->convexSweepTest( convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration );
else
world->convexSweepTest( convexShape, start, end, callback );
if( callback.hasHit() )
{
// Only modify the position if the hit was a slope and not a wall or ceiling.
//
if( callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > btScalar( 0.0 ) )
{
// We moved up only a fraction of the step height
//
currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
return currentPosition.lerp( targetPosition, callback.m_closestHitFraction );
}
m_verticalVelocity = btScalar( 0.0 );
m_verticalOffset = btScalar( 0.0 );
return currentPosition;
}
else
{
currentStepOffset = m_stepHeight;
return targetPosition;
}
}
///Reflect the vector d around the vector r
inline btVector3 reflect( const btVector3& d, const btVector3& r )
{
return d - ( btScalar( 2.0 ) * d.dot( r ) ) * r;
}
///Project a vector u on another vector v
inline btVector3 project( const btVector3& u, const btVector3& v )
{
return v * u.dot( v );
}
///Helper for computing the character sliding
inline btVector3 slide( const btVector3& direction, const btVector3& planeNormal )
{
return direction - project( direction, planeNormal );
}
btVector3 slideOnCollision( const btVector3& fromPosition, const btVector3& toPosition, const btVector3& hitNormal )
{
btVector3 moveDirection = toPosition - fromPosition;
btScalar moveLength = moveDirection.length();
if( moveLength <= btScalar( SIMD_EPSILON ) )
return toPosition;
moveDirection.normalize();
btVector3 reflectDir = reflect( moveDirection, hitNormal );
reflectDir.normalize();
return fromPosition + slide( reflectDir, hitNormal ) * moveLength;
}
btVector3 btKinematicCharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove )
{
// We go to !
//
btVector3 targetPosition = currentPosition + walkMove;
// Retrieve the collision shape
//
btCollisionShape* collisionShape = externalGhostObject->getCollisionShape();
btAssert( collisionShape->isConvex() );
btConvexShape* convexShape = ( btConvexShape* )collisionShape;
btTransform start;
start.setIdentity();
btTransform end;
end.setIdentity();
btScalar fraction = btScalar( 1.0 );
// This optimization scheme suffers in the corners.
// It basically jumps from a wall to another, then fails to find a new
// position (after 4 iterations here) and finally don't move at all.
//
// The stepping algorithm adds some problems with stairs. It seems
// the treads create some fake corner using capsules for collisions.
//
for( int i = 0; i < 4 && fraction > btScalar( 0.01 ); i++ )
{
start.setOrigin( currentPosition );
end.setOrigin( targetPosition );
btVector3 sweepDirNegative = currentPosition - targetPosition;
btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, sweepDirNegative, btScalar( 0.0 ) );
callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
if( m_useGhostObjectSweepTest )
externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
else
collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
if( callback.hasHit() )
{
// Try another target position
//
targetPosition = slideOnCollision( currentPosition, targetPosition, callback.m_hitNormalWorld );
fraction = callback.m_closestHitFraction;
}
else
// Move to the valid target position
//
return targetPosition;
}
// Don't move if you can't find a valid target position...
// It prevents some flickering.
//
return currentPosition;
}
///Handle the gravity
btScalar btKinematicCharacterController::addFallOffset( bool wasOnGround, btScalar currentStepOffset, btScalar dt )
{
btScalar downVelocity = ( m_verticalVelocity < 0.0 ? -m_verticalVelocity : btScalar( 0.0 ) ) * dt;
if( downVelocity > btScalar( 0.0 ) && downVelocity < m_stepHeight && ( wasOnGround || !m_wasJumping ) )
downVelocity = m_stepHeight;
return currentStepOffset + downVelocity;
}
btVector3 btKinematicCharacterController::stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset )
{
btVector3 stepDrop = getUpAxisDirections()[ m_upAxis ] * currentStepOffset;
// Be sure we are falling from the last m_currentPosition
// It prevents some flickering
//
btVector3 targetPosition = currentPosition - stepDrop;
btTransform start;
start.setIdentity();
start.setOrigin( currentPosition );
btTransform end;
end.setIdentity();
end.setOrigin( targetPosition );
btKinematicClosestNotMeConvexResultCallback callback( internalGhostObject, getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine );
callback.m_collisionFilterGroup = internalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
callback.m_collisionFilterMask = internalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
// Retrieve the collision shape
//
btCollisionShape* collisionShape = internalGhostObject->getCollisionShape();
btAssert( collisionShape->isConvex() );
btConvexShape* convexShape = ( btConvexShape* )collisionShape;
if( m_useGhostObjectSweepTest )
externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
else
collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
if( callback.hasHit() )
{
m_verticalVelocity = btScalar( 0.0 );
m_verticalOffset = btScalar( 0.0 );
m_wasJumping = false;
// We dropped a fraction of the height -> hit floor
//
return currentPosition.lerp( targetPosition, callback.m_closestHitFraction );
}
else
// We dropped the full height
//
return targetPosition;
}
void btKinematicCharacterController::setWalkDirection( const btVector3& walkDirection )
{
m_useWalkDirection = true;
m_walkDirection = walkDirection;
}
void btKinematicCharacterController::setVelocityForTimeInterval( const btVector3& velocity, btScalar timeInterval )
{
m_useWalkDirection = false;
m_walkDirection = velocity;
m_velocityTimeInterval = timeInterval;
}
void btKinematicCharacterController::reset()
{
}
void btKinematicCharacterController::warp( const btVector3& origin )
{
btTransform transform;
transform.setIdentity();
transform.setOrigin( -origin );
externalGhostObject->setWorldTransform( transform );
internalGhostObject->setWorldTransform( transform );
}
void btKinematicCharacterController::preStep( btCollisionWorld* collisionWorld )
{
BT_PROFILE( "preStep" );
for( int i = 0; i < 4 && recoverFromPenetration ( collisionWorld ); i++ );
}
void btKinematicCharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt )
{
BT_PROFILE( "playerStep" );
if( !m_useWalkDirection && m_velocityTimeInterval <= btScalar( 0.0 ) )
return;
bool wasOnGround = onGround();
// Handle the gravity
//
m_verticalVelocity -= m_gravity * dt;
if( m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed )
m_verticalVelocity = m_jumpSpeed;
if( m_verticalVelocity < 0.0 && btFabs( m_verticalVelocity ) > btFabs( m_fallSpeed ) )
m_verticalVelocity = -btFabs( m_fallSpeed );
m_verticalOffset = m_verticalVelocity * dt;
// This forced stepping up can cause problems when the character
// walks (jump in fact...) under too low ceilings.
//
btVector3 currentPosition = externalGhostObject->getWorldTransform().getOrigin();
btScalar currentStepOffset;
currentPosition = stepUp( collisionWorld, currentPosition, currentStepOffset );
// Move in the air and slide against the walls ignoring the stair steps.
//
if( m_useWalkDirection )
currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, m_walkDirection );
else
{
btScalar dtMoving = ( dt < m_velocityTimeInterval ) ? dt : m_velocityTimeInterval;
m_velocityTimeInterval -= dt;
// How far will we move while we are moving ?
//
btVector3 moveDirection = m_walkDirection * dtMoving;
currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, moveDirection );
}
// Finally find the ground.
//
currentStepOffset = addFallOffset( wasOnGround, currentStepOffset, dt );
currentPosition = stepDown( collisionWorld, currentPosition, currentStepOffset );
// Apply the new position to the collision objects.
//
btTransform tranform;
tranform = externalGhostObject->getWorldTransform();
tranform.setOrigin( currentPosition );
externalGhostObject->setWorldTransform( tranform );
internalGhostObject->setWorldTransform( tranform );
}
void btKinematicCharacterController::setFallSpeed( btScalar fallSpeed )
{
m_fallSpeed = fallSpeed;
}
void btKinematicCharacterController::setJumpSpeed( btScalar jumpSpeed )
{
m_jumpSpeed = jumpSpeed;
}
void btKinematicCharacterController::setMaxJumpHeight( btScalar maxJumpHeight )
{
m_maxJumpHeight = maxJumpHeight;
}
bool btKinematicCharacterController::canJump() const
{
return onGround();
}
void btKinematicCharacterController::jump()
{
if( !canJump() )
return;
m_verticalVelocity = m_jumpSpeed;
m_wasJumping = true;
}
void btKinematicCharacterController::setGravity( btScalar gravity )
{
m_gravity = gravity;
}
btScalar btKinematicCharacterController::getGravity() const
{
return m_gravity;
}
void btKinematicCharacterController::setMaxSlope( btScalar slopeRadians )
{
m_maxSlopeRadians = slopeRadians;
m_maxSlopeCosine = btCos( slopeRadians );
}
btScalar btKinematicCharacterController::getMaxSlope() const
{
return m_maxSlopeRadians;
}
bool btKinematicCharacterController::onGround() const
{
return btFabs( m_verticalVelocity ) < btScalar( SIMD_EPSILON ) &&
btFabs( m_verticalOffset ) < btScalar( SIMD_EPSILON );
}
btVector3* btKinematicCharacterController::getUpAxisDirections()
{
static btVector3 sUpAxisDirection[] =
{
btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 0.0 ) ),
btVector3( btScalar( 0.0 ), btScalar( 1.0 ), btScalar( 0.0 ) ),
btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 1.0 ) )
};
return sUpAxisDirection;
}
void btKinematicCharacterController::debugDraw( btIDebugDraw* debugDrawer )
{
}

View file

@ -0,0 +1,164 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef KINEMATIC_CHARACTER_CONTROLLER_H
#define KINEMATIC_CHARACTER_CONTROLLER_H
#include "LinearMath/btVector3.h"
#include "LinearMath\btQuickprof.h"
#include "BulletDynamics\Character\btCharacterControllerInterface.h"
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
class btCollisionShape;
class btRigidBody;
class btCollisionWorld;
class btCollisionDispatcher;
class btPairCachingGhostObject;
///btKinematicCharacterController is an object that supports a sliding motion in a world.
///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations.
///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user.
class btKinematicCharacterController : public btCharacterControllerInterface
{
public:
enum UpAxis
{
X_AXIS = 0,
Y_AXIS = 1,
Z_AXIS = 2
};
private:
btPairCachingGhostObject* externalGhostObject; // use this for querying collisions for sliding and move
btPairCachingGhostObject* internalGhostObject; // and this for recoreving from penetrations
btScalar m_verticalVelocity;
btScalar m_verticalOffset;
btScalar m_fallSpeed;
btScalar m_jumpSpeed;
btScalar m_maxJumpHeight;
btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value)
btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization)
btScalar m_gravity;
btScalar m_recoveringFactor;
btScalar m_stepHeight;
///this is the desired walk direction, set by the user
btVector3 m_walkDirection;
///keep track of the contact manifolds
btManifoldArray m_manifoldArray;
///Gravity attributes
bool m_wasJumping;
bool m_useGhostObjectSweepTest;
bool m_useWalkDirection;
btScalar m_velocityTimeInterval;
UpAxis m_upAxis;
static btVector3* getUpAxisDirections();
bool recoverFromPenetration ( btCollisionWorld* collisionWorld );
btVector3 stepUp( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar& currentStepOffset );
btVector3 stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove );
btScalar addFallOffset( bool wasJumping, btScalar currentStepOffset, btScalar dt );
btVector3 stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset );
public:
/// externalGhostObject is used for querying the collisions for sliding along the wall,
/// and internalGhostObject is used for querying the collisions for recovering from large penetrations.
/// These parameters can point on the same object.
/// Using a smaller internalGhostObject can help for removing some flickering but create some
/// stopping artefacts when sliding along stairs or small walls.
/// Don't forget to scale gravity and fallSpeed if you scale the world.
btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject,
btPairCachingGhostObject* internalGhostObject,
btScalar stepHeight,
btScalar constantScale = btScalar( 1.0 ),
btScalar gravity = btScalar( 9.8 ),
btScalar fallVelocity = btScalar( 55.0 ),
btScalar jumpVelocity = btScalar( 9.8 ),
btScalar recoveringFactor = btScalar( 0.2 ) );
~btKinematicCharacterController ();
///btActionInterface interface
virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime )
{
preStep( collisionWorld );
playerStep( collisionWorld, deltaTime );
}
///btActionInterface interface
void debugDraw( btIDebugDraw* debugDrawer );
void setUpAxis( UpAxis axis )
{
m_upAxis = axis;
}
/// This should probably be called setPositionIncrementPerSimulatorStep.
/// This is neither a direction nor a velocity, but the amount to
/// increment the position each simulation iteration, regardless
/// of dt.
/// This call will reset any velocity set by setVelocityForTimeInterval().
virtual void setWalkDirection(const btVector3& walkDirection);
/// Caller provides a velocity with which the character should move for
/// the given time period. After the time period, velocity is reset
/// to zero.
/// This call will reset any walk direction set by setWalkDirection().
/// Negative time intervals will result in no motion.
virtual void setVelocityForTimeInterval(const btVector3& velocity,
btScalar timeInterval);
void reset();
void warp( const btVector3& origin );
void preStep( btCollisionWorld* collisionWorld );
void playerStep( btCollisionWorld* collisionWorld, btScalar dt );
void setFallSpeed( btScalar fallSpeed );
void setJumpSpeed( btScalar jumpSpeed );
void setMaxJumpHeight( btScalar maxJumpHeight );
bool canJump() const;
void jump();
void setGravity( btScalar gravity );
btScalar getGravity() const;
/// The max slope determines the maximum angle that the controller can walk up.
/// The slope angle is measured in radians.
void setMaxSlope( btScalar slopeRadians );
btScalar getMaxSlope() const;
void setUseGhostSweepTest( bool useGhostObjectSweepTest )
{
m_useGhostObjectSweepTest = useGhostObjectSweepTest;
}
bool onGround() const;
};
#endif // KINEMATIC_CHARACTER_CONTROLLER_H

275
bullet/physic.cpp Normal file
View file

@ -0,0 +1,275 @@
#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <components\nifbullet\bullet_nif_loader.hpp>
//#include <apps\openmw\mwworld\world.hpp>
#include "CMotionState.h"
#include "OgreRoot.h"
#include "btKinematicCharacterController.h"
#include "BtOgrePG.h"
#include "BtOgreGP.h"
#include "BtOgreExtras.h"
#define BIT(x) (1<<(x))
namespace OEngine {
namespace Physic
{
enum collisiontypes {
COL_NOTHING = 0, //<Collide with nothing
COL_WORLD = BIT(0), //<Collide with world objects
COL_ACTOR_INTERNAL = BIT(1), //<Collide internal capsule
COL_ACTOR_EXTERNAL = BIT(2) //<collide with external capsule
};
PhysicActor::PhysicActor(std::string name)
{
mName = name;
// The capsule is at the origin
btTransform transform;
transform.setIdentity();
// External capsule
externalGhostObject = new btPairCachingGhostObject();
externalGhostObject->setWorldTransform( transform );
btScalar externalCapsuleHeight = 50;
btScalar externalCapsuleWidth = 20;
externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight );
externalCollisionShape->setMargin( 1 );
externalGhostObject->setCollisionShape( externalCollisionShape );
externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT );
// Internal capsule
internalGhostObject = new btPairCachingGhostObject();
internalGhostObject->setWorldTransform( transform );
//internalGhostObject->getBroadphaseHandle()->s
btScalar internalCapsuleHeight = 20;
btScalar internalCapsuleWidth = 5;
internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight );
internalCollisionShape->setMargin( 1 );
internalGhostObject->setCollisionShape( internalCollisionShape );
internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT );
mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 0.4 ),1,0 );
mCharacter->setUpAxis(btKinematicCharacterController::UpAxis::Z_AXIS);
}
PhysicActor::~PhysicActor()
{
delete mCharacter;
delete internalGhostObject;
delete internalCollisionShape;
delete externalGhostObject;
delete externalCollisionShape;
}
void PhysicActor::setWalkDirection(btVector3& mvt)
{
mCharacter->setWalkDirection( mvt );
}
void PhysicActor::Rotate(btQuaternion& quat)
{
externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat );
internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat );
}
void PhysicActor::setRotation(btQuaternion& quat)
{
externalGhostObject->getWorldTransform().setRotation( quat );
internalGhostObject->getWorldTransform().setRotation( quat );
}
btVector3 PhysicActor::getPosition(void)
{
return internalGhostObject->getWorldTransform().getOrigin();
}
btQuaternion PhysicActor::getRotation(void)
{
return internalGhostObject->getWorldTransform().getRotation();
}
void PhysicActor::setPosition(btVector3& pos)
{
internalGhostObject->getWorldTransform().setOrigin(pos);
externalGhostObject->getWorldTransform().setOrigin(pos);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name)
:btRigidBody(CI),mName(name)
{
};
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
PhysicEngine::PhysicEngine()
{
// Set up the collision configuration and dispatcher
collisionConfiguration = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfiguration);
// The actual physics solver
solver = new btSequentialImpulseConstraintSolver;
//TODO: memory leak?
btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache();
pairCache->setInternalGhostPairCallback( new btGhostPairCallback() );
broadphase = new btDbvtBroadphase(pairCache);
// The world.
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,0,-10));
if(BulletShapeManager::getSingletonPtr() == NULL)
{
new BulletShapeManager();
}
//TODO:singleton?
ShapeLoader = new ManualBulletShapeLoader();
isDebugCreated = false;
}
void PhysicEngine::createDebugRendering()
{
if(!isDebugCreated)
{
Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator();
iter.begin();
Ogre::SceneManager* scn = iter.getNext();
Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode();
node->pitch(Ogre::Degree(-90));
mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer);
isDebugCreated = true;
dynamicsWorld->debugDrawWorld();
}
}
void PhysicEngine::setDebugRenderingMode(int mode)
{
if(!isDebugCreated)
{
createDebugRendering();
}
mDebugDrawer->setDebugMode(mode);
}
PhysicEngine::~PhysicEngine()
{
delete dynamicsWorld;
delete solver;
delete collisionConfiguration;
delete dispatcher;
delete broadphase;
delete ShapeLoader;
}
RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name)
{
//get the shape from the .nif
ShapeLoader->load(mesh,"General");
BulletShapeManager::getSingletonPtr()->load(mesh,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General");
//create the motionState
CMotionState* newMotionState = new CMotionState(this,name);
//create the real body
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape);
RigidBody* body = new RigidBody(CI,name);
return body;
}
void PhysicEngine::addRigidBody(RigidBody* body)
{
dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL);
body->setActivationState(DISABLE_DEACTIVATION);
RigidBodyMap[body->mName] = body;
}
void PhysicEngine::removeRigidBody(std::string name)
{
RigidBody* body = RigidBodyMap[name];
if(body != NULL)
{
dynamicsWorld->removeRigidBody(RigidBodyMap[name]);
}
}
void PhysicEngine::deleteRigidBody(std::string name)
{
RigidBody* body = RigidBodyMap[name];
if(body != NULL)
{
delete body;
RigidBodyMap[name] = NULL;
}
}
RigidBody* PhysicEngine::getRigidBody(std::string name)
{
RigidBody* body = RigidBodyMap[name];
return body;
}
void PhysicEngine::stepSimulation(double deltaT)
{
dynamicsWorld->stepSimulation(deltaT,1,1/30.);
if(isDebugCreated)
{
mDebugDrawer->step();
}
}
void PhysicEngine::addCharacter(std::string name)
{
PhysicActor* newActor = new PhysicActor(name);
dynamicsWorld->addCollisionObject( newActor->externalGhostObject, COL_ACTOR_EXTERNAL, COL_WORLD |COL_ACTOR_EXTERNAL );
dynamicsWorld->addCollisionObject( newActor->internalGhostObject, COL_ACTOR_INTERNAL, COL_WORLD |COL_ACTOR_INTERNAL );
dynamicsWorld->addAction( newActor->mCharacter );
PhysicActorMap[name] = newActor;
}
void PhysicEngine::removeCharacter(std::string name)
{
PhysicActor* act = PhysicActorMap[name];
if(act != NULL)
{
dynamicsWorld->removeCollisionObject(act->externalGhostObject);
dynamicsWorld->removeCollisionObject(act->internalGhostObject);
dynamicsWorld->removeAction(act->mCharacter);
delete act;
PhysicActorMap[name] = NULL;
}
}
PhysicActor* PhysicEngine::getCharacter(std::string name)
{
PhysicActor* act = PhysicActorMap[name];
return act;
}
void PhysicEngine::emptyEventLists(void)
{
}
}};

190
bullet/physic.hpp Normal file
View file

@ -0,0 +1,190 @@
#ifndef OENGINE_BULLET_PHYSIC_H
#define OENGINE_BULLET_PHYSIC_H
#include <BulletDynamics\Dynamics\btRigidBody.h>
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include <string>
#include <list>
#include <map>
class btRigidBody;
class btBroadphaseInterface;
class btDefaultCollisionConfiguration;
class btSequentialImpulseConstraintSolver;
class btCollisionDispatcher;
class btDiscreteDynamicsWorld;
class btKinematicCharacterController;
namespace BtOgre
{
class DebugDrawer;
}
class BulletShapeManager;
class ManualBulletShapeLoader;
namespace MWWorld
{
class World;
}
namespace OEngine {
namespace Physic
{
class CMotionState;
struct PhysicEvent;
/**
*A physic Actor use a modifed KinematicCharacterController taken in the bullet forum.
*/
class PhysicActor
{
public:
PhysicActor(std::string name);
~PhysicActor();
/**
*This function set the walkDirection. This is not relative to the actor orientation.
*I think it's also needed to take time into account. A typical call should look like this:
*setWalkDirection( mvt * orientation * dt)
*/
void setWalkDirection(btVector3& mvt);
void Rotate(btQuaternion& quat);
void setRotation(btQuaternion& quat);
btVector3 getPosition(void);
btQuaternion getRotation(void);
void setPosition(btVector3& pos);
btKinematicCharacterController* mCharacter;
btPairCachingGhostObject* internalGhostObject;
btCollisionShape* internalCollisionShape;
btPairCachingGhostObject* externalGhostObject;
btCollisionShape* externalCollisionShape;
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);
std::string mName;
};
/**
*The PhysicEngine class contain everything which is needed for Physic.
*It's needed that Ogre Resources are set up before the PhysicEngine is created.
*Note:deleting it WILL NOT delete the RigidBody!
*TODO:unload unused resources?
*/
class PhysicEngine
{
public:
PhysicEngine();
~PhysicEngine();
/**
*create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map,
*so you can get it with the getRigidBody function.
*/
RigidBody* createRigidBody(std::string mesh,std::string name);
/**
*Add a RigidBody to the simulation
*/
void addRigidBody(RigidBody* body);
/**
*Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap.
*/
void removeRigidBody(std::string name);
/**
*delete a RigidBody, and remove it from RigidBodyMap.
*/
void deleteRigidBody(std::string name);
/**
*Return a pointer to a given rigid body.
*TODO:check if exist
*/
RigidBody* getRigidBody(std::string name);
/**
*Create and add a character to the scene, and add it to the ActorMap.
*/
void addCharacter(std::string name);
/**
*Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done?
*/
void removeCharacter(std::string name);
/**
*return a pointer to a character
*TODO:check if the actor exist...
*/
PhysicActor* getCharacter(std::string name);
/**
*This step the simulation of a given time.
*/
void stepSimulation(double deltaT);
/**
*Empty events lists
*/
void emptyEventLists(void);
/**
*Create a debug rendering. It is called by setDebgRenderingMode if it's not created yet.
*Important Note: this will crash if the Render is not yet initialise!
*/
void createDebugRendering();
/**
*Set the debug rendering mode. 0 to turn it off.
*Important Note: this will crash if the Render is not yet initialise!
*/
void setDebugRenderingMode(int mode);
//event list of non player object
std::list<PhysicEvent> NPEventList;
//event list affecting the player
std::list<PhysicEvent> PEventList;
//Bullet Stuff
btBroadphaseInterface* broadphase;
btDefaultCollisionConfiguration* collisionConfiguration;
btSequentialImpulseConstraintSolver* solver;
btCollisionDispatcher* dispatcher;
btDiscreteDynamicsWorld* dynamicsWorld;
//the NIF file loader.
ManualBulletShapeLoader* ShapeLoader;
std::map<std::string,RigidBody*> RigidBodyMap;
std::map<std::string,PhysicActor*> PhysicActorMap;
//debug rendering
BtOgre::DebugDrawer* mDebugDrawer;
bool isDebugCreated;
};
}}
#endif