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

Terrain creation basics

At last, we've seen enough topics to start creating our terrain. Lets start small, by connecting 3x3 specified points. However, we will make our engine dynamic, so next chapter we can easily load a much larger number of points. To do this, we have to create 2 new variables in our class:

 private int WIDTH = 4;
 private int HEIGHT = 3;

We will suppose the 4x3 points are equidistant. So the only thing we don't know about our points is the Z coordinate. We will use an array to hold this information, so we'll also add this line to the top of our class as well:

 private int[,] heightData;

For now, use this method to fill the array :

 private void LoadHeightData()
 {
     heightData = new int[4,3];
     heightData[0,0]=0;
     heightData[1,0]=0;
     heightData[2,0]=0;
     heightData[3,0]=0;
 
     heightData[0,1]=1;
     heightData[1,1]=0;
     heightData[2,1]=2;
     heightData[3,1]=2;
 
     heightData[0,2]=2;
     heightData[1,2]=2;
     heightData[2,2]=4;
     heightData[3,2]=2;
 }

Since we only have to load the data once, we will call it in our Main method. Place it as the first call in your Main method:

 our_directx_form.LoadHeightData();

With our height array filled, we can now create our vertices. Since we have a 4x3 terrain, 12 (=WIDTH*HEIGHT) vertices will do. The points are equidistant, so we can easily change our VertexDeclaration method. We will not use the Z coordinate yet, to see the difference.

 private void VertexDeclaration()
 {
     vb = new VertexBuffer(typeof(CustomVertex.PositionColored), WIDTH*HEIGHT, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
     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, 0);
            vertices[x+y*WIDTH].Color = Color.White.ToArgb();
        }
    }

    vb.SetData(vertices, 0 ,LockFlags.None);
}

Nothing magical going on here, you simply define your 12 points and make them white. Next comes a more difficult part: creating the needed triangles to connect the 12 vertices. Of course, this is again done using indexing. The best way to do this is by creating two sets of vertices:



We'll start by drawing the right-topped set of triangles. To do this, change your IndicesDeclaration method llike this:

 private void IndicesDeclaration()
 {
     ib = new IndexBuffer(typeof(int), (WIDTH-1)*(HEIGHT-1)*3, device, Usage.WriteOnly, Pool.Default);
     indices = new int[(WIDTH-1)*(HEIGHT-1)*3];

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

        for (int y=0; y< HEIGHT-1;y++)        {
            indices[(x+y*(WIDTH-1))*3] = (x+1)+(y+1)*WIDTH;
            indices[(x+y*(WIDTH-1))*3+1] = (x+1)+y*WIDTH;
            indices[(x+y*(WIDTH-1))*3+2] = x+y*WIDTH;
        }
    }
    ib.SetData(indices, 0, LockFlags.None);
}

We will need 2 rows of 3 triangles, giving 6 triangles. These will require 6 * 3 = 18 indices (=(WIDTH-1)*(HEIGHT-1)*3). You create the needed structures for this in the first 2 lines. Then, you fill the indices array. Again you scan the X and Y coordinates, and create your triangles. Every triangle needs 3 indices, that's where the *3 comes from. Remember culling? It requires us to define the points in counter-clockwise order. So first you define the top-right vertex, then the bottom-down vertex and the bottom-left vertex. That's all there is to it. The only thing that's left is to draw the triangles by changing this piece of code in your OnPaint method :

 device.BeginScene();
 device.VertexFormat = CustomVertex.PositionColored.Format;
 device.SetStreamSource(0, vb, 0);
 device.Indices = ib;
 
 device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH*HEIGHT, 0, indices.Length/3);
 device.EndScene();

Try positioning your camera at (0,0,15) and run the program. You should see 6 triangles in the left half of your window, every point of every triangle at the same Z coordinate. Now change the height of your points according to your heightData array :

 vertices[x+y*WIDTH].Position = new Vector3(x, y, heightData[x,y]);

