public void SetupFrictionConstraint(ref SolverConstraint solverConstraint, ref Vector3 normalAxis, RigidBody solverBodyA, RigidBody solverBodyB,
								ManifoldPoint cp, ref Vector3 rel_pos1, ref Vector3 rel_pos2,
								CollisionObject colObj0, CollisionObject colObj1, float relaxation,
								float desiredVelocity, float cfmSlip)
		{
			RigidBody body0 = RigidBody.Upcast(colObj0);
			RigidBody body1 = RigidBody.Upcast(colObj1);

			solverConstraint.m_contactNormal = normalAxis;

			solverConstraint.m_solverBodyA = body0 != null ? body0 : GetFixedBody();
			solverConstraint.m_solverBodyB = body1 != null ? body1 : GetFixedBody();

			solverConstraint.m_friction = cp.GetCombinedFriction();
			//solverConstraint.m_originalContactPoint = 0;

			solverConstraint.m_appliedImpulse = 0f;
			solverConstraint.m_appliedPushImpulse = 0f;

			{
				Vector3 ftorqueAxis1 = Vector3.Cross(rel_pos1, solverConstraint.m_contactNormal);
				solverConstraint.m_relpos1CrossNormal = ftorqueAxis1;
				solverConstraint.m_angularComponentA = body0 != null ? Vector3.TransformNormal(ftorqueAxis1, body0.GetInvInertiaTensorWorld()) * body0.GetAngularFactor() : Vector3.Zero;
			}
			{
				Vector3 ftorqueAxis1 = Vector3.Cross(rel_pos2, -solverConstraint.m_contactNormal);
				solverConstraint.m_relpos2CrossNormal = ftorqueAxis1;
				solverConstraint.m_angularComponentB = body1 != null ? Vector3.TransformNormal(ftorqueAxis1, body1.GetInvInertiaTensorWorld()) * body1.GetAngularFactor() : Vector3.Zero;
			}

#if COMPUTE_IMPULSE_DENOM
	        float denom0 = rb0.computeImpulseDenominator(pos1,solverConstraint.m_contactNormal);
	        float denom1 = rb1.computeImpulseDenominator(pos2,solverConstraint.m_contactNormal);
#else
			Vector3 vec;
			float denom0 = 0f;
			float denom1 = 0f;
			if (body0 != null)
			{
				vec = Vector3.Cross(solverConstraint.m_angularComponentA, rel_pos1);
				denom0 = body0.GetInvMass() + Vector3.Dot(normalAxis, vec);
			}
			if (body1 != null)
			{
				vec = Vector3.Cross(-solverConstraint.m_angularComponentB, rel_pos2);
				denom1 = body1.GetInvMass() + Vector3.Dot(normalAxis, vec);
			}


#endif //COMPUTE_IMPULSE_DENOM
			float denom = relaxation / (denom0 + denom1);
			solverConstraint.m_jacDiagABInv = denom;
			MathUtil.SanityCheckFloat(solverConstraint.m_jacDiagABInv);
#if _USE_JACOBIAN
	        solverConstraint.m_jac =  new JacobianEntry (
		        ref rel_pos1,ref rel_pos2,ref solverConstraint.m_contactNormal,
		        body0.getInvInertiaDiagLocal(),
		        body0.getInvMass(),
		        body1.getInvInertiaDiagLocal(),
		        body1.getInvMass());
#endif //_USE_JACOBIAN


			{
				float rel_vel;
				float vel1Dotn = Vector3.Dot(solverConstraint.m_contactNormal, body0 != null ? body0.GetLinearVelocity() : Vector3.Zero)
					+ Vector3.Dot(solverConstraint.m_relpos1CrossNormal, body0 != null ? body0.GetAngularVelocity() : Vector3.Zero);
				float vel2Dotn = -Vector3.Dot(solverConstraint.m_contactNormal, body1 != null ? body1.GetLinearVelocity() : Vector3.Zero)
					+ Vector3.Dot(solverConstraint.m_relpos2CrossNormal, body1 != null ? body1.GetAngularVelocity() : Vector3.Zero);

				rel_vel = vel1Dotn + vel2Dotn;

				//float positionalError = 0f;

				float velocityError = desiredVelocity - rel_vel;
				float damper = 1f;
				float velocityImpulse = (velocityError * solverConstraint.m_jacDiagABInv) * damper;
				solverConstraint.m_rhs = velocityImpulse;
				solverConstraint.m_cfm = cfmSlip;
				solverConstraint.m_lowerLimit = 0;
				solverConstraint.m_upperLimit = 1e10f;
			}
		}
		protected void SetupContactConstraint(ref SolverConstraint solverConstraint, CollisionObject colObj0, CollisionObject colObj1, ManifoldPoint cp,
								ContactSolverInfo infoGlobal, ref Vector3 vel, ref float rel_vel, ref float relaxation,
								ref Vector3 rel_pos1, ref Vector3 rel_pos2)
		{
			RigidBody rb0 = RigidBody.Upcast(colObj0);
			RigidBody rb1 = RigidBody.Upcast(colObj1);

			Vector3 pos1 = cp.GetPositionWorldOnA();
			Vector3 pos2 = cp.GetPositionWorldOnB();

			rel_pos1 = pos1 - colObj0.GetWorldTransform().Translation;
			rel_pos2 = pos2 - colObj1.GetWorldTransform().Translation;

			relaxation = 1f;

			Vector3 torqueAxis0 = Vector3.Cross(rel_pos1, cp.m_normalWorldOnB);
			solverConstraint.m_angularComponentA = rb0 != null ? Vector3.TransformNormal(torqueAxis0, rb0.GetInvInertiaTensorWorld()) * rb0.GetAngularFactor() : Vector3.Zero;
			Vector3 torqueAxis1 = Vector3.Cross(rel_pos2, cp.GetNormalWorldOnB());
			solverConstraint.m_angularComponentB = rb1 != null ? Vector3.TransformNormal(-torqueAxis1, rb1.GetInvInertiaTensorWorld()) * rb1.GetAngularFactor() : Vector3.Zero;

			{
#if COMPUTE_IMPULSE_DENOM
		        float denom0 = rb0.computeImpulseDenominator(pos1,cp.m_normalWorldOnB);
		        float denom1 = rb1.computeImpulseDenominator(pos2,cp.m_normalWorldOnB);
#else
				Vector3 vec;
				float denom0 = 0f;
				float denom1 = 0f;
				if (rb0 != null)
				{
					vec = Vector3.Cross((solverConstraint.m_angularComponentA), rel_pos1);
					denom0 = rb0.GetInvMass() + Vector3.Dot(cp.GetNormalWorldOnB(), vec);
				}
				if (rb1 != null)
				{
					vec = Vector3.Cross((-solverConstraint.m_angularComponentB), rel_pos2);
					denom1 = rb1.GetInvMass() + Vector3.Dot(cp.GetNormalWorldOnB(), vec);
				}
#endif //COMPUTE_IMPULSE_DENOM

				float denom = relaxation / (denom0 + denom1);
				MathUtil.SanityCheckFloat(denom);
				solverConstraint.m_jacDiagABInv = denom;
			}

			solverConstraint.m_contactNormal = cp.m_normalWorldOnB;
			solverConstraint.m_relpos1CrossNormal = Vector3.Cross(rel_pos1, cp.m_normalWorldOnB);
			solverConstraint.m_relpos2CrossNormal = Vector3.Cross(rel_pos2, -cp.m_normalWorldOnB);



			Vector3 vel1 = rb0 != null ? rb0.GetVelocityInLocalPoint(ref rel_pos1) : Vector3.Zero;
			Vector3 vel2 = rb1 != null ? rb1.GetVelocityInLocalPoint(ref rel_pos2) : Vector3.Zero;

			vel = vel1 - vel2;

			rel_vel = Vector3.Dot(cp.GetNormalWorldOnB(), vel);

			float penetration = cp.GetDistance() + infoGlobal.m_linearSlop;


			solverConstraint.m_friction = cp.GetCombinedFriction();

			float restitution = 0f;

			if (cp.GetLifeTime() > infoGlobal.m_restingContactRestitutionThreshold)
			{
				restitution = 0f;
			}
			else
			{
				restitution = RestitutionCurve(rel_vel, cp.GetCombinedResitution());
				if (restitution <= 0f)
				{
					restitution = 0f;
				}
			}

			///warm starting (or zero if disabled)
			if (TestSolverMode(infoGlobal.m_solverMode, SolverMode.SOLVER_USE_WARMSTARTING))
			{
				solverConstraint.m_appliedImpulse = cp.GetAppliedImpulse() * infoGlobal.m_warmstartingFactor;
				if (rb0 != null)
				{
					Vector3 contactNormalTemp = solverConstraint.m_contactNormal;
					Vector3.Multiply(ref contactNormalTemp, rb0.GetInvMass(), out contactNormalTemp);
					rb0.InternalApplyImpulse(ref contactNormalTemp, ref solverConstraint.m_angularComponentA, solverConstraint.m_appliedImpulse);
				}
				if (rb1 != null)
				{
					Vector3 contactNormalTemp = solverConstraint.m_contactNormal;
					Vector3.Multiply(ref contactNormalTemp, rb1.GetInvMass(), out contactNormalTemp);
					Vector3 negAngular = -solverConstraint.m_angularComponentB;
					rb1.InternalApplyImpulse(ref contactNormalTemp, ref negAngular, -solverConstraint.m_appliedImpulse);
				}
			}
			else
			{
				solverConstraint.m_appliedImpulse = 0f;
			}
			solverConstraint.m_appliedPushImpulse = 0f;
			{
				float rel_vel2 = 0f;
				float vel1Dotn = Vector3.Dot(solverConstraint.m_contactNormal, (rb0 != null ? rb0.GetLinearVelocity() : Vector3.Zero))
					+ Vector3.Dot(solverConstraint.m_relpos1CrossNormal, (rb0 != null ? rb0.GetAngularVelocity() : Vector3.Zero));
				float vel2Dotn = -Vector3.Dot(solverConstraint.m_contactNormal, (rb1 != null ? rb1.GetLinearVelocity() : Vector3.Zero))
					+ Vector3.Dot(solverConstraint.m_relpos2CrossNormal, (rb1 != null ? rb1.GetAngularVelocity() : Vector3.Zero));

				rel_vel2 = vel1Dotn + vel2Dotn;

				float positionalError = 0f;
				positionalError = -penetration * infoGlobal.m_erp / infoGlobal.m_timeStep;
				float velocityError = restitution - rel_vel2;// * damping;
				float penetrationImpulse = positionalError * solverConstraint.m_jacDiagABInv;
				float velocityImpulse = velocityError * solverConstraint.m_jacDiagABInv;
				if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold))
				{
					//combine position and velocity into rhs
					solverConstraint.m_rhs = penetrationImpulse + velocityImpulse;
					solverConstraint.m_rhsPenetration = 0f;
				}
				else
				{
					//split position and velocity into rhs and m_rhsPenetration
					solverConstraint.m_rhs = velocityImpulse;
					solverConstraint.m_rhsPenetration = penetrationImpulse;
				}
				solverConstraint.m_cfm = 0f;
				solverConstraint.m_lowerLimit = 0;
				solverConstraint.m_upperLimit = 1e10f;
			}
		}