//BSD License  
//DXGfx® - http://www.eteractions.com
//Copyright (c) 2005
//by Guillaume Randon
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software
//and associated documentation files (the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
//and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//The above copyright notice and this permission notice shall be included in all copies or substantial
//portions of the Software.
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
//AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;
using System.Drawing;

namespace DXGfxLib
{
    public struct TerrainVertex
    {
        public float X;
        public float Y;
        public float Z;
        public float Nx;
        public float Ny;
        public float Nz;
        public float Tu1;
        public float Tv1;
        public float Tu2;
        public float Tv2;
        public static readonly VertexFormats format = VertexFormats.Position | VertexFormats.Normal | VertexFormats.Texture2;
        public static readonly int strideSize = DXHelp.GetTypeSize(typeof(TerrainVertex)); 
    }

    /// <summary>
    /// TerrainTile class is used by the terrain class. A terrain is built of a multitude of terraintile.
    /// TerrainTile derives from SceneObject, it has bounding information, and frustrum culling will be performed to know
    /// if the tile need to be rendered or not!
    /// You should not have to mess directly with Terrain tiles, this is why this class is marked as internal. Also if one
    /// day this assembly would need to be obfuscated, this would help obfuscation tools perform a better obfuscation.
    /// </summary>
    internal class TerrainTile : MeshObject
    {
        /// <summary>
        /// We use this variable to know for which pass render state values are currently setted!
        /// </summary>
        public static int settedPass = -1;

        /// <summary>
        /// Below this value the object is judge near to the camera.
        /// </summary>
        public static float near = 500.0f;

        /// <summary>
        /// Above near and below mid the object is at 'mid' distance to the view point.
        /// </summary>
        public static float mid = 700.0f;

        public bool far = false;

        /// <summary>
        /// We keep another mesh object to keep vertex information for this tile and that will be used by DX functions
        /// calculating intersection. This allow us to perform intersection queries without affecting rendering.
        /// </summary>
        public Mesh collMesh = null;

        protected Mesh localMemMesh = null;

        public Texture texture = null;
        public Material material;
        public float maxTerrainHeigth;
        public float minTerrainHeigth;

        public float secondTexHeigth;
        public Texture secondTexture = null;


        public int width;
        public int heigth;
        public float spacing;
        public float stretchFactor;

        protected int currentTriCount = 0;
        protected IndexBuffer currentIndexBuff = null;
        protected bool useLOD = false;
        protected int LOD2TriCount = 0;
        protected IndexBuffer indexBuffLOD2 = null;
        protected int LOD3TriCount = 0;
        protected IndexBuffer indexBuffLOD3 = null;

        // I expect to have batches for all kind of static geometry present on this terrain tile.
        // I define this at the TerrainTile level and not on the quadtreenode because I expect some of these batches to
        // use terraintile properties.
        public List<Batch> batches;

        /// <summary>
        /// You should not create terrain tile yourself. Everything will be done for you by the terrain class. THis is why
        /// this class doc is less detailed than for other classes. Also the constructor of this class is marked as internal.
        /// </summary>
        internal TerrainTile()
        {
            //			parentDevice = device;
            // This means that we have 129*129 vertices. Our bitmap should also be
            // 129*129
            width = 128;
            heigth = 128;
            spacing = 10;
            stretchFactor = 1.0f;

            maxTerrainHeigth = 1000.0f;
            minTerrainHeigth = 1000.0f;

            // Default material
            material = new Material();
            material.Ambient = Color.Aqua;
            material.Diffuse = Color.Azure;
            material.Emissive = Color.Blue;
            material.Specular = Color.Brown;

            secondTexHeigth = 0.0f;

            batches = new List<Batch>();
        }

        /// <summary>
        /// Will dispose all resources used by the terrain tile.
        /// </summary>
        public override void Dispose()
        {
            if (collMesh != null)
            {
                collMesh.Dispose();
            }
            if (texture != null)
            {
                texture.Dispose();
            }
            if (batches != null)
            {
                foreach (Batch batch in batches)
                {
                    batch.Dispose();
                }
            }
        }