Running this, you will notice how the triangles on the left are slightly moving upwards. Now it's time to draw the second set of triangles. We need the same vertices, so the only thing we have to change is the IndicesDeclaration method:

 private void IndicesDeclaration()
 {
     ib = new IndexBuffer(typeof(int), (WIDTH-1)*(HEIGHT-1)*6, device, Usage.WriteOnly, Pool.Default);
     indices = new int[(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] = (x+1)+(y+1)*WIDTH;
            indices[(x+y*(WIDTH-1))*6+1] = (x+1)+y*WIDTH;
            indices[(x+y*(WIDTH-1))*6+2] = x+y*WIDTH;

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

We will be drawing twice as much vertices now, that's why the *3 has been replaced by *6 everywhere. You see the second set of triangles also has been drawn clockwise relative to the camera. Running this code will give you a better 3 dimensional view. We've especially taken care only to use the variables WIDTH and HEIGHT, so these are the only things we need change to increase the size of our map, together with the heightData array. It would be nice to find a mechanism to fill this last one automatically, which we'll do in the next chapter.




DirectX Tutorial 7 - Terrain 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:
  • Little Lost
          Everything was fine in my adventure to follow your...
  • The triangles are showing correctly
          Hi! After reading the chapter, I copied the source...
  • Need a Distance Fog Crash-Tutorial ;)
          Hi folks. thanks to Riemer for this great Tut o...
  • Vertex Cache Optimization
          Hi Riemer, I want to use indexed trianglestrip ...
  • AccessViolation Exception
          Hi, I get the following error when using things fr...
  • Resize makes graph disappear
          The tutorial is great for beginners. I have a que...
  • Group adding problem
          Hi I try to add to same new group of vertices,...
  • Out of range error
          I got an out of range error when working with the ...
  • Size of Terrain
          Hello. First of all I want to thank you for this ...
  • Why not a triangle strip?
          I was wondering why you didnt use a triangle strip...



    Our code so far:

     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;
     
     namespace DirectX_Tutorial
     {
     
         public class WinForm : System.Windows.Forms.Form
         {
             private int WIDTH = 4;
             private int HEIGHT = 3;
             private Device device;
             private System.ComponentModel.Container components = null;
             private float angle = 0f;
             private CustomVertex.PositionColored[] vertices;
             private int[,] heightData;
             private int[] indices;
             private IndexBuffer ib;
             private VertexBuffer vb;
     
     
             public WinForm()
             {
                 InitializeComponent();
                 this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
             }
     
             public void InitializeDevice()
             {
                 PresentParameters presentParams = new PresentParameters();
                 presentParams.Windowed = true;
                 presentParams.SwapEffect = SwapEffect.Discard;
                 device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
                 device.RenderState.FillMode = FillMode.WireFrame;
                 device.RenderState.CullMode = Cull.None;
             }
     
             private void CameraPositioning()
             {
                 device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI/4, this.Width/this.Height, 1f, 50f);
                 device.Transform.View = Matrix.LookAtLH(new Vector3(0,0,15), new Vector3(0,0,0), new Vector3(0,1,0));
                 device.RenderState.Lighting = false;
                 device.RenderState.CullMode = Cull.None;
             }
     
             private void VertexDeclaration()
             {
                 vb = new VertexBuffer(typeof(CustomVertex.PositionColored), WIDTH*HEIGHT, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
                 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]);
                        vertices[x+y*WIDTH].Color = Color.White.ToArgb();
                    }
                }

                vb.SetData(vertices, 0 ,LockFlags.None);
            }

            private void IndicesDeclaration()
            {
                ib = new IndexBuffer(typeof(int), (WIDTH-1)*(HEIGHT-1)*6, device, Usage.WriteOnly, Pool.Default);
                indices = new int[(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] = (x+1)+(y+1)*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+1] = (x+1)+y*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+2] = x+y*WIDTH;

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

     
             protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
             {
                 device.Clear(ClearFlags.Target, Color.DarkSlateBlue , 1.0f, 0);
     
                 device.BeginScene();
                 device.VertexFormat = CustomVertex.PositionColored.Format;
                 device.SetStreamSource(0, vb, 0);
                 device.Indices = ib;
     
                 device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH*HEIGHT, 0, indices.Length/3);
                 device.EndScene();
     
                 device.Present();
     
                 this.Invalidate();
                 angle += 0.05f;
             }
     
             private void LoadHeightData()
             {
                 heightData = new int[4,3];
     
                 heightData[0,0]=0;
                 heightData[1,0]=0;
                 heightData[2,0]=0;
                 heightData[3,0]=0;
     
                 heightData[0,1]=1;
                 heightData[1,1]=0;
                 heightData[2,1]=2;
                 heightData[3,1]=2;
     
                 heightData[0,2]=2;
                 heightData[1,2]=2;
                 heightData[2,2]=4;
                 heightData[3,2]=2;
             }
     
             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();
                     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!