mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 08:56:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			643 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			643 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
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"
 | 
						|
 | 
						|
///@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 ) );
 | 
						|
 | 
						|
    mCollision = true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
btKinematicCharacterController::~btKinematicCharacterController ()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void btKinematicCharacterController::setVerticalVelocity(float z)
 | 
						|
{
 | 
						|
    m_verticalVelocity = z;
 | 
						|
}
 | 
						|
 | 
						|
bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld )
 | 
						|
{
 | 
						|
    bool penetration = false;
 | 
						|
 | 
						|
    if(!mCollision) return penetration;
 | 
						|
 | 
						|
    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 && 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;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // ???
 | 
						|
            //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 ) );
 | 
						|
 | 
						|
    //if the no collisions mode is on, no need to go any further
 | 
						|
    if(!mCollision)
 | 
						|
    {
 | 
						|
        currentStepOffset = m_stepHeight;
 | 
						|
        return targetPosition;
 | 
						|
    }
 | 
						|
 | 
						|
    // 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;
 | 
						|
 | 
						|
    //if the no collisions mode is on, no need to go any further
 | 
						|
    if(!mCollision) return targetPosition;
 | 
						|
 | 
						|
    // 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;
 | 
						|
 | 
						|
    //if the no collisions mode is on, no need to go any further
 | 
						|
    if(!mCollision) return targetPosition;
 | 
						|
 | 
						|
    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 )
 | 
						|
{
 | 
						|
}
 |