        public override void SetEffectsValues(Device d3ddevice, Effect eff, int pass)
        {
            if (pass != settedPass)
            {
                eff.SetValue("baseTexture", texture);
                eff.SetValue("areaTexture", secondTexture);
                d3ddevice.SetTransform(TransformType.World, worldMat);
                settedPass = pass;
            }
        }

        /// <summary>
        /// Actual rendering of the tile. As always for the render method and as for the attach method, that method
        /// is called when needed on objects by the scene manager.
        /// </summary>
        public override void Draw(Device d3ddevice)
        {
            d3ddevice.SetStreamSource(0, localMemMesh.VertexBuffer, 0, TerrainVertex.strideSize);
            d3ddevice.Indices = currentIndexBuff;
            d3ddevice.VertexFormat = TerrainVertex.format;
            d3ddevice.RenderState.CullMode = Cull.None;
            d3ddevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, localMemMesh.NumberVertices,
                0, currentTriCount);
        }

        /// <summary>
        /// This method will populate vertex buffer to obtain the desired geometry.
        /// </summary>
        /// <param name="d3ddevice"></param>
        /// <param name="numColl"></param>
        /// <param name="numRow"></param>
        /// <param name="collOffsetInSource"></param>
        /// <param name="rowOffsetInSource"></param>
        /// <param name="numCollInSource"></param>
        /// <param name="numRowInSource"></param>
        /// <param name="sourceVertices"></param>
        internal void Populate(Device d3ddevice, int numColl, int numRow,
            int collOffsetInSource, int rowOffsetInSource,
            int numCollInSource, int numRowInSource,
            TerrainVertex[] sourceVertices)
        {
            TerrainVertex[] vertices = new TerrainVertex[(numColl + 1) * (numRow + 1)];

            this.width = numColl;
            this.heigth = numRow;

            for (int j = 0; j < numRow + 1; j++)
            {
                for (int i = 0; i < numColl + 1; i++)
                {
                    vertices[i + j * (numColl + 1)] = sourceVertices[(i + collOffsetInSource) + (j + rowOffsetInSource) * (numCollInSource + 1)];
                }
            }

            BuildMeshFromVertices(d3ddevice, vertices);

            collMesh = originalMesh.Clone(MeshFlags.SystemMemory, CustomVertex.PositionOnly.Format, d3ddevice);
        }

        /// <summary>
        /// As the name implies this build a Mesh object from a buffer of TerrainVertex vertices. This is quick but could be
        /// optimized as that format bring a lot of information not needed for collision detection.
        /// </summary>
        /// <param name="d3ddevice"></param>
        /// <param name="vertices"></param>
        private void BuildMeshFromVertices(Device d3ddevice, TerrainVertex[] vertices)
        {
            localBoundingBox.vMax = new Vector3(vertices[0].X, vertices[0].Y, vertices[0].Z);
            localBoundingBox.vMax = new Vector3(vertices[0].X, vertices[0].Y, vertices[0].Z);

            // Now let's copy our vertices to it.
            for (int k = 0; k < vertices.Length; k++)
            {
                if (vertices[k].X < localBoundingBox.vMin.X)
                {
                    localBoundingBox.vMin.X = vertices[k].X;
                }
                if (vertices[k].X > localBoundingBox.vMax.X)
                {
                    localBoundingBox.vMax.X = vertices[k].X;
                }
                if (vertices[k].Y < localBoundingBox.vMin.Y)
                {
                    localBoundingBox.vMin.Y = vertices[k].Y;
                }
                if (vertices[k].Y > localBoundingBox.vMax.Y)
                {
                    localBoundingBox.vMax.Y = vertices[k].Y;
                }
                if (vertices[k].Z < localBoundingBox.vMin.Z)
                {
                    localBoundingBox.vMin.Z = vertices[k].Z;
                }
                if (vertices[k].Z > localBoundingBox.vMax.Z)
                {
                    localBoundingBox.vMax.Z = vertices[k].Z;
                }
            }

            Mesh terrainMesh = new Mesh((width * heigth) * 2, (width + 1) * (heigth + 1), MeshFlags.SystemMemory,
                TerrainVertex.format,
                d3ddevice);


            GraphicsStream buffStream = terrainMesh.LockVertexBuffer(LockFlags.NoSystemLock);
            buffStream.Write(vertices);
            terrainMesh.UnlockVertexBuffer();

            // Now let's build the indices.
            Int16[] indices = new Int16[width * heigth * 6];

            for (Int16 b = 0; b < heigth; b++)
            {
                for (Int16 a = 0; a < width; a++)
                {
                    indices[((a + b * width) * 6)] = (Int16)(a + b * (width + 1));
                    indices[((a + b * width) * 6) + 1] = (Int16)((a + 1) + b * (width + 1));
                    indices[((a + b * width) * 6) + 2] = (Int16)((a + 1) + (b + 1) * (width + 1));

                    indices[((a + b * width) * 6) + 3] = (Int16)(a + b * (width + 1));
                    indices[((a + b * width) * 6) + 4] = (Int16)((a + 1) + (b + 1) * (width + 1));
                    indices[((a + b * width) * 6) + 5] = (Int16)(a + (b + 1) * (width + 1));
                }
            }

            GraphicsStream iBuffStream = terrainMesh.LockIndexBuffer(LockFlags.NoSystemLock);
            iBuffStream.Write(indices);
            terrainMesh.UnlockIndexBuffer();

            localMemMesh = terrainMesh.Clone((terrainMesh.Options.Value & ~MeshFlags.SystemMemory),
                terrainMesh.VertexFormat, d3ddevice);

            base.Attach(terrainMesh);

            
            // Now if width=heigth=8 or 32 let's generate other LOD levels. For other dimensions we won't use LOD.
            if ((width == 8) && (heigth == 8))
            {
                LODQuad8(d3ddevice);
                useLOD = true;
            }
            if ((width == 32) && (heigth == 32))
            {
                LODQuad32(d3ddevice);
                useLOD = true;
            }
        }

