XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   
My XNA Book
      
       Go to section on this site

Additional info


Latest Forum posts

 Account settings
  Posted by: Anonymous
  When: 07/05/2014 at 09:48:39

 forced subtitle
  Posted by: Applefly
  When: 07/05/2014 at 06:00:48

 convert DVD into PMS
  Posted by: Applefly
  When: 07/05/2014 at 05:55:25

 DVD to Digital Copy easily
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:52:29

 DVD on Xbox 360/Xbox One Console
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:51:47

 Extract .Srt Subtitles
  Posted by: Applefly
  When: 04/05/2014 at 03:54:38

 Encode Movie collection
  Posted by: Applefly
  When: 04/05/2014 at 03:52:41

 Convert DVD to WMV
  Posted by: Applefly
  When: 29/04/2014 at 05:53:50

 rip DVDs into digital files
  Posted by: Applefly
  When: 29/04/2014 at 05:51:20

 iTunes movies/music to Kindle Fire
  Posted by: ciciyu80
  When: 29/04/2014 at 05:10:20


Ads

Creating a Mesh from your terrain

From last chapter, you should have remembered that to get smooth transitions between triangles, in every vertex you need a Ďnormalí vector that is a weighted sum of the normal vectors of every triangle it is part of. In our case, every vertex is part of 4 triangles. It would be a huge amount of work to first calculate all these normal vectors and then to make the weighted sum of them! Luckily, there is a way we can have DirectX do the difficult work for us. But Ö it requires 3D data to be presented in a Mesh.

Before we can actually use meshes, we will need to add a new reference to our project. So go to the Projects menu and select Add reference. From the list, select Microsoft.DierctX.Direct3DX. This library contains a lot of useful helper classes, such as the Mesh..

This Mesh object is in fact nothing more than a VertexBuffer combined with an IndexBuffer, with the addition of perhaps some movement (think about a person, the arms have to move relative to the body) and some materials (think about a house, the texture of the roof will be different than the material the walls are made out of). This is the way you can import figures from other developers: you simply import the Mesh and you have all the information you need. We are simply going to meld both of our buffers together into a Mesh.

Of course, we donít have to completely redefine our buffers. However, in a Mesh, all indices have to be of the type short rather than int. First reload the code from the chapter (link) where we put some colors on our terrain. Now, everywhere we defined our indices to be an int, redefine them to be a short, like this in the variable declaration at the top of your code:

 private short[] indices;

†We also have to make some minor changes to our IndicesDeclaration method to reflect that we are filling the buffer with shorts:

 ib = new IndexBuffer(typeof(short), (WIDTH-1)*(HEIGHT-1)*6, device, Usage.WriteOnly, Pool.Default);
 indices = new short[(WIDTH-1)*(HEIGHT-1)*6];

for (int x=0;x< WIDTH-1;x++){

    for (int y=0; y< HEIGHT-1;y++)    {
        indices[(x + y * (WIDTH - 1)) * 6] = (short)(x + y * WIDTH);
        indices[(x + y * (WIDTH - 1)) * 6 + 1] = (short)((x + 1) + y * WIDTH);
        indices[(x + y * (WIDTH - 1)) * 6 + 2] = (short)((x + 1) + (y + 1) * WIDTH);

        indices[(x + y * (WIDTH - 1)) * 6 + 3] = (short)(x + (y + 1) * WIDTH);
        indices[(x + y * (WIDTH - 1)) * 6 + 4] = (short)(x + y * WIDTH);
        indices[(x + y * (WIDTH - 1)) * 6 + 5] = (short)((x + 1) + (y + 1) * WIDTH);
    }
}
ib.SetData(indices, 0, LockFlags.None);

†In C, when you place a (type) before an expression, this expression becomes a type. So, our ints are converted to floats!

Now itís time to declare our mesh at the top of our code. Call it meshTerrain:

 private Mesh meshTerrain;

†Now, in our main method, right after your call the VertexDeclaration and IndicesDeclaration methods, add a call to the method CreateMesh we are going to create:

 our_directx_form.CreateMesh();

†In this method, we already know the correct value of the WIDTH and HEIGHT variables (because the call to LoadHeightData preceded the call to CreateMesh), so we can instantiate our Mesh:

 private void CreateMesh()
 {
     meshTerrain = new Mesh((WIDTH-1)*(HEIGHT-1)*2,WIDTH*HEIGHT,MeshFlags.Managed,CustomVertex.PositionColored.Format , device);
 }

