General terrain

This chapter describes how to create landscape from a heightmap, how to use multiple textures, and how to create a sky using skydome.

Terrain Class

There are numerous ways to create a height-map to define the vertices of the terrain. Some of these solutions import an image file and process the different numbers coded into the RGB values of the image. After you have the place of the vertices you only need to determine the triangles using them. Adding multiple textures to the landscape can lead to very good-looking virtual scenery.

The Microsoft XNA framwork supports the generic function “content.Load<>” to load data from file, this can be used to import the vertices.

The variables to store the data are the following:

private VertexBuffer terrainVertexBuffer;
private IndexBuffer terrainIndexBuffer;
Texture2D heightMap;
private int WIDTH;
private int HEIGHT;
private float[,] heightData;

The index buffer determines the triangles. The indexes can be set by nested cycles:
private void SetUpTerrainterrainIndices()
{
int[] terrainIndices = new int[(WIDTH – 1) * (HEIGHT – 1) * 6];
for (int x = 0; x < WIDTH – 1; x++)
{

for (int y = 0; y < HEIGHT – 1; y++)
{

terrainIndices[(x + y * (WIDTH – 1)) * 6] = (x + 1) + (y + 1) * WIDTH;
terrainIndices[(x + y * (WIDTH – 1)) * 6 + 1] = (x + 1) + y * WIDTH;
terrainIndices[(x + y * (WIDTH – 1)) * 6 + 2] = x + y * WIDTH;terrainIndices[(x + y * (WIDTH – 1)) * 6 + 3] = (x + 1) + (y + 1) * WIDTH;
terrainIndices[(x + y * (WIDTH – 1)) * 6 + 4] = x + y * WIDTH;
terrainIndices[(x + y * (WIDTH – 1)) * 6 + 5] = x + (y + 1) * WIDTH;

}

}

terrainIndexBuffer = new IndexBuffer(device, typeof(int), (WIDTH – 1) * (HEIGHT – 1) * 6, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic);
terrainIndexBuffer.SetData(terrainIndices);
}

The method to load the height data from a file is the following:

private void LoadHeightData()
{
float minimumHeight = 255;
float maximumHeight = 0;

WIDTH = heightMap.Width;
HEIGHT = heightMap.Height;
Color[] heightMapColors = new Color[WIDTH * HEIGHT];
heightMap.GetData(heightMapColors);

heightData = new float[WIDTH, HEIGHT];
for (int x = 0; x < WIDTH; x++)

for (int y = 0; y < HEIGHT; y++)
{

heightData[x, y] = heightMapColors[x + y * WIDTH].R;
if (heightData[x, y] < minimumHeight) minimumHeight = heightData[x, y];
if (heightData[x, y] > maximumHeight) maximumHeight = heightData[x, y];

}

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

for (int y = 0; y < HEIGHT; y++)

heightData[x, y] = (heightData[x, y] – minimumHeight) / (maximumHeight – minimumHeight) * 30;

}

The variables to store the textures:

private Texture2D sandTexture;
private Texture2D grassTexture;
private Texture2D rockTexture;
private Texture2D snowTexture;

After importing the textures, the parameters are set and the passes are executed in the draw method:

private void DrawTerrain()
{
effect.CurrentTechnique = effect.Techniques[“MultiTextured”];
effect.Parameters[“xSandTexture”].SetValue(sandTexture);
effect.Parameters[“xGrassTexture”].SetValue(grassTexture);
effect.Parameters[“xRockTexture”].SetValue(rockTexture);
effect.Parameters[“xSnowTexture”].SetValue(snowTexture);

Matrix worldMatrix = Matrix.Identity;
effect.Parameters[“xWorld”].SetValue(worldMatrix);
effect.Parameters[“xView”].SetValue(viewMatrix);
effect.Parameters[“xProjection”].SetValue(projectionMatrix);
effect.Parameters[“xEnableLighting”].SetValue(true);
effect.Parameters[“xLightDirection”].SetValue(new Vector3(-0.5f, -0.5f, -1));
effect.Parameters[“xAmbient”].SetValue(0.2f);

effect.Begin();
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{

pass.Begin();

device.Vertices[0].SetSource(terrainVertexBuffer, 0, VertexMultitextured.SizeInBytes);
device.Indices = terrainIndexBuffer;
device.VertexDeclaration = new VertexDeclaration(device, VertexMultitextured.VertexElements);

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH * HEIGHT, 0, (WIDTH – 1) * (HEIGHT – 1) * 2);

pass.End();

}
effect.End();
}

Sky class

I used a skydome to create the sky in my example programs. The skydome is a special mesh: a large sphere or hemisphere over the terrain which has the texture on the inner side. The texture shows the sky with clouds and the whole sphere follows the camera position. In this way it can give the impression of a real sky.

FUNCTION

IMAGE

Following the terrain height

To simulate a first-person view it is necessary to determine the height of the terrain under the camera position and set the camera height to little above the surface. To terrain is formed by triangles, and two triangles form a square (the distance between the vertices are equal). Knowing the exact camera position it is easy to determine the square under the camera: just get the four vertices with neighboring coordinates. The two triangles have one common side as shown on the following image:

Terrain following

If the sum of the fractional parts of the X value and the Y value of the coordinates is less the 1, as for example the coordinate values of the blue cross, the camera is over the upper right triangle. If the same sum is greater than 1, as it is true for example for the green cross, the camera is over the lower right triangle.

After it is exactly determined above which triangle is the camera, it is possible to interpolate the height on the plane of the triangle using its normal-vector. The following function gets the three point of the triangle to determine the normal vector and the camera position to determine the correct height:

FUNCTION

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Liked it here?
Why not try sites on the blogroll...

%d bloggers like this: