public WheelInfo AddWheel(Vector3 connectionPointCS, Vector3 wheelDirectionCS0, Vector3 wheelAxleCS, float suspensionRestLength, float wheelRadius, RaycastVehicle.VehicleTuning tuning, bool isFrontWheel)
        {
            WheelInfoConstructionInfo ci = new WheelInfoConstructionInfo();

            ci.ChassisConnectionCS = connectionPointCS;
            ci.WheelDirectionCS = wheelDirectionCS0;
            ci.WheelAxleCS = wheelAxleCS;
            ci.SuspensionRestLength = suspensionRestLength;
            ci.WheelRadius = wheelRadius;
            ci.SuspensionStiffness = tuning.SuspensionStiffness;
            ci.WheelsDampingCompression = tuning.SuspensionCompression;
            ci.WheelsDampingRelaxation = tuning.SuspensionDamping;
            ci.FrictionSlip = tuning.FrictionSlip;
            ci.IsFrontWheel = isFrontWheel;
            ci.MaxSuspensionTravelCm = tuning.MaxSuspensionTravelCm;
            ci.MaxSuspensionForce = tuning.MaxSuspensionForce;

            wheelInfo.Add(new WheelInfo(ci));
            ci.Dispose();

            WheelInfo wheel = wheelInfo[NumWheels - 1];

            UpdateWheelTransformsWS(wheel, false);
            UpdateWheelTransform(NumWheels - 1, false);
            return wheel;
        }
 public CustomVehicle(RaycastVehicle.VehicleTuning tuning, RigidBody chassis, VehicleRaycaster raycaster)
 {
     chassisBody = chassis;
     vehicleRaycaster = raycaster;
 }
Exemple #3
0
        /// <summary>
        /// After we create our RigidBody, we turn it into a vehicle
        /// </summary>
        protected override void PostCreateBody(ThingDefinition def)
        {
            kartMotionState = MotionState as KartMotionState;

            Body.CcdMotionThreshold = 0.001f;
            Body.CcdSweptSphereRadius = 0.04f;

            Raycaster = new DefaultVehicleRaycaster(LKernel.GetG<PhysicsMain>().World);
            Tuning = new RaycastVehicle.VehicleTuning();
            _vehicle = new RaycastVehicle(Tuning, Body, Raycaster);
            _vehicle.SetCoordinateSystem(0, 1, 2); // I have no idea what this does... I'm assuming something to do with a rotation matrix?

            LKernel.GetG<PhysicsMain>().World.AddAction(_vehicle);

            var wheelFac = LKernel.GetG<WheelFactory>();
            string frontWheelName = def.GetStringProperty("FrontWheel", null);
            string backWheelName = def.GetStringProperty("BackWheel", null);
            WheelFL = wheelFac.CreateWheel(frontWheelName, WheelID.FrontLeft, this, def.GetVectorProperty("FrontLeftWheelPosition", null), def.GetStringProperty("FrontLeftWheelMesh", null));
            WheelFR = wheelFac.CreateWheel(frontWheelName, WheelID.FrontRight, this, def.GetVectorProperty("FrontRightWheelPosition", null), def.GetStringProperty("FrontRightWheelMesh", null));
            WheelBL = wheelFac.CreateWheel(backWheelName, WheelID.BackLeft, this, def.GetVectorProperty("BackLeftWheelPosition", null), def.GetStringProperty("BackLeftWheelMesh", null));
            WheelBR = wheelFac.CreateWheel(backWheelName, WheelID.BackRight, this, def.GetVectorProperty("BackRightWheelPosition", null), def.GetStringProperty("BackRightWheelMesh", null));

            LeftParticleNode.Position -= new Vector3(0, WheelBL.DefaultRadius * 0.7f, 0);
            RightParticleNode.Position -= new Vector3(0, WheelBR.DefaultRadius * 0.7f, 0);

            Body.LinearVelocity = new Vector3(0, 1, 0);

            PhysicsMain.FinaliseBeforeSimulation += FinaliseBeforeSimulation;
            RaceCountdown.OnCountdown += OnCountdown;
        }
