In case it's of any help, here's my XNATerrain Class:
#region Using Directives
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using XNACore.Graphics;
using XNACore.Helpers;
#endregion
namespace XNACore.Graphics
{
public class XNATerrain
{
#region Structures
public struct VertexPositionNormalColored
{
public Vector3 Position;
public Color Color;
public Vector3 Normal;
public static int SizeInBytes = 7 * 4;
public static VertexElement[] VertexElements = new VertexElement[]
{
new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ),
new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Color, VertexElementMethod.Default, VertexElementUsage.Color, 0 ),
new VertexElement( 0, sizeof(float) * 4, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ),
};
}
public struct VertexMultitextured
{
public Vector3 Position;
public Vector3 Normal;
public Vector4 TextureCoordinate;
public Vector4 TexWeights;
public static int SizeInBytes = (3 + 3 + 4 + 4) * 4;
public static VertexElement[] VertexElements = new VertexElement[]
{
new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ),
new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ),
new VertexElement( 0, sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0 ),
new VertexElement( 0, sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1 ),
};
}
#endregion
#region Variables
private VertexBuffer terrainVertexBuffer;
private IndexBuffer terrainIndexBuffer;
private Texture2D heightMap;
private Texture2D heightMapA;
private int WIDTH;
private int HEIGHT;
private float[,] heightData;
private XNAGame objGame = null;
private XNAViewPort objView = null;
private Effect objEffect;
private Texture2D textureSand;
private Texture2D textureGrass;
private Texture2D textureRock;
private Texture2D textureSnow;
private const int SCALE = 1;
#endregion
#region Constructor
public XNATerrain(ref XNAGame xnaGame, ref XNAViewPort xnaView,
Texture2D heightMapTexture,
Texture2D heightMapTextureA,
Texture2D sandTexture,
Texture2D grassTexture,
Texture2D rockTexture,
Texture2D snowTexture)
{
objGame = xnaGame;
objView = xnaView;
heightMap = heightMapTexture;
heightMapA = heightMapTextureA;
LoadHeightData();
SetUpTerrainVertices();
SetUpTerrainterrainIndices();
objEffect = objGame.Content.Load<Effect>(FolderHelper.ShadersFolder() + "Terrain");
textureSand = sandTexture;
textureGrass = grassTexture;
textureRock = rockTexture;
textureSnow = snowTexture;
}
#endregion
#region Private Methods
private void LoadHeightData()
{
float minimumHeight = 255;
float maximumHeight = 0;
WIDTH = heightMapA.Width;
HEIGHT = heightMapA.Height;
Color[] heightMapColors = new Color[WIDTH * HEIGHT];
heightMapA.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[y + x * HEIGHT].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;
}
}
private void SetUpTerrainVertices()
{
WIDTH = heightMap.Width;
HEIGHT = heightMap.Height;
VertexMultitextured[] terrainVertices = new VertexMultitextured[WIDTH * HEIGHT];
for (int x = 0; x < WIDTH; x++)
for (int y = 0; y < HEIGHT; y++)
{
terrainVertices[x + y * WIDTH].Position = new Vector3(x * SCALE, y * SCALE, heightData[x * SCALE, y * SCALE]);
terrainVertices[x + y * WIDTH].Normal = new Vector3(0, 0, 1);
terrainVertices[x + y * WIDTH].TextureCoordinate.X = (float)x / 30.0f;
terrainVertices[x + y * WIDTH].TextureCoordinate.Y = (float)y / 30.0f;
terrainVertices[x + y * WIDTH].TexWeights.X = MathHelper.Clamp(1.0f - Math.Abs(heightData[x * SCALE, y * SCALE] - 0) / 8.0f, 0, 1);
terrainVertices[x + y * WIDTH].TexWeights.Y = MathHelper.Clamp(1.0f - Math.Abs(heightData[x * SCALE, y * SCALE] - 12) / 6.0f, 0, 1);
terrainVertices[x + y * WIDTH].TexWeights.Z = MathHelper.Clamp(1.0f - Math.Abs(heightData[x * SCALE, y * SCALE] - 20) / 6.0f, 0, 1);
terrainVertices[x + y * WIDTH].TexWeights.W = MathHelper.Clamp(1.0f - Math.Abs(heightData[x * SCALE, y * SCALE] - 30) / 6.0f, 0, 1);
float totalWeight = terrainVertices[x + y * WIDTH].TexWeights.X;
totalWeight += terrainVertices[x + y * WIDTH].TexWeights.Y;
totalWeight += terrainVertices[x + y * WIDTH].TexWeights.Z;
totalWeight += terrainVertices[x + y * WIDTH].TexWeights.W;
terrainVertices[x + y * WIDTH].TexWeights.X /= totalWeight;
terrainVertices[x + y * WIDTH].TexWeights.Y /= totalWeight;
terrainVertices[x + y * WIDTH].TexWeights.Z /= totalWeight;
terrainVertices[x + y * WIDTH].TexWeights.W /= totalWeight;
}
for (int x = 1; x < WIDTH - 1; x++)
{
for (int y = 1; y < HEIGHT - 1; y++)
{
Vector3 normX = new Vector3((terrainVertices[x - 1 + y * WIDTH].Position.Z - terrainVertices[x + 1 + y * WIDTH].Position.Z) / 2, 0, 1);
Vector3 normY = new Vector3(0, (terrainVertices[x + (y - 1) * WIDTH].Position.Z - terrainVertices[x + (y + 1) * WIDTH].Position.Z) / 2, 1);
terrainVertices[x + y * WIDTH].Normal = normX + normY;
terrainVertices[x + y * WIDTH].Normal.Normalize();
}
}
terrainVertexBuffer = new VertexBuffer(objGame.Device , VertexMultitextured.SizeInBytes * WIDTH * HEIGHT, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic);
terrainVertexBuffer.SetData(terrainVertices);
}
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(objGame.Device, typeof(int), (WIDTH - 1) * (HEIGHT - 1) * 6, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic);
terrainIndexBuffer.SetData(terrainIndices);
}
#endregion
#region Public Methods
public void Flatten()
{
WIDTH = heightMapA.Width;
HEIGHT = heightMapA.Height;
for (int x = 0; x < WIDTH; x++)
for (int y = 0; y < HEIGHT; y++)
{
heightData[x, y] = 1.0f;
}
SetUpTerrainVertices();
SetUpTerrainterrainIndices();
}
public void Render()
{
objEffect.CurrentTechnique = objEffect.Techniques["MultiTextured"];
objEffect.Parameters["xSandTexture"].SetValue(textureSand );
objEffect.Parameters["xGrassTexture"].SetValue(textureGrass );
objEffect.Parameters["xRockTexture"].SetValue(textureRock );
objEffect.Parameters["xSnowTexture"].SetValue(textureSnow );
Matrix worldMatrix = Matrix.Identity
* Matrix.CreateRotationX(MathHelper.ToRadians(270))
* Matrix.CreateRotationY(MathHelper.ToRadians(270));
objEffect.Parameters["xWorld"].SetValue(worldMatrix);
objEffect.Parameters["xView"].SetValue(objView.MatrixView);
objEffect.Parameters["xProjection"].SetValue(objView.MatrixProjection );
objEffect.Parameters["xEnableLighting"].SetValue(true);
objEffect.Parameters["xLightDirection"].SetValue(new Vector3(-0.5f, -0.5f, -1));
objEffect.Parameters["xAmbient"].SetValue(0.15f);
objEffect.Begin();
foreach (EffectPass pass in objEffect.CurrentTechnique.Passes)
{
pass.Begin();
objGame.Device.Vertices[0].SetSource(terrainVertexBuffer, 0, VertexMultitextured.SizeInBytes);
objGame.Device.Indices = terrainIndexBuffer;
objGame.Device.VertexDeclaration = new VertexDeclaration(objGame.Device , VertexMultitextured.VertexElements);
objGame.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH * HEIGHT, 0, (WIDTH - 1) * (HEIGHT - 1) * 2);
pass.End();
}
objEffect.End();
}
public float GetHeight(int x, int y)
{
try
{ return heightData[x, y]; }
catch
{
return 0.0f;
}
finally {}
}
#endregion
}
}