773 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			773 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /*
 | ||
|  |  * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/
 | ||
|  |  * | ||
|  |  * Permission to use, copy, modify, distribute and sell this software | ||
|  |  * and its documentation for any purpose is hereby granted without fee, | ||
|  |  * provided that the above copyright notice appear in all copies. | ||
|  |  * Erwin Coumans makes no representations about the suitability  | ||
|  |  * of this software for any purpose.   | ||
|  |  * It is provided "as is" without express or implied warranty. | ||
|  | */ | ||
|  | 
 | ||
|  | #include "LinearMath/btVector3.h"
 | ||
|  | #include "btRaycastVehicle.h"
 | ||
|  | 
 | ||
|  | #include "BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h"
 | ||
|  | #include "BulletDynamics/ConstraintSolver/btJacobianEntry.h"
 | ||
|  | #include "LinearMath/btQuaternion.h"
 | ||
|  | #include "BulletDynamics/Dynamics/btDynamicsWorld.h"
 | ||
|  | #include "btVehicleRaycaster.h"
 | ||
|  | #include "btWheelInfo.h"
 | ||
|  | #include "LinearMath/btMinMax.h"
 | ||
|  | #include "LinearMath/btIDebugDraw.h"
 | ||
|  | #include "BulletDynamics/ConstraintSolver/btContactConstraint.h"
 | ||
|  | 
 | ||
|  | #define ROLLING_INFLUENCE_FIX
 | ||
|  | 
 | ||
|  | 
 | ||
|  | btRigidBody& btActionInterface::getFixedBody() | ||
|  | { | ||
|  | 	static btRigidBody s_fixed(0, 0,0); | ||
|  | 	s_fixed.setMassProps(btScalar(0.),btVector3(btScalar(0.),btScalar(0.),btScalar(0.))); | ||
|  | 	return s_fixed; | ||
|  | } | ||
|  | 
 | ||
