Wednesday, April 13, 2016

33. Basic Car Drifting Physics


       After some trial and error, I was able to get some drifting mechanics working for my vehicle physics. I am continuing to modify and improve the JigLibX Physics Library that I ported over to XNA 4.0 and MonoGame. Inside my Wheel.cs (wheel class), the key was a variable named "smallVel". This variable applies a force between the drive force and the side force. Initially, it was set to three and I decided to change it to 20 for this vehicle. At the moment, it feels like I am driving on ice but it is controllable which makes it a lot of fun to drift. 

Make the following changes so that the car doesn't slide around too much when stopped: 

            float smallVel;
            if (angVel == 0 & !locked)
            {
                smallVel = 1;
            }
            else smallVel = 20;



To give it more of an authentic feel, you can decrease smallVel to tweleve. I also greatly improved the handling. I will most likely create a starter kit based off of this sample program. So if you are using XNA 4.0 or MonoGame and need some on-road, off-road or arcade style car physics, this will help.




Triangle Mesh Collision Problem
       Many developers using JigLibX in XNA had many problems with the Triangle Mesh Object, especially when using it as a ground model but not with the Height Map Object. I sometimes run into these issues. When a physics object hits a vertex or seam between triangles in the mesh, strange things can occur. 




Solution 
       I separated the garage model into multiple meshes; just two for the most part. Using Autodesk Maya, I then selected them all, grouped them and finally exported the selection. You might have to separate models that have a lot of triangles and faces into separate models. JigLibX uses models that support 16-bit indices in size and not 32-bit. When using the TriangleMeshObject, I found that JigLibX handles collision detection better with models whose polygon faces contain quads rather than triangles. So my next step was to convert my models containing lots of triangles to quads. Converting polygon faces to quads is a good was to clean up polygons or reduce the number of faces. To convert polygon faces to quads in Autodesk Maya, simply select the Mesh option at the top and then Quadrangulate. Below is the changes made to my JigLibX TriangeMeshObject.cs code that lets you extract the vertices and indices from a loaded model.


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using JigLibX.Geometry;
using JigLibX.Physics;
using JigLibX.Collision;


namespace JigLibXGame
{
    class TriangleMeshObject : PhysicObject
    {
        TriangleMesh triangleMesh;
        Matrix xform;
        public TriangleMeshObject(Game game, Model model, Matrix orientation, Vector3 position)
            : base(game, model)
        {
            body = new Body();
            collision = new CollisionSkin(null);
            triangleMesh = new TriangleMesh();
            List<Vector3> vertexList = new List<Vector3>();
            List<TriangleVertexIndices> indexList = new List<TriangleVertexIndices>();
            ExtractData(vertexList, indexList, model);
            triangleMesh.CreateMesh(vertexList, indexList, 4, 1.0f);
            collision.AddPrimitive(triangleMesh, new MaterialProperties(0.8f, 0.7f, 0.6f));
            PhysicsSystem.CurrentPhysicsSystem.CollisionSystem.AddCollisionSkin(collision);
            // Transform
            collision.ApplyLocalTransform(new JigLibX.Math.Transform(position, orientation));
            // we also need to move this dummy, so the object is *rendered* at the correct 
               position
            // body.MoveTo(position, orientation);
            this.body.CollisionSkin = this.collision;
          
        }

    
        // Helper Method to get the vertex and index List from the model.
        public void ExtractData(List<Vector3> vertices, List<TriangleVertexIndices> indices, Model model)
        {
            Matrix[] bones_ = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(bones_);
            foreach (ModelMesh mm in model.Meshes)
            {
                Game.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
              
                xform = bones_[mm.ParentBone.Index];
                foreach (ModelMeshPart mmp in mm.MeshParts)
                {
                    int offset = vertices.Count;
                    Vector3[] a = new Vector3[mmp.NumVertices];
                    int vertexStride = mmp.VertexBuffer.VertexDeclaration.VertexStride;
                    mmp.VertexBuffer.GetData<Vector3>(mmp.VertexOffset * vertexStride, a, 0, mmp.NumVertices, vertexStride);
                   
                    for (int i = 0; i != a.Length; ++i)
                        Vector3.Transform(ref a[i], ref xform, out a[i]);
                    vertices.AddRange(a);

                    // new
                    for (int i = 0; i < a.Length; i++) vertices.Add(new Vector3(a[i].X, a[i].Y, a[i].Z));

                    if (mmp.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits)
                        throw new Exception(
                            String.Format("Model uses 32-bit indices, which are not supported."));
                    short[] s = new short[mmp.PrimitiveCount * 3];
                    
                    mmp.IndexBuffer.GetData<short>(mmp.StartIndex * 2, s, 0, mmp.PrimitiveCount * 3);
                    JigLibX.Geometry.TriangleVertexIndices[] tvi = new JigLibX.Geometry.TriangleVertexIndices[mmp.PrimitiveCount];
                    for (int i = 0; i != tvi.Length; ++i)
                    {
                        tvi[i].I0 = s[i * 3 + 2] + offset;
                        tvi[i].I1 = s[i * 3 + 1] + offset;
                        tvi[i].I2 = s[i * 3 + 0] + offset;
                    }
                    indices.AddRange(tvi);
                }
            }
        }
  
        public override void ApplyEffects(BasicEffect effect)
        {
            //effect.DiffuseColor = Vector3.One * 0.8f;
            
        }
    }
}


How to Convert a Triangulated Mesh into Quads in 3DS Max 
       So now I have encountered a situation where I need quad based meshes and I have many 3D models with triangulated ones. Initially I would manually convert the model to quads by selecting groups of triangles. If the model is very complex, this can be extremely time consuming which can pose a problem. Luckily, there is an easier way in this tutorial below by Chandan Kumar who introduces an amazing free script to save time converting triangulated meshes to quads. You can download it here.



       Next I will be making a way where the player can switch to cockpit-view and drive in first-person perspective while in-game. Thanks for reading and I look forward to posting more updates. If you have found this blog post helpful or would like to share any improvements, please comment below.





No comments:

Post a Comment