|
|
|
|
Positioning and rotating our airplane in the scene |
This chapter we're going to introduce 2 new variables, meshposition and meshangles, that will contain the position and the angles of our airplane relative to the 3D city. Go ahead and declare these 2 variables at the top of your code:
private Vector3 spacemeshposition = new Vector3(8, 2, 1); private Vector3 spacemeshangles = new Vector3(0, 0, 0);
As you can see, we’ve already initialized a starting position and rotation. One way to accomplish the goal of this chapter, would be to draw the 3D city, then draw the airplane at its correct position, and then to reposition and rotate the camera immediately behind our airplane. This, however, is generally NOT the way things are done. Instead, we draw our airplane always at position (0,0,0), so we can place our camera behind it and leave it there. Then we only have to reposition and rotate our 3D city around our airplane.
So you can already change the part in your OnPaint method that draws our airplane to this:
device.Transform.World = Matrix.Scaling(scaling, scaling, scaling) * Matrix.RotationX((float)Math.PI / 2); DrawMesh(spacemesh, spacemeshmaterials, spacemeshtextures);
Which just scales the mesh down, and rotates it so it’s flying horizontally. Then it’s drawn at position (0,0,0). Now it’s time to reposition our camera:
private void SetUpCamera() { device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (float)this.Width / (float)this.Height, 0.3f, 500f); device.Transform.View = Matrix.LookAtLH(new Vector3(0, -1f, 0.2f), new Vector3(0, 0, 0), new Vector3(0, 0, 1)); }
Since our airplane will constantly be drawn at position (0,0,0), we place our camera a little behind and above it. Also notice the near clipping plane has been set to 0.3f. Increasing this to 1f would make DirectX to not display the back half of our plane.
Now we’ve set the statics, it’s time to move on to the 3D city. As already mentioned, this is what we’ll be rotating and translating. This is done by the next line:
device.Transform.World = Matrix.Translation(-(float)spacemeshposition.X, -(float)spacemeshposition.Y, -(float)spacemeshposition.Z) * Matrix.RotationYawPitchRoll((float)spacemeshangles.X, (float)spacemeshangles.Y, (float)spacemeshangles.Z);
Make sure you replace the line where you set the Identity matrix as World matrix with this line. You’ll first rotate your 3D City around the 3 axis by using the RotationYawPitchRoll method, after which you’ll move it to the correct place.
Try running this now! This code should already give you a nice image, where your camera is focused on you airplane. The next step is to make it all move. This isn’t too hard. First we’ll need a variable that corresponds to the speed of your computer. We’ll call it gamespeed, and declare it at the top of our code:
private float gamespeed = 0.04f;
You should later on replace the value by something more appropriate for your machine, this value corresponds to my slow laptop I use while commuting.
Next, we’re going to create a small method, that updates a position accoring to its 3 angles:
private void UpdatePosition(ref Vector3 position, Vector3 angles, float speed) { Vector3 addvector = new Vector3(); addvector.X += (float)(Math.Sin(angles.Z)); addvector.Y += (float)(Math.Cos(angles.Z)); addvector.Z -= (float)(Math.Tan(angles.Y)); addvector.Normalize(); position += addvector*speed; }
If you’re a bit confident in using maths, this should be no problem to you. Otherwise, accept that the first lines add an amount to the X,Y and Z parts of the position, corresponding to the current angles. Because a tangent value can be higher than a cos or a sin, the result has to be normalized. Luckily, DirectX has another handy method that does all the maths for us. In the end, we multiply it with our speed variable. Remark again that the ‘ref’ word is used, so changes to the argument will be passed back to the calling program.
This method simply needs to be called every time from our OnPaint method! Make this line the first line of your OnPaint method:
UpdatePosition(ref spacemeshposition, spacemeshangles, gamespeed);
This will update the position of our 3D city, according to the angles of our airplane, multiplied by the gamespeed variable. Now run this code! You’ll be happy to see your airplane seems to be moving, while you know in fact it’s the 3D city that’s moving.