|  | btRaycastVehicle::btRaycastVehicle(const btVehicleTuning& tuning,btRigidBody* chassis,	btVehicleRaycaster* raycaster ) | ||
|  | :m_vehicleRaycaster(raycaster), | ||
|  | m_pitchControl(btScalar(0.)) | ||
|  | { | ||
|  | 	m_chassisBody = chassis; | ||
|  | 	m_indexRightAxis = 0; | ||
|  | 	m_indexUpAxis = 2; | ||
|  | 	m_indexForwardAxis = 1; | ||
|  | 	defaultInit(tuning); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void btRaycastVehicle::defaultInit(const btVehicleTuning& tuning) | ||
|  | { | ||
|  | 	(void)tuning; | ||
|  | 	m_currentVehicleSpeedKmHour = btScalar(0.); | ||
|  | 	m_steeringValue = btScalar(0.); | ||
|  | 	 | ||
|  | } | ||
|  | 
 | ||
|  | 	 | ||
|  | 
 | ||
|  | btRaycastVehicle::~btRaycastVehicle() | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | //
 | ||
|  | // basically most of the code is general for 2 or 4 wheel vehicles, but some of it needs to be reviewed
 | ||
|  | //
 | ||
|  | btWheelInfo&	btRaycastVehicle::addWheel( const btVector3& connectionPointCS, const btVector3& wheelDirectionCS0,const btVector3& wheelAxleCS, btScalar suspensionRestLength, btScalar wheelRadius,const btVehicleTuning& tuning, bool isFrontWheel) | ||
|  | { | ||
|  | 
 | ||
|  | 	btWheelInfoConstructionInfo ci; | ||
|  | 
 | ||
|  | 	ci.m_chassisConnectionCS = connectionPointCS; | ||
|  | 	ci.m_wheelDirectionCS = wheelDirectionCS0; | ||
|  | 	ci.m_wheelAxleCS = wheelAxleCS; | ||
|  | 	ci.m_suspensionRestLength = suspensionRestLength; | ||
|  | 	ci.m_wheelRadius = wheelRadius; | ||
|  | 	ci.m_suspensionStiffness = tuning.m_suspensionStiffness; | ||
|  | 	ci.m_wheelsDampingCompression = tuning.m_suspensionCompression; | ||
|  | 	ci.m_wheelsDampingRelaxation = tuning.m_suspensionDamping; | ||
|  | 	ci.m_frictionSlip = tuning.m_frictionSlip; | ||
|  | 	ci.m_bIsFrontWheel = isFrontWheel; | ||
|  | 	ci.m_maxSuspensionTravelCm = tuning.m_maxSuspensionTravelCm; | ||
|  | 	ci.m_maxSuspensionForce = tuning.m_maxSuspensionForce; | ||
|  | 
 | ||
|  | 	m_wheelInfo.push_back( btWheelInfo(ci)); | ||
|  | 	 | ||
|  | 	btWheelInfo& wheel = m_wheelInfo[getNumWheels()-1]; | ||
|  | 	 | ||
|  | 	updateWheelTransformsWS( wheel , false ); | ||
|  | 	updateWheelTransform(getNumWheels()-1,false); | ||
|  | 	return wheel; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | const btTransform&	btRaycastVehicle::getWheelTransformWS( int wheelIndex ) const | ||
|  | { | ||
|  | 	btAssert(wheelIndex < getNumWheels()); | ||
|  | 	const btWheelInfo& wheel = m_wheelInfo[wheelIndex]; | ||
|  | 	return wheel.m_worldTransform; | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | void	btRaycastVehicle::updateWheelTransform( int wheelIndex , bool interpolatedTransform) | ||
|  | { | ||
|  | 	 | ||
|  | 	btWheelInfo& wheel = m_wheelInfo[ wheelIndex ]; | ||
|  | 	updateWheelTransformsWS(wheel,interpolatedTransform); | ||
|  | 	btVector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS; | ||
|  | 	const btVector3& right = wheel.m_raycastInfo.m_wheelAxleWS; | ||
|  | 	btVector3 fwd = up.cross(right); | ||
|  | 	fwd = fwd.normalize(); | ||
|  | //	up = right.cross(fwd);
 | ||
|  | //	up.normalize();
 | ||
|  | 
 | ||
|  | 	//rotate around steering over de wheelAxleWS
 | ||
|  | 	btScalar steering = wheel.m_steering; | ||
|  | 	 | ||
|  | 	btQuaternion steeringOrn(up,steering);//wheel.m_steering);
 | ||
|  | 	btMatrix3x3 steeringMat(steeringOrn); | ||
|  | 
 | ||
|  | 	btQuaternion rotatingOrn(right,-wheel.m_rotation); | ||
|  | 	btMatrix3x3 rotatingMat(rotatingOrn); | ||
|  | 
 | ||
|  | 	btMatrix3x3 basis2( | ||
|  | 		right[0],fwd[0],up[0], | ||
|  | 		right[1],fwd[1],up[1], | ||
|  | 		right[2],fwd[2],up[2] | ||
|  | 	); | ||
|  | 	 | ||
|  | 	wheel.m_worldTransform.setBasis(steeringMat * rotatingMat * basis2); | ||
|  | 	wheel.m_worldTransform.setOrigin( | ||
|  | 		wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength | ||
|  | 	); | ||
|  | } | ||
|  | 
 | ||
|  | void btRaycastVehicle::resetSuspension() | ||
|  | { | ||
|  | 
 | ||
|  | 	int i; | ||
|  | 	for (i=0;i<m_wheelInfo.size();	i++) | ||
|  | 	{ | ||
|  | 			btWheelInfo& wheel = m_wheelInfo[i]; | ||
|  | 			wheel.m_raycastInfo.m_suspensionLength = wheel.getSuspensionRestLength(); | ||
|  | 			wheel.m_suspensionRelativeVelocity = btScalar(0.0); | ||
|  | 			 | ||
|  | 			wheel.m_raycastInfo.m_contactNormalWS = - wheel.m_raycastInfo.m_wheelDirectionWS; | ||
|  | 			//wheel_info.setContactFriction(btScalar(0.0));
 | ||
|  | 			wheel.m_clippedInvContactDotSuspension = btScalar(1.0); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void	btRaycastVehicle::updateWheelTransformsWS(btWheelInfo& wheel , bool interpolatedTransform) | ||
|  | { | ||
|  | 	wheel.m_raycastInfo.m_isInContact = false; | ||
|  | 
 | ||
|  | 	btTransform chassisTrans = getChassisWorldTransform(); | ||
|  | 	if (interpolatedTransform && (getRigidBody()->getMotionState())) | ||
|  | 	{ | ||
|  | 		getRigidBody()->getMotionState()->getWorldTransform(chassisTrans); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	wheel.m_raycastInfo.m_hardPointWS = chassisTrans( wheel.m_chassisConnectionPointCS ); | ||
|  | 	wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.getBasis() *  wheel.m_wheelDirectionCS ; | ||
|  | 	wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.getBasis() * wheel.m_wheelAxleCS; | ||
|  | } | ||
|  | 
 | ||
|  | btScalar btRaycastVehicle::rayCast(btWheelInfo& wheel) | ||
|  | { | ||
|  | 	updateWheelTransformsWS( wheel,false); | ||
|  | 
 | ||
|  | 	 | ||
|  | 	btScalar depth = -1; | ||
|  | 	 | ||
|  | 	btScalar raylen = wheel.getSuspensionRestLength()+wheel.m_wheelsRadius; | ||
|  | 
 | ||
|  | 	btVector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen); | ||
|  | 	const btVector3& source = wheel.m_raycastInfo.m_hardPointWS; | ||
|  | 	wheel.m_raycastInfo.m_contactPointWS = source + rayvector; | ||
|  | 	const btVector3& target = wheel.m_raycastInfo.m_contactPointWS; | ||
|  | 
 | ||
|  | 	btScalar param = btScalar(0.); | ||
|  | 	 | ||
|  | 	btVehicleRaycaster::btVehicleRaycasterResult	rayResults; | ||
|  | 
 | ||
|  | 	btAssert(m_vehicleRaycaster); | ||
|  | 
 | ||
|  | 	void* object = m_vehicleRaycaster->castRay(source,target,rayResults); | ||
|  | 
 | ||
|  | 	wheel.m_raycastInfo.m_groundObject = 0; | ||
|  | 
 | ||
|  | 	if (object) | ||
|  | 	{ | ||
|  | 		param = rayResults.m_distFraction; | ||
|  | 		depth = raylen * rayResults.m_distFraction; | ||
|  | 		wheel.m_raycastInfo.m_contactNormalWS  = rayResults.m_hitNormalInWorld; | ||
|  | 		wheel.m_raycastInfo.m_isInContact = true; | ||
|  | 		 | ||
|  | 		wheel.m_raycastInfo.m_groundObject = &getFixedBody();///@todo for driving on dynamic/movable objects!;
 | ||
|  | 		//wheel.m_raycastInfo.m_groundObject = object;
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		btScalar hitDistance = param*raylen; | ||
|  | 		wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelsRadius; | ||
|  | 		//clamp on max suspension travel
 | ||
|  | 
 | ||
|  | 		btScalar  minSuspensionLength = wheel.getSuspensionRestLength() - wheel.m_maxSuspensionTravelCm*btScalar(0.01); | ||
|  | 		btScalar maxSuspensionLength = wheel.getSuspensionRestLength()+ wheel.m_maxSuspensionTravelCm*btScalar(0.01); | ||
|  | 		if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength) | ||
|  | 		{ | ||
|  | 			wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength; | ||
|  | 		} | ||
|  | 		if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength) | ||
|  | 		{ | ||
|  | 			wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		wheel.m_raycastInfo.m_contactPointWS = rayResults.m_hitPointInWorld; | ||
|  | 
 | ||
|  | 		btScalar denominator= wheel.m_raycastInfo.m_contactNormalWS.dot( wheel.m_raycastInfo.m_wheelDirectionWS ); | ||
|  | 
 | ||
|  | 		btVector3 chassis_velocity_at_contactPoint; | ||
|  | 		btVector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition(); | ||
|  | 
 | ||
|  | 		chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos); | ||
|  | 
 | ||
|  | 		btScalar projVel = wheel.m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint ); | ||
|  | 
 | ||
|  | 		if ( denominator >= btScalar(-0.1)) | ||
|  | 		{ | ||
|  | 			wheel.m_suspensionRelativeVelocity = btScalar(0.0); | ||
|  | 			wheel.m_clippedInvContactDotSuspension = btScalar(1.0) / btScalar(0.1); | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			btScalar inv = btScalar(-1.) / denominator; | ||
|  | 			wheel.m_suspensionRelativeVelocity = projVel * inv; | ||
|  | 			wheel.m_clippedInvContactDotSuspension = inv; | ||
|  | 		} | ||
|  | 			 | ||
|  | 	} else | ||
|  | 	{ | ||
|  | 		//put wheel info as in rest position
 | ||
|  | 		wheel.m_raycastInfo.m_suspensionLength = wheel.getSuspensionRestLength(); | ||
|  | 		wheel.m_suspensionRelativeVelocity = btScalar(0.0); | ||
|  | 		wheel.m_raycastInfo.m_contactNormalWS = - wheel.m_raycastInfo.m_wheelDirectionWS; | ||
|  | 		wheel.m_clippedInvContactDotSuspension = btScalar(1.0); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return depth; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | const btTransform& btRaycastVehicle::getChassisWorldTransform() const | ||
|  | { | ||
|  | 	/*if (getRigidBody()->getMotionState())
 | ||
|  | 	{ | ||
|  | 		btTransform chassisWorldTrans; | ||
|  | 		getRigidBody()->getMotionState()->getWorldTransform(chassisWorldTrans); | ||
|  | 		return chassisWorldTrans; | ||
|  | 	} | ||
|  | 	*/ | ||
|  | 
 | ||
|  | 	 | ||
|  | 	return getRigidBody()->getCenterOfMassTransform(); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void btRaycastVehicle::updateVehicle( btScalar step ) | ||
|  | { | ||
|  | 	{ | ||
|  | 		for (int i=0;i<getNumWheels();i++) | ||
|  | 		{ | ||
|  | 			updateWheelTransform(i,false); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	m_currentVehicleSpeedKmHour = btScalar(3.6) * getRigidBody()->getLinearVelocity().length(); | ||
|  | 	 | ||
|  | 	const btTransform& chassisTrans = getChassisWorldTransform(); | ||
|  | 
 | ||
|  | 	btVector3 forwardW ( | ||
|  | 		chassisTrans.getBasis()[0][m_indexForwardAxis], | ||
|  | 		chassisTrans.getBasis()[1][m_indexForwardAxis], | ||
|  | 		chassisTrans.getBasis()[2][m_indexForwardAxis]); | ||
|  | 
 | ||
|  | 	if (forwardW.dot(getRigidBody()->getLinearVelocity()) < btScalar(0.)) | ||
|  | 	{ | ||
|  | 		m_currentVehicleSpeedKmHour *= btScalar(-1.); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	//
 | ||
|  | 	// simulate suspension
 | ||
|  | 	//
 | ||
|  | 	 | ||
|  | 	int i=0; | ||
|  | 	for (i=0;i<m_wheelInfo.size();i++) | ||
|  | 	{ | ||
|  | 		//btScalar depth; 
 | ||
|  | 		//depth = 
 | ||
|  | 		rayCast( m_wheelInfo[i]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	updateSuspension(step); | ||
|  | 
 | ||
|  | 	 | ||
|  | 	for (i=0;i<m_wheelInfo.size();i++) | ||
|  | 	{ | ||
|  | 		//apply suspension force
 | ||
|  | 		btWheelInfo& wheel = m_wheelInfo[i]; | ||
|  | 		 | ||
|  | 		btScalar suspensionForce = wheel.m_wheelsSuspensionForce; | ||
|  | 		 | ||
|  | 		if (suspensionForce > wheel.m_maxSuspensionForce) | ||
|  | 		{ | ||
|  | 			suspensionForce = wheel.m_maxSuspensionForce; | ||
|  | 		} | ||
|  | 		btVector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; | ||
|  | 		btVector3 relpos = wheel.m_raycastInfo.m_contactPointWS - getRigidBody()->getCenterOfMassPosition(); | ||
|  | 		 | ||
|  | 		getRigidBody()->applyImpulse(impulse, relpos); | ||
|  | 	 | ||
|  | 	} | ||
|  | 	 | ||
|  | 
 | ||
|  | 	 | ||
|  | 	updateFriction( step); | ||
|  | 
 | ||
|  | 	 | ||
|  | 	for (i=0;i<m_wheelInfo.size();i++) | ||
|  | 	{ | ||
|  | 		btWheelInfo& wheel = m_wheelInfo[i]; | ||
|  | 		btVector3 relpos = wheel.m_raycastInfo.m_hardPointWS - getRigidBody()->getCenterOfMassPosition(); | ||
|  | 		btVector3 vel = getRigidBody()->getVelocityInLocalPoint( relpos ); | ||
|  | 
 | ||
|  | 		if (wheel.m_raycastInfo.m_isInContact) | ||
|  | 		{ | ||
|  | 			const btTransform&	chassisWorldTransform = getChassisWorldTransform(); | ||
|  | 
 | ||
|  | 			btVector3 fwd ( | ||
|  | 				chassisWorldTransform.getBasis()[0][m_indexForwardAxis], | ||
|  | 				chassisWorldTransform.getBasis()[1][m_indexForwardAxis], | ||
|  | 				chassisWorldTransform.getBasis()[2][m_indexForwardAxis]); | ||
|  | 
 | ||
|  | 			btScalar proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS); | ||
|  | 			fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj; | ||
|  | 
 | ||
|  | 			btScalar proj2 = fwd.dot(vel); | ||
|  | 			 | ||
|  | 			wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelsRadius); | ||
|  | 			wheel.m_rotation += wheel.m_deltaRotation; | ||
|  | 
 | ||
|  | 		} else | ||
|  | 		{ | ||
|  | 			wheel.m_rotation += wheel.m_deltaRotation; | ||
|  | 		} | ||
|  | 		 | ||
|  | 		wheel.m_deltaRotation *= btScalar(0.99);//damping of rotation when not in contact
 | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void	btRaycastVehicle::setSteeringValue(btScalar steering,int wheel) | ||
|  | { | ||
|  | 	btAssert(wheel>=0 && wheel < getNumWheels()); | ||
|  | 
 | ||
|  | 	btWheelInfo& wheelInfo = getWheelInfo(wheel); | ||
|  | 	wheelInfo.m_steering = steering; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | btScalar	btRaycastVehicle::getSteeringValue(int wheel) const | ||
|  | { | ||
|  | 	return getWheelInfo(wheel).m_steering; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void	btRaycastVehicle::applyEngineForce(btScalar force, int wheel) | ||
|  | { | ||
|  | 	btAssert(wheel>=0 && wheel < getNumWheels()); | ||
|  | 	btWheelInfo& wheelInfo = getWheelInfo(wheel); | ||
|  | 	wheelInfo.m_engineForce = force; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | const btWheelInfo&	btRaycastVehicle::getWheelInfo(int index) const | ||
|  | { | ||
|  | 	btAssert((index >= 0) && (index < 	getNumWheels())); | ||
|  | 	 | ||
|  | 	return m_wheelInfo[index]; | ||
|  | } | ||
|  | 
 | ||
|  | btWheelInfo&	btRaycastVehicle::getWheelInfo(int index)  | ||
|  | { | ||
|  | 	btAssert((index >= 0) && (index < 	getNumWheels())); | ||
|  | 	 | ||
|  | 	return m_wheelInfo[index]; | ||
|  | } | ||
|  | 
 | ||
|  | void btRaycastVehicle::setBrake(btScalar brake,int wheelIndex) | ||
|  | { | ||
|  | 	btAssert((wheelIndex >= 0) && (wheelIndex < 	getNumWheels())); | ||
|  | 	getWheelInfo(wheelIndex).m_brake = brake; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void	btRaycastVehicle::updateSuspension(btScalar deltaTime) | ||
|  | { | ||
|  | 	(void)deltaTime; | ||
|  | 
 | ||
|  | 	btScalar chassisMass = btScalar(1.) / m_chassisBody->getInvMass(); | ||
|  | 	 | ||
|  | 	for (int w_it=0; w_it<getNumWheels(); w_it++) | ||
|  | 	{ | ||
|  | 		btWheelInfo &wheel_info = m_wheelInfo[w_it]; | ||
|  | 		 | ||
|  | 		if ( wheel_info.m_raycastInfo.m_isInContact ) | ||
|  | 		{ | ||
|  | 			btScalar force; | ||
|  | 			//	Spring
 | ||
|  | 			{ | ||
|  | 				btScalar	susp_length			= wheel_info.getSuspensionRestLength(); | ||
|  | 				btScalar	current_length = wheel_info.m_raycastInfo.m_suspensionLength; | ||
|  | 
 | ||
|  | 				btScalar length_diff = (susp_length - current_length); | ||
|  | 
 | ||
|  | 				force = wheel_info.m_suspensionStiffness | ||
|  | 					* length_diff * wheel_info.m_clippedInvContactDotSuspension; | ||
|  | 			} | ||
|  | 		 | ||
|  | 			// Damper
 | ||
|  | 			{ | ||
|  | 				btScalar projected_rel_vel = wheel_info.m_suspensionRelativeVelocity; | ||
|  | 				{ | ||
|  | 					btScalar	susp_damping; | ||
|  | 					if ( projected_rel_vel < btScalar(0.0) ) | ||
|  | 					{ | ||
|  | 						susp_damping = wheel_info.m_wheelsDampingCompression; | ||
|  | 					} | ||
|  | 					else | ||
|  | 					{ | ||
|  | 						susp_damping = wheel_info.m_wheelsDampingRelaxation; | ||
|  | 					} | ||
|  | 					force -= susp_damping * projected_rel_vel; | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// RESULT
 | ||
|  | 			wheel_info.m_wheelsSuspensionForce = force * chassisMass; | ||
|  | 			if (wheel_info.m_wheelsSuspensionForce < btScalar(0.)) | ||
|  | 			{ | ||
|  | 				wheel_info.m_wheelsSuspensionForce = btScalar(0.); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			wheel_info.m_wheelsSuspensionForce = btScalar(0.0); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | struct btWheelContactPoint | ||
|  | { | ||
|  | 	btRigidBody* m_body0; | ||
|  | 	btRigidBody* m_body1; | ||
|  | 	btVector3	m_frictionPositionWorld; | ||
|  | 	btVector3	m_frictionDirectionWorld; | ||
|  | 	btScalar	m_jacDiagABInv; | ||
|  | 	btScalar	m_maxImpulse; | ||
|  | 
 | ||
|  | 
 | ||
|  | 	btWheelContactPoint(btRigidBody* body0,btRigidBody* body1,const btVector3& frictionPosWorld,const btVector3& frictionDirectionWorld, btScalar maxImpulse) | ||
|  | 		:m_body0(body0), | ||
|  | 		m_body1(body1), | ||
|  | 		m_frictionPositionWorld(frictionPosWorld), | ||
|  | 		m_frictionDirectionWorld(frictionDirectionWorld), | ||
|  | 		m_maxImpulse(maxImpulse) | ||
|  | 	{ | ||
|  | 		btScalar denom0 = body0->computeImpulseDenominator(frictionPosWorld,frictionDirectionWorld); | ||
|  | 		btScalar denom1 = body1->computeImpulseDenominator(frictionPosWorld,frictionDirectionWorld); | ||
|  | 		btScalar	relaxation = 1.f; | ||
|  | 		m_jacDiagABInv = relaxation/(denom0+denom1); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | }; | ||
|  | 
 | ||
|  | btScalar calcRollingFriction(btWheelContactPoint& contactPoint); | ||
|  | btScalar calcRollingFriction(btWheelContactPoint& contactPoint) | ||
|  | { | ||
|  | 
 | ||
|  | 	btScalar j1=0.f; | ||
|  | 
 | ||
|  | 	const btVector3& contactPosWorld = contactPoint.m_frictionPositionWorld; | ||
|  | 
 | ||
|  | 	btVector3 rel_pos1 = contactPosWorld - contactPoint.m_body0->getCenterOfMassPosition();  | ||
|  | 	btVector3 rel_pos2 = contactPosWorld - contactPoint.m_body1->getCenterOfMassPosition(); | ||
|  | 	 | ||
|  | 	btScalar maxImpulse  = contactPoint.m_maxImpulse; | ||
|  | 	 | ||
|  | 	btVector3 vel1 = contactPoint.m_body0->getVelocityInLocalPoint(rel_pos1); | ||
|  | 	btVector3 vel2 = contactPoint.m_body1->getVelocityInLocalPoint(rel_pos2); | ||
|  | 	btVector3 vel = vel1 - vel2; | ||
|  | 
 | ||
|  | 	btScalar vrel = contactPoint.m_frictionDirectionWorld.dot(vel); | ||
|  | 
 | ||
|  | 	// calculate j that moves us to zero relative velocity
 | ||
|  | 	j1 = -vrel * contactPoint.m_jacDiagABInv; | ||
|  | 	btSetMin(j1, maxImpulse); | ||
|  | 	btSetMax(j1, -maxImpulse); | ||
|  | 
 | ||
|  | 	return j1; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | btScalar sideFrictionStiffness2 = btScalar(1.0); | ||
|  | void	btRaycastVehicle::updateFriction(btScalar	timeStep) | ||
|  | { | ||
|  | 
 | ||
|  | 		//calculate the impulse, so that the wheels don't move sidewards
 | ||
|  | 		int numWheel = getNumWheels(); | ||
|  | 		if (!numWheel) | ||
|  | 			return; | ||
|  | 
 | ||
|  | 		m_forwardWS.resize(numWheel); | ||
|  | 		m_axle.resize(numWheel); | ||
|  | 		m_forwardImpulse.resize(numWheel); | ||
|  | 		m_sideImpulse.resize(numWheel); | ||
|  | 		 | ||
|  | 		int numWheelsOnGround = 0; | ||
|  | 	 | ||
|  | 
 | ||
|  | 		//collapse all those loops into one!
 | ||
|  | 		for (int i=0;i<getNumWheels();i++) | ||
|  | 		{ | ||
|  | 			btWheelInfo& wheelInfo = m_wheelInfo[i]; | ||
|  | 			class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject; | ||
|  | 			if (groundObject) | ||
|  | 				numWheelsOnGround++; | ||
|  | 			m_sideImpulse[i] = btScalar(0.); | ||
|  | 			m_forwardImpulse[i] = btScalar(0.); | ||
|  | 
 | ||
|  | 		} | ||
|  | 	 | ||
|  | 		{ | ||
|  | 	 | ||
|  | 			for (int i=0;i<getNumWheels();i++) | ||
|  | 			{ | ||
|  | 
 | ||
|  | 				btWheelInfo& wheelInfo = m_wheelInfo[i]; | ||
|  | 					 | ||
|  | 				class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject; | ||
|  | 
 | ||
|  | 				if (groundObject) | ||
|  | 				{ | ||
|  | 
 | ||
|  | 					const btTransform& wheelTrans = getWheelTransformWS( i ); | ||
|  | 
 | ||
|  | 					btMatrix3x3 wheelBasis0 = wheelTrans.getBasis(); | ||
|  | 					m_axle[i] = btVector3(	 | ||
|  | 						wheelBasis0[0][m_indexRightAxis], | ||
|  | 						wheelBasis0[1][m_indexRightAxis], | ||
|  | 						wheelBasis0[2][m_indexRightAxis]); | ||
|  | 					 | ||
|  | 					const btVector3& surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; | ||
|  | 					btScalar proj = m_axle[i].dot(surfNormalWS); | ||
|  | 					m_axle[i] -= surfNormalWS * proj; | ||
|  | 					m_axle[i] = m_axle[i].normalize(); | ||
|  | 					 | ||
|  | 					m_forwardWS[i] = surfNormalWS.cross(m_axle[i]); | ||
|  | 					m_forwardWS[i].normalize(); | ||
|  | 
 | ||
|  | 				 | ||
|  | 					resolveSingleBilateral(*m_chassisBody, wheelInfo.m_raycastInfo.m_contactPointWS, | ||
|  | 							  *groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, | ||
|  | 							  btScalar(0.), m_axle[i],m_sideImpulse[i],timeStep); | ||
|  | 
 | ||
|  | 					m_sideImpulse[i] *= sideFrictionStiffness2; | ||
|  | 						 | ||
|  | 				} | ||
|  | 				 | ||
|  | 
 | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 	btScalar sideFactor = btScalar(1.); | ||
|  | 	btScalar fwdFactor = 0.5; | ||
|  | 
 | ||
|  | 	bool sliding = false; | ||
|  | 	{ | ||
|  | 		for (int wheel =0;wheel <getNumWheels();wheel++) | ||
|  | 		{ | ||
|  | 			btWheelInfo& wheelInfo = m_wheelInfo[wheel]; | ||
|  | 			class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject; | ||
|  | 
 | ||
|  | 			btScalar	rollingFriction = 0.f; | ||
|  | 
 | ||
|  | 			if (groundObject) | ||
|  | 			{ | ||
|  | 				if (wheelInfo.m_engineForce != 0.f) | ||
|  | 				{ | ||
|  | 					rollingFriction = wheelInfo.m_engineForce* timeStep; | ||
|  | 				} else | ||
|  | 				{ | ||
|  | 					btScalar defaultRollingFrictionImpulse = 0.f; | ||
|  | 					btScalar maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse; | ||
|  | 					btWheelContactPoint contactPt(m_chassisBody,groundObject,wheelInfo.m_raycastInfo.m_contactPointWS,m_forwardWS[wheel],maxImpulse); | ||
|  | 					rollingFriction = calcRollingFriction(contactPt); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			//switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break)
 | ||
|  | 			 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 			m_forwardImpulse[wheel] = btScalar(0.); | ||
|  | 			m_wheelInfo[wheel].m_skidInfo= btScalar(1.); | ||
|  | 
 | ||
|  | 			if (groundObject) | ||
|  | 			{ | ||
|  | 				m_wheelInfo[wheel].m_skidInfo= btScalar(1.); | ||
|  | 				 | ||
|  | 				btScalar maximp = wheelInfo.m_wheelsSuspensionForce * timeStep * wheelInfo.m_frictionSlip; | ||
|  | 				btScalar maximpSide = maximp; | ||
|  | 
 | ||
|  | 				btScalar maximpSquared = maximp * maximpSide; | ||
|  | 			 | ||
|  | 
 | ||
|  | 				m_forwardImpulse[wheel] = rollingFriction;//wheelInfo.m_engineForce* timeStep;
 | ||
|  | 
 | ||
|  | 				btScalar x = (m_forwardImpulse[wheel] ) * fwdFactor; | ||
|  | 				btScalar y = (m_sideImpulse[wheel] ) * sideFactor; | ||
|  | 				 | ||
|  | 				btScalar impulseSquared = (x*x + y*y); | ||
|  | 
 | ||
|  | 				if (impulseSquared > maximpSquared) | ||
|  | 				{ | ||
|  | 					sliding = true; | ||
|  | 					 | ||
|  | 					btScalar factor = maximp / btSqrt(impulseSquared); | ||
|  | 					 | ||
|  | 					m_wheelInfo[wheel].m_skidInfo *= factor; | ||
|  | 				} | ||
|  | 			}  | ||
|  | 
 | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		if (sliding) | ||
|  | 		{ | ||
|  | 			for (int wheel = 0;wheel < getNumWheels(); wheel++) | ||
|  | 			{ | ||
|  | 				if (m_sideImpulse[wheel] != btScalar(0.)) | ||
|  | 				{ | ||
|  | 					if (m_wheelInfo[wheel].m_skidInfo< btScalar(1.)) | ||
|  | 					{ | ||
|  | 						m_forwardImpulse[wheel] *=	m_wheelInfo[wheel].m_skidInfo; | ||
|  | 						m_sideImpulse[wheel] *= m_wheelInfo[wheel].m_skidInfo; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// apply the impulses
 | ||
|  | 		{ | ||
|  | 			for (int wheel = 0;wheel<getNumWheels() ; wheel++) | ||
|  | 			{ | ||
|  | 				btWheelInfo& wheelInfo = m_wheelInfo[wheel]; | ||
|  | 
 | ||
|  | 				btVector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS -  | ||
|  | 						m_chassisBody->getCenterOfMassPosition(); | ||
|  | 
 | ||
|  | 				if (m_forwardImpulse[wheel] != btScalar(0.)) | ||
|  | 				{ | ||
|  | 					m_chassisBody->applyImpulse(m_forwardWS[wheel]*(m_forwardImpulse[wheel]),rel_pos); | ||
|  | 				} | ||
|  | 				if (m_sideImpulse[wheel] != btScalar(0.)) | ||
|  | 				{ | ||
|  | 					class btRigidBody* groundObject = (class btRigidBody*) m_wheelInfo[wheel].m_raycastInfo.m_groundObject; | ||
|  | 
 | ||
|  | 					btVector3 rel_pos2 = wheelInfo.m_raycastInfo.m_contactPointWS -  | ||
|  | 						groundObject->getCenterOfMassPosition(); | ||
|  | 
 | ||
|  | 					 | ||
|  | 					btVector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel]; | ||
|  | 
 | ||
|  | #if defined ROLLING_INFLUENCE_FIX // fix. It only worked if car's up was along Y - VT.
 | ||
|  | 					btVector3 vChassisWorldUp = getRigidBody()->getCenterOfMassTransform().getBasis().getColumn(m_indexUpAxis); | ||
|  | 					rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * (1.f-wheelInfo.m_rollInfluence)); | ||
|  | #else
 | ||
|  | 					rel_pos[m_indexUpAxis] *= wheelInfo.m_rollInfluence; | ||
|  | #endif
 | ||
|  | 					m_chassisBody->applyImpulse(sideImp,rel_pos); | ||
|  | 
 | ||
|  | 					//apply friction impulse on the ground
 | ||
|  | 					groundObject->applyImpulse(-sideImp,rel_pos2); | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 	 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | void	btRaycastVehicle::debugDraw(btIDebugDraw* debugDrawer) | ||
|  | { | ||
|  | 
 | ||
|  | 	for (int v=0;v<this->getNumWheels();v++) | ||
|  | 	{ | ||
|  | 		btVector3 wheelColor(0,1,1); | ||
|  | 		if (getWheelInfo(v).m_raycastInfo.m_isInContact) | ||
|  | 		{ | ||
|  | 			wheelColor.setValue(0,0,1); | ||
|  | 		} else | ||
|  | 		{ | ||
|  | 			wheelColor.setValue(1,0,1); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		btVector3 wheelPosWS = getWheelInfo(v).m_worldTransform.getOrigin(); | ||
|  | 
 | ||
|  | 		btVector3 axle = btVector3(	 | ||
|  | 			getWheelInfo(v).m_worldTransform.getBasis()[0][getRightAxis()], | ||
|  | 			getWheelInfo(v).m_worldTransform.getBasis()[1][getRightAxis()], | ||
|  | 			getWheelInfo(v).m_worldTransform.getBasis()[2][getRightAxis()]); | ||
|  | 
 | ||
|  | 		//debug wheels (cylinders)
 | ||
|  | 		debugDrawer->drawLine(wheelPosWS,wheelPosWS+axle,wheelColor); | ||
|  | 		debugDrawer->drawLine(wheelPosWS,getWheelInfo(v).m_raycastInfo.m_contactPointWS,wheelColor); | ||
|  | 
 | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void* btDefaultVehicleRaycaster::castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result) | ||
|  | { | ||
|  | //	RayResultCallback& resultCallback;
 | ||
|  | 
 | ||
|  | 	btCollisionWorld::ClosestRayResultCallback rayCallback(from,to); | ||
|  | 
 | ||
|  | 	m_dynamicsWorld->rayTest(from, to, rayCallback); | ||
|  | 
 | ||
|  | 	if (rayCallback.hasHit()) | ||
|  | 	{ | ||
|  | 		 | ||
|  | 		const btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject); | ||
|  |         if (body && body->hasContactResponse()) | ||
|  | 		{ | ||
|  | 			result.m_hitPointInWorld = rayCallback.m_hitPointWorld; | ||
|  | 			result.m_hitNormalInWorld = rayCallback.m_hitNormalWorld; | ||
|  | 			result.m_hitNormalInWorld.normalize(); | ||
|  | 			result.m_distFraction = rayCallback.m_closestHitFraction; | ||
|  | 			return (void*)body; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 |