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