Creating Simple Physics using Heightmaps

Started by
1 comment, last by Phil_93 11 years ago

HI,

I've been working on a racing game project, bear in mind I am fairly new to working with xna and have been using tutorials supplied by my university to help me, however said tutorials are extremely vague and don't explain how the code works. It's just a case of put this code here, it works. So I apologise in advance if I don't seem to know what I'm going on about. I'm using a heightmap as my terrain for my car to drive around on and I have a class set up for this. It also contains code which should in theory move the car based on how high/low the heightmap is making it look like it is actually driving on the ground. Unfortunately I cannot get this to work, there is a statement I commented out in my main games Update() method where I tried to get this to work but when it is uncommented my model disappears and my game breaks, when it is commented out my model is there and I have full control but the model stays at a fixed height and phases through the high parts of the map. I'm not asking anyone to do my work for me because that isn't what I want. But could someone please look at my work, see where I'm going wrong and help me fix it by walking me through it so I can understand what is going on?

Thank you in advance for any input.

Heightmap Class:

{
class Heightmap
{
float[,] heightData;
Vector3[,] normals;
public int terrainWidth;
public int terrainHeight;
public Vector3 heightmapPosition;
float terrainScale = 300f;
float verticalScale = 10f;
VertexPositionNormalTexture[] vertices;
int[] faces;
BasicEffect effect;
Texture2D texture;
GraphicsDevice graphics;
Matrix world;
public Heightmap(GraphicsDevice gd, Texture2D terrainTexture, Texture2D heightmap)
{
graphics = gd;
texture = terrainTexture;
terrainWidth = heightmap.Width;
terrainHeight = heightmap.Height;
faces = new int[(terrainWidth - 1) * (terrainHeight - 1) * 6];
vertices = new VertexPositionNormalTexture[terrainWidth * terrainHeight];
effect = new BasicEffect(graphics);
buildHeightmap(heightmap);
heightmapPosition.X = 0;
heightmapPosition.Y = 0;
for (int z = 0; z < terrainHeight; z++)
{
for (int x = 0; x < terrainWidth; x++)
{
vertices[x + z * terrainWidth].Position.X = terrainScale * (x);
vertices[x + z * terrainWidth].Position.Z = terrainScale * (z);
vertices[x + z * terrainWidth].Position.Y = heightData[x, z];
vertices[x + z * terrainWidth].Normal = Vector3.Zero;
vertices[x + z * terrainWidth].TextureCoordinate.X = (float)x / terrainWidth;
vertices[x + z * terrainWidth].TextureCoordinate.Y = (float)z / terrainHeight;
}
}
int count = 0;
for (int z = 0; z < terrainHeight - 1; z++)
{
for (int x = 0; x < terrainWidth - 1; x++)
{
int lowerLeft = x + z * terrainWidth;
int lowerRight = (x + 1) + z * terrainWidth;
int topLeft = x + (z + 1) * terrainWidth;
int topRight = (x + 1) + (z + 1) * terrainWidth;
faces[count++] = topLeft;
faces[count++] = lowerRight;
faces[count++] = lowerLeft;
faces[count++] = topLeft;
faces[count++] = topRight;
faces[count++] = lowerRight;
}
}
}
public bool BoundingTest(Vector3 position)
{
if (position.X > 0 && position.X < (heightData.GetLength(0) - 1) * terrainScale && position.Z > 0 && position.Z < (heightData.GetLength(1) - 1) * terrainScale)
{
return true;
}
else
{
return false;
}
}
public float GetHeight(Vector3 position)
{
//Determining the cell the player is positioned in
int left, top;
left = (int)position.X / (int)terrainScale;
top = (int)position.Z / (int)terrainScale;
//Working out our position within the cell
float xNormalized = (position.X % terrainScale) / terrainScale;
float zNormalized = (position.Z % terrainScale) / terrainScale;
//Calculating height
float topHeight = MathHelper.Lerp(heightData[left, top],
heightData[left + 1, top],
xNormalized);
float bottomHeight = MathHelper.Lerp(heightData[left, top + 1],
heightData[left + 1, top + 1],
xNormalized);
//Finding exact height
return MathHelper.Lerp(topHeight, bottomHeight, zNormalized) * terrainHeight;
}
public void buildHeightmap(Texture2D heightMap)
{
Color[] heightmapColors = new Color[terrainWidth * terrainHeight];
heightMap.GetData(heightmapColors);
heightData = new float[terrainWidth, terrainHeight];
for (int x = 0; x < terrainWidth; x++)
for (int y = 0; y < terrainHeight; y++)
{
heightData[x, y] = heightmapColors[x + y * terrainWidth].R * verticalScale;
}
}
public void calculateNormals()
{
for (int i = 0; i < faces.Length / 3; i++)
{
Vector3 firstvec = vertices[faces[i * 3 + 1]].Position - vertices[faces[i * 3]].Position;
Vector3 secondvec = vertices[faces[i * 3]].Position - vertices[faces[i * 3 + 2]].Position;
Vector3 normal = Vector3.Cross(firstvec, secondvec);
normal.Normalize();
vertices[faces[i * 3]].Normal += normal;
vertices[faces[i * 3 + 1]].Normal += normal;
vertices[faces[i * 3 + 2]].Normal += normal;
}
normals = new Vector3[terrainWidth, terrainHeight];
for (int x = 0; x < terrainWidth; x++)
{
for (int z = 0; z < terrainHeight; z++)
{
normals[x, z] = vertices[x + z * terrainWidth].Normal;
}
}
}
public Vector3 GetNormal(Vector3 position)
{
int left, top;
left = (int)position.X / (int)terrainScale;
top = (int)position.Z / (int)terrainScale;
float xNormalized = (position.X % terrainScale) / terrainScale;
float zNormalized = (position.Z % terrainScale) / terrainScale;
Vector3 topNormal = Vector3.Lerp(normals[left, top],
normals[left + 1, top],
xNormalized);
Vector3 bottomNormal = Vector3.Lerp(normals[left, top + 1],
normals[left + 1, top + 1],
xNormalized);
Vector3 normal = Vector3.Lerp(topNormal, bottomNormal, zNormalized);
normal.Normalize();
return (normal);
}
public void Draw(Matrix projection, Matrix view)
{
effect.Texture = texture;
effect.TextureEnabled = true;
effect.EnableDefaultLighting();
effect.DirectionalLight0.Direction = new Vector3(1, -1, 1);
effect.DirectionalLight0.Enabled = true;
effect.DirectionalLight1.Enabled = false;
effect.DirectionalLight2.Enabled = false;
effect.AmbientLightColor = new Vector3(0.8f, 0.8f, 0.8f);
effect.SpecularColor = new Vector3(0, 0, 0);
effect.Projection = projection;
effect.World = Matrix.Identity;
effect.View = view;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
{
graphics.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, vertices.Length, faces, 0, faces.Length / 3);
}
}
RasterizerState rs = new RasterizerState();
rs.FillMode = FillMode.Solid;
rs.CullMode = CullMode.None;
graphics.RasterizerState = rs;
}
}
}
Game Update() Method:
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
player1 = GamePad.GetState(PlayerIndex.One);
player.InputDrive(player1);
player.UpdateDrive();
camera.update(player.world);
sky.update(player.position);
//player.world = Matrix.CreateTranslation(player.yaw, terrain.GetHeight(player.position), player.pitch);
base.Update(gameTime);
}

Advertisement

As you're starting to use XNA, have you used any samples from the XBox Live Indie Games resources? Some of those terrain functions look a lot like the heightmap collision samples, so you might want to see how it's all put together there.

One suspicious part of your code is in the Draw function. You are setting the model's world matrix to the identity every time. I believe you want to set player.world then.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

As far as I know no I haven't. The code for the height map was supplied by my university in their tutorials, I have a feeling they may have just copied it from elsewhere. How would you suggest I change the draw? I've been trying effect.World = player.world, (Matrix.Identity); but it doesn't quite seem to like that. Also thank you for the help.

This topic is closed to new replies.

Advertisement