1001 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			1001 lines
		
	
	
		
			31 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 <stdio.h>
 | ||
|  | #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"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // static helper method
 | ||
|  | static btVector3 | ||
|  | getNormalizedVector(const btVector3& v) | ||
|  | { | ||
|  | 	btVector3 n(0, 0, 0); | ||
|  | 
 | ||
|  | 	if (v.length() > SIMD_EPSILON) { | ||
|  | 		n = v.normalized(); | ||
|  | 	} | ||
|  | 	return n; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | ///@todo Interact with dynamic objects,
 | ||
|  | ///Ride kinematicly animated platforms properly
 | ||
|  | ///More realistic (or maybe just a config option) falling
 | ||
|  | /// -> Should integrate falling velocity manually and use that in stepDown()
 | ||
|  | ///Support jumping
 | ||
|  | ///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 = me; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) | ||
|  | 	{ | ||
|  | 		if (rayResult.m_collisionObject == m_me) | ||
|  | 			return 1.0; | ||
|  | 
 | ||
|  | 		return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); | ||
|  | 	} | ||
|  | protected: | ||
|  | 	btCollisionObject* m_me; | ||
|  | }; | ||
|  | 
 | ||
|  | 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.0); | ||
|  | 
 | ||
|  | 		if (!convexResult.m_hitCollisionObject->hasContactResponse()) | ||
|  | 			return btScalar(1.0); | ||
|  | 
 | ||
|  | 		btVector3 hitNormalWorld; | ||
|  | 		if (normalInWorldSpace) | ||
|  | 		{ | ||
|  | 			hitNormalWorld = convexResult.m_hitNormalLocal; | ||
|  | 		} else | ||
|  | 		{ | ||
|  | 			///need to transform normal into worldspace
 | ||
|  | 			hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		btScalar dotUp = m_up.dot(hitNormalWorld); | ||
|  | 		if (dotUp < m_minSlopeDot) { | ||
|  | 			return btScalar(1.0); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace); | ||
|  | 	} | ||
|  | protected: | ||
|  | 	btCollisionObject* m_me; | ||
|  | 	const btVector3 m_up; | ||
|  | 	btScalar m_minSlopeDot; | ||
|  | }; | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal' | ||
|  |  * | ||
|  |  * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html
 | ||
|  |  */ | ||
|  | btVector3 btKinematicCharacterController::computeReflectionDirection (const btVector3& direction, const btVector3& normal) | ||
|  | { | ||
|  | 	return direction - (btScalar(2.0) * direction.dot(normal)) * normal; | ||
|  | } | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * Returns the portion of 'direction' that is parallel to 'normal' | ||
|  |  */ | ||
|  | btVector3 btKinematicCharacterController::parallelComponent (const btVector3& direction, const btVector3& normal) | ||
|  | { | ||
|  | 	btScalar magnitude = direction.dot(normal); | ||
|  | 	return normal * magnitude; | ||
|  | } | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * Returns the portion of 'direction' that is perpindicular to 'normal' | ||
|  |  */ | ||
|  | btVector3 btKinematicCharacterController::perpindicularComponent (const btVector3& direction, const btVector3& normal) | ||
|  | { | ||
|  | 	return direction - parallelComponent(direction, normal); | ||
|  | } | ||
|  | 
 | ||
|  | btKinematicCharacterController::btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, const btVector3& up) | ||
|  | { | ||
|  | 	m_ghostObject = ghostObject; | ||
|  | 	m_up.setValue(0.0f, 0.0f, 1.0f); | ||
|  | 	m_jumpAxis.setValue(0.0f, 0.0f, 1.0f); | ||
|  | 	m_addedMargin = 0.02; | ||
|  | 	m_walkDirection.setValue(0.0,0.0,0.0); | ||
|  | 	m_AngVel.setValue(0.0, 0.0, 0.0); | ||
|  | 	m_useGhostObjectSweepTest = true;	 | ||
|  | 	m_turnAngle = btScalar(0.0); | ||
|  | 	m_convexShape=convexShape;	 | ||
|  | 	m_useWalkDirection = true;	// use walk direction by default, legacy behavior
 | ||
|  | 	m_velocityTimeInterval = 0.0; | ||
|  | 	m_verticalVelocity = 0.0; | ||
|  | 	m_verticalOffset = 0.0; | ||
|  | 	m_gravity = 9.8 * 3.0 ; // 3G acceleration.
 | ||
|  | 	m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s.
 | ||
|  | 	m_jumpSpeed = 10.0; // ?
 | ||
|  | 	m_SetjumpSpeed = m_jumpSpeed; | ||
|  | 	m_wasOnGround = false; | ||
|  | 	m_wasJumping = false; | ||
|  | 	m_interpolateUp = true; | ||
|  | 	m_currentStepOffset = 0.0; | ||
|  | 	m_maxPenetrationDepth = 0.2; | ||
|  | 	full_drop = false; | ||
|  | 	bounce_fix = false; | ||
|  | 	m_linearDamping = btScalar(0.0); | ||
|  | 	m_angularDamping = btScalar(0.0); | ||
|  | 
 | ||
|  | 	setUp(up); | ||
|  | 	setStepHeight(stepHeight); | ||
|  | 	setMaxSlope(btRadians(45.0)); | ||
|  | } | ||
|  | 
 | ||