The first parameter reflects the number of triangles to draw, followed by the number of vertices in our VertexBuffer. We let the Managed environment manage our memory, define the format of our vertices and pass our device. With the Mesh initialized, we can immediately copy our already defined buffers into the buffers of the Mesh!!

 meshTerrain.SetVertexBufferData(vertices, LockFlags.None);
 meshTerrain.SetIndexBufferData(indices, LockFlags.None);

The LockFlags allow you to change, read or do whatever your want to do with the buffers, but these actions come with a speed penalty. Because we only want to write data to the buffer only once, we donít define any special LockFlags.

We can try the best we can, but DirectX will always be able to place the vertices in a more efficient order. This can be done for you by the OptimizeInPlace method, but this requires an AdjecencyInformation array to be passed to it. From this array, DirectX can read how close every vertex lies to its neighbors. This information might be needed to optimize your mesh, for example to discard a vertex if itís very close to one of its neighbors. You can calculate the array this way:

 int[] adjac = new int[meshTerrain.NumberFaces*3];
 meshTerrain.GenerateAdjacency(0.5f, adjac);
 meshTerrain.OptimizeInPlace(MeshFlags.OptimizeVertexCache, adjac);

†First you define an array to hold the information. Since our terrain only uses triangles and every triangle has no more than 3 neighboring vertices, this will be a safe upper limit. Then we generate the Adjacency information, where we indicate that every 2 vertices, with a difference of less than 0.5f between them, can be treated as one vertex. In our case, this wonít make a difference, because all our vertices are separated from each other by a distance of at least 1f. Finally we can call the OptimizeInPlace method, where we pass our Adjacency information and define how to optimize: you can optimize for compatibility, for datasize and more. We define it to optimize for cache memory, so we will have less cache misses and thus our program will be executed faster (cache is the fast memory in the CPU, for every cache miss, data has to be transferred from the slower RAM to the cache).

Now we are going to draw the Mesh. This is made very easy: simply put this between your BeginScene and EndScene calls:

† device.Transform.World = Matrix.Translation(-HEIGHT/2, -WIDTH/2, 0)*Matrix.RotationZ(angle) ;
 †
 int numSubSets = meshTerrain.GetAttributeTable().Length;
 for (int i = 0; i < numSubSets; ++i)
 {
     meshTerrain.DrawSubset(i);
 }

†Of course we still need our translation and rotation. Most Meshes are divided in subsets. For example, a house might have 4 simple subsets: a roof, the walls, the door and the window. In our mesh, there actually is only one subset, but Iím putting the code here to explain the last line. This, obviously, draws the current subset.

When you run this code, youíll notice that not much has changed! But in the background, we are now displaying our terrain from a Mesh, a structure that has a lot of useful functions, such as the OptimizeInPlace method, as well as a method that will automatically calculate the Normal vectors for us, which we need to properly display our lights.




DirectX Tutorial 13 - Mesh creation

If you appreciate the amount of time I spend creating and updating
these pages, feel free to donate -- any amount is welcome !



Click here to go to the forum on this chapter!