Exemple #4
0
        float wheelFriction = 1000; //BT_LARGE_FLOAT;

        #endregion Fields

        #region Constructors

        public Physics(VehicleDemo game)
        {
            CollisionShape groundShape = new BoxShape(50, 3, 50);
            CollisionShapes.Add(groundShape);

            CollisionConf = new DefaultCollisionConfiguration();
            Dispatcher = new CollisionDispatcher(CollisionConf);
            Solver = new SequentialImpulseConstraintSolver();

            Vector3 worldMin = new Vector3(-10000, -10000, -10000);
            Vector3 worldMax = new Vector3(10000, 10000, 10000);
            Broadphase = new AxisSweep3(worldMin, worldMax);
            //Broadphase = new DbvtBroadphase();

            World = new DiscreteDynamicsWorld(Dispatcher, Broadphase, Solver, CollisionConf);

            int i;
            Matrix tr;
            Matrix vehicleTr;
            if (UseTrimeshGround)
            {
                const float scale = 20.0f;

                //create a triangle-mesh ground
                int vertStride = Vector3.SizeInBytes;
                int indexStride = 3 * sizeof(int);

                const int NUM_VERTS_X = 20;
                const int NUM_VERTS_Y = 20;
                const int totalVerts = NUM_VERTS_X * NUM_VERTS_Y;

                const int totalTriangles = 2 * (NUM_VERTS_X - 1) * (NUM_VERTS_Y - 1);

                TriangleIndexVertexArray vertexArray = new TriangleIndexVertexArray();
                IndexedMesh mesh = new IndexedMesh();
                mesh.Allocate(totalVerts, vertStride, totalTriangles, indexStride);

                BulletSharp.DataStream data = mesh.LockVerts();
                for (i = 0; i < NUM_VERTS_X; i++)
                {
                    for (int j = 0; j < NUM_VERTS_Y; j++)
                    {
                        float wl = .2f;
                        float height = 20.0f * (float)(Math.Sin(i * wl) * Math.Cos(j * wl));

                        data.Write((i - NUM_VERTS_X * 0.5f) * scale);
                        data.Write(height);
                        data.Write((j - NUM_VERTS_Y * 0.5f) * scale);
                    }
                }

                int index = 0;
                IntArray idata = mesh.TriangleIndices;
                for (i = 0; i < NUM_VERTS_X - 1; i++)
                {
                    for (int j = 0; j < NUM_VERTS_Y - 1; j++)
                    {
                        idata[index++] = j * NUM_VERTS_X + i;
                        idata[index++] = j * NUM_VERTS_X + i + 1;
                        idata[index++] = (j + 1) * NUM_VERTS_X + i + 1;

                        idata[index++] = j * NUM_VERTS_X + i;
                        idata[index++] = (j + 1) * NUM_VERTS_X + i + 1;
                        idata[index++] = (j + 1) * NUM_VERTS_X + i;
                    }
                }

                vertexArray.AddIndexedMesh(mesh);
                groundShape = new BvhTriangleMeshShape(vertexArray, true);

                tr = Matrix.Identity;
                vehicleTr = Matrix.Translation(0, -2, 0);
            }
            else
            {
                // Use HeightfieldTerrainShape

                int width = 40, length = 40;
                //int width = 128, length = 128; // Debugging is too slow for this
                float maxHeight = 10.0f;
                float heightScale = maxHeight / 256.0f;
                Vector3 scale = new Vector3(20.0f, maxHeight, 20.0f);

                //PhyScalarType scalarType = PhyScalarType.PhyUChar;
                //FileStream file = new FileStream(heightfieldFile, FileMode.Open, FileAccess.Read);

                // Use float data
                PhyScalarType scalarType = PhyScalarType.PhyFloat;
                byte[] terr = new byte[width * length * 4];
                MemoryStream file = new MemoryStream(terr);
                BinaryWriter writer = new BinaryWriter(file);
                for (i = 0; i < width; i++)
                    for (int j = 0; j < length; j++)
                        writer.Write((float)((maxHeight / 2) + 4 * Math.Sin(j * 0.5f) * Math.Cos(i)));
                writer.Flush();
                file.Position = 0;

                HeightfieldTerrainShape heightterrainShape = new HeightfieldTerrainShape(width, length,
                    file, heightScale, 0, maxHeight, upIndex, scalarType, false);
                heightterrainShape.SetUseDiamondSubdivision(true);

                groundShape = heightterrainShape;
                groundShape.LocalScaling = new Vector3(scale.X, 1, scale.Z);

                tr = Matrix.Translation(new Vector3(-scale.X / 2, scale.Y / 2, -scale.Z / 2));
                vehicleTr = Matrix.Translation(new Vector3(20, 3, -3));

                // Create graphics object

                file.Position = 0;
                BinaryReader reader = new BinaryReader(file);

                int totalTriangles = (width - 1) * (length - 1) * 2;
                int totalVerts = width * length;

                game.groundMesh = new Mesh(game.Device, totalTriangles, totalVerts,
                    MeshFlags.SystemMemory | MeshFlags.Use32Bit, VertexFormat.Position | VertexFormat.Normal);
                SlimDX.DataStream data = game.groundMesh.LockVertexBuffer(LockFlags.None);
                for (i = 0; i < width; i++)
                {
                    for (int j = 0; j < length; j++)
                    {
                        float height;
                        if (scalarType == PhyScalarType.PhyFloat)
                        {
                            // heightScale isn't applied internally for float data
                            height = reader.ReadSingle();
                        }
                        else if (scalarType == PhyScalarType.PhyUChar)
                        {
                            height = file.ReadByte() * heightScale;
                        }
                        else
                        {
                            height = 0.0f;
                        }

                        data.Write((j - length * 0.5f) * scale.X);
                        data.Write(height);
                        data.Write((i - width * 0.5f) * scale.Z);

                        // Normals will be calculated later
                        data.Position += 12;
                    }
                }
                game.groundMesh.UnlockVertexBuffer();
                file.Close();

                data = game.groundMesh.LockIndexBuffer(LockFlags.None);
                for (i = 0; i < width - 1; i++)
                {
                    for (int j = 0; j < length - 1; j++)
                    {
                        // Using diamond subdivision
                        if ((j + i) % 2 == 0)
                        {
                            data.Write(j * width + i);
                            data.Write((j + 1) * width + i + 1);
                            data.Write(j * width + i + 1);

                            data.Write(j * width + i);
                            data.Write((j + 1) * width + i);
                            data.Write((j + 1) * width + i + 1);
                        }
                        else
                        {
                            data.Write(j * width + i);
                            data.Write((j + 1) * width + i);
                            data.Write(j * width + i + 1);

                            data.Write(j * width + i + 1);
                            data.Write((j + 1) * width + i);
                            data.Write((j + 1) * width + i + 1);
                        }

                        /*
                        // Not using diamond subdivision
                        data.Write(j * width + i);
                        data.Write((j + 1) * width + i);
                        data.Write(j * width + i + 1);

                        data.Write(j * width + i + 1);
                        data.Write((j + 1) * width + i);
                        data.Write((j + 1) * width + i + 1);
                        */
                    }
                }
                game.groundMesh.UnlockIndexBuffer();

                game.groundMesh.ComputeNormals();
            }

            CollisionShapes.Add(groundShape);

            //create ground object
            RigidBody ground = LocalCreateRigidBody(0, tr, groundShape);
            ground.UserObject = "Ground";

            CollisionShape chassisShape = new BoxShape(1.0f, 0.5f, 2.0f);
            CollisionShapes.Add(chassisShape);

            CompoundShape compound = new CompoundShape();
            CollisionShapes.Add(compound);

            //localTrans effectively shifts the center of mass with respect to the chassis
            Matrix localTrans = Matrix.Translation(Vector3.UnitY);
            compound.AddChildShape(localTrans, chassisShape);
            RigidBody carChassis = LocalCreateRigidBody(800, Matrix.Identity, compound);
            carChassis.UserObject = "Chassis";
            //carChassis.SetDamping(0.2f, 0.2f);

            //CylinderShapeX wheelShape = new CylinderShapeX(wheelWidth, wheelRadius, wheelRadius);

            // clientResetScene();

            // create vehicle
            RaycastVehicle.VehicleTuning tuning = new RaycastVehicle.VehicleTuning();
            IVehicleRaycaster vehicleRayCaster = new DefaultVehicleRaycaster(World);
            vehicle = new RaycastVehicle(tuning, carChassis, vehicleRayCaster);

            carChassis.ActivationState = ActivationState.DisableDeactivation;
            World.AddAction(vehicle);

            float connectionHeight = 1.2f;
            bool isFrontWheel = true;

            // choose coordinate system
            vehicle.SetCoordinateSystem(rightIndex, upIndex, forwardIndex);

            Vector3 connectionPointCS0 = new Vector3(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, 2 * CUBE_HALF_EXTENTS - wheelRadius);
            WheelInfo a = vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

            connectionPointCS0 = new Vector3(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, 2 * CUBE_HALF_EXTENTS - wheelRadius);
            vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

            isFrontWheel = false;
            connectionPointCS0 = new Vector3(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, -2 * CUBE_HALF_EXTENTS + wheelRadius);
            vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

            connectionPointCS0 = new Vector3(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, -2 * CUBE_HALF_EXTENTS + wheelRadius);
            vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

            for (i = 0; i < vehicle.NumWheels; i++)
            {
                WheelInfo wheel = vehicle.GetWheelInfo(i);
                wheel.SuspensionStiffness = suspensionStiffness;
                wheel.WheelsDampingRelaxation = suspensionDamping;
                wheel.WheelsDampingCompression = suspensionCompression;
                wheel.FrictionSlip = wheelFriction;
                wheel.RollInfluence = rollInfluence;
            }

            vehicle.RigidBody.WorldTransform = vehicleTr;
        }
        float wheelFriction = 1000; //BT_LARGE_FLOAT;

        #endregion Fields

        #region Methods

        public override void Evaluate(int SpreadMax)
        {
            for (int i = 0; i < SpreadMax; i++)
            {
                if (this.CanCreate(i))
                {

                    RaycastVehicle vehicle;

                    AbstractRigidShapeDefinition shapedef = this.FShapes[i];
                    ShapeCustomData sc = new ShapeCustomData();
                    sc.ShapeDef = shapedef;

                    CompoundShape compound = new CompoundShape();

                    //List<AbstractRigidShapeDefinition> children = new List<AbstractRigidShapeDefinition>();

                    CollisionShape chassisShape = shapedef.GetShape(sc);
                    Matrix localTrans = Matrix.Translation(Vector3.UnitY);
                    compound.AddChildShape(localTrans, chassisShape);

                    float mass = shapedef.Mass;

                    bool isDynamic = (mass != 0.0f);

                    Vector3 localInertia = Vector3.Zero;
                    if (isDynamic)
                        chassisShape.CalculateLocalInertia(mass, out localInertia);

                    Vector3D pos = this.FPosition[i];
                    Vector4D rot = this.FRotation[i];

                    DefaultMotionState ms = BulletUtils.CreateMotionState(pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, rot.w);

                    RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, ms, compound, localInertia);
                    RigidBody carChassis = new RigidBody(rbInfo);

                    BodyCustomData bd = new BodyCustomData();

                    carChassis.UserObject = bd;
                    bd.Id = this.FWorld[0].GetNewBodyId();
                    bd.Custom = this.FCustom[i];

                    this.FWorld[0].Register(carChassis);

                    RaycastVehicle.VehicleTuning tuning = new RaycastVehicle.VehicleTuning();
                    VehicleRaycaster vehicleRayCaster = new DefaultVehicleRaycaster(this.FWorld[0].World);
                    vehicle = new RaycastVehicle(tuning, carChassis, vehicleRayCaster);

                    carChassis.ActivationState = ActivationState.DisableDeactivation;
                    this.FWorld[0].World.AddAction(vehicle);

                    float connectionHeight = 1.2f;
                    bool isFrontWheel = true;

                    // choose coordinate system
                    vehicle.SetCoordinateSystem(rightIndex, upIndex, forwardIndex);

                    Vector3 connectionPointCS0 = new Vector3(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, 2 * CUBE_HALF_EXTENTS - wheelRadius);
                    WheelInfo a = vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

                    connectionPointCS0 = new Vector3(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, 2 * CUBE_HALF_EXTENTS - wheelRadius);
                    vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

                    isFrontWheel = false;
                    connectionPointCS0 = new Vector3(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, -2 * CUBE_HALF_EXTENTS + wheelRadius);
                    vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

                    connectionPointCS0 = new Vector3(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, -2 * CUBE_HALF_EXTENTS + wheelRadius);
                    vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);

                    for (i = 0; i < vehicle.NumWheels; i++)
                    {
                        WheelInfo wheel = vehicle.GetWheelInfo(i);
                        wheel.SuspensionStiffness = suspensionStiffness;
                        wheel.WheelsDampingRelaxation = suspensionDamping;
                        wheel.WheelsDampingCompression = suspensionCompression;
                        wheel.FrictionSlip = wheelFriction;
                        wheel.RollInfluence = rollInfluence;
                    }

                    FOutVehicle.SliceCount = 1;
                    FOutVehicle[0] = vehicle;
                }
            }
        }
