/// <summary>
 /// Sets the mesh for a specified body part
 /// </summary>
 public void SetPartMesh( ModelPart part, ModelMesh mesh )
 {
     m_PartMeshes[ ( int )part ] = mesh;
 }
 /// <summary>
 /// Sets the root mesh, rendered 
 /// </summary>
 public void SetRootMesh( ModelMesh rootMesh )
 {
     m_RootMesh = rootMesh;
 }
        /// <summary>
        /// Reads surface vertices (positions and normals)
        /// </summary>
        private static void ReadVertices( BinaryReader reader, long offset, int numVertices, int numFrames, ModelMesh.Surface surface, Matrix44 transform )
        {
            reader.BaseStream.Seek( offset, SeekOrigin.Begin );

            //	Allocate surface frames
            surface.SurfaceFrames = new ModelMesh.SurfaceFrame[ numFrames ];

            //	Allocate temporary arrays for storing position and normal data
            float[] positions	= new float[ numVertices * 3 ];
            float[] normals		= new float[ numVertices * 3 ];

            //	Run through all frames
            for ( int frameIndex = 0; frameIndex < numFrames; ++frameIndex )
            {
                int positionIndex	= 0;
                int normalIndex		= 0;

                //	Allocate a surface frame
                ModelMesh.SurfaceFrame frame = new ModelMesh.SurfaceFrame( );

                //	Run through all vertices
                for ( int vertexCount = 0; vertexCount < numVertices; ++vertexCount )
                {
                    //	NOTE: Re-order coordinates. I use +ve Y as up, +ve X as right, +ve Z as into, MD3 default is +ve X as into, +ve Y as right, +ve Z as up
                    Point3 pt = new Point3( );
                    pt.Z = reader.ReadInt16( ) * XyzScale;
                    pt.X = reader.ReadInt16( ) * XyzScale;
                    pt.Y = reader.ReadInt16( ) * XyzScale;

                    pt = transform * pt;

                    positions[ positionIndex + 0 ] = pt.X;
                    positions[ positionIndex + 1 ] = pt.Y;
                    positions[ positionIndex + 2 ] = pt.Z;
                    positionIndex += 3;

                    float s = reader.ReadByte( ) * ByteToAngle;
                    float t = reader.ReadByte( ) * ByteToAngle;

                    normals[ normalIndex + 2 ] = ( Functions.Cos( s ) * Functions.Sin( t ) );
                    normals[ normalIndex + 0 ] = ( Functions.Sin( s ) * Functions.Sin( t ) );
                    normals[ normalIndex + 1 ] = ( Functions.Cos( t ) );
                    normalIndex += 3;
                }

                //	Convert position and normal data into vertex buffer objects
                frame.VertexBuffers = new OpenGlVertexArray[ 2 ];

                frame.VertexBuffers[ 0 ] = new OpenGlVertexArray( numVertices, 0, Gl.GL_VERTEX_ARRAY, 0, 3, Gl.GL_STATIC_DRAW_ARB, positions );
                frame.VertexBuffers[ 1 ] = new OpenGlVertexArray( numVertices, 0, Gl.GL_NORMAL_ARRAY, 0, 3, Gl.GL_STATIC_DRAW_ARB, normals );

                //	Assign the frame to the surface
                surface.SurfaceFrames[ frameIndex ] = frame;
            }
        }
        /// <summary>
        /// Reads tag information
        /// </summary>
        private static void ReadTags( BinaryReader reader, long offset, int numTags, int numFrames, ModelMesh mesh, Matrix44 transform )
        {
            reader.BaseStream.Seek( offset, SeekOrigin.Begin );

            mesh.TagNames = new string[ numTags ];

            for ( int frameIndex = 0; frameIndex < numFrames; ++frameIndex )
            {
                ModelMesh.Tag[] tags = new ModelMesh.Tag[ numTags ];
                for ( int tagCount = 0; tagCount < numTags; ++tagCount )
                {
                    string tagName = ReadString( reader, MaxPathLength );

                    if ( frameIndex == 0 )
                    {
                        mesh.TagNames[ tagCount ] = tagName;
                    }

                    Point3	origin	= transform * ReadPoint( reader );
                    Vector3 xAxis	= ReadVector( reader );
                    Vector3 yAxis	= ReadVector( reader );
                    Vector3 zAxis	= ReadVector( reader );

                    ModelMesh.Tag curTag = new ModelMesh.Tag( );
                    curTag.Transform = new Matrix44( origin, xAxis, yAxis, zAxis );
                    curTag.Name = tagName;

                    tags[ tagCount ] = curTag;
                }
                mesh.FrameInfoList[ frameIndex ].Tags = tags;
            }
        }
        /// <summary>
        /// Reads surface information
        /// </summary>
        private static void ReadSurfaces( ISource source, BinaryReader reader, long offset, int numSurfaces, int numFrames, ModelMesh mesh, IDictionary<string, ITexture2d> surfaceTextureTable, Matrix44 transform)
        {
            //	Move to the start of the first surface
            reader.BaseStream.Seek( offset, SeekOrigin.Begin );

            //	Allocate mesh surface array
            mesh.Surfaces = new ModelMesh.Surface[ numSurfaces ];

            for ( int surfaceCount = 0; surfaceCount < numSurfaces; ++surfaceCount )
            {
                //	Create a new surface
                ModelMesh.Surface curSurface		= new ModelMesh.Surface( );

                //int		ident				=
                reader.ReadInt32( );
                string	name				= ReadString ( reader, MaxPathLength );
                //int		flags				=
                reader.ReadInt32( );
                //int		surfaceNumFrames	=
                reader.ReadInt32( );
                //int		numShaders			=
                reader.ReadInt32( );
                int		numVertices			= reader.ReadInt32( );
                int		numTriangles		= reader.ReadInt32( );
                int		trianglesOffset		= reader.ReadInt32( );
                //int		shadersOffset		=
                reader.ReadInt32( );
                int		texturesOffset		= reader.ReadInt32( );
                int		verticesOffset		= reader.ReadInt32( );
                int		endOffset			= reader.ReadInt32( );

                //	Assign surface texture
                curSurface.Texture			= GetTexture( source, surfaceTextureTable, name );

                //	Assign surface shaders
            //	ReadShaders( reader, offset + shadersOffset, numShaders );

                //	Read in surface index group and texture UVs
                curSurface.Group			= ReadTriangles( reader, offset + trianglesOffset, numTriangles );
                curSurface.TextureUVs		= ReadTextureUVs( reader, offset + texturesOffset, numVertices );

                //	Read in surface vertices
            //	curSurface.NumVertices		= numVertices;
                ReadVertices( reader, offset + verticesOffset, numVertices, numFrames, curSurface, transform );

                //	Assign the new surface to the mesh
                mesh.Surfaces[ surfaceCount ] = curSurface;

                //	Move the stream to the next surface
                reader.BaseStream.Seek( offset + endOffset, SeekOrigin.Begin );
                offset += endOffset;
            }
        }
        /// <summary>
        /// Reads frame information
        /// </summary>
        private static void ReadFrames( BinaryReader reader, long offset, int numFrames, ModelMesh mesh, Matrix44 transform )
        {
            reader.BaseStream.Seek( offset, SeekOrigin.Begin );

            ModelMesh.FrameInfo[] frames = new ModelMesh.FrameInfo[ numFrames ];

            float scaleLength = ( transform * new Vector3( 1, 1, 1 ) ).Length;

            for ( int frameCount = 0; frameCount < numFrames; ++frameCount )
            {
                frames[ frameCount]		= new ModelMesh.FrameInfo( );
                ModelMesh.FrameInfo curFrame	= frames[ frameCount ];
                curFrame.MinBounds		= transform * ReadPoint( reader );
                curFrame.MaxBounds		= transform * ReadPoint( reader );
                curFrame.Origin			= transform * ReadPoint( reader );
                curFrame.Radius			= reader.ReadSingle( ) * scaleLength;
                curFrame.Name			= ReadString( reader, FrameNameLength );
            }

            mesh.FrameInfoList = frames;
        }
        /// <summary>
        /// Loads an MD3 mesh resource from a stream
        /// </summary>
        private static ModelMesh LoadMd3( ISource source, Model model, ModelPart part, Matrix44 transform, ISource md3Source, IDictionary<string, ITexture2d> surfaceTextureTable )
        {
            using ( Stream inputStream = OpenStream( md3Source ) )
            {
                BinaryReader reader = new BinaryReader( inputStream );

                //	http://icculus.org/homepages/phaethon/q3a/formats/md3format.html

                //	Make sure of the MD3 identity
                byte[] ident		= reader.ReadBytes( 4 );
                if ( ( ident[ 0 ] != 'I' ) || ( ident[ 1 ] != 'D' ) || ( ident[ 2 ] != 'P' ) || ( ident[ 3 ] != '3' ) )
                {
                    throw new ApplicationException( "Failed to load MD3 resource - stream did not start with 'IDP3' MD3 identifier" );
                }

                //	Read in header
                //int version			=
                reader.ReadInt32( );
                //string name			=
                ReadString( reader, MaxPathLength );
                //int flags			=
                reader.ReadInt32( );
                int numFrames		= reader.ReadInt32( );
                int numTags			= reader.ReadInt32( );
                int numSurfaces		= reader.ReadInt32( );
                //int numSkins		=
                reader.ReadInt32( );
                int framesOffset	= reader.ReadInt32( );
                int tagsOffset		= reader.ReadInt32( );
                int surfacesOffset	= reader.ReadInt32( );
                //int eofOffset		=
                reader.ReadInt32( );

                //	TODO: Can load directly into mesh frame, tag and surface structures - don't do this intermediate step
                ModelMesh mesh = new ModelMesh( model, part );

                ReadFrames( reader, framesOffset, numFrames, mesh, transform );
                ReadTags( reader, tagsOffset, numTags, numFrames, mesh, transform );
                ReadSurfaces( source, reader, surfacesOffset, numSurfaces, numFrames, mesh, surfaceTextureTable, transform );

                //	TODO: REMOVE. test frames
                string md3Name = md3Source.ToString( );
                if ( md3Name.IndexOf( "Upper" ) != -1 )
                {
                    mesh.DefaultFrame = 151;
                }
                else if ( md3Name.IndexOf( "Head" ) != -1 )
                {
                    mesh.DefaultFrame = 0;
                }
                else
                {
                    mesh.DefaultFrame = 0;
                }

                return mesh;
            }
        }