|  | btKinematicCharacterController::~btKinematicCharacterController () | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | btPairCachingGhostObject* btKinematicCharacterController::getGhostObject() | ||
|  | { | ||
|  | 	return m_ghostObject; | ||
|  | } | ||
|  | 
 | ||
|  | bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* collisionWorld) | ||
|  | { | ||
|  | 	// Here we must refresh the overlapping paircache as the penetrating movement itself or the
 | ||
|  | 	// previous recovery iteration might have used setWorldTransform and pushed us into an object
 | ||
|  | 	// that is not in the previous cache contents from the last timestep, as will happen if we
 | ||
|  | 	// are pushed into a new AABB overlap. Unhandled this means the next convex sweep gets stuck.
 | ||
|  | 	//
 | ||
|  | 	// Do this by calling the broadphase's setAabb with the moved AABB, this will update the broadphase
 | ||
|  | 	// paircache and the ghostobject's internal paircache at the same time.    /BW
 | ||
|  | 
 | ||
|  | 	btVector3 minAabb, maxAabb; | ||
|  | 	m_convexShape->getAabb(m_ghostObject->getWorldTransform(), minAabb,maxAabb); | ||
|  | 	collisionWorld->getBroadphase()->setAabb(m_ghostObject->getBroadphaseHandle(),  | ||
|  | 						 minAabb,  | ||
|  | 						 maxAabb,  | ||
|  | 						 collisionWorld->getDispatcher()); | ||
|  | 						  | ||
|  | 	bool penetration = false; | ||
|  | 
 | ||
|  | 	collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); | ||
|  | 
 | ||
|  | 	m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); | ||
|  | 	 | ||
|  | //	btScalar maxPen = btScalar(0.0);
 | ||
|  | 	for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++) | ||
|  | 	{ | ||
|  | 		m_manifoldArray.resize(0); | ||
|  | 
 | ||
|  | 		btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; | ||
|  | 
 | ||
|  | 		btCollisionObject* obj0 = static_cast<btCollisionObject*>(collisionPair->m_pProxy0->m_clientObject); | ||
|  |         btCollisionObject* obj1 = static_cast<btCollisionObject*>(collisionPair->m_pProxy1->m_clientObject); | ||
|  | 
 | ||
|  | 		if ((obj0 && !obj0->hasContactResponse()) || (obj1 && !obj1->hasContactResponse())) | ||
|  | 			continue; | ||
|  | 
 | ||
|  | 		if (!needsCollision(obj0, obj1)) | ||
|  | 			continue; | ||
|  | 		 | ||
|  | 		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() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0); | ||
|  | 			for (int p=0;p<manifold->getNumContacts();p++) | ||
|  | 			{ | ||
|  | 				const btManifoldPoint&pt = manifold->getContactPoint(p); | ||
|  | 
 | ||
|  | 				btScalar dist = pt.getDistance(); | ||
|  | 
 | ||
|  | 				if (dist < -m_maxPenetrationDepth) | ||
|  | 				{ | ||
|  | 					// TODO: cause problems on slopes, not sure if it is needed
 | ||
|  | 					//if (dist < maxPen)
 | ||
|  | 					//{
 | ||
|  | 					//	maxPen = dist;
 | ||
|  | 					//	m_touchingNormal = pt.m_normalWorldOnB * directionSign;//??
 | ||
|  | 
 | ||
|  | 					//}
 | ||
|  | 					m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2); | ||
|  | 					penetration = true; | ||
|  | 				} else { | ||
|  | 					//printf("touching %f\n", dist);
 | ||
|  | 				} | ||
|  | 			} | ||
|  | 			 | ||
|  | 			//manifold->clearManifold();
 | ||
|  | 		} | ||
|  | 	} | ||
|  | 	btTransform newTrans = m_ghostObject->getWorldTransform(); | ||
|  | 	newTrans.setOrigin(m_currentPosition); | ||
|  | 	m_ghostObject->setWorldTransform(newTrans); | ||
|  | //	printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]);
 | ||
