static protected float K2(Terrain.TerrainVertex v0, Terrain.TerrainVertex v1)
        {
            Vector3 delPos = v0.position - v1.position;

            float cosTwoTheta = Vector3.Dot(v0.normal, v1.normal);
            float twoSinTheta = MathHelper.Clamp((2.0f * (1.0f - cosTwoTheta)), 0.0f, 1.0f);
            float distSq      = delPos.LengthSquared();

            Debug.Assert(distSq > 0.0f);
            return((float)Math.Sqrt(twoSinTheta / distSq));
        }
        static protected float K2(Point sz, int i, int j, int di, int dj)
        {
            if (OutOfRange(i, di, sz.X) || OutOfRange(j, dj, sz.Y))
            {
                return(0.0f);
            }
            Terrain.TerrainVertex vback = vertices[i - di, j - dj];
            Terrain.TerrainVertex vhere = vertices[i, j];
            Terrain.TerrainVertex vfore = vertices[i + di, j + dj];
            float kBack = K2(vback, vhere);
            float kFore = K2(vhere, vfore);

            if (kBack * kFore <= 0.0f)
            {
                return(0.0f);
            }

            float step = 0.0f;

            if (kBack > kFore)
            {
                step = di > 0
                    ? vback.position.X - vhere.position.X
                    : vback.position.Y - vhere.position.Y;
                step *= (kBack - kFore) / kBack;
            }
            else
            {
                step = di > 0
                    ? vfore.position.X - vhere.position.X
                    : vfore.position.Y - vhere.position.Y;
                step *= (kFore - kBack) / kFore;
            }
            float kEnergy  = 0.1f;
            float kMinStep = 0.01f;

            step *= kEnergy;

            Debug.Assert(!float.IsNaN(step));
            Debug.Assert(!float.IsInfinity(step));
            if (Math.Abs(step) < kMinStep)
            {
                return(0.0f);
            }
            return(step);
        }
        }   // end of Tile UpdateTileData()

        public void LoadGraphicsContent(GraphicsDeviceManager graphics)
        {
            GraphicsDevice device = graphics.GraphicsDevice;

            // For each tile create the tile's vertex buffer.
            Terrain.TerrainVertex[] localVerts = new Terrain.TerrainVertex[NumVertices];

            // Init the vertex buffer.
            if (vbuf != null)
            {
                vbuf.Dispose();
                vbuf = null;
            }
            vbuf = new VertexBuffer(device, typeof(Terrain.TerrainVertex), NumVertices, ResourceUsage.WriteOnly, ResourceManagementMode.Automatic);

            // Copy the right vertices into the local buffer.
            int offsetX = indexX * (BuffSize - 1);
            int offsetY = indexY * (BuffSize - 1);

            Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
            Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);

            for (int j = 0; j < BuffSize; j++)
            {
                for (int i = 0; i < BuffSize; i++)
                {
                    localVerts[j * BuffSize + i] = vertices[i + offsetX, j + offsetY];

                    min = Vector3.Min(min, localVerts[j * BuffSize + i].position);
                    max = Vector3.Max(max, localVerts[j * BuffSize + i].position);
                }
            }
            box = new AABB(min, max);

            // Copy to vertex buffer.
            vbuf.SetData <Terrain.TerrainVertex>(localVerts);

            BokuGame.Load(skirt);
            BokuGame.Load(water);
        }   // end of Tile LoadGraphicsContent()