Tutorial 7, DirectX using C#


DirectX using C#

Tutorial 7

Let's move the truck

you must be waiting for this tutorial, to code the motion of truck.
First of all, we'll be loading a simple terrain (or level)

level = new cosmicX.Object("level1.x", 10);
As you can see, I have scaled the level by a factor 10. (It will be 10 times bigger)
[only this piece of code, loads the mesh, material, texture and hooks itself to the render event so as to get rendered every time!!]

The 'move' function

public void move(Vector3 axisAlong, float val)
{
    Position -= axisAlong * (val / axisAlong.Length());
}

It follows triangle law of addition of vectors
p is original position (OP is position vector), and we want to move it along 'axisAlong' by magnitude 'val' (refer function parameters)
so, q = [axisAlong / |axisAlong|] * val  (unit vector*val)


Finally, OS = p+q
Actually, the function should have been,

public void move(Vector3 axisAlong, float val)
{
    Position += axisAlong * (val / axisAlong.Length());
}
but that moves the object backward, so we'll place the minus [Left-handed system]

Take a look at this,

public void move(float val)
            {
                move(LocalToGlobal(), val);
            }


Here 'axisAlong' is the local axis of the object. By default local axis vector is (0, 0, 1). When our truck is loaded (when we called truck = new cosmicX.Object("slam\\slam.x", 1);) it is facing z-axis. And we want it to move about its facing direction, which we can calculate, if we can somehow get its local z-axis wrt global co-ordinate system. [by using LocalToGlobal() ]
Global system is centered at origin of world, while local system of an object is centered at center of that object.

Game Logic

As discussed before, game logic will come under OnPaint method of main form
            if (X.CheckKeyPress(Key.W))
                truck.move(0.08f);
            else if (X.CheckKeyPress(Key.S))
                truck.move(-0.08f);
            if (X.CheckKeyPress(Key.A))
                truck.Orientation.Y -= 0.01f;
            else if (X.CheckKeyPress(Key.D))
                truck.Orientation.Y += 0.01f;


To understand use of local axis for movement replace truck.move(0.08f); with truck.move(new Vector3(0,0,1), 0.08f); which says to move the truck about global z-axis.
Initially it will be alright, but after you rotate the truck, and move it further more, it will produce weird result

Output

Note: you can use arrow and numpad keys to move the camera

Complete Code


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using DI = Microsoft.DirectX.DirectInput;

namespace XBasic
{
    public partial class Form1 : Form
    {
        class cosmicX
        {
            event EventHandler Render;
            #region -FIELDS-
            D3D.Device device;
            DI.Device keyb;
            KeyboardState keys;
            Form form;
            float _AspectRatio, _FieldOfView;
            static cosmicX _CosmicX;
            Vector3 CameraPosition, LookAt;
            Vector3 UP = new Vector3(0, 1, 0);
            camera _Camera = new camera();
#endregion
            #region -Properties-
            public D3D.Device Device
            {
                get { return device; }
                set { device = value; }
            }
            public static cosmicX CosmicX
            {
                get { return cosmicX._CosmicX; }
                set { cosmicX._CosmicX = value; }
            }
            public float FieldOfView
            {
                get { return _FieldOfView; }
                set { _FieldOfView = value; }
            }
            public float AspectRatio
            {
                get { return _AspectRatio; }
                set { _AspectRatio = value; }
            }
            public Color SunLightColor
            {
                get { return Device.RenderState.Ambient; }
                set { Device.RenderState.Ambient = value; }
            }
            public camera Camera
            {
                get { return _Camera; }

            }
#endregion
            #region -Methods-
            public cosmicX(Form f)
        {
            CosmicX = this;
            this.form = f;
            D3D.PresentParameters presentParams = new D3D.PresentParameters();
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            this.Device = new D3D.Device(0, D3D.DeviceType.Hardware, f, CreateFlags.SoftwareVertexProcessing, presentParams);
            SetUp();
            form.Resize += new EventHandler(form_Resize);
            this.keyb = new DI.Device(SystemGuid.Keyboard);
            this.keyb.SetCooperativeLevel(f, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            this.keyb.Acquire();

        }

            void form_Resize(object sender, EventArgs e)
            {
                SunLightColor = Color.White;
                AspectRatio = (float)form.Width / (float)form.Height;
                this.device.Transform.Projection = Matrix.PerspectiveFovLH(FieldOfView, AspectRatio, Device.Viewport.MinZ, Device.Viewport.MaxZ);
            }
        void SetUp()
        {
            this.device.RenderState.Lighting = true;
            this.device.Lights[0].Type = LightType.Directional;
            this.device.Lights[0].Diffuse = Color.White;
            this.device.Lights[0].Direction = new Vector3(1, 1, -1);
            this.device.Lights[0].Update();
            this.device.Lights[0].Enabled = true;
            this.device.Lights[1].Type = LightType.Directional;
            this.device.Lights[1].Diffuse = Color.White;
            this.device.Lights[1].Direction = new Vector3(-1, -1, -1);
            this.device.Lights[1].Update();
            this.device.Lights[1].Enabled = true;
            SunLightColor = Color.White;
            FieldOfView = 3.14f / 4;
            AspectRatio = (float)form.Width / (float)form.Height;
            this.device.Transform.Projection = Matrix.PerspectiveFovLH(FieldOfView , AspectRatio , 0.3f, 500f);
        }
        public void RenderX()
        {
            this.device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
            this.device.BeginScene();
            keys = keyb.GetCurrentKeyboardState();
            if (Render != null)
                Render(this, null);
            this.device.EndScene();
            this.device.Present();
            this.form.Invalidate();
        }
        public void Stare(Object o, float distance, float height)
        {
            CameraPosition = LookAt = o.Position;
            LookAt.Y += 1;
            Vector3 v = o.LocalToGlobal();
            v.Multiply(distance / v.Length());
            CameraPosition += v;
            CameraPosition.Y = o.Position.Y + height;
            this.device.Transform.View = Matrix.LookAtLH(CameraPosition, LookAt, UP);
        }
        public bool CheckKeyPress(Key k)
        {
            return this.keys[k];
        }
        public void ControlCamera(float angle, float moveSpeed)
        {
            //form.Text = camerapos.ToString();
            if (keys[Key.Up])
            {
                Camera.Move(moveSpeed);


            } if (keys[Key.Down])
            {
                Camera.Move(-moveSpeed);
            }

            
            if (keys[Key.NumPad6])
            {
                Camera.RotateCamera(UP, angle);
                /*Vector4 v = Vector3.Transform(LookAt - CameraPosition, Matrix.RotationY(angle));
                LookAt.X = v.X + CameraPosition.X;
                LookAt.Y = v.Y + CameraPosition.Y;
                LookAt.Z = v.Z + CameraPosition.Z;*/
            }
            if (keys[Key.NumPad4])
            {
                Camera.RotateCamera(UP, -angle);
            }
            if (keys[Key.NumPad8])
            {
                Vector3 left = LookAt - CameraPosition;
                Vector3 axis = Vector3.Cross(LookAt - CameraPosition, UP);
                Camera.RotateCamera(axis, angle);
                //System.Diagnostics.Debug.Print(axis.ToString());
            }
            if (keys[Key.NumPad2])
            {
                Vector3 left = LookAt - CameraPosition;
                Vector3 axis;
                axis = Vector3.Cross(left, UP);
                Camera.RotateCamera(axis, -angle);


            }
            this.device.Transform.View = Matrix.LookAtLH(CameraPosition, LookAt, UP);
        }
            #endregion
        public class camera
        {
            public float X
            {
                get { return CosmicX.CameraPosition.X; }
                set
                {
                    float x = CosmicX.LookAt.X - CosmicX.CameraPosition.X;
                    CosmicX.CameraPosition.X = value;
                    CosmicX.LookAt.X = value + x;

                }
            }
            public float Y
            {
                get { return CosmicX.CameraPosition.Y; }
                set
                {
                    float y = CosmicX.LookAt.Y - CosmicX.CameraPosition.Y;
                    CosmicX.CameraPosition.Y = value;
                    CosmicX.LookAt.Y = value + y;

                }
            }
            public float Z
            {
                get { return CosmicX.CameraPosition.X; }
                set
                {
                    float z = CosmicX.LookAt.Z - CosmicX.CameraPosition.Z;
                    CosmicX.CameraPosition.Z = value;
                    CosmicX.LookAt.Z = value + z;
                }
            }
            public void Move(float distance)
            {
                Vector3 v = CosmicX.LookAt - CosmicX.CameraPosition;
                v.Multiply(distance / v.Length());
                CosmicX.CameraPosition += v;
                CosmicX.LookAt += v;
            }
            public void Move(float distance, Vector3 along)
            {
                along.Multiply(distance / along.Length());
                Vector3 dirn = CosmicX.LookAt - CosmicX.CameraPosition;
                Position += along;
                CosmicX.LookAt = Position + dirn;
            }
            public void RotateCamera(Vector3 axis, float angle)
            {
                Vector4 v = Vector3.Transform(CosmicX.LookAt - CosmicX.CameraPosition, Matrix.RotationAxis(axis, angle));
                CosmicX.LookAt.X = v.X + CosmicX.CameraPosition.X;
                CosmicX.LookAt.Y = v.Y + CosmicX.CameraPosition.Y;
                CosmicX.LookAt.Z = v.Z + CosmicX.CameraPosition.Z;
            }
            public Vector3 Position
            {
                get { return CosmicX.CameraPosition; }
                set { X = value.X; Y = value.Y; Z = value.Z; }
            }
            public Vector3 Direction
            {
                get { return -CosmicX.LookAt + CosmicX.CameraPosition; }

            }
        }
        public class Object
        {
            private Mesh mesh;
            private Material[] materials;
            private Texture[] textures;
            object _Tag;
            public object Tag
            {
                get { return _Tag; }
                set { _Tag = value; }
            }
            private float radius;
            public Vector3 Position, centre, Orientation, LocalAxis, Scaling;
            public Object(string Filename, float scaling)
                : this(Filename, scaling, true)
            {

            }
            public Object(string Filename, float scaling, bool renderable)
            {
                Scaling = new Vector3(scaling, scaling, scaling);
                LoadMesh(Filename, ref mesh, ref materials, ref textures, ref radius, scaling, out centre);
                Position = new Vector3(0, 0, 0);
                if (renderable)
                    CosmicX.Render += new EventHandler(_CosmicX_Render);
                Orientation = new Vector3(0, 0, 0);
                LocalAxis = new Vector3(0, 0, 1);
            }
            public void LoadMesh(string filename, ref Mesh mesh, ref Material[] meshmaterials, ref Texture[] meshtextures, ref float meshradius, float scaling, out Vector3 meshcenter)
            {
                if (!System.IO.File.Exists(filename))
                { throw new Exception("File not found " + filename); }
                ExtendedMaterial[] materialarray;
                mesh = Mesh.FromFile(filename, MeshFlags.Managed, CosmicX.device, out materialarray);

                if ((materialarray != null) && (materialarray.Length > 0))
                {
                    meshmaterials = new Material[materialarray.Length];
                    meshtextures = new Texture[materialarray.Length];

                    for (int i = 0; i < materialarray.Length; i++)
                    {
                        meshmaterials[i] = materialarray[i].Material3D;
                        meshmaterials[i].Ambient = meshmaterials[i].Diffuse;

                        if ((materialarray[i].TextureFilename != null) && (materialarray[i].TextureFilename != string.Empty))
                        {
                            System.IO.FileInfo fi = new System.IO.FileInfo(filename);
                            if (System.IO.File.Exists(fi.DirectoryName + "\\" + materialarray[i].TextureFilename))
                            {
                                meshtextures[i] = TextureLoader.FromFile(CosmicX.device, fi.DirectoryName + "\\" + materialarray[i].TextureFilename);
                            }
                            else System.Diagnostics.Debug.Print("File not found: " + materialarray[i].TextureFilename + " !");
                        }
                    }
                }

                mesh = mesh.Clone(mesh.Options.Value, CustomVertex.PositionNormalTextured.Format, CosmicX.device);
                mesh.ComputeNormals();

                VertexBuffer vertices = mesh.VertexBuffer;
                GraphicsStream stream = vertices.Lock(0, 0, LockFlags.None);
                meshradius = Geometry.ComputeBoundingSphere(stream, mesh.NumberVertices, mesh.VertexFormat, out meshcenter) * scaling;

            }
            public Vector3 LocalToGlobal()
            {
                Vector4 v = Vector3.Transform(LocalAxis, Matrix.RotationY(Orientation.Y) * Matrix.RotationX(Orientation.X) * Matrix.RotationZ(Orientation.Z));
                return new Vector3(v.X, v.Y, v.Z);
            }
            
            public void DrawMesh()
            {
                for (int i = 0; i < materials.Length; i++)
                {
                    CosmicX.device.Material = materials[i];
                    CosmicX.device.SetTexture(0, textures[i]);
                    mesh.DrawSubset(i);
                }
            }
            void _CosmicX_Render(object sender, EventArgs e)
            {
                Render();
            }
            public void Render()
            {
                CosmicX.device.Transform.World = Matrix.Scaling(Scaling.X, Scaling.Y, Scaling.Z) * Matrix.RotationX(Orientation.X) * Matrix.RotationZ(Orientation.Z) * Matrix.RotationY(Orientation.Y) * Matrix.Translation(Position);
                DrawMesh();
            }
            public void Dispose()
            {
                CosmicX.Render -= _CosmicX_Render;
                mesh = null;
                materials = null;
                textures = null;
                System.GC.SuppressFinalize(this);
            }
            public void move(float val)
            {
                move(LocalToGlobal(), val);
            }
            public void move(Vector3 axisAlong, float val)
            {
                Position -= axisAlong * (val / axisAlong.Length());
            }
        }
        
        }
        cosmicX X;
        cosmicX.Object truck, level;
        public Form1()
        {
            InitializeComponent();
            this.Size = new Size(800, 800);
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
            X = new cosmicX(this);
            truck = new cosmicX.Object("slam\\slam.x", 1);
            level = new cosmicX.Object("level1.x", 10);
            X.Stare(truck, 10, 4);
            Resize += new EventHandler(Form1_Resize);
            
        }

        void Form1_Resize(object sender, EventArgs e)
        {
            X.Stare(truck, 10, 4);
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            X.RenderX();
            X.ControlCamera(0.03f, 0.5f);

            #region -Game Logic-

            if (X.CheckKeyPress(Key.W))
                truck.move(0.08f);
            //Try this -> truck.move(new Vector3(0,0,1), 0.08f);
            else if (X.CheckKeyPress(Key.S))
                truck.move(-0.08f);
            if (X.CheckKeyPress(Key.A))
                truck.Orientation.Y -= 0.01f;
            else if (X.CheckKeyPress(Key.D))
                truck.Orientation.Y += 0.01f;
            #endregion
        }
    }
}

Comments

Popular Posts