- Website design & DirectX code : Riemer Grootjans -
- Terrain creation from file
It's time to finally
create a nice looking landscape. Instead of manually entering
the HeightData array, we are going to fill it from a file. To
do this, we are going to load a 64x64 black&white image,
and use the 'white value' of every pixel as the Z coordinate
for the corresponding pixel! You can download my example file here. To open and read files, you need
to add the following line to your using-block
:
using
System.IO;
Change your
LoadHeightData method to this:
private
void LoadHeightData()
{
heightData
= new int[WIDTH,HEIGHT];
FileStream
fs = new FileStream("heightdata.raw",FileMode.Open,
FileAccess.Read);
BinaryReader
r = new BinaryReader(fs);
for
(int i = 0;i<HEIGHT;i++)
{
for (int y=0; y<WIDTH; y++)
{
int height
=(int)(r.ReadByte()/50);
heightData[WIDTH-1-y,HEIGHT-1-i]
= height;
}
}
r.Close();
}
First we create a
heightData array capable of storing the 64x64 Z coordinates.
The 2 following lines open the file heightdata.raw that should
be in the same directory as your .exe file for binary access.
In a .raw file, the 'white value' of every pixel if stored
byte after byte. So the only thing we have to do is load byte
after byte into our heightData array! We divide by 50,
otherwise the Z coordinates would be way to high. We have to
use the WIDTH-1-y structures, because the data is stored
inversely in the .raw format. Now change our width and height
variables so we can display the whole terrain
:
private int WIDTH =
64;
private int HEIGHT
= 64;
You can try running
this code, but you'll notice that you can't see the whole
terrain with these camera settings. First we'll introduce a
translation before drawing the triangles, so the middle of the
terrain is in the (0,0,0) position. Add the following line
right before you call the DrawIndexedPrimitives :
device.Transform.World =
Matrix.Translation(-HEIGHT/2, -WIDTH/2,
0);
With the terrain in
the center of our window, the only thing left to do is
reposition our camera!
device.Transform.Projection =
Matrix.PerspectiveFovLH((float)Math.PI/4,
this.Width/this.Height, 1f, 150f);
device.Transform.View
= Matrix.LookAtLH(new Vector3(0,-40,50), new Vector3(0,-5,0),
new Vector3(0,1,0));
Don't forget to set
the far clipping plane to 150f! Just set the background color
to black to have a nicer result. Now run the program and
you'll see a nice terrain :-)
Here's the total
code:
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;
namespace
DirectX_Tutorial
{
public class WinForm :
System.Windows.Forms.Form
{
private int WIDTH = 64;
private int HEIGHT = 64;
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,
150f);
device.Transform.View = Matrix.LookAtLH(new
Vector3(0,-40,50), new Vector3(0,-5,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].SetPosition(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.Black , 1.0f,
0);
device.BeginScene();
device.VertexFormat =
CustomVertex.PositionColored.Format;
device.SetStreamSource(0, vb, 0);
device.Indices = ib;
device.Transform.World =
Matrix.Translation(-HEIGHT/2, -WIDTH/2,
0);
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[WIDTH,HEIGHT];
FileStream fs = new
FileStream("heightdata.raw",FileMode.Open,
FileAccess.Read);
BinaryReader r = new
BinaryReader(fs);
for (int i =
0;i<HEIGHT;i++)
{
for (int y=0; y<WIDTH;
y++)
{
int height
=(int)(r.ReadByte()/50);
heightData[WIDTH-1-y,HEIGHT-1-i] =
height;
}
}
r.Close();
}
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.Show();
Application.Run(our_directx_form);
}
}
}
}
|