        /// <summary>
        /// If our width and height is equal to 8 we know what to put in index buffers for different LOD Level.
        /// </summary>
        private void LODQuad32(Device d3ddevice)
        {
            Int16[] indicesLOD2 = {0000,0001,0272,  0001,0002,0272,  0002,0003,0272,  0003,0004,0272,  0004,0005,0272,  0005,0006,0272,  0006,0007,0272,  0007,0008,0272,
                                0000,0272,0033,  0033,0272,0066,  0066,0272,0099,  0099,0272,0132,  0132,0272,0165,  0165,0272,0198,  0198,0272,0231,  0231,0272,0264,


                                0008,0280,0272,  0008,0009,0280,  0280,0024,0288,  0023,0024,0280,
                                0009,0010,0280,  0010,0011,0280,  0011,0012,0280,  0012,0013,0280,  0013,0014,0280,  0014,0015,0280,  0015,0016,0280,
                                0016,0017,0280,  0017,0018,0280,  0018,0019,0280,  0019,0020,0280,  0020,0021,0280,  0021,0022,0280,  0022,0023,0280,

                                0024,0025,0288,  0025,0026,0288,  0026,0027,0288,  0027,0028,0288,  0028,0029,0288,  0029,0030,0288,  0030,0031,0288,  0031,0032,0288,
                                0032,0065,0288,  0065,0098,0288,  0098,0131,0288,  0131,0164,0288,  0164,0197,0288,  0197,0230,0288,  0230,0263,0288,  0263,0296,0288,

                                1064,1063,0800,  1063,1062,0800,  1062,1061,0800,  1061,1060,0800,  1060,1059,0800,  1059,1058,0800,  1058,1057,0800,  1057,1056,0800,
                                1056,1023,0800,  1023,0990,0800,  0990,0957,0800,  0957,0924,0800,  0924,0891,0800,  0891,0858,0800,  0858,0825,0800,  0825,0792,0800,

                                1064,0800,0808,  1065,1064,0808,  1080,0808,0816,  1080,1079,0808,
                                1079,1078,0808,  1078,1077,0808,  1077,1076,0808,  1076,1075,0808,  1075,1074,0808,  1074,1073,0808,  1073,1072,0808,
                                1072,1071,0808,  1071,1070,0808,  1070,1069,0808,  1069,1068,0808,  1068,1067,0808,  1067,1066,0808,  1066,1065,0808,

                                0824,0857,0816,  0857,0890,0816,  0890,0923,0816,  0923,0956,0816,  0956,0989,0816,  0989,1022,0816,  1022,1055,0816,  1055,1088,0816,
                                1088,1087,0816,  1087,1086,0816,  1086,1085,0816,  1085,1084,0816,  1084,1083,0816,  1083,1082,0816,  1082,1081,0816,  1081,1080,0816,

                                0792,0536,0800,  0792,0759,0536,  0264,0272,0536,  0297,0264,0536,  
                                0759,0726,0536,  0726,0693,0536,  0693,0660,0536,  0660,0627,0536,  0627,0594,0536,  0594,0561,0536,  0561,0528,0536,  
                                0528,0495,0536,  0495,0462,0536,  0462,0429,0536,  0429,0396,0536,  0396,0363,0536,  0363,0330,0536,  0330,0297,0536,

                                0824,0816,0552,  0791,0824,0552,  0296,0552,0288,  0296,0329,0552,  
                                0329,0362,0552,  0362,0395,0552,  0395,0428,0552,  0428,0461,0552,  0461,0494,0552,  0494,0527,0552,  0527,0560,0552,
                                0560,0593,0552,  0593,0626,0552,  0626,0659,0552,  0659,0692,0552,  0692,0725,0552,  0725,0758,0552,  0758,0791,0552,

                                0272,0280,0544,  0272,0544,0536,  0280,0288,0544,  0288,0552,0544,  0800,0536,0544,  0800,0544,0808,  0808,0544,0816,  0816,0544,0552};

            indexBuffLOD2 = new IndexBuffer(d3ddevice, sizeof(Int16) * indicesLOD2.Length, Usage.None, Pool.SystemMemory, true);
            GraphicsStream iBuffStream2 = indexBuffLOD2.Lock(0, 0, LockFlags.NoSystemLock);
            for (int t = 0; t < indicesLOD2.Length; t++)
            {
                iBuffStream2.Write((Int16)indicesLOD2[t]);
            }
            indexBuffLOD2.Unlock();

            int reminder = 0;
            LOD2TriCount = Math.DivRem(indicesLOD2.Length, 3, out reminder);
            if (reminder != 0)
            {
                throw new Exception("Really the number of indices should be a multiple of three, there is an issue here!");
            }

            int[] indicesLOD3 = {0000,0001,0544,  0001,0002,0544,  0002,0003,0544,  0003,0004,0544,  0004,0005,0544, 0005,0006,0544, 0006,0007,0544,  0007,0008,0544,  
                                0008,0009,0544,  0009,0010,0544,  0010,0011,0544,  0011,0012,0544,  0012,0013,0544, 0013,0014,0544, 0014,0015,0544,  0015,0016,0544,
                                0016,0017,0544,  0017,0018,0544,  0018,0019,0544,  0019,0020,0544,  0020,0021,0544, 0021,0022,0544, 0022,0023,0544,  0023,0024,0544,
                                0024,0025,0544,  0025,0026,0544,  0026,0027,0544,  0027,0028,0544,  0028,0029,0544, 0029,0030,0544, 0030,0031,0544,  0031,0032,0544,

                                0032,0065,0544,  0065,0098,0544,  0098,0131,0544,  0131,0164,0544,  0164,0197,0544,  0197,0230,0544,  0230,0263,0544,  0263,0296,0544,
                                0296,0329,0544,  0329,0362,0544,  0362,0395,0544,  0395,0428,0544,  0428,0461,0544,  0461,0494,0544,  0494,0527,0544,  0527,0560,0544,
                                0560,0593,0544,  0593,0626,0544,  0626,0659,0544,  0659,0692,0544,  0692,0725,0544,  0725,0758,0544,  0758,0791,0544,  0791,0824,0544,
                                0824,0857,0544,  0857,0890,0544,  0890,0923,0544,  0923,0956,0544,  0956,0989,0544,  0989,1022,0544,  1022,1055,0544,  1055,1088,0544,

                                1088,1087,0544,  1087,1086,0544,  1086,1085,0544,  1085,1084,0544,  1084,1083,0544,  1083,1082,0544,  1082,1081,0544,  1081,1080,0544,
                                1080,1079,0544,  1079,1078,0544,  1078,1077,0544,  1077,1076,0544,  1076,1075,0544,  1075,1074,0544,  1074,1073,0544,  1073,1072,0544,
                                1072,1071,0544,  1071,1070,0544,  1070,1069,0544,  1069,1068,0544,  1068,1067,0544,  1067,1066,0544,  1066,1065,0544,  1065,1064,0544,
                                1064,1063,0544,  1063,1062,0544,  1062,1061,0544,  1061,1060,0544,  1060,1059,0544,  1059,1058,0544,  1058,1057,0544,  1057,1056,0544,

                                1056,1023,0544,  1023,0990,0544,  0990,0957,0544,  0957,0924,0544,  0924,0891,0544,  0891,0858,0544,  0858,0825,0544,  0825,0792,0544,
                                0792,0759,0544,  0759,0726,0544,  0726,0693,0544,  0693,0660,0544,  0660,0627,0544,  0627,0594,0544,  0594,0561,0544,  0561,0528,0544,
                                0528,0495,0544,  0495,0462,0544,  0462,0429,0544,  0429,0396,0544,  0396,0363,0544,  0363,0330,0544,  0330,0297,0544,  0297,0264,0544,
                                0264,0231,0544,  0231,0198,0544,  0198,0165,0544,  0165,0132,0544,  0132,0099,0544,  0099,0066,0544,  0066,0033,0544,  0033,0000,0544};

            indexBuffLOD3 = new IndexBuffer(d3ddevice, sizeof(Int16) * indicesLOD3.Length, Usage.None, Pool.SystemMemory, true);
            GraphicsStream iBuffStream3 = indexBuffLOD3.Lock(0, 0, LockFlags.NoSystemLock);
            for (int t = 0; t < indicesLOD3.Length; t++)
            {
                iBuffStream3.Write((Int16)indicesLOD3[t]);
            }
            indexBuffLOD3.Unlock();

            LOD3TriCount = Math.DivRem(indicesLOD3.Length, 3, out reminder);
            if (reminder != 0)
            {
                throw new Exception("Really the number of indices should be a multiple of three, there is an issue here!");
            }
        }

        /// <summary>
        /// If our width and height is equal to 32 we know what to put in index buffers for different LOD Level.
        /// </summary>
        private void LODQuad8(Device d3ddevice)
        {
            int[] indicesLOD2 = {00,01,20, 00,20,09, 01,02,20, 09,20,18,
                                02,22,20,            22,06,24,
                                02,03,22,            05,06,22,            03,04,22,            04,05,22,
                                06,07,24,            07,08,24,            08,17,24,            17,26,24,
                                72,63,56,            72,56,73,            63,54,56,            73,56,74,
                                74,56,58,            78,58,60,
                                74,58,75,            76,75,58,            76,58,77,            77,58,78,
                                78,60,79,            79,60,80,            71,80,60,            71,60,62,
                                54,38,56,            18,20,38,
                                54,45,38,            45,36,38,            36,27,38,            27,18,38,
                                62,60,42,            42,24,26,
                                62,42,53,            53,42,44,            44,42,35,            35,42,26,
                                20,22,40,  20,40,38,  22,24,40, 24,42,40,
                                56,38,40,  56,40,58,  60,58,40, 60,40,42};
            indexBuffLOD2 = new IndexBuffer(d3ddevice, sizeof(Int16) * indicesLOD2.Length, Usage.None, Pool.SystemMemory, true);
            GraphicsStream iBuffStream2 = indexBuffLOD2.Lock(0, 0, LockFlags.NoSystemLock);
            for (int t = 0; t < indicesLOD2.Length; t++)
            {
                iBuffStream2.Write((Int16)indicesLOD2[t]);
            }
            indexBuffLOD2.Unlock();

            int reminder = 0;
            LOD2TriCount = Math.DivRem(indicesLOD2.Length, 3, out reminder);
            if (reminder != 0)
            {
                throw new Exception("Really the number of indices should be a multiple of three, there is an issue here!");
            }

            int[] indicesLOD3 = {00,01,40, 01,02,40, 02,03,40,  03,04,40, 04,05,40, 05,06,40, 06,07,40, 07,08,40,
                                08,17,40, 17,26,40, 26,35,40, 35,44,40, 44,53,40, 53,62,40, 62,71,40, 71,80,40,
                                80,79,40, 79,78,40, 78,77,40, 77,76,40, 76,75,40, 75,74,40, 74,73,40, 73,72,40,
                                72,63,40, 63,54,40, 54,45,40, 45,36,40, 36,27,40, 27,18,40, 18,09,40, 09,00,40};

            indexBuffLOD3 = new IndexBuffer(d3ddevice, sizeof(Int16) * indicesLOD3.Length, Usage.None, Pool.SystemMemory, true);
            GraphicsStream iBuffStream3 = indexBuffLOD3.Lock(0, 0, LockFlags.NoSystemLock);
            for (int t = 0; t < indicesLOD3.Length; t++)
            {
                iBuffStream3.Write((Int16)indicesLOD3[t]);
            }
            indexBuffLOD3.Unlock();

            LOD3TriCount = Math.DivRem(indicesLOD3.Length, 3, out reminder);
            if (reminder != 0)
            {
                throw new Exception("Really the number of indices should be a multiple of three, there is an issue here!");
            }
        }