|  | 	return penetration; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::stepUp ( btCollisionWorld* world) | ||
|  | { | ||
|  | 	btScalar stepHeight = 0.0f; | ||
|  | 	if (m_verticalVelocity < 0.0) | ||
|  | 		stepHeight = m_stepHeight; | ||
|  | 
 | ||
|  | 	// phase 1: up
 | ||
|  | 	btTransform start, end; | ||
|  | 
 | ||
|  | 	start.setIdentity (); | ||
|  | 	end.setIdentity (); | ||
|  | 
 | ||
|  | 	/* FIXME: Handle penetration properly */ | ||
|  | 	start.setOrigin(m_currentPosition); | ||
|  | 
 | ||
|  | 	m_targetPosition = m_currentPosition + m_up * (stepHeight) + m_jumpAxis * ((m_verticalOffset > 0.f ? m_verticalOffset : 0.f)); | ||
|  | 	m_currentPosition = m_targetPosition; | ||
|  | 
 | ||
|  | 	end.setOrigin (m_targetPosition); | ||
|  | 
 | ||
|  | 	start.setRotation(m_currentOrientation); | ||
|  | 	end.setRotation(m_targetOrientation); | ||
|  | 
 | ||
|  | 	btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, -m_up, m_maxSlopeCosine); | ||
|  | 	callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; | ||
|  | 	callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; | ||
|  | 	 | ||
|  | 	if (m_useGhostObjectSweepTest) | ||
|  | 	{ | ||
|  | 		m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 		world->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject)) | ||
|  | 	{ | ||
|  | 		// Only modify the position if the hit was a slope and not a wall or ceiling.
 | ||
|  | 		if (callback.m_hitNormalWorld.dot(m_up) > 0.0) | ||
|  | 		{ | ||
|  | 			// we moved up only a fraction of the step height
 | ||
|  | 			m_currentStepOffset = stepHeight * callback.m_closestHitFraction; | ||
|  | 			if (m_interpolateUp == true) | ||
|  | 				m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); | ||
|  | 			else | ||
|  | 				m_currentPosition = m_targetPosition; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		btTransform& xform = m_ghostObject->getWorldTransform(); | ||
|  | 		xform.setOrigin(m_currentPosition); | ||
|  | 		m_ghostObject->setWorldTransform(xform); | ||
|  | 
 | ||
|  | 		// fix penetration if we hit a ceiling for example
 | ||
|  | 		int numPenetrationLoops = 0; | ||
|  | 		m_touchingContact = false; | ||
|  | 		while (recoverFromPenetration(world)) | ||
|  | 		{ | ||
|  | 			numPenetrationLoops++; | ||
|  | 			m_touchingContact = true; | ||
|  | 			if (numPenetrationLoops > 4) | ||
|  | 			{ | ||
|  | 				//printf("character could not recover from penetration = %d\n", numPenetrationLoops);
 | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		m_targetPosition = m_ghostObject->getWorldTransform().getOrigin(); | ||
|  | 		m_currentPosition = m_targetPosition; | ||
|  | 
 | ||
|  | 		if (m_verticalOffset > 0) | ||
|  | 		{ | ||
|  | 			m_verticalOffset = 0.0; | ||
|  | 			m_verticalVelocity = 0.0; | ||
|  | 			m_currentStepOffset = m_stepHeight; | ||
|  | 		} | ||
|  | 	} else { | ||
|  | 		m_currentStepOffset = stepHeight; | ||
|  | 		m_currentPosition = m_targetPosition; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | bool btKinematicCharacterController::needsCollision(const btCollisionObject* body0, const btCollisionObject* body1) | ||
|  | { | ||
|  | 	bool collides = (body0->getBroadphaseHandle()->m_collisionFilterGroup & body1->getBroadphaseHandle()->m_collisionFilterMask) != 0; | ||
|  | 	collides = collides && (body1->getBroadphaseHandle()->m_collisionFilterGroup & body0->getBroadphaseHandle()->m_collisionFilterMask); | ||
|  | 	return collides; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag) | ||
|  | { | ||
|  | 	btVector3 movementDirection = m_targetPosition - m_currentPosition; | ||
|  | 	btScalar movementLength = movementDirection.length(); | ||
|  | 	if (movementLength>SIMD_EPSILON) | ||
|  | 	{ | ||
|  | 		movementDirection.normalize(); | ||
|  | 
 | ||
|  | 		btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal); | ||
|  | 		reflectDir.normalize(); | ||
|  | 
 | ||
|  | 		btVector3 parallelDir, perpindicularDir; | ||
|  | 
 | ||
|  | 		parallelDir = parallelComponent (reflectDir, hitNormal); | ||
|  | 		perpindicularDir = perpindicularComponent (reflectDir, hitNormal); | ||
|  | 
 | ||
|  | 		m_targetPosition = m_currentPosition; | ||
|  | 		if (0)//tangentMag != 0.0)
 | ||
|  | 		{ | ||
|  | 			btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength); | ||
|  | //			printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]);
 | ||
|  | 			m_targetPosition +=  parComponent; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (normalMag != 0.0) | ||
|  | 		{ | ||
|  | 			btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength); | ||
|  | //			printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]);
 | ||
|  | 			m_targetPosition += perpComponent; | ||
|  | 		} | ||
|  | 	} else | ||
|  | 	{ | ||
|  | //		printf("movementLength don't normalize a zero vector\n");
 | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* collisionWorld, const btVector3& walkMove) | ||
|  | { | ||
|  | 	// printf("m_normalizedDirection=%f,%f,%f\n",
 | ||
|  | 	// 	m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]);
 | ||
|  | 	// phase 2: forward and strafe
 | ||
|  | 	btTransform start, end; | ||
|  | 
 | ||
|  | 	m_targetPosition = m_currentPosition + walkMove; | ||
|  | 
 | ||
|  | 	start.setIdentity (); | ||
|  | 	end.setIdentity (); | ||
|  | 	 | ||
|  | 	btScalar fraction = 1.0; | ||
|  | 	btScalar distance2 = (m_currentPosition-m_targetPosition).length2(); | ||
|  | //	printf("distance2=%f\n",distance2);
 | ||
|  | 
 | ||
|  | 	int maxIter = 10; | ||
|  | 
 | ||
|  | 	while (fraction > btScalar(0.01) && maxIter-- > 0) | ||
|  | 	{ | ||
|  | 		start.setOrigin (m_currentPosition); | ||
|  | 		end.setOrigin (m_targetPosition); | ||
|  | 		btVector3 sweepDirNegative(m_currentPosition - m_targetPosition); | ||
|  | 
 | ||
|  | 		start.setRotation(m_currentOrientation); | ||
|  | 		end.setRotation(m_targetOrientation); | ||
|  | 
 | ||
|  | 		btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, sweepDirNegative, btScalar(0.0)); | ||
|  | 		callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; | ||
|  | 		callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; | ||
|  | 
 | ||
|  | 
 | ||
|  | 		btScalar margin = m_convexShape->getMargin(); | ||
|  | 		m_convexShape->setMargin(margin + m_addedMargin); | ||
|  | 
 | ||
|  | 		if (!(start == end)) | ||
|  | 		{ | ||
|  | 			if (m_useGhostObjectSweepTest) | ||
|  | 			{ | ||
|  | 				m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		m_convexShape->setMargin(margin); | ||
|  | 
 | ||
|  | 		 | ||
|  | 		fraction -= callback.m_closestHitFraction; | ||
|  | 
 | ||
|  | 		if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject)) | ||
|  | 		{	 | ||
|  | 			// we moved only a fraction
 | ||
|  | 			//btScalar hitDistance;
 | ||
|  | 			//hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();
 | ||
|  | 
 | ||
|  | //			m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction);
 | ||
|  | 
 | ||
|  | 			updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld); | ||
|  | 			btVector3 currentDir = m_targetPosition - m_currentPosition; | ||
|  | 			distance2 = currentDir.length2(); | ||
|  | 			if (distance2 > SIMD_EPSILON) | ||
|  | 			{ | ||
|  | 				currentDir.normalize(); | ||
|  | 				/* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */ | ||
|  | 				if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0)) | ||
|  | 				{ | ||
|  | 					break; | ||
|  | 				} | ||
|  | 			} else | ||
|  | 			{ | ||
|  | //				printf("currentDir: don't normalize a zero vector\n");
 | ||
|  | 				break; | ||
|  | 			} | ||
|  | 
 | ||
|  | 		} | ||
|  |         else | ||
|  |         { | ||
|  |             m_currentPosition = m_targetPosition; | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt) | ||
|  | { | ||
|  | 	btTransform start, end, end_double; | ||
|  | 	bool runonce = false; | ||
|  | 
 | ||
|  | 	// phase 3: down
 | ||
|  | 	/*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0;
 | ||
|  | 	btVector3 step_drop = m_up * (m_currentStepOffset + additionalDownStep); | ||
|  | 	btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt; | ||
|  | 	btVector3 gravity_drop = m_up * downVelocity;  | ||
|  | 	m_targetPosition -= (step_drop + gravity_drop);*/ | ||
|  | 
 | ||
|  | 	btVector3 orig_position = m_targetPosition; | ||
|  | 	 | ||
|  | 	btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; | ||
|  | 
 | ||
|  | 	if (m_verticalVelocity > 0.0) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	if(downVelocity > 0.0 && downVelocity > m_fallSpeed | ||
|  | 		&& (m_wasOnGround || !m_wasJumping)) | ||
|  | 		downVelocity = m_fallSpeed; | ||
|  | 
 | ||
|  | 	btVector3 step_drop = m_up * (m_currentStepOffset + downVelocity); | ||
|  | 	m_targetPosition -= step_drop; | ||
|  | 
 | ||
|  | 	btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, m_up, m_maxSlopeCosine); | ||
|  |         callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; | ||
|  |         callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; | ||
|  | 
 | ||
