//--------------------------------------------------------------------------------------------------------------------------------------------------- // // 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; } } } } } */ } }