## Tutorial 7

Let's move the truck

you must be waiting for this tutorial, to code the motion of truck.

```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.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);
}

{
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;*/
}
{
Camera.RotateCamera(UP, -angle);
}
{
Vector3 left = LookAt - CameraPosition;
Vector3 axis = Vector3.Cross(LookAt - CameraPosition, UP);
Camera.RotateCamera(axis, angle);
//System.Diagnostics.Debug.Print(axis.ToString());
}
{
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; }
}
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))
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);
}
}
}
}

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
}
}
}
```