/// <summary>
        /// Creates a bitmap from texture data
        /// </summary>
        public static unsafe Bitmap CreateBitmapFromTextureData( Texture2dData data )
        {
            PixelFormat format;
            switch ( data.Format )
            {
                case TextureFormat.Depth16			:	format = PixelFormat.Format16bppGrayScale;	break;
            //	case TextureFormat.Depth24			:	break;	//	No mapping
            //	case TextureFormat.Depth32			:	break;	//	No mapping

                case TextureFormat.R8G8B8			:	format = PixelFormat.Format24bppRgb; break;
            //	case TextureFormat.B8G8R8			:	break;	//	No mapping

                case TextureFormat.R8G8B8A8			:	format = PixelFormat.Format32bppRgb; break;
            //	case TextureFormat.B8G8R8A8			:	break;	//	No mapping

                case TextureFormat.A8R8G8B8			:	format = PixelFormat.Format32bppArgb; break;
            //	case TextureFormat.A8B8G8R8			:	break;	//	No mapping

                default :
                    throw new NotSupportedException( "Unsupported texture format: " + data.Format );
            }
            fixed ( byte* dataBytes = data.Bytes )
            {
                int stride = data.Width * TextureFormatInfo.GetSizeInBytes( data.Format );
                Bitmap bmp = new Bitmap( data.Width, data.Height, stride, format, new IntPtr( dataBytes ) );
                return bmp;
            }
        }
        /// <summary>
        /// Default construcor
        /// </summary>
        public SpherePlanetHomogenousProceduralTerrainRenderer( )
        {
            m_Renderer = new SpherePlanetTerrainPatchRenderer( this );
            m_Technique = new SpherePlanetPackTextureTechnique( this );

            m_PackTexture = ( ITexture2d )AssetManager.Instance.Load( "Terrain\\dirt0.jpg", new TextureLoadParameters( true ) );
            m_LookupTexture = RbGraphics.Factory.CreateTexture2d( );
            Texture2dData lookupData = new Texture2dData( 1, 1, TextureFormat.R8G8B8 );
            m_LookupTexture.Create( lookupData, true );
        }
        /// <summary>
        /// Creates texture data from a bitmap
        /// </summary>
        public static unsafe Texture2dData CreateTextureDataFromBitmap( Bitmap bmp )
        {
            Texture2dData data = new Texture2dData( );
            TextureFormat format = GetCompatibleTextureFormatFromBitmap( ref bmp );
            BitmapData bmpData = LockEntireBitmap( bmp );
            data.Create( bmp.Width, bmp.Height, format );
            fixed ( byte* dstBytes = data.Bytes )
            {
                MsvCrt.memcpy( dstBytes, ( void* )bmpData.Scan0, data.Bytes.Length );
            }

            return data;
        }
        /// <summary>
        /// Loads 2d texture data from the specified stream
        /// </summary>
        private static Texture2dData[] Load2dTextureData( BinaryReader reader, TextureFileFormatVersion1.Header header )
        {
            Texture2dData[] textureDataArray = new Texture2dData[ header.TextureDataEntries ];
            for ( int textureDataCount = 0; textureDataCount < header.TextureDataEntries; ++textureDataCount )
            {
                TextureFileFormatVersion1.Group textureGroup = TextureFileFormatVersion1.Group.Read( reader );
                if ( textureGroup.GroupId != GroupIdentifier.Texture2dDataGroup )
                {
                    throw new FileLoadException( "Expected texture group" );
                }

                int width = reader.ReadInt32( );
                int height = reader.ReadInt32( );

                Texture2dData texData = new Texture2dData( );
                texData.Create( width, height, header.Format );

                reader.Read( texData.Bytes, 0, texData.Bytes.Length );

                textureDataArray[ textureDataCount ] = texData;
            }

            return textureDataArray;
        }
        /// <summary>
        /// Creates a texture image from an array of texture data objects
        /// </summary>
        public static unsafe TextureInfo CreateTextureImageFromTextureData( int target, Texture2dData[] data )
        {
            if ( data == null )
            {
                throw new ArgumentNullException( "data" );
            }
            if ( data.Length == 0 )
            {
                throw new ArgumentException( "Can't create texture from empty texture data array", "data" );
            }
            TextureInfo info = CheckTextureFormat( data[ 0 ].Format );

            for ( int level = 0; level < data.Length; ++level )
            {
                if ( data[ level ].Format != data[ 0 ].Format )
                {
                    throw new ArgumentException( string.Format( "Data in level {0} has format {1}, should be format {2}", level, data[ level ].Format, data[ 0 ].Format ) );
                }
                fixed ( void* texels = data[ level ].Bytes )
                {
                    Gl.glTexImage2D( target, level, info.GlInternalFormat, data[ level ].Width, data[ level ].Height, 0, info.GlFormat, info.GlType, new IntPtr( texels ) );
                }
            }

            return info;
        }
        /// <summary>
        /// Creates a texture image from a texture data object
        /// </summary>
        public static unsafe TextureInfo CreateTextureImageFromTextureData( int target, Texture2dData data, bool generateMipMaps )
        {
            if ( data == null )
            {
                throw new ArgumentNullException( "data" );
            }
            TextureInfo info = CheckTextureFormat( data.Format );

            fixed ( void* texels = data.Bytes )
            {
                if ( generateMipMaps )
                {
                    Glu.gluBuild2DMipmaps( target, info.GlInternalFormat, data.Width, data.Height, info.GlFormat, info.GlType, new IntPtr( texels ) );
                }
                else
                {
                    Gl.glTexImage2D( target, 0, info.GlInternalFormat, data.Width, data.Height, 0, info.GlFormat, info.GlType, new IntPtr( texels ) );
                }
            }

            return info;
        }
        /// <summary>
        /// Creates texture data from the currently bound texture
        /// </summary>
        public static Texture2dData[] CreateTextureDataFromTexture( int target, TextureFormat format )
        {
            int width;
            int height;
            int level = 0;
            List< Texture2dData > textureDataList = new List< Texture2dData >( );
            do
            {
                width = GetTextureLevelParameterInt32( target, level, Gl.GL_TEXTURE_WIDTH );
                height = GetTextureLevelParameterInt32( target, level, Gl.GL_TEXTURE_HEIGHT );

                GraphicsLog.Verbose( "Creating texture data from level {0} ({1}x{2}) {3} texture", level, width, height, format );

                //	Get texture memory
                int bytesPerPixel = TextureFormatInfo.GetSizeInBytes( format );
                TextureInfo info = CheckTextureFormat( format );
                byte[] textureMemory = new byte[ width * height * bytesPerPixel ];
                Gl.glGetTexImage( Gl.GL_TEXTURE_2D, level, info.GlFormat, info.GlType, textureMemory );

                Texture2dData textureData = new Texture2dData( );
                textureData.Create( width, height, format, textureMemory );
                textureDataList.Add( textureData );

                ++level;

            } while ( ( width > 1 ) && ( height > 1 ) );

            return textureDataList.ToArray( );
        }
        //
        //    Discrepancies in atmospheric modelling papers:
        //        - HG:
        //            - Neilsen: HG(t,g) = (1-g2)/(4pi(1 + g2 - 2cos(t))^1.5)
        //            - Wolfram: HG(t,g) = (1-g2)/(1 + g2 - 2.g.cos(t))^1.5
        //            - O'Neil (adapted HG): HG(t,g) = 3(1-g2)/2(2+g2) . (1+t2)/(1+g2 - 2gt)^1.5
        //            - O'Neil and Wolfram work well. Neilsen doesn't (denominator can evalute to < 0 before raising to 3/2 power)
        //    - Scattering coefficients: (# is wavelength)
        //            - Neilsen Rayleigh coefficient: 8pi^2(n2-1)^2/3N#^4
        //            - Neilsen Mie coefficient: 0.434c.pi.4pi^2/#^2K
        //                - K=non-wavelength dependent fudge factor for Bm: ~0.67
        //                - c=concentration factor(0.6544T-0.6510).10e-16 (T=turbidity. ~555nm)
        //
        //
        //
        //
        //    Realtime rendering of atmospheric scattering for flight simulators:
        //        http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/2554/pdf/imm2554.pdf
        //
        //    Display of The Earth Taking into Account Atmospheric Scattering: (Nishita)
        //        http://nis-lab.is.s.u-tokyo.ac.jp/~nis/abs_sig.html#sig93
        //
        //    Implementation of Nishita's paper:
        //        http://www.gamedev.net/reference/articles/article2093.asp
        //
        //    A practical analytical model of daylight:
        //        http://www.cs.utah.edu/vissim/papers/sunsky/sunsky.pdf
        //
        //    Discussion about atmospheric shaders:
        //        http://www.gamedev.net/community/forums/topic.asp?topic_id=335023
        //
        //    Paper that this model is based on:
        //        http://www.vis.uni-stuttgart.de/~schafhts/HomePage/pubs/wscg07-schafhitzel.pdf
        //
        //    Heyney-Greenstein on Mathworld:
        //        http://scienceworld.wolfram.com/physics/Heyney-GreensteinPhaseFunction.html
        //
        /// <summary>
        /// Builds a 3D lookup texture
        /// </summary>
        /// <param name="model">The atmosphere model</param>
        /// <param name="parameters">Parameters for the build process</param>
        /// <param name="progress">Optional progress object</param>
        /// <returns>Returns a 3d texture data. Returns null if cancel was flagged in the progress object</returns>
        public unsafe AtmosphereBuildOutputs Build( AtmosphereBuildModel model, AtmosphereBuildParameters parameters, AtmosphereBuildProgress progress )
        {
            Texture2dData opticalDepthTexture = new Texture2dData( );
            Texture3dData scatteringTexture = new Texture3dData( );
            scatteringTexture.Create( parameters.ViewAngleSamples, parameters.SunAngleSamples, parameters.HeightSamples, TextureFormat.R8G8B8A8 );

            SetupModelAndParameters( parameters, model );
            progress = progress ?? new AtmosphereBuildProgress( );

            fixed ( byte* voxels = scatteringTexture.Bytes )
            {
                if ( !BuildScatteringTexture( parameters, voxels, progress ) )
                {
                    return null;
                }
            }
            if ( progress.Cancel )
            {
                return null;
            }

            opticalDepthTexture.Create( parameters.OpticalDepthResolution, parameters.OpticalDepthResolution, TextureFormat.R8G8B8 );
            fixed ( byte* pixels = opticalDepthTexture.Bytes )
            {
                if ( !BuildOpticalDepthTexture( parameters, pixels, progress ) )
                {
                    return null;
                }
            }

            return new AtmosphereBuildOutputs( scatteringTexture, opticalDepthTexture );
        }
        /// <summary>
        /// Creates the texture from a texture data model
        /// </summary>
        /// <param name="data">Texture data</param>
        public unsafe override void Create( Texture2dData[] data )
        {
            DestroyCurrent( );

            m_TextureHandle = OpenGlTextureHandle.CreateAndBindHandle( m_Target );
            m_Format = OpenGlTexture2dBuilder.CreateTextureImageFromTextureData( m_Target, data ).TextureFormat;
            UpdateDimensions( );
        }
        /// <summary>
        /// Creates the texture from a texture data model
        /// </summary>
        /// <param name="data">Texture data</param>
        /// <param name="generateMipMaps">Generate mipmaps flag</param>
        public unsafe override void Create( Texture2dData data, bool generateMipMaps )
        {
            DestroyCurrent( );

            m_TextureHandle = OpenGlTextureHandle.CreateAndBindHandle( m_Target );
            OpenGlTexture2dBuilder.TextureInfo info = OpenGlTexture2dBuilder.CreateTextureImageFromTextureData( m_Target, data, generateMipMaps );
            m_Format = info.TextureFormat;
            UpdateDimensions( );
        }
 /// <summary>
 /// Sets up the build outputs
 /// </summary>
 /// <param name="scatteringTexture">Scattering texture data</param>
 /// <param name="opticalDepthTexture">Optical depth texture data</param>
 public AtmosphereBuildOutputs( Texture3dData scatteringTexture, Texture2dData opticalDepthTexture )
 {
     m_ScatteringTexture = scatteringTexture;
     m_OpticalDepthTexture = opticalDepthTexture;
 }
 /// <summary>
 /// Creates the texture from an array of texture data objects, that specify decreasing mipmap levels
 /// </summary>
 /// <param name="data">Texture data used to create the texture and its mipmaps</param>
 public abstract void Create( Texture2dData[] data );
 /// <summary>
 /// Creates the texture from a single texture data object
 /// </summary>
 /// <param name="data">Texture data used to create the texture</param>
 /// <param name="generateMipMaps">Mipmap generation flag</param>
 public abstract void Create( Texture2dData data, bool generateMipMaps );