Index: code/trunk/src/external/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp
===================================================================
--- code/trunk/src/external/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp	(revision 5781)
+++ code/trunk/src/external/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp	(revision 8351)
@@ -19,5 +19,5 @@
 */
 
-//-----------------------------------------------------------------------------
+
 
 #include "btSliderConstraint.h"
@@ -26,5 +26,5 @@
 #include <new>
 
-//-----------------------------------------------------------------------------
+#define USE_OFFSET_FOR_CONSTANT_FRAME true
 
 void btSliderConstraint::initParams()
@@ -37,19 +37,25 @@
 	m_restitutionDirLin = SLIDER_CONSTRAINT_DEF_RESTITUTION;
 	m_dampingDirLin = btScalar(0.);
+	m_cfmDirLin = SLIDER_CONSTRAINT_DEF_CFM;
 	m_softnessDirAng = SLIDER_CONSTRAINT_DEF_SOFTNESS;
 	m_restitutionDirAng = SLIDER_CONSTRAINT_DEF_RESTITUTION;
 	m_dampingDirAng = btScalar(0.);
+	m_cfmDirAng = SLIDER_CONSTRAINT_DEF_CFM;
 	m_softnessOrthoLin = SLIDER_CONSTRAINT_DEF_SOFTNESS;
 	m_restitutionOrthoLin = SLIDER_CONSTRAINT_DEF_RESTITUTION;
 	m_dampingOrthoLin = SLIDER_CONSTRAINT_DEF_DAMPING;
+	m_cfmOrthoLin = SLIDER_CONSTRAINT_DEF_CFM;
 	m_softnessOrthoAng = SLIDER_CONSTRAINT_DEF_SOFTNESS;
 	m_restitutionOrthoAng = SLIDER_CONSTRAINT_DEF_RESTITUTION;
 	m_dampingOrthoAng = SLIDER_CONSTRAINT_DEF_DAMPING;
+	m_cfmOrthoAng = SLIDER_CONSTRAINT_DEF_CFM;
 	m_softnessLimLin = SLIDER_CONSTRAINT_DEF_SOFTNESS;
 	m_restitutionLimLin = SLIDER_CONSTRAINT_DEF_RESTITUTION;
 	m_dampingLimLin = SLIDER_CONSTRAINT_DEF_DAMPING;
+	m_cfmLimLin = SLIDER_CONSTRAINT_DEF_CFM;
 	m_softnessLimAng = SLIDER_CONSTRAINT_DEF_SOFTNESS;
 	m_restitutionLimAng = SLIDER_CONSTRAINT_DEF_RESTITUTION;
 	m_dampingLimAng = SLIDER_CONSTRAINT_DEF_DAMPING;
+	m_cfmLimAng = SLIDER_CONSTRAINT_DEF_CFM;
 
 	m_poweredLinMotor = false;
@@ -63,62 +69,116 @@
 	m_accumulatedAngMotorImpulse = btScalar(0.0);
 