Click here to go to the forum on this chapter!
Or click on one of the topics on this chapter to go there: incorrect maths im making a flightsim that uses the terrain from s...fixed camera position Hello,
first and for all, very sweet tutorial...UpdatePosition() First of all thank for this great tutorial. It is ...Bullet picture presentation trouble Hello!
First of all I wish to thank you for your ...gamespeed variable By using Environment.TickCount property and an int...
Nice job! You just created your first moving object using DirectX. Now let’s make it controllable using the keyboard and the mouse.
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using D3D = Microsoft.DirectX.Direct3D; namespace DirectX_Tutorial { public class WinForm : System.Windows.Forms.Form { private int[,] int_Floorplan; private int WIDTH; private int HEIGHT; private int differentbuildings = 5;
private float gamespeed = 0.04f;
private int[] buildingheights = new int[] { 0, 10, 1, 3, 2, 5 }; private System.ComponentModel.Container components = null; private D3D.Device device; private Texture scenerytexture; private Material material; private CustomVertex.PositionNormalTextured[] verticesarray; ArrayList verticeslist = new ArrayList(); private Mesh spacemesh; private Material[] spacemeshmaterials; private Texture[] spacemeshtextures; private float spacemeshradius; private float scaling = 0.0005f;
private Vector3 spacemeshposition = new Vector3(8, 2, 1); private Vector3 spacemeshangles = new Vector3(0, 0, 0);
public WinForm() { InitializeComponent(); this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true); } public void InitializeDevice() { PresentParameters presentParams = new PresentParameters(); presentParams.Windowed = true; presentParams.SwapEffect = SwapEffect.Discard; presentParams.AutoDepthStencilFormat = DepthFormat.D16; presentParams.EnableAutoDepthStencil = true; device = new D3D.Device(0, D3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams); device.RenderState.Lighting = true; device.Lights[0].Type = LightType.Directional; device.Lights[0].Diffuse = Color.White; device.Lights[0].Direction = new Vector3(1, 1, -1); device.Lights[0].Update(); device.Lights[0].Enabled = true; device.Lights[1].Type = LightType.Directional; device.Lights[1].Diffuse = Color.White; device.Lights[1].Direction = new Vector3(-1, -1, -1); device.Lights[1].Update(); device.Lights[1].Enabled = true; } protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
UpdatePosition(ref spacemeshposition, spacemeshangles, gamespeed);
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0); device.BeginScene(); device.RenderState.Ambient = Color.DarkGray;
device.Transform.World = Matrix.Translation(-(float)spacemeshposition.X, -(float)spacemeshposition.Y, -(float)spacemeshposition.Z) * Matrix.RotationYawPitchRoll((float)spacemeshangles.X, (float)spacemeshangles.Y, (float)spacemeshangles.Z);
device.VertexFormat = CustomVertex.PositionNormalTextured.Format; device.SetTexture(0, scenerytexture); device.DrawUserPrimitives(PrimitiveType.TriangleList, verticeslist.Count / 3, verticesarray); device.Transform.World = Matrix.Scaling(scaling, scaling, scaling) * Matrix.RotationX((float)Math.PI / 2); DrawMesh(spacemesh, spacemeshmaterials, spacemeshtextures); device.EndScene(); device.Present(); this.Invalidate(); } private void SetUpCamera() { device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (float)this.Width / (float)this.Height, 0.3f, 500f);
device.Transform.View = Matrix.LookAtLH(new Vector3(0, -1f, 0.2f), new Vector3(0, 0, 0), new Vector3(0, 0, 1));
} private void LoadTexturesAndMaterials() { material = new Material(); material.Diffuse = Color.White; material.Ambient = Color.White; device.Material = material; scenerytexture = TextureLoader.FromFile(device, "texturemap.jpg"); } private void LoadFloorplan() { WIDTH = 20; HEIGHT = 15; int_Floorplan = new int[,] { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,1,1,0,0,0,1,1,0,0,1,0,1}, {1,0,0,1,1,0,0,0,1,0,0,0,1,0,1}, {1,0,0,0,1,1,0,1,1,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,1,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,1,1,0,0,0,1,0,0,0,0,0,0,1}, {1,0,1,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,1,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,1,0,0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,0,0,0,1,0,0,0,0,1}, {1,0,1,1,0,0,0,0,1,1,0,0,0,1,1}, {1,0,0,0,0,0,0,0,1,1,0,0,0,1,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, }; Random random = new Random(); for (int x = 0; x < WIDTH; x++) { for (int y = 0; y < HEIGHT; y++) { if (int_Floorplan[x, y] == 1) { int_Floorplan[x, y] = random.Next(differentbuildings) + 1; } } } } private void LoadMesh(string filename, ref Mesh mesh, ref Material[] meshmaterials, ref Texture[] meshtextures, ref float meshradius) { ExtendedMaterial[] materialarray; mesh = Mesh.FromFile(filename, MeshFlags.Managed, 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)) { meshtextures[i] = TextureLoader.FromFile(device, materialarray[i].TextureFilename); } } } mesh = mesh.Clone(mesh.Options.Value, CustomVertex.PositionNormalTextured.Format, device); mesh.ComputeNormals(); VertexBuffer vertices = mesh.VertexBuffer; GraphicsStream stream = vertices.Lock(0, 0, LockFlags.None); Vector3 meshcenter; meshradius = Geometry.ComputeBoundingSphere(stream, mesh.NumberVertices, mesh.VertexFormat, out meshcenter) * scaling; } private void LoadMeshes() { LoadMesh("xwing.x", ref spacemesh, ref spacemeshmaterials, ref spacemeshtextures, ref spacemeshradius); } private void DrawMesh(Mesh mesh, Material[] meshmaterials, Texture[] meshtextures) { for (int i = 0; i < meshmaterials.Length; i++) { device.Material = meshmaterials[i]; device.SetTexture(0, meshtextures[i]); mesh.DrawSubset(i); } }
private void UpdatePosition(ref Vector3 position, Vector3 angles, float speed) { Vector3 addvector = new Vector3(); addvector.X += (float)(Math.Sin(angles.Z)); addvector.Y += (float)(Math.Cos(angles.Z)); addvector.Z -= (float)(Math.Tan(angles.Y)); addvector.Normalize(); position += addvector * speed; }
private void VertexDeclaration() { float imagesintexture = 1 + differentbuildings * 2; for (int x = 0; x < WIDTH; x++) { for (int y = 0; y < HEIGHT; y++) { int currentbuilding = int_Floorplan[x, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, 0, 1), (currentbuilding * 2 + 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, 0, 1), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, buildingheights[currentbuilding]), new Vector3(0, 0, 1), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, buildingheights[currentbuilding]), new Vector3(0, 0, 1), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(0, 0, 1), (currentbuilding * 2 + 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, 0, 1), (currentbuilding * 2 + 1) / imagesintexture, 1)); if (y > 0) { if (int_Floorplan[x, y - 1] != int_Floorplan[x, y]) { if (int_Floorplan[x, y - 1] > 0) { currentbuilding = int_Floorplan[x, y - 1]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, 1, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, 1, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, 0f), new Vector3(0, 1, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, 1, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, 1, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, 1, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); } if (int_Floorplan[x, y] > 0) { currentbuilding = int_Floorplan[x, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, -1, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, 0f), new Vector3(0, -1, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, -1, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, -1, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, -1, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, -1, 0), currentbuilding * 2 / imagesintexture, 0)); } } } if (x > 0) { if (int_Floorplan[x - 1, y] != int_Floorplan[x, y]) { if (int_Floorplan[x - 1, y] > 0) { currentbuilding = int_Floorplan[x - 1, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0f), new Vector3(1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(1, 0, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); } if (int_Floorplan[x, y] > 0) { currentbuilding = int_Floorplan[x, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(-1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(-1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0f), new Vector3(-1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(-1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(-1, 0, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0f), new Vector3(-1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); } } } } } verticesarray = (CustomVertex.PositionNormalTextured[])verticeslist.ToArray(typeof(CustomVertex.PositionNormalTextured)); } protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.Size = new System.Drawing.Size(500, 500); this.Text = "Riemer's DirectX Tutorial using C# -- Season 2"; } static void Main() { using (WinForm our_directx_form = new WinForm()) { our_directx_form.InitializeDevice(); our_directx_form.SetUpCamera(); our_directx_form.LoadFloorplan(); our_directx_form.VertexDeclaration(); our_directx_form.LoadTexturesAndMaterials(); our_directx_form.LoadMeshes(); Application.Run(our_directx_form); } } } }
- Website design & XNA + DirectX code : Riemer Grootjans - ©2003 - 2008 Riemer Grootjans
|
|
|
|
|