        /// <summary>
        /// When a scene is updated it will first call the Update method on the node hierarchy and then it
        /// will call the AttachForRendering method on the node hierarchy. This will give an opportunity to each
        /// scene object (or any instance of a derived class) to decide whether it needs to be rendered this frame or not.
        /// </summary>
        /// <param name="frustrum"></param>
        /// <param name="objectsToBeRendered"></param>
        public override void AttachForRendering(Frustrum frustrum, List<IDrawable> objectsToBeRendered)
        {
            // If we are in the frustrum we attach for rendering an let's our children decide for
            // themselves.
            if (useFrustrumCulling)
            {
                if (!frustrum.IsInFrustrum(worldAABBox))
                    return;
            }

            objectsToBeRendered.Add(this);

            if (far)
                return;
            
            foreach (Batch batch in batches)
            {
                objectsToBeRendered.Add(batch);
            }

            foreach (SceneNode child in children)
            {
                child.AttachForRendering(frustrum, objectsToBeRendered);
            }
        }

        public override void Update(Frustrum frustrum, float elapsedTime, double appTime, List<IDrawable> objectsToBeRendered)
        {
            base.Update(frustrum, elapsedTime, appTime, objectsToBeRendered);

            if (useLOD)
            {
                float distToNearPlane = MathUtil.SignedDistance(frustrum.NearPlane, barycenter);

                if (distToNearPlane < near)
                {
                    currentIndexBuff = this.originalMesh.IndexBuffer;
                    currentTriCount = this.originalMesh.NumberFaces;
                    far = false;
                }
                else if (distToNearPlane < mid)
                {
                    currentIndexBuff = indexBuffLOD2;
                    currentTriCount = LOD2TriCount;
                    far = true;
                }
                else
                {
                    currentIndexBuff = indexBuffLOD3;
                    currentTriCount = LOD3TriCount;
                    far = true;
                }
            }
        }

