A vehicle raycast-based physics controller ported from Bullet physics. It manages wheels, suspension raycasts, friction, steering, and applies impulses to the chassis and ground bodies to simulate vehicle behavior.
/* * C# / XNA port of Bullet (c) 2011 Mark Neale <[email protected]> * * Bullet Continuous Collision Detection and Physics Library * Copyright (c) 2003-2008 Erwin Coumans http://www.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. */ // Adapted from BulletXNA 73b2e1b9: namespace isolation, no XNA rendering, one type per file. #nullable disable #pragma warning disable CS1591, CS0168, CS0219, CS0414, CS0660, CS0661, CS0659, CS0693, CS8981, CS0108, CS0114 using System; using System.Collections.Generic; using System.Diagnostics; using HumanoidRetargeter.Core.Formats.Mmd.Bullet.BulletCollision; using HumanoidRetargeter.Core.Formats.Mmd.Bullet.LinearMath; namespace HumanoidRetargeter.Core.Formats.Mmd.Bullet.BulletDynamics; internal class RaycastVehicle : IActionInterface { public RaycastVehicle(VehicleTuning tuning, RigidBody chassis, IVehicleRaycaster raycaster) { m_vehicleRaycaster = raycaster; m_pitchControl = 0f; m_chassisBody = chassis; m_indexRightAxis = 0; m_indexUpAxis = 1; m_indexForwardAxis = 2; DefaultInit(ref tuning); } public virtual void UpdateAction(CollisionWorld collisionWorld, float step) { UpdateVehicle(step); } public virtual void DebugDraw(IDebugDraw debugDrawer) { for (int v = 0; v < GetNumWheels(); v++) { IndexedVector3 wheelColor = new IndexedVector3(0, 1, 1); if (GetWheelInfo(v).m_raycastInfo.m_isInContact) { wheelColor = new IndexedVector3(0, 0, 1); } else { wheelColor = new IndexedVector3(1, 0, 1); } IndexedVector3 wheelPosWS = GetWheelInfo(v).m_worldTransform._origin; IndexedMatrix temp = GetWheelInfo(v).m_worldTransform; IndexedVector3 axle = new IndexedVector3(GetWheelInfo(v).m_worldTransform._basis._el0[GetRightAxis()], GetWheelInfo(v).m_worldTransform._basis._el1[GetRightAxis()], GetWheelInfo(v).m_worldTransform._basis._el2[GetRightAxis()]); //debug wheels (cylinders) debugDrawer.DrawLine(wheelPosWS, wheelPosWS + axle, wheelColor); debugDrawer.DrawLine(wheelPosWS, GetWheelInfo(v).m_raycastInfo.m_contactPointWS, wheelColor); } } public IndexedMatrix GetChassisWorldTransform() { /*if (getRigidBody()->getMotionState()) { btTransform chassisWorldTrans; getRigidBody()->getMotionState()->getWorldTransform(chassisWorldTrans); return chassisWorldTrans; } */ return GetRigidBody().GetCenterOfMassTransform(); } public float RayCast(WheelInfo wheel) { UpdateWheelTransformsWS(wheel, false); float depth = -1; float raylen = wheel.GetSuspensionRestLength() + wheel.m_wheelsRadius; IndexedVector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen); IndexedVector3 source = wheel.m_raycastInfo.m_hardPointWS; wheel.m_raycastInfo.m_contactPointWS = source + rayvector; IndexedVector3 target = wheel.m_raycastInfo.m_contactPointWS; float param = 0f; VehicleRaycasterResult rayResults = new VehicleRaycasterResult(); { } Object object1 = m_vehicleRaycaster.CastRay(ref source, ref target, ref rayResults); if (object1 != null && (object1 as RigidBody).m_debugBodyId != 1) { int ibreak = 0; } //{ // IndexedVector3 from1 = new IndexedVector3(0.7957098f, -9.13606f, 1.794605f); // IndexedVector3 to1 = new IndexedVector3(0.886791f, -10.23207f, 1.815941f); // VehicleRaycasterResult results1 = new VehicleRaycasterResult(); // Object o2 = m_vehicleRaycaster.castRay(ref from1, ref to1, ref results1); // IndexedVector3 from2 = new IndexedVector3(0.7957281f, -9.136093f, 1.794625f); // IndexedVector3 to2 = new IndexedVector3(0.8867911f, -10.23211f, 1.815956f); // VehicleRaycasterResult results2 = new VehicleRaycasterResult(); // Object o3 = m_vehicleRaycaster.castRay(ref from2, ref to2, ref results2); // if (Math.Abs(results1.m_distFraction - results2.m_distFraction) > 0.1f) // { // int ibreak = 0; // } //} wheel.m_raycastInfo.m_groundObject = null; if (object1 != null) { 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 = s_fixedObject; //wheel.m_raycastInfo.m_groundObject = object; float hitDistance = param * raylen; wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelsRadius; //clamp on max suspension travel float minSuspensionLength = wheel.GetSuspensionRestLength() - wheel.m_maxSuspensionTravelCm * 0.01f; float maxSuspensionLength = wheel.GetSuspensionRestLength() + wheel.m_maxSuspensionTravelCm * 0.01f; 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; } if (Math.Abs(wheel.m_raycastInfo.m_suspensionLength - wheel.m_raycastInfo.m_suspensionLengthBak) > 0.1f) { int ibreak = 0; } wheel.m_raycastInfo.m_suspensionLengthBak = wheel.m_raycastInfo.m_suspensionLength; wheel.m_raycastInfo.m_contactPointWS = rayResults.m_hitPointInWorld; float denominator = IndexedVector3.Dot(wheel.m_raycastInfo.m_contactNormalWS, wheel.m_raycastInfo.m_wheelDirectionWS); IndexedVector3 chassis_velocity_at_contactPoint; IndexedVector3 relpos = wheel.m_raycastInfo.m_contactPointWS - GetRigidBody().GetCenterOfMassPosition(); chassis_velocity_at_contactPoint = GetRigidBody().GetVelocityInLocalPoint(ref relpos); float projVel = IndexedVector3.Dot(wheel.m_raycastInfo.m_contactNormalWS, chassis_velocity_at_contactPoint); if (projVel > 1f) { int ibreak = 0; } if (denominator >= -0.1f) { wheel.m_suspensionRelativeVelocity = 0f; wheel.m_clippedInvContactDotSuspension = 1.0f / 0.1f; } else { float inv = -1f / 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 = 0.0f; wheel.m_raycastInfo.m_contactNormalWS = -wheel.m_raycastInfo.m_wheelDirectionWS; wheel.m_clippedInvContactDotSuspension = 1.0f; } return depth; } public virtual void UpdateVehicle(float step) { int numWheels = GetNumWheels(); { for (int i = 0; i < numWheels; i++) { UpdateWheelTransform(i, false); } } m_currentVehicleSpeedKmHour = 3.6f * GetRigidBody().GetLinearVelocity().Length(); IndexedMatrix chassisTrans = GetChassisWorldTransform(); IndexedVector3 forwardW = new IndexedVector3(chassisTrans._basis[0, m_indexForwardAxis], chassisTrans._basis[1, m_indexForwardAxis], chassisTrans._basis[2, m_indexForwardAxis]); if (IndexedVector3.Dot(forwardW, GetRigidBody().GetLinearVelocity()) < 0f) { m_currentVehicleSpeedKmHour *= -1f; } // // simulate suspension // float[] depthData = new float[numWheels]; float depth = 0f; for (int i = 0; i < numWheels; i++) { depth = RayCast(m_wheelInfo[i]); depthData[i] = depth; if (m_wheelInfo[i].m_raycastInfo.m_isInContact == false) { int ibreak = 0; depth = RayCast(m_wheelInfo[i]); depthData[i] = depth; } } UpdateSuspension(step); for (int i = 0; i < numWheels; ++i) { WheelInfo wheel = m_wheelInfo[i]; //apply suspension force float suspensionForce = wheel.m_wheelsSuspensionForce; if (suspensionForce > wheel.m_maxSuspensionForce) { suspensionForce = wheel.m_maxSuspensionForce; } IndexedVector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; IndexedVector3 relpos = wheel.m_raycastInfo.m_contactPointWS - GetRigidBody().GetCenterOfMassPosition(); if (impulse.Y < 30 || impulse.Y > 40) { int ibreak = 0; } //impulse = new IndexedVector3(0f, 1f, 0f); GetRigidBody().ApplyImpulse(ref impulse, ref relpos); } UpdateFriction(step); for (int i = 0; i < numWheels; ++i) { WheelInfo wheel = m_wheelInfo[i]; IndexedVector3 relpos = wheel.m_raycastInfo.m_hardPointWS - GetRigidBody().GetCenterOfMassPosition(); IndexedVector3 vel = GetRigidBody().GetVelocityInLocalPoint(ref relpos); if (wheel.m_raycastInfo.m_isInContact) { IndexedMatrix chassisWorldTransform = GetChassisWorldTransform(); IndexedVector3 fwd = new IndexedVector3(chassisWorldTransform._basis[0, m_indexForwardAxis], chassisWorldTransform._basis[1, m_indexForwardAxis], chassisWorldTransform._basis[2, m_indexForwardAxis]); float proj = IndexedVector3.Dot(fwd, wheel.m_raycastInfo.m_contactNormalWS); fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj; float proj2 = IndexedVector3.Dot(fwd, 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 *= 0.99f; //damping of rotation when not in contact } } public void ResetSuspension() { foreach (WheelInfo wheel in m_wheelInfo) { wheel.m_raycastInfo.m_suspensionLength = wheel.GetSuspensionRestLength(); wheel.m_suspensionRelativeVelocity = 0f; wheel.m_raycastInfo.m_contactNormalWS = -wheel.m_raycastInfo.m_wheelDirectionWS; //wheel_info.setContactFriction(float(0.0)); wheel.m_clippedInvContactDotSuspension = 1f; } } public float GetSteeringValue(int wheel) { return GetWheelInfo(wheel).m_steering; } public void SetSteeringValue(float steering, int wheel) { { } WheelInfo wheelInfo = GetWheelInfo(wheel); wheelInfo.m_steering = steering; } public void ApplyEngineForce(float force, int wheel) { { } WheelInfo wheelInfo = GetWheelInfo(wheel); wheelInfo.m_engineForce = force; } public IndexedMatrix GetWheelTransformWS(int wheelIndex) { { } WheelInfo wheel = m_wheelInfo[wheelIndex]; return wheel.m_worldTransform; } public void UpdateWheelTransform(int wheelIndex, bool interpolatedTransform) { WheelInfo wheel = m_wheelInfo[wheelIndex]; UpdateWheelTransformsWS(wheel, interpolatedTransform); IndexedVector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS; IndexedVector3 right = wheel.m_raycastInfo.m_wheelAxleWS; IndexedVector3 fwd = IndexedVector3.Cross(up, right); fwd.Normalize(); // up = right.cross(fwd); // up.normalize(); //rotate around steering over de wheelAxleWS float steering = wheel.m_steering; IndexedQuaternion steeringOrn = new IndexedQuaternion(up, steering); //wheel.m_steering); IndexedBasisMatrix steeringMat = new IndexedBasisMatrix(ref steeringOrn); IndexedQuaternion rotatingOrn = new IndexedQuaternion(right, -wheel.m_rotation); IndexedBasisMatrix rotatingMat = new IndexedBasisMatrix(ref rotatingOrn); IndexedBasisMatrix basis2 = new IndexedBasisMatrix(right.X, fwd.X, up.X, right.Y, fwd.Y, up.Y, right.Z, fwd.Z, up.Z); // FIXME MAN - MATRIX ORDER //wheel.m_worldTransform = steeringMat * rotatingMat * basis2; wheel.m_worldTransform._basis = steeringMat * rotatingMat * basis2; wheel.m_worldTransform._origin = wheel.m_raycastInfo.m_hardPointWS + (wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength); } public WheelInfo AddWheel(ref IndexedVector3 connectionPointCS0, ref IndexedVector3 wheelDirectionCS0, ref IndexedVector3 wheelAxleCS, float suspensionRestLength, float wheelRadius, VehicleTuning tuning, bool isFrontWheel) { WheelInfoConstructionInfo ci = new WheelInfoConstructionInfo(); ci.m_chassisConnectionCS = connectionPointCS0; 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; WheelInfo wheel = new WheelInfo(ref ci); m_wheelInfo.Add(wheel); UpdateWheelTransformsWS(wheel, false); UpdateWheelTransform(GetNumWheels() - 1, false); return wheel; } public int GetNumWheels() { return m_wheelInfo.Count; } public virtual WheelInfo GetWheelInfo(int index) { return m_wheelInfo[index]; } public void UpdateWheelTransformsWS(WheelInfo wheel, bool interpolatedTransform) { wheel.m_raycastInfo.m_isInContact = false; IndexedMatrix chassisTrans = GetChassisWorldTransform(); if (interpolatedTransform && (GetRigidBody().GetMotionState() != null)) { GetRigidBody().GetMotionState().GetWorldTransform(out chassisTrans); } wheel.m_raycastInfo.m_hardPointWS = chassisTrans * wheel.m_chassisConnectionPointCS; wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans._basis * wheel.m_wheelDirectionCS; wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans._basis * wheel.m_wheelAxleCS; } public void SetBrake(float brake, int wheelIndex) { { } GetWheelInfo(wheelIndex).m_brake = brake; } public void SetPitchControl(float pitch) { m_pitchControl = pitch; } public void UpdateSuspension(float deltaTime) { float chassisMass = 1f / m_chassisBody.GetInvMass(); for (int w_it = 0; w_it < GetNumWheels(); w_it++) { WheelInfo wheel_info = m_wheelInfo[w_it]; if (wheel_info.m_raycastInfo.m_isInContact) { float force = 0f; // Spring { float susp_length = wheel_info.GetSuspensionRestLength(); float current_length = wheel_info.m_raycastInfo.m_suspensionLength; float length_diff = (susp_length - current_length); //length_diff = current_length; force = wheel_info.m_suspensionStiffness * length_diff * wheel_info.m_clippedInvContactDotSuspension; } // Damper { float projected_rel_vel = wheel_info.m_suspensionRelativeVelocity; { float susp_damping; if (projected_rel_vel < 0f) { 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 < 0f) { wheel_info.m_wheelsSuspensionForce = 0f; } } else { wheel_info.m_wheelsSuspensionForce = 0f; } } } public virtual void UpdateFriction(float timeStep) { //calculate the impulse, so that the wheels don't move sidewards int numWheel = GetNumWheels(); if (numWheel == 0) 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 < numWheel; i++) { WheelInfo wheelInfo = m_wheelInfo[i]; RigidBody groundObject = wheelInfo.m_raycastInfo.m_groundObject as RigidBody; if (groundObject != null) { numWheelsOnGround++; } m_sideImpulse[i] = 0f; m_forwardImpulse[i] = 0f; } if (numWheelsOnGround != 4) { int ibreak = 0; } { //foreach(WheelInfo wheelInfo in m_wheelInfo) for (int i = 0; i < numWheel; ++i) { WheelInfo wheelInfo = m_wheelInfo[i]; RigidBody groundObject = wheelInfo.m_raycastInfo.m_groundObject as RigidBody; if (groundObject != null) { IndexedMatrix wheelTrans = GetWheelTransformWS(i); IndexedBasisMatrix wheelBasis0 = wheelTrans._basis; m_axle[i] = new IndexedVector3(wheelBasis0._el0[m_indexRightAxis], wheelBasis0._el1[m_indexRightAxis], wheelBasis0._el2[m_indexRightAxis]); IndexedVector3 surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; float proj = IndexedVector3.Dot(m_axle[i], surfNormalWS); m_axle[i] -= surfNormalWS * proj; m_axle[i].Normalize(); m_forwardWS[i] = IndexedVector3.Cross(surfNormalWS, m_axle[i]); m_forwardWS[i].Normalize(); IndexedVector3 tempAxle = m_axle[i]; float tempImpulse = m_sideImpulse[i]; ContactConstraint.ResolveSingleBilateral(m_chassisBody, ref wheelInfo.m_raycastInfo.m_contactPointWS, groundObject, ref wheelInfo.m_raycastInfo.m_contactPointWS, 0f, ref tempAxle, ref tempImpulse, timeStep); m_sideImpulse[i] = (tempImpulse * sideFrictionStiffness2); } } } float sideFactor = 1f; float fwdFactor = 0.5f; bool sliding = false; { for (int wheel = 0; wheel < numWheel; wheel++) { WheelInfo wheelInfo = m_wheelInfo[wheel]; RigidBody groundObject = wheelInfo.m_raycastInfo.m_groundObject as RigidBody; float rollingFriction = 0f; if (groundObject != null) { if (wheelInfo.m_engineForce != 0.0f) { rollingFriction = wheelInfo.m_engineForce * timeStep; } else { float defaultRollingFrictionImpulse = 0f; float maxImpulse = (wheelInfo.m_brake != 0f) ? wheelInfo.m_brake : defaultRollingFrictionImpulse; IndexedVector3 tempWheel = m_forwardWS[wheel]; WheelContactPoint contactPt = new WheelContactPoint(m_chassisBody, groundObject, ref wheelInfo.m_raycastInfo.m_contactPointWS, ref tempWheel, maxImpulse); m_forwardWS[wheel] = tempWheel; rollingFriction = CalcRollingFriction(contactPt); } } //switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break) m_forwardImpulse[wheel] = 0f; m_wheelInfo[wheel].m_skidInfo = 1f; if (groundObject != null) { m_wheelInfo[wheel].m_skidInfo = 1f; float maximp = wheelInfo.m_wheelsSuspensionForce * timeStep * wheelInfo.m_frictionSlip; float maximpSide = maximp; float maximpSquared = maximp * maximpSide; m_forwardImpulse[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep; float x = (m_forwardImpulse[wheel]) * fwdFactor; float y = (m_sideImpulse[wheel]) * sideFactor; float impulseSquared = (x * x + y * y); if (impulseSquared > maximpSquared) { sliding = true; float factor = (float)(maximp / Math.Sqrt(impulseSquared)); m_wheelInfo[wheel].m_skidInfo *= factor; } } } } if (sliding) { for (int wheel = 0; wheel < numWheel; wheel++) { if (m_sideImpulse[wheel] != 0f) { if (m_wheelInfo[wheel].m_skidInfo < 1f) { m_forwardImpulse[wheel] *= m_wheelInfo[wheel].m_skidInfo; m_sideImpulse[wheel] *= m_wheelInfo[wheel].m_skidInfo; } } } } // apply the impulses { for (int wheel = 0; wheel < numWheel; wheel++) { WheelInfo wheelInfo = m_wheelInfo[wheel]; IndexedVector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS - m_chassisBody.GetCenterOfMassPosition(); if (m_forwardImpulse[wheel] > 5f || m_sideImpulse[wheel] > 5f) { int ibreak = 0; } if (m_forwardImpulse[wheel] != 0f) { m_chassisBody.ApplyImpulse(m_forwardWS[wheel] * (m_forwardImpulse[wheel]), rel_pos); } if (m_sideImpulse[wheel] != 0f) { RigidBody groundObject = m_wheelInfo[wheel].m_raycastInfo.m_groundObject as RigidBody; IndexedVector3 rel_pos2 = wheelInfo.m_raycastInfo.m_contactPointWS - groundObject.GetCenterOfMassPosition(); IndexedVector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel]; IndexedVector3 vChassisWorldUp = GetRigidBody().GetCenterOfMassTransform()._basis.GetColumn(m_indexUpAxis); rel_pos -= vChassisWorldUp * (IndexedVector3.Dot(vChassisWorldUp, rel_pos) * (1.0f - wheelInfo.m_rollInfluence)); m_chassisBody.ApplyImpulse(ref sideImp, ref rel_pos); //apply friction impulse on the ground IndexedVector3 temp = -sideImp; groundObject.ApplyImpulse(ref temp, ref rel_pos2); } } } } public RigidBody GetRigidBody() { return m_chassisBody; } public int GetRightAxis() { return m_indexRightAxis; } public int GetUpAxis() { return m_indexUpAxis; } public int GetForwardAxis() { return m_indexForwardAxis; } public IndexedVector3 GetForwardVector() { IndexedMatrix chassisTrans = GetChassisWorldTransform(); IndexedVector3 forwardW = new IndexedVector3(chassisTrans._basis[0, m_indexForwardAxis], chassisTrans._basis[1, m_indexForwardAxis], chassisTrans._basis[2, m_indexForwardAxis]); return forwardW; } public float GetCurrentSpeedKmHour() { return m_currentVehicleSpeedKmHour; } public virtual void SetCoordinateSystem(int rightIndex, int upIndex, int forwardIndex) { m_indexRightAxis = rightIndex; m_indexUpAxis = upIndex; m_indexForwardAxis = forwardIndex; } public int GetUserConstraintType() { return m_userConstraintType; } public void SetUserConstraintType(int userConstraintType) { m_userConstraintType = userConstraintType; } public void SetUserConstraintId(int uid) { m_userConstraintId = uid; } public int GetUserConstraintId() { return m_userConstraintId; } public static float CalcRollingFriction(WheelContactPoint contactPoint) { float j1 = 0f; IndexedVector3 contactPosWorld = contactPoint.m_frictionPositionWorld; IndexedVector3 rel_pos1 = contactPosWorld - contactPoint.m_body0.GetCenterOfMassPosition(); IndexedVector3 rel_pos2 = contactPosWorld - contactPoint.m_body1.GetCenterOfMassPosition(); float maxImpulse = contactPoint.m_maxImpulse; IndexedVector3 vel1 = contactPoint.m_body0.GetVelocityInLocalPoint(ref rel_pos1); IndexedVector3 vel2 = contactPoint.m_body1.GetVelocityInLocalPoint(ref rel_pos2); IndexedVector3 vel = vel1 - vel2; float vrel = IndexedVector3.Dot(contactPoint.m_frictionDirectionWorld, vel); // calculate j that moves us to zero relative velocity j1 = -vrel * contactPoint.m_jacDiagABInv; j1 = Math.Min(j1, maxImpulse); j1 = Math.Max(j1, -maxImpulse); return j1; } private void DefaultInit(ref VehicleTuning tuning) { //(void)tuning; m_currentVehicleSpeedKmHour = 0f; m_steeringValue = 0f; } private float m_tau; private float m_damping; private IVehicleRaycaster m_vehicleRaycaster; private float m_pitchControl; private float m_steeringValue; private float m_currentVehicleSpeedKmHour; private RigidBody m_chassisBody; private int m_indexRightAxis; private int m_indexUpAxis; private int m_indexForwardAxis; //private IList<IndexedVector3> m_forwardWS = new List<IndexedVector3>(); //private IList<IndexedVector3> m_axle = new List<IndexedVector3>(); //private IList<float> m_forwardImpulse = new List<float>(); //private IList<float> m_sideImpulse = new List<float>(); private IndexedVector3[] m_axle = new IndexedVector3[4]; private IndexedVector3[] m_forwardWS = new IndexedVector3[4]; private float[] m_forwardImpulse = new float[4]; private float[] m_sideImpulse = new float[4]; private int m_userConstraintType; private int m_userConstraintId; private IList<WheelInfo> m_wheelInfo = new List<WheelInfo>(); public const float sideFrictionStiffness2 = 1.0f; public static RigidBody s_fixedObject = new RigidBody(0f, null, null, IndexedVector3.Zero); }