|  | 	btKinematicClosestNotMeConvexResultCallback callback2(m_ghostObject, m_up, m_maxSlopeCosine); | ||
|  |         callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; | ||
|  |         callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; | ||
|  | 
 | ||
|  | 	while (1) | ||
|  | 	{ | ||
|  | 		start.setIdentity (); | ||
|  | 		end.setIdentity (); | ||
|  | 
 | ||
|  | 		end_double.setIdentity (); | ||
|  | 
 | ||
|  | 		start.setOrigin (m_currentPosition); | ||
|  | 		end.setOrigin (m_targetPosition); | ||
|  | 
 | ||
|  | 		start.setRotation(m_currentOrientation); | ||
|  | 		end.setRotation(m_targetOrientation); | ||
|  | 
 | ||
|  | 		//set double test for 2x the step drop, to check for a large drop vs small drop
 | ||
|  | 		end_double.setOrigin (m_targetPosition - step_drop); | ||
|  | 
 | ||
|  | 		if (m_useGhostObjectSweepTest) | ||
|  | 		{ | ||
|  | 			m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 
 | ||
|  | 			if (!callback.hasHit() && m_ghostObject->hasContactResponse()) | ||
|  | 			{ | ||
|  | 				//test a double fall height, to see if the character should interpolate it's fall (full) or not (partial)
 | ||
|  | 				m_ghostObject->convexSweepTest (m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 			} | ||
|  | 		} else | ||
|  | 		{ | ||
|  | 			collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 
 | ||
|  | 			if (!callback.hasHit() && m_ghostObject->hasContactResponse()) | ||
|  | 			{ | ||
|  | 				//test a double fall height, to see if the character should interpolate it's fall (large) or not (small)
 | ||
|  | 				collisionWorld->convexSweepTest (m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	 | ||
|  | 		btScalar downVelocity2 = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; | ||
|  | 		bool has_hit; | ||
|  | 		if (bounce_fix == true) | ||
|  | 			has_hit = (callback.hasHit() || callback2.hasHit()) && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject); | ||
|  | 		else | ||
|  | 			has_hit = callback2.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback2.m_hitCollisionObject); | ||
|  | 
 | ||
|  | 		btScalar stepHeight = 0.0f; | ||
|  | 		if (m_verticalVelocity < 0.0) | ||
|  | 			stepHeight = m_stepHeight; | ||
|  | 
 | ||
|  | 		if (downVelocity2 > 0.0 && downVelocity2 < stepHeight && has_hit == true && runonce == false | ||
|  | 					&& (m_wasOnGround || !m_wasJumping)) | ||
|  | 		{ | ||
|  | 			//redo the velocity calculation when falling a small amount, for fast stairs motion
 | ||
|  | 			//for larger falls, use the smoother/slower interpolated movement by not touching the target position
 | ||
|  | 
 | ||
|  | 			m_targetPosition = orig_position; | ||
|  | 			downVelocity = stepHeight; | ||
|  | 
 | ||
|  | 			step_drop = m_up * (m_currentStepOffset + downVelocity); | ||
|  | 			m_targetPosition -= step_drop; | ||
|  | 			runonce = true; | ||
|  | 			continue; //re-run previous tests
 | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ((m_ghostObject->hasContactResponse() && (callback.hasHit() && needsCollision(m_ghostObject, callback.m_hitCollisionObject))) || runonce == true) | ||
|  | 	{ | ||
|  | 		// we dropped a fraction of the height -> hit floor
 | ||
|  | 		btScalar fraction = (m_currentPosition.getY() - callback.m_hitPointWorld.getY()) / 2; | ||
|  | 
 | ||
|  | 		//printf("hitpoint: %g - pos %g\n", callback.m_hitPointWorld.getY(), m_currentPosition.getY());
 | ||
|  | 
 | ||
|  | 		if (bounce_fix == true) | ||
|  | 		{ | ||
|  | 			if (full_drop == true) | ||
|  | 				m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); | ||
|  |             else | ||
|  | 				//due to errors in the closestHitFraction variable when used with large polygons, calculate the hit fraction manually
 | ||
|  | 				m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, fraction); | ||
|  | 		} | ||
|  | 		else | ||
|  | 			m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); | ||
|  | 
 | ||
|  | 		full_drop = false; | ||
|  | 
 | ||
|  | 		m_verticalVelocity = 0.0; | ||
|  | 		m_verticalOffset = 0.0; | ||
|  | 		m_wasJumping = false; | ||
|  | 	} else { | ||
|  | 		// we dropped the full height
 | ||
|  | 
 | ||
|  | 		full_drop = true; | ||
|  | 
 | ||
|  | 		if (bounce_fix == true) | ||
|  | 		{ | ||
|  | 			downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; | ||
|  | 			if (downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping)) | ||
|  | 			{ | ||
|  | 				m_targetPosition += step_drop; //undo previous target change
 | ||
|  | 				downVelocity = m_fallSpeed; | ||
|  | 				step_drop = m_up * (m_currentStepOffset + downVelocity); | ||
|  | 				m_targetPosition -= step_drop; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		//printf("full drop - %g, %g\n", m_currentPosition.getY(), m_targetPosition.getY());
 | ||
|  | 
 | ||
|  | 		m_currentPosition = m_targetPosition; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setWalkDirection | ||
|  | ( | ||
|  | const btVector3& walkDirection | ||
|  | ) | ||
|  | { | ||
|  | 	m_useWalkDirection = true; | ||
|  | 	m_walkDirection = walkDirection; | ||
|  | 	m_normalizedDirection = getNormalizedVector(m_walkDirection); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setVelocityForTimeInterval | ||
|  | ( | ||
|  | const btVector3& velocity, | ||
|  | btScalar timeInterval | ||
|  | ) | ||
|  | { | ||
|  | //	printf("setVelocity!\n");
 | ||
|  | //	printf("  interval: %f\n", timeInterval);
 | ||
|  | //	printf("  velocity: (%f, %f, %f)\n",
 | ||
|  | //		 velocity.x(), velocity.y(), velocity.z());
 | ||
|  | 
 | ||
|  | 	m_useWalkDirection = false; | ||
|  | 	m_walkDirection = velocity; | ||
|  | 	m_normalizedDirection = getNormalizedVector(m_walkDirection); | ||
|  | 	m_velocityTimeInterval += timeInterval; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setAngularVelocity(const btVector3& velocity) | ||
|  | { | ||
|  | 	m_AngVel = velocity; | ||
|  | } | ||
|  | 
 | ||
|  | const btVector3& btKinematicCharacterController::getAngularVelocity() const | ||
|  | { | ||
|  | 	return m_AngVel; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setLinearVelocity(const btVector3& velocity) | ||
|  | { | ||
|  | 	m_walkDirection = velocity; | ||
|  | 
 | ||
|  | 	// HACK: if we are moving in the direction of the up, treat it as a jump :(
 | ||
|  | 	if (m_walkDirection.length2() > 0) | ||
|  | 	{ | ||
|  | 		btVector3 w = velocity.normalized(); | ||
|  | 		btScalar c = w.dot(m_up); | ||
|  | 		if (c != 0) | ||
|  | 		{ | ||
|  | 			//there is a component in walkdirection for vertical velocity
 | ||
|  | 			btVector3 upComponent = m_up * (btSin(SIMD_HALF_PI - btAcos(c)) * m_walkDirection.length()); | ||
|  | 			m_walkDirection -= upComponent; | ||
|  | 			m_verticalVelocity = (c < 0.0f ? -1 : 1) * upComponent.length(); | ||
|  | 			 | ||
|  | 			if (c > 0.0f) | ||
|  | 			{ | ||
|  | 				m_wasJumping = true; | ||
|  | 				m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin(); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else | ||
|  | 		m_verticalVelocity = 0.0f; | ||
|  | } | ||
|  | 
 | ||
|  | btVector3 btKinematicCharacterController::getLinearVelocity() const | ||
|  | { | ||
|  | 	return m_walkDirection + (m_verticalVelocity * m_up); | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::reset ( btCollisionWorld* collisionWorld ) | ||
|  | { | ||
|  |     m_verticalVelocity = 0.0; | ||
|  |     m_verticalOffset = 0.0; | ||
|  |     m_wasOnGround = false; | ||
|  |     m_wasJumping = false; | ||
|  |     m_walkDirection.setValue(0,0,0); | ||
|  |     m_velocityTimeInterval = 0.0; | ||
|  | 
 | ||
|  |     //clear pair cache
 | ||
|  |     btHashedOverlappingPairCache *cache = m_ghostObject->getOverlappingPairCache(); | ||
|  |     while (cache->getOverlappingPairArray().size() > 0) | ||
|  |     { | ||
|  |             cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher()); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::warp (const btVector3& origin) | ||
|  | { | ||
|  | 	btTransform xform; | ||
|  | 	xform.setIdentity(); | ||
|  | 	xform.setOrigin (origin); | ||
|  | 	m_ghostObject->setWorldTransform (xform); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void btKinematicCharacterController::preStep (  btCollisionWorld* collisionWorld) | ||
|  | { | ||
|  | 	m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); | ||
|  | 	m_targetPosition = m_currentPosition; | ||
|  | 
 | ||
|  | 	m_currentOrientation = m_ghostObject->getWorldTransform().getRotation(); | ||
|  | 	m_targetOrientation = m_currentOrientation; | ||
|  | //	printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]);
 | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::playerStep (  btCollisionWorld* collisionWorld, btScalar dt) | ||
|  | { | ||
|  | //	printf("playerStep(): ");
 | ||
|  | //	printf("  dt = %f", dt);
 | ||
|  | 
 | ||
|  | 	if (m_AngVel.length2() > 0.0f) | ||
|  | 	{ | ||
|  | 		m_AngVel *= btPow(btScalar(1) - m_angularDamping, dt); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// integrate for angular velocity
 | ||
|  | 	if (m_AngVel.length2() > 0.0f) | ||
|  | 	{ | ||
|  | 		btTransform xform; | ||
|  | 		xform = m_ghostObject->getWorldTransform(); | ||
|  | 
 | ||
|  | 		btQuaternion rot(m_AngVel.normalized(), m_AngVel.length() * dt); | ||
|  | 
 | ||
|  | 		btQuaternion orn = rot * xform.getRotation(); | ||
|  | 
 | ||
|  | 		xform.setRotation(orn); | ||
|  | 		m_ghostObject->setWorldTransform(xform); | ||
|  | 
 | ||
|  | 		m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); | ||
|  | 		m_targetPosition = m_currentPosition; | ||
|  | 		m_currentOrientation = m_ghostObject->getWorldTransform().getRotation(); | ||
|  | 		m_targetOrientation = m_currentOrientation; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// quick check...
 | ||
|  | 	if (!m_useWalkDirection && (m_velocityTimeInterval <= 0.0)) { | ||
|  | //		printf("\n");
 | ||
|  | 		return;		// no motion
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	m_wasOnGround = onGround(); | ||
|  | 
 | ||
|  | 	//btVector3 lvel = m_walkDirection;
 | ||
|  | 	//btScalar c = 0.0f;
 | ||
|  | 	 | ||
|  | 	if (m_walkDirection.length2() > 0) | ||
|  | 	{ | ||
|  | 		// apply damping
 | ||
|  | 		m_walkDirection *= btPow(btScalar(1) - m_linearDamping, dt); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	m_verticalVelocity *= btPow(btScalar(1) - m_linearDamping, dt); | ||
|  | 	 | ||
|  | 	// Update fall velocity.
 | ||
|  | 	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; | ||
|  | 
 | ||
|  | 	btTransform xform; | ||
|  | 	xform = m_ghostObject->getWorldTransform(); | ||
|  | 
 | ||
|  | //	printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]);
 | ||
|  | //	printf("walkSpeed=%f\n",walkSpeed);
 | ||
|  | 
 | ||
|  | 	stepUp(collisionWorld); | ||
|  | 	//todo: Experimenting with behavior of controller when it hits a ceiling..
 | ||
|  | 	//bool hitUp = stepUp (collisionWorld);	
 | ||
|  | 	//if (hitUp)
 | ||
|  | 	//{
 | ||
|  | 	//	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;
 | ||
|  | 
 | ||
|  | 	//	xform = m_ghostObject->getWorldTransform();
 | ||
|  | 	//}
 | ||
|  | 
 | ||
|  | 	if (m_useWalkDirection) { | ||
|  | 		stepForwardAndStrafe (collisionWorld, m_walkDirection); | ||
|  | 	} else { | ||
|  | 		//printf("  time: %f", m_velocityTimeInterval);
 | ||
|  | 		// still have some time left for moving!
 | ||
|  | 		btScalar dtMoving = | ||
|  | 			(dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval; | ||
|  | 		m_velocityTimeInterval -= dt; | ||
|  | 
 | ||
|  | 		// how far will we move while we are moving?
 | ||
|  | 		btVector3 move = m_walkDirection * dtMoving; | ||
|  | 
 | ||
|  | 		//printf("  dtMoving: %f", dtMoving);
 | ||
|  | 
 | ||
|  | 		// okay, step
 | ||
|  | 		stepForwardAndStrafe(collisionWorld, move); | ||
|  | 	} | ||
|  | 	stepDown (collisionWorld, dt); | ||
|  | 
 | ||
|  | 	//todo: Experimenting with max jump height
 | ||
|  | 	//if (m_wasJumping)
 | ||
|  | 	//{
 | ||
|  | 	//	btScalar ds = m_currentPosition[m_upAxis] - m_jumpPosition[m_upAxis];
 | ||
|  | 	//	if (ds > m_maxJumpHeight)
 | ||
|  | 	//	{
 | ||
|  | 	//		// substract the overshoot
 | ||
|  | 	//		m_currentPosition[m_upAxis] -= ds - m_maxJumpHeight;
 | ||
|  | 
 | ||
|  | 	//		// max height was reached, so potential energy is at max 
 | ||
|  | 	//		// and kinematic energy is 0, thus velocity is 0.
 | ||
|  | 	//		if (m_verticalVelocity > 0.0)
 | ||
|  | 	//			m_verticalVelocity = 0.0;
 | ||
|  | 	//	}
 | ||
|  | 	//}
 | ||
|  | 	// printf("\n");
 | ||
|  | 
 | ||
|  | 	xform.setOrigin (m_currentPosition); | ||
|  | 	m_ghostObject->setWorldTransform (xform); | ||
|  | 
 | ||
|  | 	int numPenetrationLoops = 0; | ||
|  | 	m_touchingContact = false; | ||
|  | 	while (recoverFromPenetration(collisionWorld)) | ||
|  | 	{ | ||
|  | 		numPenetrationLoops++; | ||
|  | 		m_touchingContact = true; | ||
|  | 		if (numPenetrationLoops > 4) | ||
|  | 		{ | ||
|  | 			//printf("character could not recover from penetration = %d\n", numPenetrationLoops);
 | ||
|  | 			break; | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setFallSpeed (btScalar fallSpeed) | ||
|  | { | ||
|  | 	m_fallSpeed = fallSpeed; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setJumpSpeed (btScalar jumpSpeed) | ||
|  | { | ||
|  | 	m_jumpSpeed = jumpSpeed; | ||
|  | 	m_SetjumpSpeed = m_jumpSpeed; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight) | ||
|  | { | ||
|  | 	m_maxJumpHeight = maxJumpHeight; | ||
|  | } | ||
|  | 
 | ||
|  | bool btKinematicCharacterController::canJump () const | ||
|  | { | ||
|  | 	return onGround(); | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::jump(const btVector3& v) | ||
|  | { | ||
|  | 	m_jumpSpeed = v.length2() == 0 ? m_SetjumpSpeed : v.length(); | ||
|  | 	m_verticalVelocity = m_jumpSpeed; | ||
|  | 	m_wasJumping = true; | ||
|  | 
 | ||
|  | 	m_jumpAxis = v.length2() == 0 ? m_up : v.normalized(); | ||
|  | 
 | ||
|  | 	m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin(); | ||
|  | 
 | ||
|  | #if 0
 | ||
|  | 	currently no jumping. | ||
|  | 	btTransform xform; | ||
|  | 	m_rigidBody->getMotionState()->getWorldTransform (xform); | ||
|  | 	btVector3 up = xform.getBasis()[1]; | ||
|  | 	up.normalize (); | ||
|  | 	btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0); | ||
|  | 	m_rigidBody->applyCentralImpulse (up * magnitude); | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setGravity(const btVector3& gravity) | ||
|  | { | ||
|  | 	if (gravity.length2() > 0) setUpVector(-gravity); | ||
|  | 
 | ||
|  | 	m_gravity = gravity.length(); | ||
|  | } | ||
|  | 
 | ||
|  | btVector3 btKinematicCharacterController::getGravity() const | ||
|  | { | ||
|  | 	return -m_gravity * m_up; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setMaxSlope(btScalar slopeRadians) | ||
|  | { | ||
|  | 	m_maxSlopeRadians = slopeRadians; | ||
|  | 	m_maxSlopeCosine = btCos(slopeRadians); | ||
|  | } | ||
|  | 
 | ||
|  | btScalar btKinematicCharacterController::getMaxSlope() const | ||
|  | { | ||
|  | 	return m_maxSlopeRadians; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setMaxPenetrationDepth(btScalar d) | ||
|  | { | ||
|  | 	m_maxPenetrationDepth = d; | ||
|  | } | ||
|  | 
 | ||
|  | btScalar btKinematicCharacterController::getMaxPenetrationDepth() const | ||
|  | { | ||
|  | 	return m_maxPenetrationDepth; | ||
|  | } | ||
|  | 
 | ||
|  | bool btKinematicCharacterController::onGround () const | ||
|  | { | ||
|  | 	return (fabs(m_verticalVelocity) < SIMD_EPSILON) && (fabs(m_verticalOffset) < SIMD_EPSILON); | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setStepHeight(btScalar h)  | ||
|  | { | ||
|  | 	m_stepHeight = h; | ||
|  | } | ||
|  | 
 | ||
|  | btVector3* btKinematicCharacterController::getUpAxisDirections() | ||
|  | { | ||
|  | 	static btVector3 sUpAxisDirection[3] = { btVector3(1.0f, 0.0f, 0.0f), btVector3(0.0f, 1.0f, 0.0f), btVector3(0.0f, 0.0f, 1.0f) }; | ||
|  | 	 | ||
|  | 	return sUpAxisDirection; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer) | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setUpInterpolate(bool value) | ||
|  | { | ||
|  | 	m_interpolateUp = value; | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setUp(const btVector3& up) | ||
|  | { | ||
|  | 	if (up.length2() > 0 && m_gravity > 0.0f) | ||
|  | 	{ | ||
|  | 		setGravity(-m_gravity * up.normalized()); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	setUpVector(up); | ||
|  | } | ||
|  | 
 | ||
|  | void btKinematicCharacterController::setUpVector(const btVector3& up) | ||
|  | { | ||
|  | 	if (m_up == up) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	btVector3 u = m_up; | ||
|  | 
 | ||
|  | 	if (up.length2() > 0) | ||
|  | 		m_up = up.normalized(); | ||
|  | 	else | ||
|  | 		m_up = btVector3(0.0, 0.0, 0.0); | ||
|  | 
 | ||
|  | 	if (!m_ghostObject) return; | ||
|  | 	btQuaternion rot = getRotation(m_up, u); | ||
|  | 
 | ||
|  | 	//set orientation with new up
 | ||
|  | 	btTransform xform; | ||
|  | 	xform = m_ghostObject->getWorldTransform(); | ||
|  | 	btQuaternion orn = rot.inverse() * xform.getRotation(); | ||
|  | 	xform.setRotation(orn); | ||
|  | 	m_ghostObject->setWorldTransform(xform); | ||
|  | } | ||
|  | 
 | ||
|  | btQuaternion btKinematicCharacterController::getRotation(btVector3& v0, btVector3& v1) const | ||
|  | { | ||
|  | 	if (v0.length2() == 0.0f || v1.length2() == 0.0f) | ||
|  | 	{ | ||
|  | 		btQuaternion q; | ||
|  | 		return q; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return shortestArcQuatNormalize2(v0, v1); | ||
|  | } | ||
|  | 
 |