/// <summary>
 /// Setup constructor
 /// </summary>
 /// <param name="camera">Camera, used to determine patch error</param>
 /// <param name="generator">Object used to generate the patch vertices</param>
 /// <param name="patch">Terrain patch</param>
 /// <param name="calculatePatchError">If true, the maximum error between the patch geometry and the terrain model is calculated</param>
 private unsafe TerrainPatchBuildItem( IProjectionCamera camera, ITerrainPatchGenerator generator, ITerrainPatch patch, bool calculatePatchError )
 {
     m_Camera = camera;
     m_Generator = generator;
     m_Patch = patch;
     m_CalculatePatchError = calculatePatchError;
 }
        /// <summary>
        /// Allocates a terrain patch build item from an internal pool
        /// </summary>
        public static TerrainPatchBuildItem Allocate( IProjectionCamera camera, ITerrainPatchGenerator generator, ITerrainPatch patch, bool calculatePatchError )
        {
            if ( s_BuildItems.Count == 0 )
            {
                return new TerrainPatchBuildItem( camera, generator, patch, calculatePatchError );
            }
            TerrainPatchBuildItem item = s_BuildItems[ 0 ];
            s_BuildItems.RemoveAt( 0 );

            item.m_Camera = camera;
            item.m_Generator = generator;
            item.m_Patch = patch;
            item.m_CalculatePatchError = calculatePatchError;

            return item;
        }
        /// <summary>
        /// Determines the distance at which a patch LOD should be increased, from the patch error
        /// </summary>
        private static float DistanceFromError( IProjectionCamera camera, float error )
        {
            //	Extract frustum from projection matrix:
            //	http://www.opengl.org/resources/faq/technical/transformations.htm
            //	http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=209274
            float near = camera.PerspectiveZNear;
            float top = Functions.Tan( camera.PerspectiveFovDegrees * Constants.DegreesToRadians * 0.5f ) * near;
            float a = near / top;
            float t = ( TerrainPatchConstants.ErrorThreshold * 2 ) / Graphics.Renderer.ViewportHeight;
            float c = a / t;
            float d = error * c;

            return d;
        }
 public void Update( IProjectionCamera camera )
 {
     if ( m_Children != null )
     {
         foreach ( QuadPatch childPatch in m_Children )
         {
             childPatch.Update( camera );
         }
     }
     else
     {
         if ( m_BuildVertices )
         {
             m_IncreaseDetailDistance = BuildVertices( camera );
             m_BuildVertices = false;
         }
         if ( m_BuildIndices )
         {
             BuildIndices( );
             m_BuildIndices = false;
         }
     }
 }
        private unsafe float BuildVertices( IProjectionCamera camera )
        {
            float maxError = 0;
            using ( IVertexBufferLock vbLock = m_Vertices.VertexBuffer.Lock( m_VbIndex, PatchArea, false, true ) )
            {
                PatchVertex* curVertex = ( PatchVertex* )vbLock.Bytes;
                float incX = m_Width / ( PatchResolution - 1 );
                float incZ = m_Depth / ( PatchResolution - 1 );
                float z = m_Z;
                for ( int row = 0; row < PatchResolution; ++row, z += incZ )
                {
                    float x = m_X;
                    for ( int col = 0; col < PatchResolution; ++col, x += incX )
                    {
                        float curHeight = m_Terrain.GetHeight( x, z );
                        float nextHeightX = m_Terrain.GetHeight( x + incX, z );
                        float nextHeightY = m_Terrain.GetHeight( x, z + incZ );

                        float estXHeight = ( curHeight + nextHeightX ) / 2;
                        float estYHeight = ( curHeight + nextHeightY ) / 2;
                        float xError = Math.Abs( m_Terrain.GetHeight( x + incX / 2, z ) - estXHeight );
                        float yError = Math.Abs( m_Terrain.GetHeight( x, z + incZ / 2 ) - estYHeight );
                        float error = Utils.Max( xError, yError );
                        maxError = error > maxError ? error : maxError;

                        curVertex->Position = new Point3( x, curHeight, z );
                        ++curVertex;
                    }
                }
            }
            return DistanceFromError( camera, maxError );
        }
 private void ReduceDetail( ITerrainPatchGenerator generator, IProjectionCamera camera )
 {
     Build( generator, camera );
 }
        /// <summary>
        /// Increases the detail of this paqtch
        /// </summary>
        private void IncreaseDetail( ITerrainPatchGenerator generator, IProjectionCamera camera )
        {
            if ( m_CachedChildren != null )
            {
                //	Child nodes have already been created - they just need new vertex buffers
                m_PendingChildren = m_CachedChildren;
                m_CachedChildren = null;
            }
            else
            {
                Vector3 uOffset = m_LocalUAxis * 0.5f;
                Vector3 vOffset = m_LocalVAxis * 0.5f;
                float error = m_PatchError / 2;
                //	float error = float.MaxValue;//	Use this value to force a recalculation of the patch error
                float uvRes = m_UvRes / 2;
                Point2 tlUv = m_Uv;
                Point2 trUv = m_Uv + new Vector2( uvRes, 0 );
                Point2 brUv = m_Uv + new Vector2( uvRes, uvRes );
                Point2 blUv = m_Uv + new Vector2( 0, uvRes );

                TerrainPatch tl = new TerrainPatch( this, m_Vertices, m_LocalOrigin, uOffset, vOffset, error, tlUv,uvRes );
                TerrainPatch tr = new TerrainPatch( this, m_Vertices, m_LocalOrigin + uOffset, uOffset, vOffset, error, trUv, uvRes );
                TerrainPatch bl = new TerrainPatch( this, m_Vertices, m_LocalOrigin + vOffset, uOffset, vOffset, error, blUv, uvRes );
                TerrainPatch br = new TerrainPatch( this, m_Vertices, m_LocalOrigin + uOffset + vOffset, uOffset, vOffset, error, brUv, uvRes );

                m_PendingChildren = new TerrainPatch[] { tl, tr, bl, br };
            }

            foreach ( TerrainPatch patch in m_PendingChildren )
            {
                patch.Build( generator, camera );
            }
        }
 /// <summary>
 /// Queues up a work item to builds this patch's vertex and index buffers
 /// </summary>
 private void Build( ITerrainPatchGenerator generator, IProjectionCamera camera )
 {
     m_Building = true;
     TerrainPatchBuildItem builder = TerrainPatchBuildItem.Allocate( camera, generator, this, ( PatchError == float.MaxValue ) );
     TerrainPatchBuilder.QueueWork( builder );
 }
 /// <summary>
 /// Updates the level of detail of this patch. 
 /// </summary>
 public void UpdateLod( Point3 cameraPos, ITerrainPatchGenerator generator, IProjectionCamera camera )
 {
     if ( m_IncreaseDetailDistance == float.MaxValue )
     {
         //	Detail up distance has not been calculated for this patch yet - exit
         return;
     }
     float distanceToPatch = AccurateDistanceToPatch( cameraPos, PatchCentre, m_Radius );
     m_DistToPatch = distanceToPatch;
     if ( distanceToPatch < m_IncreaseDetailDistance )
     {
         if ( IsLeafNode )
         {
             IncreaseDetail( generator, camera );
         }
         else if ( m_Children != null )
         {
             foreach ( TerrainPatch childPatch in m_Children )
             {
                 childPatch.UpdateLod( cameraPos, generator, camera );
             }
         }
     }
     else if ( distanceToPatch > m_IncreaseDetailDistance )
     {
         if ( !IsLeafNode && CanReduceDetail )
         {
             ReduceDetail( generator, camera );
         }
     }
 }
 /// <summary>
 /// Updates this patch
 /// </summary>
 public void Update( IProjectionCamera camera, ITerrainPatchGenerator generator )
 {
     if ( m_Children != null )
     {
         foreach ( TerrainPatch childPatch in m_Children )
         {
             childPatch.Update( camera, generator );
         }
     }
     else
     {
         //	Bodge to generate vertices (blocking) on the first frame
         if ( m_Parent == null && m_VbIndex == -1 && m_IncreaseDetailDistance == float.MaxValue )
         {
             TerrainPatchBuildItem builder = TerrainPatchBuildItem.Allocate( camera, generator, this, ( PatchError == float.MaxValue ) );
             builder.Build( );
         }
     }
 }