Exemple #6
0
        /// <summary>
        /// This should only be used by the WheelFactory
        /// </summary>
        /// <param name="owner">Which kart is the wheel attached to?</param>
        /// <param name="connectionPoint">Where is the wheel attached?</param>
        /// <param name="wheelID">ID number of the wheel</param>
        /// <param name="dict">The properties and values from the .wheel file this wheel was built from</param>
        /// <param name="meshName">The filename of the mesh we should use for this wheel</param>
        public Wheel(Kart owner, Vector3 connectionPoint, WheelID wheelID, IDictionary<string, float> dict, string meshName)
        {
            // set up these
            kart = owner;
            ID = wheelID;
            vehicle = kart.Vehicle;

            // set up our readonlies
            DefaultRadius = dict["Radius"];
            DefaultWidth = dict["Width"];
            DefaultSuspensionRestLength = dict["SuspensionRestLength"];
            DefaultSpringStiffness = dict["SpringStiffness"];
            DefaultSpringCompression = dict["SpringCompression"];
            DefaultSpringDamping = dict["SpringDamping"];
            Friction = dict["FrictionSlip"];
            DefaultRollInfluence = dict["RollInfluence"];
            DefaultBrakeForce = dict["BrakeForce"];
            DefaultMotorForce = dict["MotorForce"];
            DefaultMaxTurnAngle = new Degree(dict["TurnAngle"]).ValueRadians;
            DefaultSlowSpeed = dict["SlowSpeed"];
            DefaultHighSpeed = dict["HighSpeed"];
            DefaultSlowTurnAngleMultiplier = dict["SlowTurnAngleMultiplier"];
            DefaultSlowTurnSpeedMultiplier = dict["SlowTurnSpeedMultiplier"];
            DefaultDriftingTurnAngle = new Degree(dict["DriftingTurnAngle"]).ValueRadians;
            DefaultDriftingTurnSpeed = new Degree(dict["DriftingTurnSpeed"]).ValueRadians;
            DefaultSteerIncrementTurn = new Degree(dict["SteerIncrementTurn"]).ValueRadians;
            DefaultSteerDecrementTurn = new Degree(dict["SteerDecrementTurn"]).ValueRadians;

            // give our fields some default values
            AccelerateMultiplier = 0;
            TurnMultiplier = 0;
            IsBrakeOn = false;
            DriftState = WheelDriftState.None;
            IntWheelID = (int) wheelID;
            DefaultFrictionSlip = Friction;
            IdealSteerAngle = 0f;

            // need to tell bullet whether it's a front wheel or not
            bool isFrontWheel;
            if (ID == WheelID.FrontLeft || ID == WheelID.FrontRight)
                isFrontWheel = true;
            else
                isFrontWheel = false;

            vehicle.AddWheel(connectionPoint, WheelDirection, WheelAxle, DefaultSuspensionRestLength, DefaultRadius, kart.Tuning, isFrontWheel);

            WheelInfo info = vehicle.GetWheelInfo(IntWheelID);
            info.SuspensionStiffness = DefaultSpringStiffness;
            info.WheelDampingRelaxation = DefaultSpringDamping;
            info.WheelDampingCompression = DefaultSpringCompression;
            info.FrictionSlip = Friction;
            info.RollInfluence = DefaultRollInfluence;

            AxlePoint = connectionPoint + new Vector3(0, -DefaultSuspensionRestLength, 0);

            // create our node and entity
            Node = owner.RootNode.CreateChildSceneNode("wheelNode" + kart.ID + ID, AxlePoint);
            Entity = LKernel.GetG<SceneManager>().CreateEntity("wheelNode" + kart.ID + ID, meshName);
            Node.AttachObject(Entity);
            Node.InheritOrientation = false;

            Node.Orientation = kart.ActualOrientation;

            // and then hook up to the event
            PhysicsMain.PostSimulate += PostSimulate;
        }
