//---------------------------------------------------------------------------------------------------------------------------------------------------
//
// Copyright (C)2007 DarkWynter Studios. All rights reserved.
//
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {License Information: Creative Commons}
//---------------------------------------------------------------------------------------------------------------------------------------------------
namespace ElementalGame
{
#region Using Statements
using System;
using System.Threading;
using System.Collections.Generic;
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 System.Xml;
#endregion
public class Terrain : GameObject
{
// Diffuse cofactor
new private float[] materialDiffuse = { 0.5f, 0.5f, 0.5f, 1.0f };
// Specular cofactor
new private float[] materialSpecular = { 0.1f, 0.1f, 0.1f, 1.0f };
// Vertex Height Map
private Texture2D heightMapTexture;
private Texture2D terrainNormalMap;
public static int vertexMapSize;
// Terrain Vertex Buffer
//private VertexBuffer vertexBuffer;
//private VertexDeclaration vertexDeclaration;
// Vertex indices buffer
//private IndexBuffer meshIndexBuffer;
//private int[] meshIndices;
new public VertexPositionNormalTexture[] vertices;
// Collision Height Map - Interpolated height values
public static int collisionMapSize;
// Terrain scaling factor
public static int terrainScaleFactor = 8;
private float terrainHeightScale = 255.0f;
// Terrain modification distance limit
private bool terrainChanged = false;
public float playerSurfaceTension = 2000.0f;
public float propSurfaceTension = 200.0f;
public float particleSurfaceTension = 600.0f;
public const float MaxHeight = 3;
public const float MinHeight= -1.5f;
public string heightMapFile;
public string normalMapFile;
private float[,] vertexHeightData;
Vector4[] normalData;
Vector4[] heightData;
public bool lightUpTerrainMod = true;
public Vector3 planeRayIntersectionPoint = new Vector3();
public float terrainModRange = 0;
public Terrain(Mass darkMass)
{
mass = darkMass;
mass.objectType = Mass.ObjectType.TERRAIN;
mass.gameObjectPointer = this;
mass.mass = 1.0f;
mass.staticFrictionCoefficient = 0.6f;
}
/* GameObject Interface Methods
*
*/
public override void Load(XmlNode node)
{
base.Load( node);
if (node.Name == "terrain")
{
//try
//{
// Load high, medium, and low ground textures for terrain
heightMapFile = node.Attributes["heightmap"].Value;
normalMapFile = node.Attributes["normalmap"].Value;
textureList.Clear();
textureList.Add(ElementalGame.content.Load(node.Attributes["textureLow"].Value));
textureList.Add(ElementalGame.content.Load(node.Attributes["textureMid"].Value));
textureList.Add(ElementalGame.content.Load(node.Attributes["textureHigh"].Value));
textureList.Add(ElementalGame.content.Load(node.Attributes["textureSlope"].Value));
// Load slope texture and bump map for terrain
textureList.Add(ElementalGame.content.Load(node.Attributes["textureBumpLow"].Value));
textureList.Add(ElementalGame.content.Load(node.Attributes["textureBumpMid"].Value));
textureList.Add(ElementalGame.content.Load(node.Attributes["textureBumpHigh"].Value));
textureList.Add(ElementalGame.content.Load(node.Attributes["textureBumpSlope"].Value));
textureList.Add(ElementalGame.content.Load(node.Attributes["textureTerrainMod"].Value));
// Set the scale of the terrain
Terrain.terrainScaleFactor = int.Parse(node.Attributes["scale"].Value);
//}
//catch
//{
// System.Diagnostics.Debug.WriteLine("Error reading terrain attributes");
// return;
//}
}
heightMapTexture = ElementalGame.content.Load(heightMapFile);
//heightMapTexture = Texture2D.FromFile(graphics.GraphicsDevice, "GameObjects/HeightMaps/Air.tif", tcp);
terrainNormalMap = ElementalGame.content.Load(normalMapFile);
effect = ElementalGame.content.Load("Shaders/ElementalGPU");
ShaderParameters.worldMinY.SetValue(-384.0f);
ShaderParameters.worldMaxY.SetValue(768.0f);
ShaderParameters.terrainModTexture.SetValue(textureList[8]);
ShaderParameters.terrainScaleFactor.SetValue(terrainScaleFactor);
ShaderParameters.heightMapTexture.SetValue(heightMapTexture);
ShaderParameters.terrainNormalMapTexture.SetValue(terrainNormalMap);
model = ElementalGame.content.Load("_models/TerrainMesh2");
foreach (ModelMesh mesh in model.Meshes)
{
for (int i = 0; i < mesh.MeshParts.Count; i++)
{
// Add effect to mesh
mesh.MeshParts[i].Effect = effect;
}
}
//basicEffect = new BasicEffect(graphics.GraphicsDevice, null);
// Procedurally generate terrain from heightmap and scale
// Need to do a try/catch to make sure it can cleanly abort when threaded
//try
//{
CreateTerrain();
//}
//catch (ThreadAbortException)
//{
//}
mass.dynamicFrictionCoefficient = XML.LevelSettings.TERRAIN_FRICTION;
}
public void CreateTerrain()
{
// Set size variables
vertexMapSize = heightMapTexture.Height;
ShaderParameters.terrainMapSize.SetValue(vertexMapSize);
collisionMapSize = vertexMapSize * terrainScaleFactor;
// Create Vertex and Collision Height Maps
vertexHeightData = null;
vertexHeightData = new float[vertexMapSize, vertexMapSize];
//collisionHeightData = null;
GC.Collect();
//collisionHeightData = new float[collisionMapSize, collisionMapSize];
// Set texture step
int index = 0;
float textureStep = 1.0f / (float)vertexMapSize;
// Set up color array
heightData = new Vector4[vertexMapSize * vertexMapSize];
heightMapTexture.GetData(heightData, 0, vertexMapSize * vertexMapSize);
normalData = new Vector4[vertexMapSize * vertexMapSize];
terrainNormalMap.GetData(normalData, 0, vertexMapSize * vertexMapSize);
// Create Vertices
vertices = new VertexPositionNormalTexture[vertexMapSize * vertexMapSize];
for (int x = 0; x < vertexMapSize; x++)
{
for (int z = 0; z < vertexMapSize; z++)
{
vertexHeightData[x, z] = (float)heightData[index].X * terrainHeightScale;
vertices[z + x * vertexMapSize].Position = new Vector3(x, vertexHeightData[x, z], z);
vertices[z + x * vertexMapSize].Normal = Vector3.Normalize(new Vector3(normalData[index].X, normalData[index].Y, normalData[index].Z));
index++;
// Fill collision array with known height values from vertices
//collisionHeightData[x * terrainScaleFactor, z * terrainScaleFactor] = vertexHeightData[x, z];
}
}
//terrainNormalMap.SetData(normalData);
//terrainNormalMap.Save("terrainNormalMap.png", ImageFileFormat.Png);
}
public override void Update(ObjectLibrary objectLibrary)
{
if (terrainChanged)
{
try
{
terrainChanged = false;
terrainNormalMap.SetData(normalData);
heightMapTexture.SetData(heightData);
ShaderParameters.terrainNormalMapTexture.SetValue(terrainNormalMap);
ShaderParameters.heightMapTexture.SetValue(heightMapTexture);
}
catch (Exception e)
{
Console.Write(e.ToString());
}
}
}
public void DrawLevelSelectionScreen(Matrix matrixModelView, Matrix matrixProjection)
{
// Load vertices and indicies into graphics device
//graphicsDevice.VertexDeclaration = vertexDeclaration;
//graphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
//graphicsDevice.Indices = meshIndexBuffer;
// Set this object's global transformation matrix
Matrix matrix = Matrix.CreateScale(new Vector3(terrainScaleFactor, 1, terrainScaleFactor)) *
Matrix.CreateTranslation(new Vector3(-255.0f, 0.0f, 255.0f)) *
Matrix.CreateRotationY(-(float)Math.PI / 2.0f);
ShaderParameters.World.SetValue(matrix);
// Lighting
ShaderParameters.coreShininess.SetValue(7000.0f);
ShaderParameters.coreMaterialDiffuse.SetValue(materialDiffuse);
ShaderParameters.coreMaterialSpecular.SetValue(materialSpecular);
// Texture
ShaderParameters.modelTexture1.SetValue(textureList[0]);
ShaderParameters.modelTexture2.SetValue(textureList[1]);
ShaderParameters.bumpTexture1.SetValue(textureList[4]);
ShaderParameters.bumpTexture2.SetValue(textureList[5]);
ShaderParameters.LightUpTerrainMod.SetValue(false);
ShaderParameters.playerPosition.SetValue(Vector3.Zero);
// Technique
//effect.CurrentTechnique = effect.Techniques["TerrainNoBumpsShaderMain"];
foreach (ModelMesh mesh in model.Meshes)
{
ElementalGame.graphics.GraphicsDevice.Indices = mesh.IndexBuffer;
foreach (ModelMeshPart part in mesh.MeshParts)
{
part.Effect.CurrentTechnique = part.Effect.Techniques["TerrainNoBumpsShaderMain"];
ElementalGame.graphics.GraphicsDevice.VertexDeclaration = part.VertexDeclaration;
ElementalGame.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
int numPrimitives;
if (mesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits)
{
numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(ushort);
}
else
{
numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(int);
}
numPrimitives -= 2;
part.Effect.Begin();
foreach (EffectPass pass in part.Effect.CurrentTechnique.Passes)
{
pass.Begin();
ElementalGame.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip,
part.BaseVertex,
0,
part.NumVertices,
part.StartIndex,
numPrimitives);
pass.End();
}
part.Effect.End();
}
}
//foreach (ModelMesh mesh in model.Meshes)
//{
// foreach (Effect currentEffect in mesh.Effects)
// {
// currentEffect.CurrentTechnique = effect.Techniques["TerrainNoBumpsShaderMain"];
// }
// mesh.Draw();
//}
//effect.Begin();
//foreach (EffectPass pass in effect.CurrentTechnique.Passes)
//{
// pass.Begin();
// graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip,
// 0,
// 0, // minimum vertex index
// vertexMapSize * vertexMapSize, // number of vertices
// 0, // first index element to read
// meshIndices.Length - 2 // number of primitives to draw
// );
// pass.End();
//}
//effect.End();
}
public override void DrawShadow(ModelManager modelManager)
{
// Load vertices and indicies into graphics device
//graphicsDevice.VertexDeclaration = vertexDeclaration;
//graphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
//graphicsDevice.Indices = meshIndexBuffer;
int snapX = (int)((ObjectLibrary.playerPosition.X - 255.0f) / 4.0f);
int snapZ = (int)((ObjectLibrary.playerPosition.Y - 255.0f) / 4.0f);
snapX *= 4;
snapZ *= 4;
// Set this object's global transformation matrix
Matrix matrix = Matrix.CreateRotationY(-(float)Math.PI / 2.0f) *
Matrix.CreateTranslation(new Vector3(snapX, 0.0f, snapZ)) *
Matrix.CreateScale(new Vector3(terrainScaleFactor, 1.0f, terrainScaleFactor));
// Set light shading parameters
ShaderParameters.World.SetValue(matrix);
foreach (ModelMesh mesh in model.Meshes)
{
ElementalGame.graphics.GraphicsDevice.Indices = mesh.IndexBuffer;
foreach (ModelMeshPart part in mesh.MeshParts)
{
part.Effect.CurrentTechnique = part.Effect.Techniques["TerrainShadowMap"];
ElementalGame.graphics.GraphicsDevice.VertexDeclaration = part.VertexDeclaration;
ElementalGame.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
int numPrimitives;
if (mesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits)
{
numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(ushort);
}
else
{
numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(int);
}
numPrimitives -= 2;
part.Effect.Begin();
foreach (EffectPass pass in part.Effect.CurrentTechnique.Passes)
{
pass.Begin();
ElementalGame.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip,
part.BaseVertex,
0,
part.NumVertices,
part.StartIndex,
numPrimitives);
pass.End();
}
part.Effect.End();
}
}
//foreach (ModelMesh mesh in model.Meshes)
//{
// foreach (Effect currentEffect in mesh.Effects)
// {
// currentEffect.CurrentTechnique = effect.Techniques["TerrainShadowMap"];
// }
// mesh.Draw();
//}
//effect.Begin();
//foreach (EffectPass pass in effect.CurrentTechnique.Passes)
//{
// pass.Begin();
// graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip,
// 0,
// 0, // minimum vertex index
// vertexMapSize * vertexMapSize, // number of vertices
// 0, // first index element to read
// meshIndices.Length - 2 // number of primitives to draw
// );
// pass.End();
//}
//effect.End();
}
public override void Draw(ModelManager modelManager)
{
// Load vertices and indicies into graphics device
//graphicsDevice.VertexDeclaration = vertexDeclaration;
//graphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
//graphicsDevice.Indices = meshIndexBuffer;
// Snap grid to nearest 32 boundary
int snapX = (int)((ObjectLibrary.playerPosition.X - 255.0f) / 4.0f);
int snapZ = (int)((ObjectLibrary.playerPosition.Y - 255.0f) / 4.0f);
snapX *= 4;
snapZ *= 4;
// Set this object's global transformation matrix
Matrix matrix = Matrix.CreateRotationY(-(float)Math.PI / 2.0f) *
Matrix.CreateTranslation(new Vector3(snapX, 0.0f, snapZ)) *
Matrix.CreateScale(new Vector3(terrainScaleFactor, 1.0f, terrainScaleFactor));
// Set light shading parameters
ShaderParameters.World.SetValue(matrix);
// Lighting
ShaderParameters.coreShininess.SetValue(7000.0f);
ShaderParameters.coreMaterialDiffuse.SetValue(materialDiffuse);
ShaderParameters.coreMaterialSpecular.SetValue(materialSpecular);
// Texture
ShaderParameters.modelTexture1.SetValue(textureList[0]);
ShaderParameters.modelTexture2.SetValue(textureList[1]);
ShaderParameters.bumpTexture1.SetValue(textureList[4]);
ShaderParameters.bumpTexture2.SetValue(textureList[5]);
ShaderParameters.modelTexture3.SetValue(textureList[2]);
ShaderParameters.modelTexture4.SetValue(textureList[3]);
ShaderParameters.bumpTexture3.SetValue(textureList[6]);
ShaderParameters.bumpTexture4.SetValue(textureList[7]);
ShaderParameters.LightUpTerrainMod.SetValue(lightUpTerrainMod);
ShaderParameters.PlaneRayIntersectionPoint.SetValue(planeRayIntersectionPoint);
ShaderParameters.TerrainModRange.SetValue((terrainModRange + 1) * terrainScaleFactor);
ShaderParameters.playerPosition.SetValue(ObjectLibrary.playerPosition * terrainScaleFactor);
foreach (ModelMesh mesh in model.Meshes)
{
ElementalGame.graphics.GraphicsDevice.Indices = mesh.IndexBuffer;
foreach (ModelMeshPart part in mesh.MeshParts)
{
if (XML.SystemSettings.enableTerrainBumpMapping)
{
part.Effect.CurrentTechnique = part.Effect.Techniques["TerrainShaderMain"];
}
else
{
part.Effect.CurrentTechnique = part.Effect.Techniques["TerrainNoBumpsShaderMain"];
}
ElementalGame.graphics.GraphicsDevice.VertexDeclaration = part.VertexDeclaration;
ElementalGame.graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
int numPrimitives;
if (mesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits)
{
numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(ushort);
}
else
{
numPrimitives = mesh.IndexBuffer.SizeInBytes / sizeof(int);
}
numPrimitives -= 2;
part.Effect.Begin();
foreach (EffectPass pass in part.Effect.CurrentTechnique.Passes)
{
pass.Begin();
ElementalGame.graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip,
part.BaseVertex,
0,
part.NumVertices,
part.StartIndex,
numPrimitives);
pass.End();
}
part.Effect.End();
}
}
//foreach (ModelMesh mesh in model.Meshes)
//{
// foreach (Effect currentEffect in mesh.Effects)
// {
// if (XML.SystemSettings.enableTerrainBumpMapping)
// {
// currentEffect.CurrentTechnique = effect.Techniques["TerrainShaderMain"];
// }
// else
// {
// currentEffect.CurrentTechnique = effect.Techniques["TerrainNoBumpsShaderMain"];
// }
// }
// mesh.Draw();
//}
}
// ======================= OLD TERRAIN DRAW CODE W/O TRIANGLE STRIPS =======================
/*
public void DrawLevelSelectionScreen(Matrix matrixModelView, Matrix matrixProjection)
{
// Load vertices and indicies into graphics device
//graphicsDevice.VertexDeclaration = vertexDeclaration;
//graphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
//graphicsDevice.Indices = meshIndexBuffer;
// Set this object's global transformation matrix
Matrix matrix = Matrix.CreateScale(new Vector3(terrainScaleFactor, 1, terrainScaleFactor)) *
Matrix.CreateTranslation(new Vector3(-255.0f, 0.0f, 255.0f)) *
Matrix.CreateRotationY(-(float)Math.PI / 2.0f);
ShaderParameters.World.SetValue(matrix);
// Lighting
ShaderParameters.coreShininess.SetValue(7000.0f);
ShaderParameters.coreMaterialDiffuse.SetValue(materialDiffuse);
ShaderParameters.coreMaterialSpecular.SetValue(materialSpecular);
// Texture
ShaderParameters.modelTexture1.SetValue(textureList[0]);
ShaderParameters.modelTexture2.SetValue(textureList[1]);
ShaderParameters.bumpTexture1.SetValue(textureList[4]);
ShaderParameters.bumpTexture2.SetValue(textureList[5]);
ShaderParameters.LightUpTerrainMod.SetValue(false);
ShaderParameters.playerPosition.SetValue(Vector3.Zero);
// Technique
//effect.CurrentTechnique = effect.Techniques["TerrainNoBumpsShaderMain"];
foreach (ModelMesh mesh in model.Meshes)
{
foreach (Effect currentEffect in mesh.Effects)
{
currentEffect.CurrentTechnique = effect.Techniques["TerrainNoBumpsShaderMain"];
}
mesh.Draw();
}
//effect.Begin();
//foreach (EffectPass pass in effect.CurrentTechnique.Passes)
//{
// pass.Begin();
// graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip,
// 0,
// 0, // minimum vertex index
// vertexMapSize * vertexMapSize, // number of vertices
// 0, // first index element to read
// meshIndices.Length - 2 // number of primitives to draw
// );
// pass.End();
//}
//effect.End();
}
public override void DrawShadow(ModelManager modelManager)
{
// Load vertices and indicies into graphics device
//graphicsDevice.VertexDeclaration = vertexDeclaration;
//graphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
//graphicsDevice.Indices = meshIndexBuffer;
int snapX = (int)((ObjectLibrary.playerPosition.X - 255.0f) / 4.0f);
int snapZ = (int)((ObjectLibrary.playerPosition.Y - 255.0f) / 4.0f);
snapX *= 4;
snapZ *= 4;
// Set this object's global transformation matrix
Matrix matrix = Matrix.CreateRotationY(-(float)Math.PI / 2.0f) *
Matrix.CreateTranslation(new Vector3(snapX, 0.0f, snapZ)) *
Matrix.CreateScale(new Vector3(terrainScaleFactor, 1.0f, terrainScaleFactor));
// Set light shading parameters
ShaderParameters.World.SetValue(matrix);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (Effect currentEffect in mesh.Effects)
{
currentEffect.CurrentTechnique = effect.Techniques["TerrainShadowMap"];
}
mesh.Draw();
}
//effect.Begin();
//foreach (EffectPass pass in effect.CurrentTechnique.Passes)
//{
// pass.Begin();
// graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip,
// 0,
// 0, // minimum vertex index
// vertexMapSize * vertexMapSize, // number of vertices
// 0, // first index element to read
// meshIndices.Length - 2 // number of primitives to draw
// );
// pass.End();
//}
//effect.End();
}
public override void Draw(ModelManager modelManager)
{
// Load vertices and indicies into graphics device
//graphicsDevice.VertexDeclaration = vertexDeclaration;
//graphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);
//graphicsDevice.Indices = meshIndexBuffer;
// Snap grid to nearest 32 boundary
int snapX = (int)((ObjectLibrary.playerPosition.X - 255.0f) / 4.0f);
int snapZ = (int)((ObjectLibrary.playerPosition.Y - 255.0f) / 4.0f);
snapX *= 4;
snapZ *= 4;
// Set this object's global transformation matrix
Matrix matrix = Matrix.CreateRotationY(-(float)Math.PI / 2.0f) *
Matrix.CreateTranslation(new Vector3(snapX, 0.0f, snapZ)) *
Matrix.CreateScale(new Vector3(terrainScaleFactor, 1.0f, terrainScaleFactor));
// Set light shading parameters
ShaderParameters.World.SetValue(matrix);
// Lighting
ShaderParameters.coreShininess.SetValue(7000.0f);
ShaderParameters.coreMaterialDiffuse.SetValue(materialDiffuse);
ShaderParameters.coreMaterialSpecular.SetValue(materialSpecular);
// Texture
ShaderParameters.modelTexture1.SetValue(textureList[0]);
ShaderParameters.modelTexture2.SetValue(textureList[1]);
ShaderParameters.bumpTexture1.SetValue(textureList[4]);
ShaderParameters.bumpTexture2.SetValue(textureList[5]);
ShaderParameters.modelTexture3.SetValue(textureList[2]);
ShaderParameters.modelTexture4.SetValue(textureList[3]);
ShaderParameters.bumpTexture3.SetValue(textureList[6]);
ShaderParameters.bumpTexture4.SetValue(textureList[7]);
ShaderParameters.LightUpTerrainMod.SetValue(lightUpTerrainMod);
ShaderParameters.PlaneRayIntersectionPoint.SetValue(planeRayIntersectionPoint);
ShaderParameters.TerrainModRange.SetValue((terrainModRange + 1) * terrainScaleFactor);
ShaderParameters.playerPosition.SetValue(ObjectLibrary.playerPosition * terrainScaleFactor);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (Effect currentEffect in mesh.Effects)
{
if (XML.SystemSettings.enableTerrainBumpMapping)
{
currentEffect.CurrentTechnique = effect.Techniques["TerrainShaderMain"];
}
else
{
currentEffect.CurrentTechnique = effect.Techniques["TerrainNoBumpsShaderMain"];
}
}
mesh.Draw();
}
}
*/
// Procedurally let the user modify the terrain height values
public void ModifyTerrain(float modFactor, Vector3 lookAtPoint, int modRadius)
{
int minX = (int)lookAtPoint.X - modRadius;
int maxX = (int)lookAtPoint.X + modRadius;
int minZ = (int)lookAtPoint.Z - modRadius;
int maxZ = (int)lookAtPoint.Z + modRadius;
int collisionMinX, collisionMaxX;
int collisionMinZ, collisionMaxZ;
if (maxX >= vertexMapSize)
{
maxX = vertexMapSize - 2;
}
if (minX < 0)
{
minX = 0;
}
if (maxZ >= vertexMapSize)
{
maxZ = vertexMapSize - 2;
}
if (minZ < 0)
{
minZ = 0;
}
// The Tip
for (int x = minX; x <= maxX; x++)
{
for (int z = minZ; z <= maxZ; z++)
{
if ((modFactor > 0 && heightData[z + x * vertexMapSize].Y < MaxHeight) ||
(modFactor < 0 && heightData[z + x * vertexMapSize].Y > MinHeight))
{
//vertices[z + x * vertexMapSize].Position.Y += 10 * modFactor;
if (x == minX || x == maxX - 1 || z == minZ || z == maxZ - 1)
{
vertices[z + x * vertexMapSize].Normal = new Vector3();
}
heightData[z + x * vertexMapSize] += new Vector4(modFactor / 25.5f);
}
}
}
minX -= 1;
maxX += 1;
minZ -= 1;
maxZ += 1;
if (maxX >= vertexMapSize)
{
maxX = vertexMapSize - 2;
}
if (minX < 0)
{
minX = 0;
}
if (maxZ >= vertexMapSize)
{
maxZ = vertexMapSize - 2;
}
if (minZ < 0)
{
minZ = 0;
}
Vector3 edge1, edge2, normal;
for (int x = minX; x < maxX; x++)
{
for (int z = minZ; z < maxZ; z++)
{
if (x == minX || x == maxX - 1 || z == minZ || z == maxZ - 1)
{
edge1 = vertices[(z + 1) + x * vertexMapSize].Position - vertices[z + x * vertexMapSize].Position;
edge2 = vertices[z + x * vertexMapSize].Position - vertices[(z + 1) + (x + 1) * vertexMapSize].Position;
normal = Vector3.Cross(edge2, edge1); // normal = Vector3.Cross(edge1, edge2); --> To play the NAN-mine game:-)
normal.Normalize();
vertices[z + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + (x + 1) * vertexMapSize].Normal += normal;
edge1 = vertices[z + x * vertexMapSize].Position - vertices[z + (x + 1) * vertexMapSize].Position;
edge2 = vertices[z + (x + 1) * vertexMapSize].Position - vertices[(z + 1) + (x + 1) * vertexMapSize].Position;
normal = Vector3.Cross(edge2, edge1); // normal = Vector3.Cross(edge1, edge2); --> To play the NAN-mine game:-)
normal.Normalize();
vertices[z + (x + 1) * vertexMapSize].Normal += normal;
vertices[z + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + (x + 1) * vertexMapSize].Normal += normal;
}
}
}
for (int x = minX; x <= maxX; x++)
{
for (int z = minZ; z <= maxZ; z++)
{
if (x == minX || x == maxX - 1 || z == minZ || z == maxZ - 1)
{
vertices[z + x * vertexMapSize].Normal.Normalize();
normalData[z + x * vertexMapSize] = new Vector4(vertices[z + x * vertexMapSize].Normal, 0.0f);
}
}
}
//Recalculate collision map
collisionMinX = (int)minX - 1;
collisionMaxX = (int)maxX + 1;
collisionMinZ = (int)minZ - 1;
collisionMaxZ = (int)maxZ + 1;
if (collisionMaxX >= vertexMapSize)
{
collisionMaxX = vertexMapSize - 2;
}
if (collisionMinX < 0)
{
collisionMinX = 0;
}
if (collisionMaxZ >= vertexMapSize)
{
collisionMaxZ = vertexMapSize - 2;
}
if (collisionMinZ < 0)
{
collisionMinZ = 0;
}
//UpdateCollisionMap(collisionMinX, collisionMaxX, collisionMinZ, collisionMaxZ);
terrainChanged = true;
}
public void ModifyTerrain(float modFactor, Vector3 lookAtPoint)
{
// TERRAIN OBJECT
Vector3 normal = new Vector3();
Vector3 edge1 = new Vector3();
Vector3 edge2 = new Vector3();
int minX, maxX, minZ, maxZ;
// Change the height value and renormalize vertex normals in surrounding area
if (lookAtPoint.X >= 0 && lookAtPoint.Z >= 0)
{
vertices[((int)lookAtPoint.Z + (int)lookAtPoint.X * vertexMapSize)].Position.Y += 10 * modFactor;
vertices[((int)lookAtPoint.Z + (int)lookAtPoint.X * vertexMapSize)].Normal = new Vector3();
minX = (int)lookAtPoint.X - 1;
maxX = (int)lookAtPoint.X + 1;
minZ = (int)lookAtPoint.Z - 1;
maxZ = (int)lookAtPoint.Z + 1;
if (maxX >= vertexMapSize)
{
maxX = vertexMapSize - 2;
}
if (minX < 0)
{
minX = 0;
}
if (maxZ >= vertexMapSize)
{
maxZ = vertexMapSize - 2;
}
if (minZ < 0)
{
minZ = 0;
}
//UpdateCollisionMap(minX, maxX, minZ, maxZ);
// Renormalize normals
for (int x = minX; x < maxX; x++)
{
for (int z = minZ; z < maxZ; z++)
{
if ((x > lookAtPoint.X && z < lookAtPoint.Z) || (x < lookAtPoint.X && z > lookAtPoint.Z))
{
continue;
}
edge1 = vertices[(z + 1) + x * vertexMapSize].Position - vertices[z + x * vertexMapSize].Position;
edge2 = vertices[z + x * vertexMapSize].Position - vertices[(z + 1) + (x + 1) * vertexMapSize].Position;
normal = Vector3.Cross(edge2, edge1); // normal = Vector3.Cross(edge1, edge2); --> To play the NAN-mine game:-)
normal.Normalize();
vertices[z + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + (x + 1) * vertexMapSize].Normal += normal;
edge1 = vertices[z + x * vertexMapSize].Position - vertices[z + (x + 1) * vertexMapSize].Position;
edge2 = vertices[z + (x + 1) * vertexMapSize].Position - vertices[(z + 1) + (x + 1) * vertexMapSize].Position;
normal = Vector3.Cross(edge2, edge1); // normal = Vector3.Cross(edge1, edge2); --> To play the NAN-mine game:-)
normal.Normalize();
vertices[z + (x + 1) * vertexMapSize].Normal += normal;
vertices[z + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + (x + 1) * vertexMapSize].Normal += normal;
}
}
for (int x = minX; x <= maxX; x++)
{
for (int z = minZ; z <= maxZ; z++)
{
vertices[z + x * vertexMapSize].Normal.Normalize();
}
}
terrainChanged = true;
}
}
public Vector3 GetTerrainNormal(float x, float z, Vector3 velocity)
{
int floorX = (int)x;
int floorZ = (int)z;
int ceilX = 0;
int ceilZ = 0;
velocity.Normalize();
if (velocity.X < 0)
{
if (x > 0)
{
floorX = (int)(x - 1);
}
else
{
floorX = 0;
}
if (x < vertexMapSize - 1)
{
ceilX = (int)(x + 1);
}
else
{
ceilX = vertexMapSize - 1;
}
}
else //other way
{
if (x < vertexMapSize - 1)
{
floorX = (int)(x + 1);
}
else
{
floorX = vertexMapSize - 1;
}
if (x > 0)
{
ceilX = (int)(x - 1);
}
else
{
ceilX = 0;
}
}
if (velocity.Z < 0)
{
if (z > 0)
{
floorZ = (int)(z - 1);
}
else
{
floorZ = 0;
}
if (z < vertexMapSize - 1)
{
ceilZ = (int)(z + 1);
}
else
{
ceilZ = vertexMapSize - 1;
}
}
else //other way
{
if (z < vertexMapSize - 1)
{
floorZ = (int)(z + 1);
}
else
{
floorZ = vertexMapSize - 1;
}
if (z > 0)
{
ceilZ = (int)(z - 1);
}
else
{
ceilZ = 0;
}
}
Vector3 A = new Vector3(floorX, GetTerrainHeight(floorX, floorZ), floorZ);
Vector3 B = new Vector3(ceilX, GetTerrainHeight(ceilX, ceilZ), ceilZ);
Vector3 C;
if (GetTerrainHeight(floorX, ceilZ) > GetTerrainHeight(ceilX, floorZ))
{
C = new Vector3(floorX, GetTerrainHeight(floorX, ceilZ), ceilZ);
}
else
{
C = new Vector3(ceilX, GetTerrainHeight(ceilX, floorZ), floorZ);
}
/*
Vector3 vec1 = new Vector3(ceilX, GetTerrainHeight(ceilX,ceilZ), ceilZ) - new Vector3(floorX, GetTerrainHeight(floorX,floorZ), floorZ);
//vertices[ceilZ + ceilX * vertexMapSize].Position - vertices[floorZ + floorX * vertexMapSize].Position;
Vector3 vec2;
if (GetTerrainHeight(floorX, ceilZ) > GetTerrainHeight(ceilX, floorZ))
{
vec2 = new Vector3(floorX, GetTerrainHeight(floorX, ceilZ), ceilZ) - new Vector3(floorX, GetTerrainHeight(floorX, floorZ), floorZ);
}
else
{
vec2 = new Vector3(ceilX, GetTerrainHeight(ceilX, floorZ), floorZ) - new Vector3(floorX, GetTerrainHeight(floorX, floorZ), floorZ);
}
*/
Vector3 vec1 = B - A;
Vector3 vec2 = C - A;
//vertices[floorZ + ceilX * vertexMapSize].Position - vertices[floorZ + floorX * vertexMapSize].Position;
Vector3 result = Vector3.Cross(vec1, vec2);
result.Normalize();
if (float.IsNaN(result.X) || float.IsNaN(result.Y) || float.IsNaN(result.Z))
{
result = Vector3.Zero;
}
if (result.Y < 0)
{
//can never be pointing down!!!!
result = -result;
}
return result;
}
public float GetTerrainHeight(float x, float z)
{
int floorX = (int)x;
int floorZ = (int)z;
int ceilX = vertexMapSize - 1;
int ceilZ = vertexMapSize - 1;
if (x < vertexMapSize - 1)
{
ceilX = (int)(x + 1);
}
if (z < vertexMapSize - 1)
{
ceilZ = (int)(z + 1);
}
float lerpX = MathHelper.Lerp(heightData[floorZ + floorX * vertexMapSize].X,
heightData[ceilZ + ceilX * vertexMapSize].X,
x - (float)floorX);
float lerpZ = MathHelper.Lerp(heightData[floorZ + floorX * vertexMapSize].X,
heightData[ceilZ + ceilX * vertexMapSize].X,
z - (float)floorZ);
float diff;
if ((x - (float)floorX) - (z - (float)floorZ) > 0)
{
diff = 1 - ((x - (float)floorX) - (z - (float)floorZ));
}
else if ((x - (float)floorX) - (z - (float)floorZ) < 0)
{
diff = 1 + (x - (float)floorX) - (z - (float)floorZ);
}
else
{
diff = 0.5f;
}
return MathHelper.Lerp(lerpX, lerpZ, diff) * terrainHeightScale;
}
// ======================= OLD TERRAIN CODE =======================
/*
// Procedurally generate terrain from heightmap and scale
private void CreateTerrain_CPU(GraphicsDeviceManager graphics)
{
// Set size variables
vertexMapSize = heightMapTexture.Height;
collisionMapSize = vertexMapSize * terrainScaleFactor;
// Create Vertex and Collision Height Maps
vertexHeightData = new float[vertexMapSize, vertexMapSize];
collisionHeightData = new float[collisionMapSize, collisionMapSize];
// Set texture step
int index = 0;
float textureStep = 1.0f / (float)vertexMapSize;
// Set up color array
Color[] data = new Color[vertexMapSize * vertexMapSize];
heightMapTexture.GetData(data);
// Create Vertex Declaration and Buffer
vertexDeclaration = new VertexDeclaration(graphics.GraphicsDevice,
VertexPositionNormalTexture.VertexElements);
vertexBuffer = new VertexBuffer(graphics.GraphicsDevice,
VertexPositionNormalTexture.SizeInBytes * vertexMapSize * vertexMapSize,
ResourceUsage.Dynamic,
ResourceManagementMode.Manual);
// Create Vertecies
vertices = new VertexPositionNormalTexture[vertexMapSize * vertexMapSize];
for (int x = 0; x < vertexMapSize; x++)
{
for (int z = 0; z < vertexMapSize; z++)
{
vertexHeightData[x, z] = (float)data[index].R * terrainHeightScale;
vertices[z + x * vertexMapSize].Position = new Vector3(x, vertexHeightData[x, z], z);
vertices[z + x * vertexMapSize].Normal = new Vector3(0, 0, 0);
vertices[z + x * vertexMapSize].TextureCoordinate = new Vector2(x * textureStep, z * textureStep);
index++;
// Fill collision array with known height values from vertices
collisionHeightData[x * terrainScaleFactor, z * terrainScaleFactor] = vertexHeightData[x, z];
}
}
// Holds the order in which triangles are created from vertex buffer
meshIndexBuffer = new IndexBuffer(graphics.GraphicsDevice,
sizeof(int) * ((vertexMapSize - 1) * (vertexMapSize - 1) * 6),
ResourceUsage.Dynamic,
ResourceManagementMode.Manual,
IndexElementSize.ThirtyTwoBits);
meshIndices = new int[(vertexMapSize - 1) * (vertexMapSize - 1) * 6];
Vector3 normal = new Vector3();
Vector3 edge1 = new Vector3();
Vector3 edge2 = new Vector3();
// Stitch the verteces together into triangles. Two triangles per square
for (int x = 0; x < vertexMapSize - 1; x++)
{
for (int z = 0; z < vertexMapSize - 1; z++)
{
meshIndices[(z + x * (vertexMapSize - 1)) * 6] = (z + x * vertexMapSize);
meshIndices[(z + x * (vertexMapSize - 1)) * 6 + 1] = ((z + 1) + x * vertexMapSize);
meshIndices[(z + x * (vertexMapSize - 1)) * 6 + 2] = ((z + 1) + (x + 1) * vertexMapSize);
edge1 = vertices[(z + 1) + x * vertexMapSize].Position - vertices[z + x * vertexMapSize].Position;
edge2 = vertices[z + x * vertexMapSize].Position - vertices[(z + 1) + (x + 1) * vertexMapSize].Position;
normal = Vector3.Cross(edge2, edge1);
normal.Normalize();
vertices[z + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + (x + 1) * vertexMapSize].Normal += normal;
meshIndices[(z + x * (vertexMapSize - 1)) * 6 + 3] = (z + (x + 1) * vertexMapSize);
meshIndices[(z + x * (vertexMapSize - 1)) * 6 + 4] = (z + x * vertexMapSize);
meshIndices[(z + x * (vertexMapSize - 1)) * 6 + 5] = ((z + 1) + (x + 1) * vertexMapSize);
edge1 = vertices[z + x * vertexMapSize].Position - vertices[z + (x + 1) * vertexMapSize].Position;
edge2 = vertices[z + (x + 1) * vertexMapSize].Position - vertices[(z + 1) + (x + 1) * vertexMapSize].Position;
normal = Vector3.Cross(edge2, edge1);
normal.Normalize();
vertices[z + (x + 1) * vertexMapSize].Normal += normal;
vertices[z + x * vertexMapSize].Normal += normal;
vertices[(z + 1) + (x + 1) * vertexMapSize].Normal += normal;
}
}
// Normalize vertex normals
for (int i = 0; i < vertexMapSize * vertexMapSize; i++)
{
vertices[i].Normal.Normalize();
}
// Set the data
vertexBuffer.SetData(vertices);
meshIndexBuffer.SetData(meshIndices);
}
public void GenerateCollisionMap()
{
// Interpolate the missing values between scaled vertex values for smoother collision go through each block
for (int blockX = 0; blockX < vertexMapSize; blockX++)
{
for (int blockZ = 0; blockZ < vertexMapSize; blockZ++)
{
float diffx = 0;
float diffz = 0;
// This crap is b/c we need to avoid array out of bounds exception
if ((blockX != (vertexMapSize - 1)) && (blockZ != (vertexMapSize - 1)))
{
diffx = vertexHeightData[blockX + 1, blockZ] - vertexHeightData[blockX, blockZ];
diffz = vertexHeightData[blockX, blockZ + 1] - vertexHeightData[blockX, blockZ];
}
else if ((blockX == (vertexMapSize - 1)) && (blockZ == (vertexMapSize - 1)))
{
// Leave 0
}
else if (blockX == vertexMapSize - 1)
{
// Calculate difference between height values from base point to next point in array
diffz = vertexHeightData[blockX, blockZ + 1] - vertexHeightData[blockX, blockZ];
}
else if (blockZ == vertexMapSize - 1)
{
// Calculate difference between height values from base point to next point in array
diffx = vertexHeightData[blockX + 1, blockZ] - vertexHeightData[blockX, blockZ];
}
// Moving on, For each empty block in array
for (int x = 0; x < terrainScaleFactor; x++)
{
for (int z = 0; z < terrainScaleFactor; z++)
{
// Distance from base position to current collision point
float distx = 1.0f * x / terrainScaleFactor;
float distz = 1.0f * z / terrainScaleFactor;
// Calc height of new value
float interpValueX = vertexHeightData[blockX, blockZ] + distx * diffx;
float interpValueZ = vertexHeightData[blockX, blockZ] + distz * diffz;
collisionHeightData[blockX * terrainScaleFactor + x,
blockZ * terrainScaleFactor + z] = (interpValueX + interpValueZ) / 2;
}
}
}
}
}
private void UpdateCollisionMap(int minX, int maxX, int minZ, int maxZ)
{
for (int blockX = minX; blockX < maxX; blockX++)
{
for (int blockZ = minZ; blockZ < maxZ; blockZ++)
{
float diffx = 0;
float diffz = 0;
// Calculate difference between height values from base point to next point in array
diffx = vertices[blockZ + (blockX + 1) * vertexMapSize].Position.Y - vertices[blockZ + blockX * vertexMapSize].Position.Y;
diffz = vertices[(blockZ + 1) + blockX * vertexMapSize].Position.Y - vertices[blockZ + blockX * vertexMapSize].Position.Y;
// Moving on, For each empty block in array
for (int x = 0; x < terrainScaleFactor; x++)
{
for (int z = 0; z < terrainScaleFactor; z++)
{
// Distance from base position to current collision point
float distx = 1.0f * x / terrainScaleFactor;
float distz = 1.0f * z / terrainScaleFactor;
// Calc height of new value
float interpValueX = vertices[blockZ + blockX * vertexMapSize].Position.Y + distx * diffx;
float interpValueZ = vertices[blockZ + blockX * vertexMapSize].Position.Y + distz * diffz;
//collisionHeightData[blockX * terrainScaleFactor + x,
// blockZ * terrainScaleFactor + z] = (interpValueX + interpValueZ) / 2;
}
}
}
}
}
*/
}
}