public void MergeLeft(Heightmap leftTile, int transitionWidth) { if (leftTile == null) { throw new ArgumentNullException("leftTile"); } if (leftTile.Width != Width || leftTile.Depth != Depth) { throw new ArgumentException("Tiles must have equal widths and depths", "leftTile"); } if (transitionWidth >= Width) { throw new ArgumentException("Transition width must be less than tile width", "transitionWidth"); } for (int x = 0; x < transitionWidth; x++) { for (int z = 0; z < Depth; z++) { float leftEdgeHeight = leftTile.GetHeightValue(leftTile.Width - 1, z); float originalHeight = GetHeightValue(x, z); float newHeight = MathHelper.Lerp(leftEdgeHeight, originalHeight, (float)x / (transitionWidth - 1)); SetHeightValue(x, z, newHeight); } } }
/// <summary> /// Clone the heightmap. /// </summary> /// <returns></returns> public object Clone() { var heightmap = new Heightmap(_width, _depth, _minimumHeight, _maximumHeight, (float[])_heightValues.Clone(), _randomSeed); return(heightmap); }
/// <summary> /// Default constructor /// </summary> /// <param name="game"></param> /// <param name="cameraProvider"></param> /// <param name="heightmap"></param> /// <param name="world"></param> public TerrainMesh(Game game, ICameraProvider cameraProvider, Heightmap heightmap, Matrix world) : base(game) { Patches = new List<TerrainPatch>(); Heightmap = heightmap; _cameraProvider = cameraProvider; World = world; }
// /// <summary> // /// Build a terrain patch. // /// </summary> // /// <param name="game"></param> // /// <param name="heightmap"></param> // /// <param name="worldMatrix"></param> // /// <param name="width"></param> // /// <param name="depth"></param> // /// <param name="offsetX"></param> // /// <param name="offsetZ"></param> // public static void BuildPatch(Game game, Heightmap heightmap, Matrix worldMatrix, int width, int depth, int offsetX, int offsetZ) // { // // } /// <summary> /// Build the vertex buffer as well as the bounding box. /// </summary> /// <param name="heightmap"></param> private void BuildVertexBuffer(Heightmap heightmap) { int index = 0; _boundingBox.Min.Y = float.MaxValue; _boundingBox.Max.Y = float.MinValue; Geometry = new VertexPositionNormalTexture[Width * Depth]; for (int z = _offsetZ; z < _offsetZ + Depth; ++z) { for (int x = _offsetX; x < _offsetX + Width; ++x) { // We need to connect the patches by increasing each patch width and depth by 1, // but this means the patches along some of the edges will run out of // heightmap data, so make sure we cap the index at the edge int cappedX = Math.Min(x, heightmap.Width - 1); int cappedZ = Math.Min(z, heightmap.Depth - 1); float height = heightmap.GetHeightValue(cappedX, cappedZ); if (height < _boundingBox.Min.Y) { _boundingBox.Min.Y = height; } if (height > _boundingBox.Max.Y) { _boundingBox.Max.Y = height; } var position = new Vector3(x, height, z); Vector3 normal; ComputeVertexNormal(heightmap, cappedX, cappedZ, out normal); Geometry[index] = new VertexPositionNormalTexture(position, normal, new Vector2(x, z)); ++index; } } }
/// <summary> /// Add two heightmaps together. /// </summary> /// <param name="heightmap"></param> public void AddHeightmap(Heightmap heightmap) { // Reject the heightmap if it doesn't fit. if ((_width != heightmap.Width) || (_depth != heightmap.Depth) || (_minimumHeight != heightmap.MinimumHeight) || (_maximumHeight != heightmap.MaximumHeight)) { return; } // Add the heightmaps together. for (int x = 0; x < _width; ++x) { for (int z = 0; z < _depth; ++z) { _heightValues[x + z * _width] = _heightValues[x + z * _width] + heightmap.GetHeightValue(x, z); } } }
/// <summary> /// Combine the heightmap with the passed heightmap. /// The amount variable is the ratio of the heightmap passed as a parameter. /// </summary> /// <param name="heightmap"></param> /// <param name="amount"></param> public void CombineHeightmap(Heightmap heightmap, float amount) { // Reject the heightmap if it doesn't fit. if ((_width != heightmap.Width) || (_depth != heightmap.Depth) || (_minimumHeight != heightmap.MinimumHeight) || (_maximumHeight != heightmap.MaximumHeight)) { return; } // Combine the heightmaps. // H1 = H1 * (1.0f - amount) + H2 * amount for (int x = 0; x < _width; ++x) { for (int z = 0; z < _depth; ++z) { _heightValues[x + z * _width] = _heightValues[x + z * _width] * (1.0f - amount) + heightmap.GetHeightValue(x, z) * amount; } } }
/// <summary> /// Default constructor. /// </summary> public TerrainPatch(Game game, Heightmap heightmap, Matrix worldMatrix, int width, int depth, int offsetX, int offsetZ) { _boundingBox = new BoundingBox(); _game = game; Width = width; Depth = depth; _offsetX = offsetX; _offsetZ = offsetZ; _boundingBox.Min.X = offsetX; _boundingBox.Min.Z = offsetZ; _boundingBox.Max.X = offsetX + width; _boundingBox.Max.Z = offsetZ + depth; BuildVertexBuffer(heightmap); _vertexBuffer = new VertexBuffer(_game.GraphicsDevice, VertexPositionNormalTexture.SizeInBytes * Geometry.Length, BufferUsage.WriteOnly); _vertexBuffer.SetData(Geometry); BuildIndexBuffer(); _indexBuffer = new IndexBuffer(_game.GraphicsDevice, sizeof(short) * _indices.Length, BufferUsage.WriteOnly, IndexElementSize.SixteenBits); _indexBuffer.SetData(_indices); // Apply the world matrix transformation to the bounding box. _boundingBox.Min = Vector3.Transform(_boundingBox.Min, worldMatrix); _boundingBox.Max = Vector3.Transform(_boundingBox.Max, worldMatrix); }
/// <summary> /// Default constructor. /// </summary> public TerrainPatch(Game game, Heightmap heightmap, Matrix worldMatrix, int width, int depth, int offsetX, int offsetZ) { _boundingBox = new BoundingBox(); _game = game; Width = width; Depth = depth; _offsetX = offsetX; _offsetZ = offsetZ; _boundingBox.Min.X = offsetX; _boundingBox.Min.Z = offsetZ; _boundingBox.Max.X = offsetX + width; _boundingBox.Max.Z = offsetZ + depth; BuildVertexBuffer(heightmap); _vertexBuffer = new VertexBuffer(_game.GraphicsDevice, VertexPositionNormalTexture.SizeInBytes*Geometry.Length, BufferUsage.WriteOnly); _vertexBuffer.SetData(Geometry); BuildIndexBuffer(); _indexBuffer = new IndexBuffer(_game.GraphicsDevice, sizeof (short)*_indices.Length, BufferUsage.WriteOnly, IndexElementSize.SixteenBits); _indexBuffer.SetData(_indices); // Apply the world matrix transformation to the bounding box. _boundingBox.Min = Vector3.Transform(_boundingBox.Min, worldMatrix); _boundingBox.Max = Vector3.Transform(_boundingBox.Max, worldMatrix); }
/// <summary> /// Add two heightmaps together. /// </summary> /// <param name="heightmap"></param> public void AddHeightmap(Heightmap heightmap) { // Reject the heightmap if it doesn't fit. if ((_width != heightmap.Width) || (_depth != heightmap.Depth) || (_minimumHeight != heightmap.MinimumHeight) || (_maximumHeight != heightmap.MaximumHeight)) { return; } // Add the heightmaps together. for (int x = 0; x < _width; ++x) { for (int z = 0; z < _depth; ++z) { _heightValues[x + z*_width] = _heightValues[x + z*_width] + heightmap.GetHeightValue(x, z); } } }
public void MergeLeft(Heightmap leftTile, int transitionWidth) { if (leftTile == null) throw new ArgumentNullException("leftTile"); if (leftTile.Width != Width || leftTile.Depth != Depth) throw new ArgumentException("Tiles must have equal widths and depths", "leftTile"); if (transitionWidth >= Width) throw new ArgumentException("Transition width must be less than tile width", "transitionWidth"); for (int x = 0; x < transitionWidth; x++) { for (int z = 0; z < Depth; z++) { float leftEdgeHeight = leftTile.GetHeightValue(leftTile.Width - 1, z); float originalHeight = GetHeightValue(x, z); float newHeight = MathHelper.Lerp(leftEdgeHeight, originalHeight, (float) x/(transitionWidth - 1)); SetHeightValue(x, z, newHeight); } } }
/// <summary> /// Returns a 2D array of [rows, cols]. /// </summary> /// <param name="hm"></param> /// <returns></returns> private static float[,] HeightmapToValues(Heightmap hm) { var heightValues = new float[hm.Depth, hm.Width]; for (int i = 0; i < hm.HeightValues.Length; i++) { int col = i % hm.Depth; int row = i / hm.Width; heightValues[row, col] = hm.HeightValues[i]; } return heightValues; }
/// <summary> /// Compute vertex normal at the given x,z coordinate. /// </summary> /// <param name="heightmap"></param> /// <param name="x"></param> /// <param name="z"></param> /// <param name="normal"></param> private static void ComputeVertexNormal(Heightmap heightmap, int x, int z, out Vector3 normal) { int width = heightmap.Width; int depth = heightmap.Depth; Vector3 p1; Vector3 p2; Vector3 avgNormal = Vector3.Zero; int avgCount = 0; bool spaceAbove = false; bool spaceBelow = false; bool spaceLeft = false; bool spaceRight = false; Vector3 tmpNormal; Vector3 v1; Vector3 v2; var center = new Vector3(x, heightmap.GetHeightValue(x, z), z); if (x > 0) { spaceLeft = true; } if (x < width - 1) { spaceRight = true; } if (z > 0) { spaceAbove = true; } if (z < depth - 1) { spaceBelow = true; } if (spaceAbove && spaceLeft) { p1 = new Vector3(x - 1, heightmap.GetHeightValue(x - 1, z), z); p2 = new Vector3(x - 1, heightmap.GetHeightValue(x - 1, z - 1), z - 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } if (spaceAbove && spaceRight) { p1 = new Vector3(x, heightmap.GetHeightValue(x, z - 1), z - 1); p2 = new Vector3(x + 1, heightmap.GetHeightValue(x + 1, z - 1), z - 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } if (spaceBelow && spaceRight) { p1 = new Vector3(x + 1, heightmap.GetHeightValue(x + 1, z), z); p2 = new Vector3(x + 1, heightmap.GetHeightValue(x + 1, z + 1), z + 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } if (spaceBelow && spaceLeft) { p1 = new Vector3(x, heightmap.GetHeightValue(x, z + 1), z + 1); p2 = new Vector3(x - 1, heightmap.GetHeightValue(x - 1, z + 1), z + 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } normal = avgNormal / avgCount; }
// /// <summary> // /// Build a terrain patch. // /// </summary> // /// <param name="game"></param> // /// <param name="heightmap"></param> // /// <param name="worldMatrix"></param> // /// <param name="width"></param> // /// <param name="depth"></param> // /// <param name="offsetX"></param> // /// <param name="offsetZ"></param> // public static void BuildPatch(Game game, Heightmap heightmap, Matrix worldMatrix, int width, int depth, int offsetX, int offsetZ) // { // // } /// <summary> /// Build the vertex buffer as well as the bounding box. /// </summary> /// <param name="heightmap"></param> private void BuildVertexBuffer(Heightmap heightmap) { int index = 0; _boundingBox.Min.Y = float.MaxValue; _boundingBox.Max.Y = float.MinValue; Geometry = new VertexPositionNormalTexture[Width*Depth]; for (int z = _offsetZ; z < _offsetZ + Depth; ++z) { for (int x = _offsetX; x < _offsetX + Width; ++x) { // We need to connect the patches by increasing each patch width and depth by 1, // but this means the patches along some of the edges will run out of // heightmap data, so make sure we cap the index at the edge int cappedX = Math.Min(x, heightmap.Width - 1); int cappedZ = Math.Min(z, heightmap.Depth - 1); float height = heightmap.GetHeightValue(cappedX, cappedZ); if (height < _boundingBox.Min.Y) { _boundingBox.Min.Y = height; } if (height > _boundingBox.Max.Y) { _boundingBox.Max.Y = height; } var position = new Vector3(x, height, z); Vector3 normal; ComputeVertexNormal(heightmap, cappedX, cappedZ, out normal); Geometry[index] = new VertexPositionNormalTexture(position, normal, new Vector2(x, z)); ++index; } } }
public void BuildTerrain(int width, float minHeight, float maxHeight) { _width = width; _minHeight = minHeight; _maxHeight = maxHeight; // When running test scenarios over and over we need to generate the exact same terrains const int myRandomTerrainSeed = 1337; Heightmap = new Heightmap(myRandomTerrainSeed); Heightmap.GenerateFaultHeightmap(_width, _width, _minHeight, _maxHeight, new HeightmapFaultSettings(16, 256, 1024, 128, 0.5f)); // TODO Adding world transformation confuses the sunlight/terrain shader.. not sure why // const float scale = 10; Matrix world = Matrix.Identity; // Matrix.CreateScale(scale) * Matrix.CreateTranslation(scale * Position); Mesh = new TerrainMesh(Game, _cameraProvider, Heightmap, world); Mesh.Initialize(); // TerrainMeshes.Add(first); return; }
/// <summary> /// Clone the heightmap. /// </summary> /// <returns></returns> public object Clone() { var heightmap = new Heightmap(_width, _depth, _minimumHeight, _maximumHeight, (float[]) _heightValues.Clone(), _randomSeed); return heightmap; }
/// <summary> /// Combine the heightmap with the passed heightmap. /// The amount variable is the ratio of the heightmap passed as a parameter. /// </summary> /// <param name="heightmap"></param> /// <param name="amount"></param> public void CombineHeightmap(Heightmap heightmap, float amount) { // Reject the heightmap if it doesn't fit. if ((_width != heightmap.Width) || (_depth != heightmap.Depth) || (_minimumHeight != heightmap.MinimumHeight) || (_maximumHeight != heightmap.MaximumHeight)) { return; } // Combine the heightmaps. // H1 = H1 * (1.0f - amount) + H2 * amount for (int x = 0; x < _width; ++x) { for (int z = 0; z < _depth; ++z) { _heightValues[x + z*_width] = _heightValues[x + z*_width]*(1.0f - amount) + heightmap.GetHeightValue(x, z)*amount; } } }
/// <summary> /// Compute vertex normal at the given x,z coordinate. /// </summary> /// <param name="heightmap"></param> /// <param name="x"></param> /// <param name="z"></param> /// <param name="normal"></param> private static void ComputeVertexNormal(Heightmap heightmap, int x, int z, out Vector3 normal) { int width = heightmap.Width; int depth = heightmap.Depth; Vector3 p1; Vector3 p2; Vector3 avgNormal = Vector3.Zero; int avgCount = 0; bool spaceAbove = false; bool spaceBelow = false; bool spaceLeft = false; bool spaceRight = false; Vector3 tmpNormal; Vector3 v1; Vector3 v2; var center = new Vector3(x, heightmap.GetHeightValue(x, z), z); if (x > 0) { spaceLeft = true; } if (x < width - 1) { spaceRight = true; } if (z > 0) { spaceAbove = true; } if (z < depth - 1) { spaceBelow = true; } if (spaceAbove && spaceLeft) { p1 = new Vector3(x - 1, heightmap.GetHeightValue(x - 1, z), z); p2 = new Vector3(x - 1, heightmap.GetHeightValue(x - 1, z - 1), z - 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } if (spaceAbove && spaceRight) { p1 = new Vector3(x, heightmap.GetHeightValue(x, z - 1), z - 1); p2 = new Vector3(x + 1, heightmap.GetHeightValue(x + 1, z - 1), z - 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } if (spaceBelow && spaceRight) { p1 = new Vector3(x + 1, heightmap.GetHeightValue(x + 1, z), z); p2 = new Vector3(x + 1, heightmap.GetHeightValue(x + 1, z + 1), z + 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } if (spaceBelow && spaceLeft) { p1 = new Vector3(x, heightmap.GetHeightValue(x, z + 1), z + 1); p2 = new Vector3(x - 1, heightmap.GetHeightValue(x - 1, z + 1), z + 1); v1 = p1 - center; v2 = p2 - p1; tmpNormal = Vector3.Cross(v1, v2); avgNormal += tmpNormal; ++avgCount; } normal = avgNormal/avgCount; }