public static Vector3 CalculateNormal(HeightSampler sampler, float x, float y, float d = 1.0f)
 {
     float dy0 = sampler(x + d, y) - sampler(x - d, y);
     float dy1 = sampler(x, y + d) - sampler(x, y - d);
     Vector3 result = new Vector3(-dy0, d, -dy1);
     result.Normalize();
     return result;
 }
 public static Vector3 CalculateTangent(HeightSampler sampler, float x, float y, float d = 1.0f)
 {
     float dy0 = sampler(x + d, y) - sampler(x - d, y);
     Vector3 result = new Vector3(d * 2.0f, dy0, 0.0f);
     result.Normalize();
     return result;
     //return Vector3.UnitX;
 }
        public static Mesh BuildHeightmap(HeightSampler sampler, int width, int height, float tilesize, int subdivide = 1)
        {
            // Determine sizes
            int totalW = width * subdivide;
            int totalH = height * subdivide;
            float invtotalW = 1.0f / totalW;
            float invtotalH = 1.0f / totalH;

            // Precache an array of heights
            float[,] heights = new float[totalW, totalH];
            for (int x = 0; x < totalW; x++)
            {
                float fX = x * invtotalW * width;
                for (int y = 0; y < totalH; y++)
                {
                    float fY = y * invtotalH * height;
                    heights[x, y] = sampler(fX, fY);
                }
            }

            // Recreate the sampler to use the cache
            sampler = new HeightSampler((x, y) =>
            {
                int ix = (int)(x * subdivide);
                int iy = (int)(y * subdivide);
                if (ix < 0) ix = 0; if (iy < 0) iy = 0;
                if (ix >= totalW) ix = totalW - 1;
                if (iy >= totalH) iy = totalH - 1;
                return heights[ix, iy];
            });

            // Prepare the builder
            MeshBuilder builder = new MeshBuilder();
            builder.UseNormals = true;
            builder.UseTangents = true;

            // Perform the loop
            for (int x = 0; x < totalW; x++)
            {
                float fX = x * invtotalW * width;
                for (int y = 0; y < totalH; y++)
                {
                    float fY = y * invtotalH * height;
                    builder.AddPosition(new Vector3(fX * tilesize, sampler(fX, fY), fY * tilesize));
                    builder.AddNormal(CalculateNormal(sampler, fX, fY));
                    builder.AddTangent(CalculateTangent(sampler, fX, fY));
                    if ((x < totalW - 1) && (y < totalH - 1))
                    {
                        int idx = (x * totalH) + y;
                        builder.AddIndex((ushort)idx);
                        builder.AddIndex((ushort)(idx + 1));
                        builder.AddIndex((ushort)(idx + totalH + 1));
                        builder.AddIndex((ushort)(idx + totalH + 1));
                        builder.AddIndex((ushort)(idx + totalH));
                        builder.AddIndex((ushort)idx);
                    }
                }
            }
            return builder.Build();
        }