From adc1fa8e2ce1303a3fd612da77045cdcecf53587 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 22 Feb 2011 14:02:50 +0100 Subject: [PATCH] add physic support --- bullet/CMotionState.cpp | 45 ++ bullet/CMotionState.h | 52 ++ bullet/btKinematicCharacterController.cpp | 629 ++++++++++++++++++++++ bullet/btKinematicCharacterController.h | 164 ++++++ bullet/physic.cpp | 275 ++++++++++ bullet/physic.hpp | 190 +++++++ 6 files changed, 1355 insertions(+) create mode 100644 bullet/CMotionState.cpp create mode 100644 bullet/CMotionState.h create mode 100644 bullet/btKinematicCharacterController.cpp create mode 100644 bullet/btKinematicCharacterController.h create mode 100644 bullet/physic.cpp create mode 100644 bullet/physic.hpp diff --git a/bullet/CMotionState.cpp b/bullet/CMotionState.cpp new file mode 100644 index 0000000000..9cb662caa4 --- /dev/null +++ b/bullet/CMotionState.cpp @@ -0,0 +1,45 @@ +#include "CMotionState.h" +#include "physic.hpp" + +#include +#include +#include +//#include + +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); + } + } + +}} \ No newline at end of file diff --git a/bullet/CMotionState.h b/bullet/CMotionState.h new file mode 100644 index 0000000000..9ccc35adbb --- /dev/null +++ b/bullet/CMotionState.h @@ -0,0 +1,52 @@ +#ifndef OENGINE_CMOTIONSTATE_H +#define OENGINE_CMOTIONSTATE_H + +#include +#include + +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 \ No newline at end of file diff --git a/bullet/btKinematicCharacterController.cpp b/bullet/btKinematicCharacterController.cpp new file mode 100644 index 0000000000..8c81d5a42c --- /dev/null +++ b/bullet/btKinematicCharacterController.cpp @@ -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 +///@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 ) +{ +} diff --git a/bullet/btKinematicCharacterController.h b/bullet/btKinematicCharacterController.h new file mode 100644 index 0000000000..8f32b32e98 --- /dev/null +++ b/bullet/btKinematicCharacterController.h @@ -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 diff --git a/bullet/physic.cpp b/bullet/physic.cpp new file mode 100644 index 0000000000..d5c504cfdd --- /dev/null +++ b/bullet/physic.cpp @@ -0,0 +1,275 @@ +#include "physic.hpp" +#include +#include +#include +//#include +#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, //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) + { + } +}}; \ No newline at end of file diff --git a/bullet/physic.hpp b/bullet/physic.hpp new file mode 100644 index 0000000000..5fdc03e7d3 --- /dev/null +++ b/bullet/physic.hpp @@ -0,0 +1,190 @@ +#ifndef OENGINE_BULLET_PHYSIC_H +#define OENGINE_BULLET_PHYSIC_H + +#include +#include "BulletCollision/CollisionDispatch/btGhostObject.h" +#include +#include +#include + +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 NPEventList; + + //event list affecting the player + std::list PEventList; + + //Bullet Stuff + btBroadphaseInterface* broadphase; + btDefaultCollisionConfiguration* collisionConfiguration; + btSequentialImpulseConstraintSolver* solver; + btCollisionDispatcher* dispatcher; + btDiscreteDynamicsWorld* dynamicsWorld; + + //the NIF file loader. + ManualBulletShapeLoader* ShapeLoader; + + std::map RigidBodyMap; + std::map PhysicActorMap; + + //debug rendering + BtOgre::DebugDrawer* mDebugDrawer; + bool isDebugCreated; + }; + +}} + +#endif \ No newline at end of file