Or click on one of the topics on this chapter to go there:
  • Mesh creation issues
           vertices = new CustomVertex.Po...
  • Subtract two meshes
          Hi i was wondering if there is a way to subtract 2...
  • SetIndexBufferData
          Hi Reimer, I have a grid with 1015 rows and 270 c...
  • C# to C++
          Hello.Sorry about bad English. I read this tutori...
  • Create Mesh in C++
          Could anyone give a sample of how to create a mesh...
  • missing vertex
          int[] adjacencyIn = new int[IndexCount]; mesh.Gen...
  • Mesh problem
          the mesh creation function throws and error when i...
  • D3DERR_INVALIDCALL while creating Mesh
          Hi. I found topic like this, but there was no answ...
  • Invalid-Call-Exception creating Mesh
          Hi Guys. I got a problem with Mesh creation. Wen...
  • Terrain 256x256
          How to create mesh?? _numFaces and _numVertices la...
  • meshTerrain.GetAttributeTable()
          First off I would like to say thanks for the tutor...
  • Cylinder and Sphere
          I want to join two know 3D points by using cylinde...
  • About Mesh Picking in 3D's space
          HI All: I want to create a 3D's terrain editer ,...
  • Cylinders
          How do you draw a cylinder between two known 3d po...
  • Sorting
          I am having major sorting issues using the code in...
  • The Type Mesh could not be found.
           Hello. It seems that my directX SDK does not ...
  • Please help me , about mesh
          After i complete readed series 1 and 2,i found tha...
  • Complete failure?
          Hi...it's me again! I'm losing my sanity righ...
  • terrain on a sphere
          Hi All, I have created a routine that generates...



    Because we are loading our terrain from a Mesh, you can delete all lines concerning VertexBuffers and IndexBuffers, so you can cut you code to what you find below:

     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 System.IO;
     using Microsoft.DirectX.DirectInput;
     
     namespace DirectX_Tutorial
     {
         public class WinForm : System.Windows.Forms.Form
         {
             private int WIDTH = 64;
             private int HEIGHT = 64;
             private Microsoft.DirectX.Direct3D.Device device;
             private System.ComponentModel.Container components = null;
             private float angle = 0f;
             private CustomVertex.PositionColored[] vertices;
             private int[,] heightData;
             private short[] indices;
             private Microsoft.DirectX.DirectInput.Device keyb;
             private int MinimumHeight = 255;
             private int MaximumHeight = 0;
             private Mesh meshTerrain;
     
             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 Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
             }
     
             public void InitializeKeyboard()
             {
                 keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
                 keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
                 keyb.Acquire();
             }
     
             private void CameraPositioning()
             {
                 device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1f, 250f);
                 device.Transform.View = Matrix.LookAtLH(new Vector3(80, 0, 120), new Vector3(-20, 0, 0), new Vector3(0, 0, 1));
                 device.RenderState.Lighting = false;
                 device.RenderState.CullMode = Cull.None;
             }
     
             private void VertexDeclaration()
             {
                 vertices = new CustomVertex.PositionColored[WIDTH*HEIGHT];

                for (int x=0;x< WIDTH;x++)            {

                    for (int y=0; y< HEIGHT;y++)                {
                        vertices[x + y * WIDTH].Position = new Vector3(x, y, heightData[x,y]);
                        if (heightData[x, y] < MinimumHeight + (MaximumHeight - MinimumHeight) / 4)
                        {
                            vertices[x + y * WIDTH].Color = Color.Blue.ToArgb();
                        }
                        else if (heightData[x, y] < MinimumHeight + (MaximumHeight - MinimumHeight) * 2 / 4)
                        {
                            vertices[x + y * WIDTH].Color = Color.Green.ToArgb();
                        }
                        else if (heightData[x, y] < MinimumHeight + (MaximumHeight - MinimumHeight) * 3 / 4)
                        {
                            vertices[x + y * WIDTH].Color = Color.Brown.ToArgb();
                        }
                        else
                        {
                            vertices[x + y * WIDTH].Color = Color.White.ToArgb();
                        }
                    }
                }
            }

            private void IndicesDeclaration()
            {
                indices = new short[(WIDTH-1)*(HEIGHT-1)*6];

                for (int x=0;x< WIDTH-1;x++)            {

                    for (int y=0; y< HEIGHT-1;y++)                {
                        indices[(x + y * (WIDTH - 1)) * 6] = (short)(x + y * WIDTH);
                        indices[(x + y * (WIDTH - 1)) * 6 + 1] = (short)((x + 1) + y * WIDTH);
                        indices[(x + y * (WIDTH - 1)) * 6 + 2] = (short)((x + 1) + (y + 1) * WIDTH);

                        indices[(x + y * (WIDTH - 1)) * 6 + 3] = (short)(x + (y + 1) * WIDTH);
                        indices[(x + y * (WIDTH - 1)) * 6 + 4] = (short)(x + y * WIDTH);
                        indices[(x + y * (WIDTH - 1)) * 6 + 5] = (short)((x + 1) + (y + 1) * WIDTH);
                    }
                }
            }

            protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
            {
                device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
                device.BeginScene();


                 device.Transform.World = Matrix.Translation(-HEIGHT / 2, -WIDTH / 2, 0) * Matrix.RotationZ(angle);
     
                 int numSubSets = meshTerrain.GetAttributeTable().Length;
                 for (int i = 0; i < numSubSets; ++i)
                 {
                     meshTerrain.DrawSubset(i);
                 }
     
                 device.EndScene();
                 device.Present();
                 this.Invalidate();
                 ReadKeyboard();
             }
     
             private void ReadKeyboard()
             {
                 KeyboardState keys = keyb.GetCurrentKeyboardState();
                 if (keys[Key.Delete])
                 {
                     angle+=0.03f;
                 }
                 if (keys[Key.Next])
                 {
                     angle-=0.03f;
                 }
             }
     
             private void LoadHeightData()
             {
                 int offset;
                 byte dummy;
     
                 FileStream fs = new FileStream("heightmap.bmp", FileMode.Open, FileAccess.Read);
                 BinaryReader r = new BinaryReader(fs);
     
                 for (int i = 0; i < 10; i++)
                 {
                     dummy = r.ReadByte();
                 }
     
                 offset = r.ReadByte();
                 offset += r.ReadByte() * 256;
                 offset += r.ReadByte() * 256 * 256;
                 offset += r.ReadByte() * 256 * 256 * 256;
     
                 for (int i = 0; i < 4; i++)
                 {
                     dummy = r.ReadByte();
                 }
     
                 WIDTH = r.ReadByte();
                 WIDTH += r.ReadByte() * 256;
                 WIDTH += r.ReadByte() * 256 * 256;
                 WIDTH += r.ReadByte() * 256 * 256 * 256;
     
                 HEIGHT = r.ReadByte();
                 HEIGHT += r.ReadByte() * 256;
                 HEIGHT += r.ReadByte() * 256 * 256;
                 HEIGHT += r.ReadByte() * 256 * 256 * 256;
     
                 heightData = new int[WIDTH, HEIGHT];
                 for (int i = 0; i < (offset - 26); i++)
                 {
                     dummy = r.ReadByte();
                 }
     
                 for (int i = 0; i < HEIGHT; i++)
                 {
                     for (int y = 0; y < WIDTH; y++)
                     {
                         int height = (int)(r.ReadByte());
                         height += (int)(r.ReadByte());
                         height += (int)(r.ReadByte());
                         height /= 8;
                         heightData[WIDTH - 1 - y, HEIGHT - 1 - i] = height;
                         if (height < MinimumHeight)
                         {
                             MinimumHeight = height;
                         }
                         if (height > MaximumHeight)
                         {
                             MaximumHeight = height;
                         }
                     }
                 }
             }
     
             private void CreateMesh()
             {
                 meshTerrain = new Mesh((WIDTH - 1) * (HEIGHT - 1) * 2, WIDTH * HEIGHT, MeshFlags.Managed, CustomVertex.PositionColored.Format, device);
     
                 meshTerrain.SetVertexBufferData(vertices, LockFlags.None);
                 meshTerrain.SetIndexBufferData(indices, LockFlags.None);
     
                 int[] adjac = new int[meshTerrain.NumberFaces * 3];
                 meshTerrain.GenerateAdjacency(0.5f, adjac);
                 meshTerrain.OptimizeInPlace(MeshFlags.OptimizeVertexCache, adjac);
             }
     
             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 = "DirectX Tutorial";
             }
     
             static void Main()
             {
                 using (WinForm our_directx_form = new WinForm())
                 {
                     our_directx_form.LoadHeightData();
                     our_directx_form.InitializeDevice();
                     our_directx_form.CameraPositioning();
                     our_directx_form.VertexDeclaration();
                     our_directx_form.IndicesDeclaration();
                     our_directx_form.CreateMesh();
                     our_directx_form.InitializeKeyboard();
                     our_directx_form.Show();
                     Application.Run(our_directx_form);
                 }
             }
         }
     }
     


    Google
     
    Webwww.riemers.net


    If you appreciate the amount of time I spend creating and updating
    these pages, feel free to donate -- any amount is welcome !



    - Website design & XNA + DirectX code : Riemer Grootjans -
    ©2003 - 2011 Riemer Grootjans
  • Translations

    This site in English
    This site in Korean
    This site in Czech

    Microsoft MVP Award



    2007 - 2011 MVP Award
    DirectX - XNA

    Contents

    News
    Home
    Forum
    XNA 2.0 Recipes Book (8)
    XNA 3.0 Recipes Book (8)
    Downloads
    Extra Reading (3)
    Matrices: geometrical
    Matrix Mathematics
    Homogenous matrices
    Community Projects (1)
    Tutorials (160)
    XNA 4.0 using C# (89)
    DirectX using C# (54)
    Series 1:Terrain (14)
    Opening a window
    Linking to the Device
    Drawing a triangle
    Camera
    Rotation - Translation
    Indices
    Terrain creation
    Terrain from file
    DirectInput
    Importing bmp files
    Colored vertices
    DirectX Light basics
    Mesh creation
    Mesh lighting
    Series 2: Flightsim (19)
    Series 3: HLSL (19)
    Short Tuts (2)
    DirectX using C++ (15)
    DirectX using VB (2)
    -- Expand all --


    Thank you!

    Support this site --
    any amount is welcome !

    Stay up-to-date

    I don't have the time to keep a News section, so stay informed about the updates by clicking on this RSS file!