public void Unload(long dataX, long dataZ)
        {
            Data2D data = data2D[dataX][dataZ];

            if (data.IsLoaded)
            {
                data.Unload();
            }
        }
        public void Load(long dataX, long dataZ)
        {
            Data2D data = data2D[dataX][dataZ];

            if (!data.IsLoaded)
            {
                data.Load(dataX, dataZ);
            }
        }
        public Vector3 GetNormalAt(long dataX, long dataZ, float x, float z)
        {
            Data2D data = data2D[dataX][dataZ];

            if (data.IsLoaded)
            {
#if !_LOADEDNORM
                return(data.GetNormalAt(x, z));
#else
                {
                    // First General method : (9 adds and 6 muls + a normalization)
                    //        *---v3--*
                    //        |   |   |
                    //        |   |   |
                    //        v1--X--v2
                    //        |   |   |
                    //        |   |   |
                    //        *---v4--*
                    //
                    //        U = v2 - v1;
                    //        V = v4 - v3;
                    //        N = Cross(U, V);
                    //        N.normalise;
                    //
                    // BUT IN CASE OF A HEIGHTMAP :
                    //
                    //   if you do some math by hand before you code,
                    //   you can see that N is immediately given by
                    //  Approximation (2 adds and a normalization)
                    //
                    //        N = Vector3(z[x-1][y] - z[x+1][y], z[x][y-1] - z[x][y+1], 2);
                    //        N.normalise();
                    //
                    // or even using SOBEL operator VERY accurate!
                    // (14 adds and a normalization)
                    //
                    //       N = Vector3 (z[x-1][y-1] + z[x-1][y] + z[x-1][y] + z[x-1][y+1] - z[x+1][y-1] - z[x+1][y] - z[x+1][y] - z[x+1][y+1],
                    //                     z[x-1][y-1] + z[x][y-1] + z[x][y-1] + z[x+1][y-1] - z[x-1][y+1] - z[x][y+1] - z[x][y+1] - z[x+1][y+1],
                    //                     8);
                    //       N.normalize();


                    // Fast SOBEL filter
                    Vector3 result = new Vector3
                                         (this.GetHeightAtPage(dataX, dataZ, x - 1.0F, z) - this.GetHeightAtPage(dataX, dataZ, x + 1.0F, z),
                                         2.0f,
                                         this.GetHeightAtPage(dataX, dataZ, x, z - 1.0F) - this.GetHeightAtPage(dataX, dataZ, x, z + 1.0F));

                    result.Normalize();

                    return(result);
                }
#endif
            }
            return(Vector3.UnitY);
        }
        public ColorEx GetBaseAt(long dataX, long dataZ, float x, float z)
        {
            Data2D data = data2D[dataX][dataZ];

            if (data.IsLoaded)
            {
                return(data.GetBase(x, z));
            }
            return(ColorEx.White);
        }
        //This function will return the max possible value of height base on the current 2D Data implementation
        public float GetMaxHeight(long x, long z)
        {
            Data2D data = data2D[x][z];

            if (data.IsLoaded)
            {
                return(data2D[x][z].MaxHeight);
            }
            return(maxHeight);
        }
        public float GetHeight(long dataX, long dataZ, long x, long z)
        {
            Data2D data = data2D[dataX][dataZ];

            if (data.IsLoaded)
            {
                return(data.GetHeight(x, z));
            }
            return(0.0f);
        }
        public void DeformHeight(Vector3 deformationPoint, float modificationHeight, TileInfo info)
        {
            long pSize = Options.Instance.PageSize;
            long pX    = info.PageX;
            long pZ    = info.PageZ;
            long wL    = Options.Instance.World_Width;
            long hL    = Options.Instance.World_Height;
            // adjust x and z to be local to page
            long x = (long)((deformationPoint.x) - ((pX - wL * 0.5f) * (pSize)));
            long z = (long)((deformationPoint.z) - ((pZ - hL * 0.5f) * (pSize)));

            Data2D data = data2D[pX][pZ];

            if (data.IsLoaded)
            {
                float h = data.DeformHeight(x, z, modificationHeight);

                // If we're on a page edge, we must duplicate the change on the
                // neighbour page (if it has one...)
                if (x == 0 && pX != 0)
                {
                    data = data2D[pX - 1][pZ];
                    if (data.IsLoaded)
                    {
                        data.SetHeight(Options.Instance.PageSize - 1, z, h);
                    }
                }
                if (x == pSize - 1 && pX < wL - 1)
                {
                    data = data2D[pX + 1][pZ];
                    if (data.IsLoaded)
                    {
                        data.SetHeight(0, z, h);
                    }
                }

                if (z == 0 && pZ != 0)
                {
                    data = data2D[pX][pZ - 1];
                    if (data.IsLoaded)
                    {
                        data.SetHeight(x, Options.Instance.PageSize - 1, h);
                    }
                }
                if (z == pSize - 1 && pZ < hL - 1)
                {
                    data = data2D[pX][pZ + 1];
                    if (data.IsLoaded)
                    {
                        data.SetHeight(x, 0, h);
                    }
                }
            }
        }
        public Data2D GetData2D(long x, long z)
        {
            Data2D data = data2D[x][z];

            return((data.IsLoaded)? data: null);
        }
        public float GetRealPageHeight(float x, float z, long pageX, long pageZ, long Lod)
        {
            // scale position from world to page scale
            float localX = x / Options.Instance.Scale.x;
            float localZ = z / Options.Instance.Scale.z;

            // adjust x and z to be local to page
            long pSize = Options.Instance.PageSize - 1;

            localX -= (float)(pageX - Options.Instance.World_Width * 0.5f) * pSize;
            localZ -= (float)(pageZ - Options.Instance.World_Height * 0.5f) * pSize;

            // make sure x and z do not go outside the world boundaries
            if (localX < 0)
            {
                localX = 0;
            }
            else if (localX > pSize)
            {
                localX = pSize;
            }

            if (localZ < 0)
            {
                localZ = 0;
            }
            else if (localZ > pSize)
            {
                localZ = pSize;
            }

            // find the 4 vertices that surround the point
            // use LOD info to determine vertex spacing - this is passed into the method
            // determine vertices on left and right of point and top and bottom
            // don't access VBO since a big performance hit when only 4 vertices are needed
            int vertex_spread = 1 << (int)Lod;

            // find the vertex to the bottom left of the point
            int bottom_left_x = ((int)(localX / vertex_spread)) * vertex_spread;
            int bottom_left_z = ((int)(localZ / vertex_spread)) * vertex_spread;

            // find the 4 heights around the point
            Data2D data           = data2D[pageX][pageZ];
            float  bottom_left_y  = data.GetHeight(bottom_left_x, bottom_left_z);
            float  bottom_right_y = data.GetHeight(bottom_left_x + vertex_spread, bottom_left_z);
            float  top_left_y     = data.GetHeight(bottom_left_x, bottom_left_z + vertex_spread);
            float  top_right_y    = data.GetHeight(bottom_left_x + vertex_spread, bottom_left_z + vertex_spread);

            float x_pct = (localX - (float)bottom_left_x) / (float)vertex_spread;
            float z_pct = (localZ - (float)bottom_left_z) / (float)vertex_spread;

            //bilinear interpolate to find the height.

            // figure out which 3 vertices are closest to the point and use those to form triangle plane for intersection
            // Triangle strip has diagonal going from bottom left to top right
            if ((x_pct - z_pct) >= 0)
            {
                return((bottom_left_y + (bottom_right_y - bottom_left_y) * x_pct + (top_right_y - bottom_right_y) * z_pct));
            }
            else
            {
                return((top_left_y + (top_right_y - top_left_y) * x_pct + (bottom_left_y - top_left_y) * (1 - z_pct)));
            }
        }
        public float GetHeightAtPage(long dataX, long dataZ, int x, int z)
        {
            Data2D data = data2D[dataX][dataZ];

            if (data.IsLoaded)
            {
                int lX    = x;
                int lZ    = z;
                int pSize = (int)Options.Instance.PageSize;
                //check if we have to change current page
                if (lX < 0)
                {
                    if (dataX == 0)
                    {
                        lX = 0;
                    }
                    else
                    {
                        data = data2D[dataX - 1][dataZ];
                        if (data.IsLoaded)
                        {
                            lX = pSize;
                        }
                        else
                        {
                            lX   = 0;
                            data = data2D[dataX][dataZ];
                        }
                    }
                }
                else if (lX > pSize)
                {
                    if (dataX == Options.Instance.World_Width)
                    {
                        lX = pSize;
                    }
                    else
                    {
                        data = data2D[dataX + 1][dataZ];
                        if (data.IsLoaded)
                        {
                            lX = 0;
                        }
                        else
                        {
                            lX   = pSize;
                            data = data2D[dataX][dataZ];
                        }
                    }
                }

                if (lZ < 0)
                {
                    if (dataZ == 0)
                    {
                        lZ = 0;
                    }
                    else
                    {
                        data = data2D[dataX][dataZ - 1];
                        if (data.IsLoaded)
                        {
                            lZ = pSize;
                        }
                        else
                        {
                            lZ   = 0;
                            data = data2D[dataX][dataZ];
                        }
                    }
                }
                else if (lZ > pSize)
                {
                    if (dataZ == Options.Instance.World_Height)
                    {
                        lZ = pSize;
                    }
                    else
                    {
                        data = data2D[dataX][dataZ + 1];
                        if (data.IsLoaded)
                        {
                            lZ = 0;
                        }
                        else
                        {
                            lZ   = pSize;
                            data = data2D[dataX][dataZ];
                        }
                    }
                }
                return(data.GetHeight(x, z));
            }
            return(0.0f);
        }
        public float GetHeightAtPage(long dataX, long dataZ, float x, float z)
        {
            Data2D data = null;

            float lX    = x;
            float lZ    = z;
            float pSize = Options.Instance.PageSize;

            //check if we have to change current page
            if (lX < 0.0f)
            {
                if (dataX == 0)
                {
                    lX = 0.0f;
                }
                else
                {
                    data = data2D[dataX - 1][dataZ];
                    if (data.IsLoaded)
                    {
                        lX = (float)(pSize - 1);
                    }
                    else
                    {
                        lX   = 0.0f;
                        data = data2D[dataX][dataZ];
                    }
                }
            }
            else if (lX > pSize)
            {
                if (dataX == Options.Instance.World_Width - 1)
                {
                    lX = (float)(pSize);
                }
                else
                {
                    data = data2D[dataX + 1][dataZ];
                    if (data.IsLoaded)
                    {
                        lX = 0.0f;
                    }
                    else
                    {
                        lX   = (float)(pSize);
                        data = data2D[dataX][dataZ];
                    }
                }
            }

            if (lZ < 0.0f)
            {
                if (dataZ == 0)
                {
                    lZ = 0.0f;
                }
                else
                {
                    data = data2D[dataX][dataZ - 1];
                    if (data.IsLoaded)
                    {
                        lZ = (float)(pSize);
                    }
                    else
                    {
                        lZ   = 0.0f;
                        data = data2D[dataX][dataZ];
                    }
                }
            }
            else if (lZ > pSize)
            {
                if (dataZ == Options.Instance.World_Height - 1)
                {
                    lZ = (float)(pSize);
                }
                else
                {
                    data = data2D[dataX][dataZ + 1];
                    if (data.IsLoaded)
                    {
                        lZ = 0.0f;
                    }
                    else
                    {
                        lZ   = (float)(pSize);
                        data = data2D[dataX][dataZ];
                    }
                }
            }
            if (data == null)
            {
                data = data2D[dataX][dataZ];
            }

            if (data.IsLoaded)
            {
                return(data.GetHeight(lX, lZ));
            }
            return(0.0f);
        }