-} // btSliderConstraint::initParams()
-
-//-----------------------------------------------------------------------------
-
-btSliderConstraint::btSliderConstraint()
-        :btTypedConstraint(SLIDER_CONSTRAINT_TYPE),
-		m_useLinearReferenceFrameA(true),
-		m_useSolveConstraintObsolete(false)
-//		m_useSolveConstraintObsolete(true)
+	m_flags = 0;
+	m_flags = 0;
+
+	m_useOffsetForConstraintFrame = USE_OFFSET_FOR_CONSTANT_FRAME;
+
+	calculateTransforms(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform());
+}
+
+
+
+
+
+btSliderConstraint::btSliderConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB, bool useLinearReferenceFrameA)
+        : btTypedConstraint(SLIDER_CONSTRAINT_TYPE, rbA, rbB),
+		m_useSolveConstraintObsolete(false),
+		m_frameInA(frameInA),
+        m_frameInB(frameInB),
+		m_useLinearReferenceFrameA(useLinearReferenceFrameA)
 {
 	initParams();
-} // btSliderConstraint::btSliderConstraint()
-
-//-----------------------------------------------------------------------------
-
-btSliderConstraint::btSliderConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB, bool useLinearReferenceFrameA)
-        : btTypedConstraint(SLIDER_CONSTRAINT_TYPE, rbA, rbB)
-        , m_frameInA(frameInA)
-        , m_frameInB(frameInB),
-		m_useLinearReferenceFrameA(useLinearReferenceFrameA),
-		m_useSolveConstraintObsolete(false)
-//		m_useSolveConstraintObsolete(true)
-{
+}
+
+
+
+btSliderConstraint::btSliderConstraint(btRigidBody& rbB, const btTransform& frameInB, bool useLinearReferenceFrameA)
+        : btTypedConstraint(SLIDER_CONSTRAINT_TYPE, getFixedBody(), rbB),
+		m_useSolveConstraintObsolete(false),
+		m_frameInB(frameInB),
+		m_useLinearReferenceFrameA(useLinearReferenceFrameA)
+{
+	///not providing rigidbody A means implicitly using worldspace for body A
+	m_frameInA = rbB.getCenterOfMassTransform() * m_frameInB;
+//	m_frameInA.getOrigin() = m_rbA.getCenterOfMassTransform()(m_frameInA.getOrigin());
+
 	initParams();
-} // btSliderConstraint::btSliderConstraint()
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::buildJacobian()
-{
-	if (!m_useSolveConstraintObsolete) 
-	{
-		return;
-	}
-	if(m_useLinearReferenceFrameA)
-	{
-		buildJacobianInt(m_rbA, m_rbB, m_frameInA, m_frameInB);
+}
+
+
+
+
+
+
+void btSliderConstraint::getInfo1(btConstraintInfo1* info)
+{
+	if (m_useSolveConstraintObsolete)
+	{
+		info->m_numConstraintRows = 0;
+		info->nub = 0;
 	}
 	else
 	{
-		buildJacobianInt(m_rbB, m_rbA, m_frameInB, m_frameInA);
-	}
-} // btSliderConstraint::buildJacobian()
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::buildJacobianInt(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB)
-{
-	//calculate transforms
-    m_calculatedTransformA = rbA.getCenterOfMassTransform() * frameInA;
-    m_calculatedTransformB = rbB.getCenterOfMassTransform() * frameInB;
+		info->m_numConstraintRows = 4; // Fixed 2 linear + 2 angular
+		info->nub = 2; 
+		//prepare constraint
+		calculateTransforms(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform());
+		testAngLimits();
+		testLinLimits();
+		if(getSolveLinLimit() || getPoweredLinMotor())
+		{
+			info->m_numConstraintRows++; // limit 3rd linear as well
+			info->nub--; 
+		}
+		if(getSolveAngLimit() || getPoweredAngMotor())
+		{
+			info->m_numConstraintRows++; // limit 3rd angular as well
+			info->nub--; 
+		}
+	}
+}
+
+void btSliderConstraint::getInfo1NonVirtual(btConstraintInfo1* info)
+{
+
+	info->m_numConstraintRows = 6; // Fixed 2 linear + 2 angular + 1 limit (even if not used)
+	info->nub = 0; 
+}
+
+void btSliderConstraint::getInfo2(btConstraintInfo2* info)
+{
+	getInfo2NonVirtual(info,m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(), m_rbA.getLinearVelocity(),m_rbB.getLinearVelocity(), m_rbA.getInvMass(),m_rbB.getInvMass());
+}
+
+
+
+
+
+
+
+void btSliderConstraint::calculateTransforms(const btTransform& transA,const btTransform& transB)
+{
+	if(m_useLinearReferenceFrameA || (!m_useSolveConstraintObsolete))
+	{
+		m_calculatedTransformA = transA * m_frameInA;
+		m_calculatedTransformB = transB * m_frameInB;
+	}
+	else
+	{
+		m_calculatedTransformA = transB * m_frameInB;
+		m_calculatedTransformB = transA * m_frameInA;
+	}
 	m_realPivotAInW = m_calculatedTransformA.getOrigin();
 	m_realPivotBInW = m_calculatedTransformB.getOrigin();
 	m_sliderAxis = m_calculatedTransformA.getBasis().getColumn(0); // along X
-	m_delta = m_realPivotBInW - m_realPivotAInW;
+	if(m_useLinearReferenceFrameA || m_useSolveConstraintObsolete)
+	{
+		m_delta = m_realPivotBInW - m_realPivotAInW;
+	}
+	else
+	{
+		m_delta = m_realPivotAInW - m_realPivotBInW;
+	}
 	m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis;
-	m_relPosA = m_projPivotInW - rbA.getCenterOfMassPosition();
-	m_relPosB = m_realPivotBInW - rbB.getCenterOfMassPosition();
     btVector3 normalWorld;
     int i;
@@ -127,79 +187,130 @@
     {
 		normalWorld = m_calculatedTransformA.getBasis().getColumn(i);
-		new (&m_jacLin[i]) btJacobianEntry(
-			rbA.getCenterOfMassTransform().getBasis().transpose(),
-			rbB.getCenterOfMassTransform().getBasis().transpose(),
-			m_relPosA,
-			m_relPosB,
-			normalWorld,
-			rbA.getInvInertiaDiagLocal(),
-			rbA.getInvMass(),
-			rbB.getInvInertiaDiagLocal(),
-			rbB.getInvMass()
-			);
-		m_jacLinDiagABInv[i] = btScalar(1.) / m_jacLin[i].getDiagonal();
 		m_depth[i] = m_delta.dot(normalWorld);
     }
-	testLinLimits();
-    // angular part
-    for(i = 0; i < 3; i++)
-    {
-		normalWorld = m_calculatedTransformA.getBasis().getColumn(i);
-		new (&m_jacAng[i])	btJacobianEntry(
-			normalWorld,
-            rbA.getCenterOfMassTransform().getBasis().transpose(),
-            rbB.getCenterOfMassTransform().getBasis().transpose(),
-            rbA.getInvInertiaDiagLocal(),
-            rbB.getInvInertiaDiagLocal()
-			);
-	}
-	testAngLimits();
-	btVector3 axisA = m_calculatedTransformA.getBasis().getColumn(0);
-	m_kAngle = btScalar(1.0 )/ (rbA.computeAngularImpulseDenominator(axisA) + rbB.computeAngularImpulseDenominator(axisA));
-	// clear accumulator for motors
-	m_accumulatedLinMotorImpulse = btScalar(0.0);
-	m_accumulatedAngMotorImpulse = btScalar(0.0);
-} // btSliderConstraint::buildJacobianInt()
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::getInfo1(btConstraintInfo1* info)
-{
-	if (m_useSolveConstraintObsolete)
-	{
-		info->m_numConstraintRows = 0;
-		info->nub = 0;
+}
+ 
+
+
+void btSliderConstraint::testLinLimits(void)
+{
+	m_solveLinLim = false;
+	m_linPos = m_depth[0];
+	if(m_lowerLinLimit <= m_upperLinLimit)
+	{
+		if(m_depth[0] > m_upperLinLimit)
+		{
+			m_depth[0] -= m_upperLinLimit;
+			m_solveLinLim = true;
+		}
+		else if(m_depth[0] < m_lowerLinLimit)
+		{
+			m_depth[0] -= m_lowerLinLimit;
+			m_solveLinLim = true;
+		}
+		else
+		{
+			m_depth[0] = btScalar(0.);
+		}
 	}
 	else
 	{
-		info->m_numConstraintRows = 4; // Fixed 2 linear + 2 angular
-		info->nub = 2; 
-		//prepare constraint
-		calculateTransforms();
-		testLinLimits();
-		if(getSolveLinLimit() || getPoweredLinMotor())
-		{
-			info->m_numConstraintRows++; // limit 3rd linear as well
-			info->nub--; 
-		}
-		testAngLimits();
-		if(getSolveAngLimit() || getPoweredAngMotor())
-		{
-			info->m_numConstraintRows++; // limit 3rd angular as well
-			info->nub--; 
-		}
-	}
-} // btSliderConstraint::getInfo1()
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::getInfo2(btConstraintInfo2* info)
-{
+		m_depth[0] = btScalar(0.);
+	}
+}
+
+
+
+void btSliderConstraint::testAngLimits(void)
+{
+	m_angDepth = btScalar(0.);
+	m_solveAngLim = false;
+	if(m_lowerAngLimit <= m_upperAngLimit)
+	{
+		const btVector3 axisA0 = m_calculatedTransformA.getBasis().getColumn(1);
+		const btVector3 axisA1 = m_calculatedTransformA.getBasis().getColumn(2);
+		const btVector3 axisB0 = m_calculatedTransformB.getBasis().getColumn(1);
+//		btScalar rot = btAtan2Fast(axisB0.dot(axisA1), axisB0.dot(axisA0));  
+		btScalar rot = btAtan2(axisB0.dot(axisA1), axisB0.dot(axisA0));  
+		rot = btAdjustAngleToLimits(rot, m_lowerAngLimit, m_upperAngLimit);
+		m_angPos = rot;
+		if(rot < m_lowerAngLimit)
+		{
+			m_angDepth = rot - m_lowerAngLimit;
+			m_solveAngLim = true;
+		} 
+		else if(rot > m_upperAngLimit)
+		{
+			m_angDepth = rot - m_upperAngLimit;
+			m_solveAngLim = true;
+		}
+	}
+}
+
+btVector3 btSliderConstraint::getAncorInA(void)
+{
+	btVector3 ancorInA;
+	ancorInA = m_realPivotAInW + (m_lowerLinLimit + m_upperLinLimit) * btScalar(0.5) * m_sliderAxis;
+	ancorInA = m_rbA.getCenterOfMassTransform().inverse() * ancorInA;
+	return ancorInA;
+}
+
+
+
+btVector3 btSliderConstraint::getAncorInB(void)
+{
+	btVector3 ancorInB;
+	ancorInB = m_frameInB.getOrigin();
+	return ancorInB;
+}
+
+
+void btSliderConstraint::getInfo2NonVirtual(btConstraintInfo2* info, const btTransform& transA,const btTransform& transB, const btVector3& linVelA,const btVector3& linVelB, btScalar rbAinvMass,btScalar rbBinvMass  )
+{
+	const btTransform& trA = getCalculatedTransformA();
+	const btTransform& trB = getCalculatedTransformB();
+	
 	btAssert(!m_useSolveConstraintObsolete);
 	int i, s = info->rowskip;
-	const btTransform& trA = getCalculatedTransformA();
-	const btTransform& trB = getCalculatedTransformB();
+	
 	btScalar signFact = m_useLinearReferenceFrameA ? btScalar(1.0f) : btScalar(-1.0f);
-	// make rotations around Y and Z equal
+	
+	// difference between frames in WCS
+	btVector3 ofs = trB.getOrigin() - trA.getOrigin();
+	// now get weight factors depending on masses
+	btScalar miA = rbAinvMass;
+	btScalar miB = rbBinvMass;
+	bool hasStaticBody = (miA < SIMD_EPSILON) || (miB < SIMD_EPSILON);
+	btScalar miS = miA + miB;
+	btScalar factA, factB;
+	if(miS > btScalar(0.f))
+	{
+		factA = miB / miS;
+	}
+	else 
+	{
+		factA = btScalar(0.5f);
+	}
+	factB = btScalar(1.0f) - factA;
+	btVector3 ax1, p, q;
+	btVector3 ax1A = trA.getBasis().getColumn(0);
+	btVector3 ax1B = trB.getBasis().getColumn(0);
+	if(m_useOffsetForConstraintFrame)
+	{
+		// get the desired direction of slider axis
+		// as weighted sum of X-orthos of frameA and frameB in WCS
+		ax1 = ax1A * factA + ax1B * factB;
+		ax1.normalize();
+		// construct two orthos to slider axis
+		btPlaneSpace1 (ax1, p, q);
+	}
+	else
+	{ // old way - use frameA
+		ax1 = trA.getBasis().getColumn(0);
+		// get 2 orthos to slider axis (Y, Z)
+		p = trA.getBasis().getColumn(1);
+		q = trA.getBasis().getColumn(2);
+	}
+	// make rotations around these orthos equal
 	// the slider axis should be the only unconstrained
 	// rotational axis, the angular velocity of the two bodies perpendicular to
@@ -209,10 +320,4 @@
 	// where p and q are unit vectors normal to the slider axis, and w1 and w2
 	// are the angular velocity vectors of the two bodies.
-	// get slider axis (X)
-	btVector3 ax1 = trA.getBasis().getColumn(0);
-	// get 2 orthos to slider axis (Y, Z)
-	btVector3 p = trA.getBasis().getColumn(1);
-	btVector3 q = trA.getBasis().getColumn(2);
-	// set the two slider rows 
 	info->m_J1angularAxis[0] = p[0];
 	info->m_J1angularAxis[1] = p[1];
@@ -230,6 +335,6 @@
 	// compute the right hand side of the constraint equation. set relative
 	// body velocities along p and q to bring the slider back into alignment.
-	// if ax1,ax2 are the unit length slider axes as computed from body1 and
-	// body2, we need to rotate both bodies along the axis u = (ax1 x ax2).
+	// if ax1A,ax1B are the unit length slider axes as computed from bodyA and
+	// bodyB, we need to rotate both bodies along the axis u = (ax1 x ax2).
 	// if "theta" is the angle between ax1 and ax2, we need an angular velocity
 	// along u to cover angle erp*theta in one step :
@@ -243,56 +348,118 @@
 	// ax1 x ax2 is in the plane space of ax1, so we project the angular
 	// velocity to p and q to find the right hand side.
-	btScalar k = info->fps * info->erp * getSoftnessOrthoAng();
-    btVector3 ax2 = trB.getBasis().getColumn(0);
-	btVector3 u = ax1.cross(ax2);
+//	btScalar k = info->fps * info->erp * getSoftnessOrthoAng();
+	btScalar currERP = (m_flags & BT_SLIDER_FLAGS_ERP_ORTANG) ? m_softnessOrthoAng : m_softnessOrthoAng * info->erp;
+	btScalar k = info->fps * currERP;
+
+	btVector3 u = ax1A.cross(ax1B);
 	info->m_constraintError[0] = k * u.dot(p);
 	info->m_constraintError[s] = k * u.dot(q);
-	// pull out pos and R for both bodies. also get the connection
-	// vector c = pos2-pos1.
-	// next two rows. we want: vel2 = vel1 + w1 x c ... but this would
-	// result in three equations, so we project along the planespace vectors
-	// so that sliding along the slider axis is disregarded. for symmetry we
-	// also consider rotation around center of mass of two bodies (factA and factB).
-	btTransform bodyA_trans = m_rbA.getCenterOfMassTransform();
-	btTransform bodyB_trans = m_rbB.getCenterOfMassTransform();
-	int s2 = 2 * s, s3 = 3 * s;
-	btVector3 c;
-	btScalar miA = m_rbA.getInvMass();
-	btScalar miB = m_rbB.getInvMass();
-	btScalar miS = miA + miB;
-	btScalar factA, factB;
-	if(miS > btScalar(0.f))
-	{
-		factA = miB / miS;
-	}
-	else 
-	{
-		factA = btScalar(0.5f);
-	}
-	if(factA > 0.99f) factA = 0.99f;
-	if(factA < 0.01f) factA = 0.01f;
-	factB = btScalar(1.0f) - factA;
-	c = bodyB_trans.getOrigin() - bodyA_trans.getOrigin();
-	btVector3 tmp = c.cross(p);
-	for (i=0; i<3; i++) info->m_J1angularAxis[s2+i] = factA*tmp[i];
-	for (i=0; i<3; i++) info->m_J2angularAxis[s2+i] = factB*tmp[i];
-	tmp = c.cross(q);
-	for (i=0; i<3; i++) info->m_J1angularAxis[s3+i] = factA*tmp[i];
-	for (i=0; i<3; i++) info->m_J2angularAxis[s3+i] = factB*tmp[i];
-
-	for (i=0; i<3; i++) info->m_J1linearAxis[s2+i] = p[i];
-	for (i=0; i<3; i++) info->m_J1linearAxis[s3+i] = q[i];
-	// compute two elements of right hand side. we want to align the offset
-	// point (in body 2's frame) with the center of body 1.
-	btVector3 ofs; // offset point in global coordinates
-	ofs = trB.getOrigin() - trA.getOrigin();
-	k = info->fps * info->erp * getSoftnessOrthoLin();
-	info->m_constraintError[s2] = k * p.dot(ofs);
-	info->m_constraintError[s3] = k * q.dot(ofs);
-	int nrow = 3; // last filled row
+	if(m_flags & BT_SLIDER_FLAGS_CFM_ORTANG)
+	{
+		info->cfm[0] = m_cfmOrthoAng;
+		info->cfm[s] = m_cfmOrthoAng;
+	}
+
+	int nrow = 1; // last filled row
 	int srow;
-	// check linear limits linear
-	btScalar limit_err = btScalar(0.0);
-	int limit = 0;
+	btScalar limit_err;
+	int limit;
+	int powered;
+
+	// next two rows. 
+	// we want: velA + wA x relA == velB + wB x relB ... but this would
+	// result in three equations, so we project along two orthos to the slider axis
+
+	btTransform bodyA_trans = transA;
+	btTransform bodyB_trans = transB;
+	nrow++;
+	int s2 = nrow * s;
+	nrow++;
+	int s3 = nrow * s;
+	btVector3 tmpA(0,0,0), tmpB(0,0,0), relA(0,0,0), relB(0,0,0), c(0,0,0);
+	if(m_useOffsetForConstraintFrame)
+	{
+		// get vector from bodyB to frameB in WCS
+		relB = trB.getOrigin() - bodyB_trans.getOrigin();
+		// get its projection to slider axis
+		btVector3 projB = ax1 * relB.dot(ax1);
+		// get vector directed from bodyB to slider axis (and orthogonal to it)
+		btVector3 orthoB = relB - projB;
+		// same for bodyA
+		relA = trA.getOrigin() - bodyA_trans.getOrigin();
+		btVector3 projA = ax1 * relA.dot(ax1);
+		btVector3 orthoA = relA - projA;
+		// get desired offset between frames A and B along slider axis
+		btScalar sliderOffs = m_linPos - m_depth[0];
+		// desired vector from projection of center of bodyA to projection of center of bodyB to slider axis
+		btVector3 totalDist = projA + ax1 * sliderOffs - projB;
+		// get offset vectors relA and relB
+		relA = orthoA + totalDist * factA;
+		relB = orthoB - totalDist * factB;
+		// now choose average ortho to slider axis
+		p = orthoB * factA + orthoA * factB;
+		btScalar len2 = p.length2();
+		if(len2 > SIMD_EPSILON)
+		{
+			p /= btSqrt(len2);
+		}
+		else
+		{
+			p = trA.getBasis().getColumn(1);
+		}
+		// make one more ortho
+		q = ax1.cross(p);
+		// fill two rows
+		tmpA = relA.cross(p);
+		tmpB = relB.cross(p);
+		for (i=0; i<3; i++) info->m_J1angularAxis[s2+i] = tmpA[i];
+		for (i=0; i<3; i++) info->m_J2angularAxis[s2+i] = -tmpB[i];
+		tmpA = relA.cross(q);
+		tmpB = relB.cross(q);
+		if(hasStaticBody && getSolveAngLimit())
+		{ // to make constraint between static and dynamic objects more rigid
+			// remove wA (or wB) from equation if angular limit is hit
+			tmpB *= factB;
+			tmpA *= factA;
+		}
+		for (i=0; i<3; i++) info->m_J1angularAxis[s3+i] = tmpA[i];
+		for (i=0; i<3; i++) info->m_J2angularAxis[s3+i] = -tmpB[i];
+		for (i=0; i<3; i++) info->m_J1linearAxis[s2+i] = p[i];
+		for (i=0; i<3; i++) info->m_J1linearAxis[s3+i] = q[i];
+	}
+	else
+	{	// old way - maybe incorrect if bodies are not on the slider axis
+		// see discussion "Bug in slider constraint" http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=4024&start=0
+		c = bodyB_trans.getOrigin() - bodyA_trans.getOrigin();
+		btVector3 tmp = c.cross(p);
+		for (i=0; i<3; i++) info->m_J1angularAxis[s2+i] = factA*tmp[i];
+		for (i=0; i<3; i++) info->m_J2angularAxis[s2+i] = factB*tmp[i];
+		tmp = c.cross(q);
+		for (i=0; i<3; i++) info->m_J1angularAxis[s3+i] = factA*tmp[i];
+		for (i=0; i<3; i++) info->m_J2angularAxis[s3+i] = factB*tmp[i];
+
+		for (i=0; i<3; i++) info->m_J1linearAxis[s2+i] = p[i];
+		for (i=0; i<3; i++) info->m_J1linearAxis[s3+i] = q[i];
+	}
+	// compute two elements of right hand side
+
+	//	k = info->fps * info->erp * getSoftnessOrthoLin();
+	currERP = (m_flags & BT_SLIDER_FLAGS_ERP_ORTLIN) ? m_softnessOrthoLin : m_softnessOrthoLin * info->erp;
+	k = info->fps * currERP;
+
+	btScalar rhs = k * p.dot(ofs);
+	info->m_constraintError[s2] = rhs;
+	rhs = k * q.dot(ofs);
+	info->m_constraintError[s3] = rhs;
+	if(m_flags & BT_SLIDER_FLAGS_CFM_ORTLIN)
+	{
+		info->cfm[s2] = m_cfmOrthoLin;
+		info->cfm[s3] = m_cfmOrthoLin;
+	}
+
+
+	// check linear limits
+	limit_err = btScalar(0.0);
+	limit = 0;
 	if(getSolveLinLimit())
 	{
@@ -300,5 +467,5 @@
 		limit = (limit_err > btScalar(0.0)) ? 2 : 1;
 	}
-	int powered = 0;
+	powered = 0;
 	if(getPoweredLinMotor())
 	{
@@ -320,14 +487,30 @@
 		// a torque couple will result in limited slider-jointed free
 		// bodies from gaining angular momentum.
-		// the solution used here is to apply the constraint forces at the center of mass of the two bodies
-		btVector3 ltd;	// Linear Torque Decoupling vector (a torque)
-//		c = btScalar(0.5) * c;
-		ltd = c.cross(ax1);
-		info->m_J1angularAxis[srow+0] = factA*ltd[0];
-		info->m_J1angularAxis[srow+1] = factA*ltd[1];
-		info->m_J1angularAxis[srow+2] = factA*ltd[2];
-		info->m_J2angularAxis[srow+0] = factB*ltd[0];
-		info->m_J2angularAxis[srow+1] = factB*ltd[1];
-		info->m_J2angularAxis[srow+2] = factB*ltd[2];
+		if(m_useOffsetForConstraintFrame)
+		{
+			// this is needed only when bodyA and bodyB are both dynamic.
+			if(!hasStaticBody)
+			{
+				tmpA = relA.cross(ax1);
+				tmpB = relB.cross(ax1);
+				info->m_J1angularAxis[srow+0] = tmpA[0];
+				info->m_J1angularAxis[srow+1] = tmpA[1];
+				info->m_J1angularAxis[srow+2] = tmpA[2];
+				info->m_J2angularAxis[srow+0] = -tmpB[0];
+				info->m_J2angularAxis[srow+1] = -tmpB[1];
+				info->m_J2angularAxis[srow+2] = -tmpB[2];
+			}
+		}
+		else
+		{ // The old way. May be incorrect if bodies are not on the slider axis
+			btVector3 ltd;	// Linear Torque Decoupling vector (a torque)
+			ltd = c.cross(ax1);
+			info->m_J1angularAxis[srow+0] = factA*ltd[0];
+			info->m_J1angularAxis[srow+1] = factA*ltd[1];
+			info->m_J1angularAxis[srow+2] = factA*ltd[2];
+			info->m_J2angularAxis[srow+0] = factB*ltd[0];
+			info->m_J2angularAxis[srow+1] = factB*ltd[1];
+			info->m_J2angularAxis[srow+2] = factB*ltd[2];
+		}
 		// right-hand part
 		btScalar lostop = getLowerLinLimit();
@@ -340,10 +523,13 @@
 		info->m_lowerLimit[srow] = 0.;
 		info->m_upperLimit[srow] = 0.;
+		currERP = (m_flags & BT_SLIDER_FLAGS_ERP_LIMLIN) ? m_softnessLimLin : info->erp;
 		if(powered)
 		{
-            info->cfm[nrow] = btScalar(0.0); 
+			if(m_flags & BT_SLIDER_FLAGS_CFM_DIRLIN)
+			{
+				info->cfm[srow] = m_cfmDirLin;
+			}
 			btScalar tag_vel = getTargetLinMotorVelocity();
-			btScalar mot_fact = getMotorFactor(m_linPos, m_lowerLinLimit, m_upperLinLimit, tag_vel, info->fps * info->erp);
-//			info->m_constraintError[srow] += mot_fact * getTargetLinMotorVelocity();
+			btScalar mot_fact = getMotorFactor(m_linPos, m_lowerLinLimit, m_upperLinLimit, tag_vel, info->fps * currERP);
 			info->m_constraintError[srow] -= signFact * mot_fact * getTargetLinMotorVelocity();
 			info->m_lowerLimit[srow] += -getMaxLinMotorForce() * info->fps;
@@ -352,7 +538,10 @@
 		if(limit)
 		{
-			k = info->fps * info->erp;
+			k = info->fps * currERP;
 			info->m_constraintError[srow] += k * limit_err;
-			info->cfm[srow] = btScalar(0.0); // stop_cfm;
+			if(m_flags & BT_SLIDER_FLAGS_CFM_LIMLIN)
+			{
+				info->cfm[srow] = m_cfmLimLin;
+			}
 			if(lostop == histop) 
 			{	// limited low and high simultaneously
@@ -374,6 +563,6 @@
 			if(bounce > btScalar(0.0))
 			{
-				btScalar vel = m_rbA.getLinearVelocity().dot(ax1);
-				vel -= m_rbB.getLinearVelocity().dot(ax1);
+				btScalar vel = linVelA.dot(ax1);
+				vel -= linVelB.dot(ax1);
 				vel *= signFact;
 				// only apply bounce if the velocity is incoming, and if the
@@ -437,8 +626,12 @@
 			powered = 0;
 		}
+		currERP = (m_flags & BT_SLIDER_FLAGS_ERP_LIMANG) ? m_softnessLimAng : info->erp;
 		if(powered)
 		{
-            info->cfm[srow] = btScalar(0.0); 
-			btScalar mot_fact = getMotorFactor(m_angPos, m_lowerAngLimit, m_upperAngLimit, getTargetAngMotorVelocity(), info->fps * info->erp);
+			if(m_flags & BT_SLIDER_FLAGS_CFM_DIRANG)
+			{
+				info->cfm[srow] = m_cfmDirAng;
+			}
+			btScalar mot_fact = getMotorFactor(m_angPos, m_lowerAngLimit, m_upperAngLimit, getTargetAngMotorVelocity(), info->fps * currERP);
 			info->m_constraintError[srow] = mot_fact * getTargetAngMotorVelocity();
 			info->m_lowerLimit[srow] = -getMaxAngMotorForce() * info->fps;
@@ -447,7 +640,10 @@
 		if(limit)
 		{
-			k = info->fps * info->erp;
+			k = info->fps * currERP;
 			info->m_constraintError[srow] += k * limit_err;
-			info->cfm[srow] = btScalar(0.0); // stop_cfm;
+			if(m_flags & BT_SLIDER_FLAGS_CFM_LIMANG)
+			{
+				info->cfm[srow] = m_cfmLimAng;
+			}
 			if(lostop == histop) 
 			{
@@ -500,318 +696,162 @@
 		} // if(limit)
 	} // if angular limit or powered
-} // btSliderConstraint::getInfo2()
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep)
-{
-	if (m_useSolveConstraintObsolete)
-	{
-		m_timeStep = timeStep;
-		if(m_useLinearReferenceFrameA)
-		{
-			solveConstraintInt(m_rbA,bodyA, m_rbB,bodyB);
+}
+
+
+///override the default global value of a parameter (such as ERP or CFM), optionally provide the axis (0..5). 
+///If no axis is provided, it uses the default axis for this constraint.
+void btSliderConstraint::setParam(int num, btScalar value, int axis)
+{
+	switch(num)
+	{
+	case BT_CONSTRAINT_STOP_ERP :
+		if(axis < 1)
+		{
+			m_softnessLimLin = value;
+			m_flags |= BT_SLIDER_FLAGS_ERP_LIMLIN;
+		}
+		else if(axis < 3)
+		{
+			m_softnessOrthoLin = value;
+			m_flags |= BT_SLIDER_FLAGS_ERP_ORTLIN;
+		}
+		else if(axis == 3)
+		{
+			m_softnessLimAng = value;
+			m_flags |= BT_SLIDER_FLAGS_ERP_LIMANG;
+		}
+		else if(axis < 6)
+		{
+			m_softnessOrthoAng = value;
+			m_flags |= BT_SLIDER_FLAGS_ERP_ORTANG;
 		}
 		else
 		{
-			solveConstraintInt(m_rbB,bodyB, m_rbA,bodyA);
-		}
-	}
-} // btSliderConstraint::solveConstraint()
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::solveConstraintInt(btRigidBody& rbA, btSolverBody& bodyA,btRigidBody& rbB, btSolverBody& bodyB)
-{
-    int i;
-    // linear
-    btVector3 velA;
-	bodyA.getVelocityInLocalPointObsolete(m_relPosA,velA);
-    btVector3 velB;
-	bodyB.getVelocityInLocalPointObsolete(m_relPosB,velB);
-    btVector3 vel = velA - velB;
-	for(i = 0; i < 3; i++)
-    {
-		const btVector3& normal = m_jacLin[i].m_linearJointAxis;
-		btScalar rel_vel = normal.dot(vel);
-		// calculate positional error
-		btScalar depth = m_depth[i];
-		// get parameters
-		btScalar softness = (i) ? m_softnessOrthoLin : (m_solveLinLim ? m_softnessLimLin : m_softnessDirLin);
-		btScalar restitution = (i) ? m_restitutionOrthoLin : (m_solveLinLim ? m_restitutionLimLin : m_restitutionDirLin);
-		btScalar damping = (i) ? m_dampingOrthoLin : (m_solveLinLim ? m_dampingLimLin : m_dampingDirLin);
-		// calcutate and apply impulse
-		btScalar normalImpulse = softness * (restitution * depth / m_timeStep - damping * rel_vel) * m_jacLinDiagABInv[i];
-		btVector3 impulse_vector = normal * normalImpulse;
-		
-		//rbA.applyImpulse( impulse_vector, m_relPosA);
-		//rbB.applyImpulse(-impulse_vector, m_relPosB);
-		{
-			btVector3 ftorqueAxis1 = m_relPosA.cross(normal);
-			btVector3 ftorqueAxis2 = m_relPosB.cross(normal);
-			bodyA.applyImpulse(normal*rbA.getInvMass(), rbA.getInvInertiaTensorWorld()*ftorqueAxis1,normalImpulse);
-			bodyB.applyImpulse(normal*rbB.getInvMass(), rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-normalImpulse);
-		}
-
-
-
-		if(m_poweredLinMotor && (!i))
-		{ // apply linear motor
-			if(m_accumulatedLinMotorImpulse < m_maxLinMotorForce)
-			{
-				btScalar desiredMotorVel = m_targetLinMotorVelocity;
-				btScalar motor_relvel = desiredMotorVel + rel_vel;
-				normalImpulse = -motor_relvel * m_jacLinDiagABInv[i];
-				// clamp accumulated impulse
-				btScalar new_acc = m_accumulatedLinMotorImpulse + btFabs(normalImpulse);
-				if(new_acc  > m_maxLinMotorForce)
-				{
-					new_acc = m_maxLinMotorForce;
-				}
-				btScalar del = new_acc  - m_accumulatedLinMotorImpulse;
-				if(normalImpulse < btScalar(0.0))
-				{
-					normalImpulse = -del;
-				}
-				else
-				{
-					normalImpulse = del;
-				}
-				m_accumulatedLinMotorImpulse = new_acc;
-				// apply clamped impulse
-				impulse_vector = normal * normalImpulse;
-				//rbA.applyImpulse( impulse_vector, m_relPosA);
-				//rbB.applyImpulse(-impulse_vector, m_relPosB);
-
-				{
-					btVector3 ftorqueAxis1 = m_relPosA.cross(normal);
-					btVector3 ftorqueAxis2 = m_relPosB.cross(normal);
-					bodyA.applyImpulse(normal*rbA.getInvMass(), rbA.getInvInertiaTensorWorld()*ftorqueAxis1,normalImpulse);
-					bodyB.applyImpulse(normal*rbB.getInvMass(), rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-normalImpulse);
-				}
-
-
-
-			}
-		}
-    }
-	// angular 
-	// get axes in world space
-	btVector3 axisA =  m_calculatedTransformA.getBasis().getColumn(0);
-	btVector3 axisB =  m_calculatedTransformB.getBasis().getColumn(0);
-
-	btVector3 angVelA;
-	bodyA.getAngularVelocity(angVelA);
-	btVector3 angVelB;
-	bodyB.getAngularVelocity(angVelB);
-
-	btVector3 angVelAroundAxisA = axisA * axisA.dot(angVelA);
-	btVector3 angVelAroundAxisB = axisB * axisB.dot(angVelB);
-
-	btVector3 angAorthog = angVelA - angVelAroundAxisA;
-	btVector3 angBorthog = angVelB - angVelAroundAxisB;
-	btVector3 velrelOrthog = angAorthog-angBorthog;
-	//solve orthogonal angular velocity correction
-	btScalar len = velrelOrthog.length();
-	btScalar orthorImpulseMag = 0.f;
-
-	if (len > btScalar(0.00001))
-	{
-		btVector3 normal = velrelOrthog.normalized();
-		btScalar denom = rbA.computeAngularImpulseDenominator(normal) + rbB.computeAngularImpulseDenominator(normal);
-		//velrelOrthog *= (btScalar(1.)/denom) * m_dampingOrthoAng * m_softnessOrthoAng;
-		orthorImpulseMag = (btScalar(1.)/denom) * m_dampingOrthoAng * m_softnessOrthoAng;
-	}
-	//solve angular positional correction
-	btVector3 angularError = axisA.cross(axisB) *(btScalar(1.)/m_timeStep);
-	btVector3 angularAxis = angularError;
-	btScalar angularImpulseMag = 0;
-
-	btScalar len2 = angularError.length();
-	if (len2>btScalar(0.00001))
-	{
-		btVector3 normal2 = angularError.normalized();
-		btScalar denom2 = rbA.computeAngularImpulseDenominator(normal2) + rbB.computeAngularImpulseDenominator(normal2);
-		angularImpulseMag = (btScalar(1.)/denom2) * m_restitutionOrthoAng * m_softnessOrthoAng;
-		angularError *= angularImpulseMag;
-	}
-	// apply impulse
-	//rbA.applyTorqueImpulse(-velrelOrthog+angularError);
-	//rbB.applyTorqueImpulse(velrelOrthog-angularError);
-
-	bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*velrelOrthog,-orthorImpulseMag);
-	bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*velrelOrthog,orthorImpulseMag);
-	bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*angularAxis,angularImpulseMag);
-	bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*angularAxis,-angularImpulseMag);
-
-
-	btScalar impulseMag;
-	//solve angular limits
-	if(m_solveAngLim)
-	{
-		impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingLimAng + m_angDepth * m_restitutionLimAng / m_timeStep;
-		impulseMag *= m_kAngle * m_softnessLimAng;
-	}
-	else
-	{
-		impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingDirAng + m_angDepth * m_restitutionDirAng / m_timeStep;
-		impulseMag *= m_kAngle * m_softnessDirAng;
-	}
-	btVector3 impulse = axisA * impulseMag;
-	//rbA.applyTorqueImpulse(impulse);
-	//rbB.applyTorqueImpulse(-impulse);
-
-	bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*axisA,impulseMag);
-	bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*axisA,-impulseMag);
-
-
-
-	//apply angular motor
-	if(m_poweredAngMotor) 
-	{
-		if(m_accumulatedAngMotorImpulse < m_maxAngMotorForce)
-		{
-			btVector3 velrel = angVelAroundAxisA - angVelAroundAxisB;
-			btScalar projRelVel = velrel.dot(axisA);
-
-			btScalar desiredMotorVel = m_targetAngMotorVelocity;
-			btScalar motor_relvel = desiredMotorVel - projRelVel;
-
-			btScalar angImpulse = m_kAngle * motor_relvel;
-			// clamp accumulated impulse
-			btScalar new_acc = m_accumulatedAngMotorImpulse + btFabs(angImpulse);
-			if(new_acc  > m_maxAngMotorForce)
-			{
-				new_acc = m_maxAngMotorForce;
-			}
-			btScalar del = new_acc  - m_accumulatedAngMotorImpulse;
-			if(angImpulse < btScalar(0.0))
-			{
-				angImpulse = -del;
-			}
-			else
-			{
-				angImpulse = del;
-			}
-			m_accumulatedAngMotorImpulse = new_acc;
-			// apply clamped impulse
-			btVector3 motorImp = angImpulse * axisA;
-			//rbA.applyTorqueImpulse(motorImp);
-			//rbB.applyTorqueImpulse(-motorImp);
-
-			bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*axisA,angImpulse);
-			bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*axisA,-angImpulse);
-		}
-	}
-} // btSliderConstraint::solveConstraint()
-
-//-----------------------------------------------------------------------------
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::calculateTransforms(void){
-	if(m_useLinearReferenceFrameA || (!m_useSolveConstraintObsolete))
-	{
-		m_calculatedTransformA = m_rbA.getCenterOfMassTransform() * m_frameInA;
-		m_calculatedTransformB = m_rbB.getCenterOfMassTransform() * m_frameInB;
-	}
-	else
-	{
-		m_calculatedTransformA = m_rbB.getCenterOfMassTransform() * m_frameInB;
-		m_calculatedTransformB = m_rbA.getCenterOfMassTransform() * m_frameInA;
-	}
-	m_realPivotAInW = m_calculatedTransformA.getOrigin();
-	m_realPivotBInW = m_calculatedTransformB.getOrigin();
-	m_sliderAxis = m_calculatedTransformA.getBasis().getColumn(0); // along X
-	if(m_useLinearReferenceFrameA || m_useSolveConstraintObsolete)
-	{
-		m_delta = m_realPivotBInW - m_realPivotAInW;
-	}
-	else
-	{
-		m_delta = m_realPivotAInW - m_realPivotBInW;
-	}
-	m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis;
-    btVector3 normalWorld;
-    int i;
-    //linear part
-    for(i = 0; i < 3; i++)
-    {
-		normalWorld = m_calculatedTransformA.getBasis().getColumn(i);
-		m_depth[i] = m_delta.dot(normalWorld);
-    }
-} // btSliderConstraint::calculateTransforms()
- 
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::testLinLimits(void)
-{
-	m_solveLinLim = false;
-	m_linPos = m_depth[0];
-	if(m_lowerLinLimit <= m_upperLinLimit)
-	{
-		if(m_depth[0] > m_upperLinLimit)
-		{
-			m_depth[0] -= m_upperLinLimit;
-			m_solveLinLim = true;
-		}
-		else if(m_depth[0] < m_lowerLinLimit)
-		{
-			m_depth[0] -= m_lowerLinLimit;
-			m_solveLinLim = true;
+			btAssertConstrParams(0);
+		}
+		break;
+	case BT_CONSTRAINT_CFM :
+		if(axis < 1)
+		{
+			m_cfmDirLin = value;
+			m_flags |= BT_SLIDER_FLAGS_CFM_DIRLIN;
+		}
+		else if(axis == 3)
+		{
+			m_cfmDirAng = value;
+			m_flags |= BT_SLIDER_FLAGS_CFM_DIRANG;
 		}
 		else
 		{
-			m_depth[0] = btScalar(0.);
-		}
-	}
-	else
-	{
-		m_depth[0] = btScalar(0.);
-	}
-} // btSliderConstraint::testLinLimits()
-
-//-----------------------------------------------------------------------------
-
-void btSliderConstraint::testAngLimits(void)
-{
-	m_angDepth = btScalar(0.);
-	m_solveAngLim = false;
-	if(m_lowerAngLimit <= m_upperAngLimit)
-	{
-		const btVector3 axisA0 = m_calculatedTransformA.getBasis().getColumn(1);
-		const btVector3 axisA1 = m_calculatedTransformA.getBasis().getColumn(2);
-		const btVector3 axisB0 = m_calculatedTransformB.getBasis().getColumn(1);
-		btScalar rot = btAtan2Fast(axisB0.dot(axisA1), axisB0.dot(axisA0));  
-		m_angPos = rot;
-		if(rot < m_lowerAngLimit)
-		{
-			m_angDepth = rot - m_lowerAngLimit;
-			m_solveAngLim = true;
-		} 
-		else if(rot > m_upperAngLimit)
-		{
-			m_angDepth = rot - m_upperAngLimit;
-			m_solveAngLim = true;
-		}
-	}
-} // btSliderConstraint::testAngLimits()
-	
-//-----------------------------------------------------------------------------
-
-btVector3 btSliderConstraint::getAncorInA(void)
-{
-	btVector3 ancorInA;
-	ancorInA = m_realPivotAInW + (m_lowerLinLimit + m_upperLinLimit) * btScalar(0.5) * m_sliderAxis;
-	ancorInA = m_rbA.getCenterOfMassTransform().inverse() * ancorInA;
-	return ancorInA;
-} // btSliderConstraint::getAncorInA()
-
-//-----------------------------------------------------------------------------
-
-btVector3 btSliderConstraint::getAncorInB(void)
-{
-	btVector3 ancorInB;
-	ancorInB = m_frameInB.getOrigin();
-	return ancorInB;
-} // btSliderConstraint::getAncorInB();
+			btAssertConstrParams(0);
+		}
+		break;
+	case BT_CONSTRAINT_STOP_CFM :
+		if(axis < 1)
+		{
+			m_cfmLimLin = value;
+			m_flags |= BT_SLIDER_FLAGS_CFM_LIMLIN;
+		}
+		else if(axis < 3)
+		{
+			m_cfmOrthoLin = value;
+			m_flags |= BT_SLIDER_FLAGS_CFM_ORTLIN;
+		}
+		else if(axis == 3)
+		{
+			m_cfmLimAng = value;
+			m_flags |= BT_SLIDER_FLAGS_CFM_LIMANG;
+		}
+		else if(axis < 6)
+		{
+			m_cfmOrthoAng = value;
+			m_flags |= BT_SLIDER_FLAGS_CFM_ORTANG;
+		}
+		else
+		{
+			btAssertConstrParams(0);
+		}
+		break;
+	}
+}
+
+///return the local value of parameter
+btScalar btSliderConstraint::getParam(int num, int axis) const 
+{
+	btScalar retVal(SIMD_INFINITY);
+	switch(num)
+	{
+	case BT_CONSTRAINT_STOP_ERP :
+		if(axis < 1)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_ERP_LIMLIN);
+			retVal = m_softnessLimLin;
+		}
+		else if(axis < 3)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_ERP_ORTLIN);
+			retVal = m_softnessOrthoLin;
+		}
+		else if(axis == 3)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_ERP_LIMANG);
+			retVal = m_softnessLimAng;
+		}
+		else if(axis < 6)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_ERP_ORTANG);
+			retVal = m_softnessOrthoAng;
+		}
+		else
+		{
+			btAssertConstrParams(0);
+		}
+		break;
+	case BT_CONSTRAINT_CFM :
+		if(axis < 1)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_CFM_DIRLIN);
+			retVal = m_cfmDirLin;
+		}
+		else if(axis == 3)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_CFM_DIRANG);
+			retVal = m_cfmDirAng;
+		}
+		else
+		{
+			btAssertConstrParams(0);
+		}
+		break;
+	case BT_CONSTRAINT_STOP_CFM :
+		if(axis < 1)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_CFM_LIMLIN);
+			retVal = m_cfmLimLin;
+		}
+		else if(axis < 3)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_CFM_ORTLIN);
+			retVal = m_cfmOrthoLin;
+		}
+		else if(axis == 3)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_CFM_LIMANG);
+			retVal = m_cfmLimAng;
+		}
+		else if(axis < 6)
+		{
+			btAssertConstrParams(m_flags & BT_SLIDER_FLAGS_CFM_ORTANG);
+			retVal = m_cfmOrthoAng;
+		}
+		else
+		{
+			btAssertConstrParams(0);
+		}
+		break;
+	}
+	return retVal;
+}
+
+
+