Exemple #7
0
        public override void Load(Vector3 Location)
        {
            VehicleMesh.MeshColor = PrimaryColors[Global.RNG.Next(PrimaryColors.Length)];

            CollisionShape chassisShape = new BoxShape(Dimensions.Y / 2, Dimensions.Z / 2, Dimensions.X / 2);
            collisionShape = new CompoundShape();
            //localTrans effectively shifts the center of mass with respect to the chassis
            Matrix4 localTrans = Matrix4.CreateTranslation(0 * Vector3.UnitY);
            ((CompoundShape)collisionShape).AddChildShape(localTrans, chassisShape);
            collisionShape = VehicleMesh.GetCollisionMesh(true);
            vehicleBody = World.CreateRigidBody(Mass, Matrix4.CreateTranslation(Location), collisionShape);

            // create vehicle
            RaycastVehicle.VehicleTuning tuning = new RaycastVehicle.VehicleTuning();
            raycastVehicle = new RaycastVehicle(tuning, vehicleBody, new DefaultVehicleRaycaster(World.DynamicsWorld));

            vehicleBody.ActivationState = ActivationState.DisableDeactivation;

            // choose coordinate system
            raycastVehicle.SetCoordinateSystem(rightIndex, upIndex, forwardIndex);

            Vector3 connectionPointCS0 = FrontWheel.Location +(Vector3.UnitY*SuspensionRestLength); // Front left
            raycastVehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, SuspensionRestLength, WheelRadius, tuning, true);

            connectionPointCS0 = FrontWheel.Location * new Vector3(-1, 1, 1) + (Vector3.UnitY * SuspensionRestLength); // Front right
            raycastVehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, -wheelAxleCS, SuspensionRestLength, WheelRadius, tuning, true);

            connectionPointCS0 = RearWheel.Location * new Vector3(-1, 1, 1) + (Vector3.UnitY * SuspensionRestLength); // Rear right
            raycastVehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, -wheelAxleCS, SuspensionRestLength, WheelRadius, tuning, false);

            connectionPointCS0 = RearWheel.Location + (Vector3.UnitY * SuspensionRestLength); // Rear left
            raycastVehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, SuspensionRestLength, WheelRadius, tuning, false);

            for (int i = 0; i < raycastVehicle.NumWheels; i++)
            {
                WheelInfo wheel = raycastVehicle.GetWheelInfo(i);
                wheel.SuspensionStiffness = SuspensionStiffness;
                wheel.WheelsDampingRelaxation = SuspensionDamping;
                wheel.WheelsDampingCompression = SuspensionCompression;
                wheel.FrictionSlip = WheelFriction;
                wheel.RollInfluence = RollInfluence;
            }

            World.DynamicsWorld.AddAction(raycastVehicle);
        }