        /// <summary>
        /// If the x and z is above the terrain tile you will get the associated terrain heigth.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public float GetHeigth(float x, float z)
        {
            Vector3 rayPos = new Vector3(x, maxTerrainHeigth, z);
            Vector3 rayDir = new Vector3(0.0f, -1.0f, 0.0f);

            Microsoft.DirectX.Direct3D.IntersectInformation closestHit = new IntersectInformation();

            if (this.collMesh.Intersect(rayPos, rayDir, out closestHit))
                return (maxTerrainHeigth - closestHit.Dist);
            else
                return maxTerrainHeigth;
        }


        /// <summary>
        /// If the x y and z is above the terrain tile you will get the associated terrain heigth.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public float GetHeigth(float x, float y, float z)
        {
            Vector3 rayPos = new Vector3(x, y, z);
            Vector3 rayDir = new Vector3(0.0f, -1.0f, 0.0f);

            Microsoft.DirectX.Direct3D.IntersectInformation closestHit = new IntersectInformation();

            if (this.collMesh.Intersect(rayPos, rayDir, out closestHit))
                return (y - closestHit.Dist);
            else
                return 65536;
        }

        /// <summary>
        /// Get the heigth of the tile under the point given in parameter. If the point is under the terrain
        /// level at that x and z 65536 is returned.
        /// </summary>
        /// <param name="underPoint"></param>
        /// <returns></returns>
        public float GetHeigth(Vector3 underPoint)
        {
            Vector3 rayDir = new Vector3(0.0f, -1.0f, 0.0f);

            Microsoft.DirectX.Direct3D.IntersectInformation closestHit = new IntersectInformation();

            if (this.collMesh.Intersect(underPoint, rayDir, out closestHit))
                return (underPoint.Y - closestHit.Dist);
            else
                return 65536;
        }

        /// <summary>
        /// Calculate the bounding box for the terrain tile, same remark as for the bounding sphere.
        /// </summary>
        /// <returns></returns>
        public BBox CalculateBBox()
        {
            if (localBoundingBox == null)
            {
                localBoundingBox = new BBox();

                // Retrieve the vertex buffer data in the terrainMesh object
                VertexBuffer vb = collMesh.VertexBuffer;

                // Lock the vertex buffer to generate a simple bounding sphere
                GraphicsStream vertexData = vb.Lock(0, 0, LockFlags.NoSystemLock);
                Geometry.ComputeBoundingBox(vertexData,
                    collMesh.NumberVertices,
                    collMesh.VertexFormat,
                    out this.localBoundingBox.vMin, out this.localBoundingBox.vMax);
                vb.Unlock();
                vb.Dispose();
            }
            worldAABBox = new AABBox(localBoundingBox.Transform(this.WorldMat));
            return worldAABBox;
        }

        /// <summary>
        /// This will determine if the ray intersect with the tile and where!
        /// </summary>
        /// <param name="rayPos"></param>
        /// <param name="rayDir"></param>
        /// <param name="closestHit"></param>
        /// <returns></returns>
        public override bool Intersect(Vector3 rayPos, Vector3 rayDir, out IntersectInformation closestHit)
        {
            // Let's transform rayPos and rayDir to local coordinates
            //Matrix invWorldMat = Matrix.Invert(worldMat);
            //Vector3 rayPosLocal = Vector3.TransformCoordinate(rayPos, invWorldMat);
            //Vector3 rayDirLocal = Vector3.TransformCoordinate(rayDir, invWorldMat);
            //rayDirLocal.Normalize();

            //if (collMesh != null)
            //    return collMesh.Intersect(rayPosLocal, rayDirLocal, out closestHit);
            //else
            //    return collMesh.Intersect(rayPosLocal, rayDirLocal, out closestHit);
            return collMesh.Intersect(rayPos, rayDir, out closestHit);
        }

        public void AddInstances(FrameworkMesh mesh, InstanceInfo[] instancesInfo)
        {

            Batch batch = new Batch();
            batches.Add(batch);
        }
    }
}
