Created
April 26, 2011 12:12
-
-
Save koenbollen/942155 to your computer and use it in GitHub Desktop.
A multitextured heightmap.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Microsoft.Xna.Framework; | |
using Microsoft.Xna.Framework.Audio; | |
using Microsoft.Xna.Framework.Content; | |
using Microsoft.Xna.Framework.GamerServices; | |
using Microsoft.Xna.Framework.Graphics; | |
using Microsoft.Xna.Framework.Input; | |
using Microsoft.Xna.Framework.Media; | |
namespace KXNA.Environment | |
{ | |
public class Terrain : DrawableGameComponent | |
{ | |
public struct VertexMultitextured | |
{ | |
public Vector3 Position; | |
public Vector3 Normal; | |
public Vector4 TextureCoordinate; | |
public Vector4 TexWeights; | |
public static int SizeInBytes = (3 + 3 + 4 + 4) * sizeof(float); | |
public static VertexElement[] | |
VertexElements = new[] | |
{ | |
new VertexElement(0, VertexElementFormat.Vector3, | |
VertexElementUsage.Position, 0), | |
new VertexElement(sizeof (float)*3, | |
VertexElementFormat.Vector3, | |
VertexElementUsage.TextureCoordinate, 0), | |
new VertexElement(sizeof (float)*6, | |
VertexElementFormat.Vector4, | |
VertexElementUsage.TextureCoordinate, 1), | |
new VertexElement(sizeof (float)*10, | |
VertexElementFormat.Vector4, | |
VertexElementUsage.TextureCoordinate, 2), | |
}; | |
} | |
public int Width { get; private set; } | |
public int Height { get; private set; } | |
private Effect effect; | |
private Texture2D bitmap; | |
private Texture2D[] textures; | |
private string asset; | |
private string[] textureAssets; | |
private float[,] data; | |
private int minheight; | |
private int maxheight; | |
private VertexMultitextured[] vertices; | |
private int[] indices; | |
private VertexDeclaration vertexDeclaration; | |
private Matrix world; | |
public Terrain(Game game, string heightmap, string[] textures ) | |
: base(game) | |
{ | |
if (textures.Length < 4) | |
throw new ArgumentException("Need four terrain textures."); | |
this.asset = heightmap; | |
this.textureAssets = textures; | |
} | |
protected override void LoadContent() | |
{ | |
this.effect = Game.Content.Load<Effect>("FX/terrain"); | |
this.bitmap = Game.Content.Load<Texture2D>(this.asset); | |
this.textures = new Texture2D[4]; | |
for (int i = 0; i < 4; i++) | |
textures[i] = Game.Content.Load<Texture2D>(this.textureAssets[i]); | |
this.LoadHeightData(); // Width & Height are available from this point. | |
this.SetUpVertices(); | |
this.SetUpIndices(); | |
this.InitializeNormals(); | |
world = Matrix.CreateTranslation(-Width / 2.0f, 0, Height / 2.0f); | |
KXNA.Console.WriteLine("Terrain: heightmap '" + this.asset + "' loaded."); | |
base.LoadContent(); | |
} | |
private void LoadHeightData() | |
{ | |
Width = bitmap.Width; | |
Height = bitmap.Height; | |
Color[] pixels = new Color[Width * Height]; | |
bitmap.GetData(pixels); | |
data = new float[Width, Height]; | |
minheight = int.MaxValue; | |
maxheight = int.MinValue; | |
for (int x = 0; x < Width; x++) | |
{ | |
for (int y = 0; y < Height; y++) | |
{ | |
data[x, y] = pixels[x + y * Width].R / 5.0f; // using red channel only. | |
minheight = (int)Math.Min(data[x, y], minheight); | |
maxheight = (int)Math.Max(data[x, y], maxheight); | |
} | |
} | |
} | |
private void SetUpVertices() | |
{ | |
if (data == null) | |
throw new InvalidOperationException("Call LoadHeightData() first!"); | |
float step = (maxheight - minheight) / 3; | |
vertices = new VertexMultitextured[Width * Height]; | |
for (int x = 0; x < Width; x++) | |
{ | |
for (int y = 0; y < Height; y++) | |
{ | |
vertices[x + y * Width].Position = new Vector3(x, data[x, y], -y); | |
vertices[x + y * Width].TextureCoordinate.X = x; | |
vertices[x + y * Width].TextureCoordinate.Y = y; | |
vertices[x + y * Width].TexWeights = Vector4.Zero; | |
vertices[x + y * Width].TexWeights.X = | |
MathHelper.Clamp(1.0f - Math.Abs(data[x, y]) / step, 0, 1); | |
vertices[x + y * Width].TexWeights.Y = | |
MathHelper.Clamp(1.0f - Math.Abs(data[x, y] - step) / step, 0, 1); | |
vertices[x + y * Width].TexWeights.Z = | |
MathHelper.Clamp(1.0f - Math.Abs(data[x, y] - 2 * step) / step, 0, 1); | |
vertices[x + y * Width].TexWeights.W = | |
MathHelper.Clamp(1.0f - Math.Abs(data[x, y] - 3 * step) / step, 0, 1); | |
float total = vertices[x + y * Width].TexWeights.X; | |
total += vertices[x + y * Width].TexWeights.Y; | |
total += vertices[x + y * Width].TexWeights.Z; | |
total += vertices[x + y * Width].TexWeights.W; | |
vertices[x + y * Width].TexWeights.X /= total; | |
vertices[x + y * Width].TexWeights.Y /= total; | |
vertices[x + y * Width].TexWeights.Z /= total; | |
vertices[x + y * Width].TexWeights.W /= total; | |
} | |
} | |
vertexDeclaration = new VertexDeclaration(VertexMultitextured.VertexElements); | |
} | |
private void SetUpIndices() | |
{ | |
if (vertices == null) | |
throw new InvalidOperationException("Call SetUpVertices() first!"); | |
indices = new int[(Width - 1) * (Height - 1) * 6]; | |
int counter = 0; | |
for (int y = 0; y < Height - 1; y++) | |
{ | |
for (int x = 0; x < Width - 1; x++) | |
{ | |
int lowerLeft = x + y * Width; | |
int lowerRight = (x + 1) + y * Width; | |
int topLeft = x + (y + 1) * Width; | |
int topRight = (x + 1) + (y + 1) * Width; | |
indices[counter++] = topLeft; | |
indices[counter++] = lowerRight; | |
indices[counter++] = lowerLeft; | |
indices[counter++] = topLeft; | |
indices[counter++] = topRight; | |
indices[counter++] = lowerRight; | |
} | |
} | |
} | |
private void InitializeNormals() | |
{ | |
for (int i = 0; i < vertices.Length; i++) | |
vertices[i].Normal = Vector3.Zero; | |
for (int i = 0; i < indices.Length / 3; i++) | |
{ | |
int index0 = indices[i * 3]; | |
int index1 = indices[i * 3 + 1]; | |
int index2 = indices[i * 3 + 2]; | |
Vector3 side0 = vertices[index0].Position - vertices[index2].Position; | |
Vector3 side1 = vertices[index0].Position - vertices[index1].Position; | |
Vector3 normal = Vector3.Cross(side0, side1); | |
vertices[index0].Normal += normal; | |
vertices[index1].Normal += normal; | |
vertices[index2].Normal += normal; | |
} | |
for (int i = 0; i < vertices.Length; i++) | |
vertices[i].Normal.Normalize(); | |
} | |
public override void Update(GameTime gameTime) | |
{ | |
base.Update(gameTime); | |
} | |
public override void Draw(GameTime gameTime) | |
{ | |
effect.CurrentTechnique = effect.Techniques["Multitextured"]; | |
effect.Parameters["World"].SetValue(this.world); | |
// Set camera (KXNA is the game class): | |
effect.Parameters["View"].SetValue(KXNA.Camera.View); | |
effect.Parameters["Projection"].SetValue(KXNA.Camera.Projection); | |
effect.Parameters["LightDirection"].SetValue(new Vector3(-0.5f, -1, -0.5f)); | |
effect.Parameters["Ambient"].SetValue(0.4f); | |
for (int i = 0; i < 4; i++) | |
effect.Parameters["Texture" + i].SetValue(this.textures[i]); | |
//*/ | |
Game.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; | |
Game.GraphicsDevice.DepthStencilState = DepthStencilState.Default; | |
//*/ | |
foreach (EffectPass pass in effect.CurrentTechnique.Passes) | |
{ | |
pass.Apply(); | |
Game.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3, this.vertexDeclaration); | |
} | |
base.Draw(gameTime); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
float4x4 World; | |
float4x4 View; | |
float4x4 Projection; | |
float3 LightDirection; | |
float Ambient; | |
Texture Texture0; | |
sampler TextureSampler0 = sampler_state { | |
texture = <Texture0> ; | |
magfilter = LINEAR; | |
minfilter = LINEAR; | |
mipfilter = LINEAR; | |
AddressU = mirror; | |
AddressV = mirror; | |
}; | |
Texture Texture1; | |
sampler TextureSampler1 = sampler_state { | |
texture = <Texture1> ; | |
magfilter = LINEAR; | |
minfilter = LINEAR; | |
mipfilter = LINEAR; | |
AddressU = wrap; | |
AddressV = wrap; | |
}; | |
Texture Texture2; | |
sampler TextureSampler2 = sampler_state { | |
texture = <Texture2> ; | |
magfilter = LINEAR; | |
minfilter = LINEAR; | |
mipfilter = LINEAR; | |
AddressU = mirror; | |
AddressV = mirror; | |
}; | |
Texture Texture3; | |
sampler TextureSampler3 = sampler_state { | |
texture = <Texture3> ; | |
magfilter = LINEAR; | |
minfilter = LINEAR; | |
mipfilter = LINEAR; | |
AddressU = wrap; | |
AddressV = wrap; | |
}; | |
struct VertexShaderInput | |
{ | |
float4 Position : POSITION0; | |
float3 Normal : TEXCOORD0; | |
float2 TexCoords : TEXCOORD1; | |
float4 TexWeights : TEXCOORD2; | |
}; | |
struct VertexShaderOutput | |
{ | |
float4 Position : POSITION0; | |
float3 Normal : TEXCOORD0; | |
float2 TexCoords : TEXCOORD1; | |
float4 TexWeights : TEXCOORD2; | |
float4 Light : TEXCOORD3; | |
}; | |
VertexShaderOutput MultitexturedVertexShader(VertexShaderInput input) | |
{ | |
VertexShaderOutput output; | |
float4 worldPosition = mul(input.Position, World); | |
float4 viewPosition = mul(worldPosition, View); | |
output.Position = mul(viewPosition, Projection); | |
output.Normal = input.Normal; | |
output.TexCoords = input.TexCoords; | |
output.TexWeights = input.TexWeights; | |
output.Light.xyz = -LightDirection; | |
output.Light.w = 1; | |
return output; | |
} | |
float4 MultitexturedPixelShader(VertexShaderOutput input) : COLOR0 | |
{ | |
float4 output; | |
float lighting = saturate(saturate(dot(input.Normal, input.Light)) + Ambient); | |
output = tex2D(TextureSampler0, input.TexCoords)*input.TexWeights.x; | |
output += tex2D(TextureSampler1, input.TexCoords)*input.TexWeights.y; | |
output += tex2D(TextureSampler2, input.TexCoords)*input.TexWeights.z; | |
output += tex2D(TextureSampler3, input.TexCoords)*input.TexWeights.w; | |
output.rgb *= lighting; | |
return output; | |
} | |
technique Multitextured | |
{ | |
pass Pass1 | |
{ | |
VertexShader = compile vs_2_0 MultitexturedVertexShader(); | |
PixelShader = compile ps_2_0 MultitexturedPixelShader(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment