/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   MD2Mesh
// Purpose:      Loads and stores MD2 model data according to the generalised
//               mesh class. If the model cannot be loaded for whatever reason
//               then a default model is generated instead (a cube).
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "MD2Mesh.h"

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     MD2Mesh::MD2Mesh()
Purpose:  Constructs an MD2 mesh from the given file or a box if a problem occured
          on loading. Also loads and saves the skin given.
Params:   MD2 mesh file name, Skin file name
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

MD2Mesh::MD2Mesh( char * modelFile, char * skinFile )
{
    //--------------------------------------------------------------------------
    // Firstly load and save the skin
    //--------------------------------------------------------------------------

    meshSkin = new GLTexture(skinFile);

    //--------------------------------------------------------------------------
    // Handy macros for aborting file reading and loading the default model as well
    // as checking for errors. I gets tired typing u see.. : )
    //--------------------------------------------------------------------------

    #define ABORT_LOAD      { fclose(md2File); defaultMesh(); return; }
    #define EVERYTHING_OK   ( ( ferror(md2File) | feof(md2File) ) == 0 )

    //--------------------------------------------------------------------------
    // Open a file stream to specified model file
    //--------------------------------------------------------------------------

    FILE * md2File = 0; fopen_s(&md2File,modelFile,"rb");

    //--------------------------------------------------------------------------
    // If the file isn't open then make the default model and abort
    //--------------------------------------------------------------------------

    if ( md2File == 0 )
    {
        // Can't read the file

        defaultMesh();      // create the default cube model
        return;             // abort any further operations
    }

    //--------------------------------------------------------------------------
    // Read the MD2 header and save
    //--------------------------------------------------------------------------

    // Store MD2 Data here

    static MD2Header md2FileHeader;

    // Attempt to read the MD2 file header

    fread(&md2FileHeader,sizeof(MD2Header),1,md2File);

    // If everything isn't ok then abort the load

    if ( ! EVERYTHING_OK ) ABORT_LOAD;

    // Validate the header contents:

    if ( md2FileHeader.MD2FileID    != MD2_FILE_ID       ) ABORT_LOAD;
    if ( md2FileHeader.MD2Version   != MD2_VERSION       ) ABORT_LOAD;
    if ( md2FileHeader.numSkins     >  MD2_MAX_SKINS     ) ABORT_LOAD;
    if ( md2FileHeader.numFrames    >  MD2_MAX_FRAMES    ) ABORT_LOAD;
    if ( md2FileHeader.numTriangles >  MD2_MAX_TRIANGLES ) ABORT_LOAD;
    if ( md2FileHeader.numVertices  >  MD2_MAX_VERTICES  ) ABORT_LOAD;
    if ( md2FileHeader.numTexCoords >  MD2_MAX_VERTICES  ) ABORT_LOAD;

    if ( md2FileHeader.numFrames    ==  0 ) ABORT_LOAD;
    if ( md2FileHeader.numTriangles ==  0 ) ABORT_LOAD;
    if ( md2FileHeader.numVertices  ==  0 ) ABORT_LOAD;
    if ( md2FileHeader.numTexCoords ==  0 ) ABORT_LOAD;

    if ( md2FileHeader.offsetFrames     >= md2FileHeader.offsetEnd ) ABORT_LOAD;
    if ( md2FileHeader.offsetTexCoords  >= md2FileHeader.offsetEnd ) ABORT_LOAD;
    if ( md2FileHeader.offsetTriangles  >= md2FileHeader.offsetEnd ) ABORT_LOAD;
    if ( md2FileHeader.offsetGlCommands >= md2FileHeader.offsetEnd ) ABORT_LOAD;
    if ( md2FileHeader.offsetSkins      >= md2FileHeader.offsetEnd ) ABORT_LOAD;

    //--------------------------------------------------------------------------
    // Save the number of triangles and vertices for future use
    //--------------------------------------------------------------------------

    this->numTriangles = md2FileHeader.numTriangles;
    this->numVertices  = md2FileHeader.numTriangles * 3;

    //--------------------------------------------------------------------------
    // Read the MD2 texture coordinates
    //--------------------------------------------------------------------------

    // First seek to the where the data is stored

    fseek(md2File,md2FileHeader.offsetTexCoords,SEEK_SET);

    // If everything isn't ok then abort the load

    if ( ! EVERYTHING_OK ) ABORT_LOAD;

    // Create a temporary array of texture coordinates

    MD2TexCoord * texCoords = new MD2TexCoord[md2FileHeader.numTexCoords];

    // Try and read the texture coordinates from the file

    fread(texCoords,sizeof(MD2TexCoord),md2FileHeader.numTexCoords,md2File);

    // If something went wrong then free all resources and abort loading

    if ( ! EVERYTHING_OK )
    {
        delete[] texCoords;
        ABORT_LOAD;
    }

    //--------------------------------------------------------------------------
    // Read the MD2 triangle data
    //--------------------------------------------------------------------------

    // First seek to the where the data is stored

    fseek(md2File,md2FileHeader.offsetTriangles,SEEK_SET);

    // If something went wrong then free all resources and abort loading

    if ( ! EVERYTHING_OK )
    {
        delete[] texCoords;
        ABORT_LOAD;
    }

    // Create a temporary array of triangle structs

    MD2Triangle * triangles = new MD2Triangle[md2FileHeader.numTriangles];

    // Try and read the triangles from the file

    fread(triangles,sizeof(MD2Triangle),md2FileHeader.numTriangles,md2File);

    // If something went wrong then free all resources and abort loading

    if ( ! EVERYTHING_OK )
    {
        delete[] triangles;
        delete[] texCoords;
        ABORT_LOAD;
    }

    //--------------------------------------------------------------------------
    // Read just one MD2 Frame: not interested in animated meshes
    //--------------------------------------------------------------------------

    // First seek to where the first frame is

    fseek(md2File,md2FileHeader.offsetFrames,SEEK_SET);

    // If something went wrong then free all resources and abort loading

    if ( ! EVERYTHING_OK )
    {
        delete[] triangles;
        delete[] texCoords;
        ABORT_LOAD;
    }

    // Hold the frame header here

    static MD2FrameHeader frameHeader;

    // Try and read the frame header

    fread(&frameHeader,sizeof(MD2FrameHeader),1,md2File);

    // If something went wrong then free all resources and abort loading

    if ( ! EVERYTHING_OK )
    {
        delete[] triangles;
        delete[] texCoords;
        ABORT_LOAD;
    }

    // Now create an array of compressed vertices for this frame

    MD2Vertex * vertices = new MD2Vertex[md2FileHeader.numVertices];

    // Now try and read all of the vertices for this frame

    fread(vertices,sizeof(MD2Vertex),md2FileHeader.numVertices,md2File);

    // If something went wrong then free all resources and abort loading

    if ( ! EVERYTHING_OK )
    {
        delete[] triangles;
        delete[] texCoords;
        delete[] vertices;
        ABORT_LOAD;
    }

    //--------------------------------------------------------------------------
    // Now close up the file stream. we're done reading.
    //--------------------------------------------------------------------------

    fclose(md2File);

    //--------------------------------------------------------------------------
    // Create the arrays that hold the xyz and uv elements of the vertices and
    // texture coordinates used in GL rendering
    //--------------------------------------------------------------------------

    vertexArray     = new float[ md2FileHeader.numTriangles * 3 * 3 ];
    texCoordArray   = new float[ md2FileHeader.numTriangles * 3 * 2 ];

    //--------------------------------------------------------------------------
    // Fill the vertex and texture coordinate array
    //--------------------------------------------------------------------------

    for ( unsigned t = 0 ; t < md2FileHeader.numTriangles ; t++ )
    {
        //----------------------------------------------------------------------
        // Get the Indexes of the three compressed vertices of this triangle
        //----------------------------------------------------------------------

        unsigned short v1_i = triangles[t].vertex1_index;
        unsigned short v2_i = triangles[t].vertex2_index;
        unsigned short v3_i = triangles[t].vertex3_index;

        //----------------------------------------------------------------------
        // Get the compressed byte representation of eeach vertex
        //----------------------------------------------------------------------

        unsigned char v1_x_c = vertices[v1_i].x;
        unsigned char v1_y_c = vertices[v1_i].y;
        unsigned char v1_z_c = vertices[v1_i].z;

        unsigned char v2_x_c = vertices[v2_i].x;
        unsigned char v2_y_c = vertices[v2_i].y;
        unsigned char v2_z_c = vertices[v2_i].z;

        unsigned char v3_x_c = vertices[v3_i].x;
        unsigned char v3_y_c = vertices[v3_i].y;
        unsigned char v3_z_c = vertices[v3_i].z;

        //----------------------------------------------------------------------
        // Declare uncompressed versions of the vertices
        //----------------------------------------------------------------------

        float v1_x , v1_y , v1_z;
        float v2_x , v2_y , v2_z;
        float v3_x , v3_y , v3_z;

        //----------------------------------------------------------------------
        // Decompress each vertex using the frame scaling and translation values
        //----------------------------------------------------------------------

        v1_x = ( v1_x_c * frameHeader.frameScaling_x ) + frameHeader.frameTranslation_x;
        v1_y = ( v1_y_c * frameHeader.frameScaling_y ) + frameHeader.frameTranslation_y;
        v1_z = ( v1_z_c * frameHeader.frameScaling_z ) + frameHeader.frameTranslation_z;

        v2_x = ( v2_x_c * frameHeader.frameScaling_x ) + frameHeader.frameTranslation_x;
        v2_y = ( v2_y_c * frameHeader.frameScaling_y ) + frameHeader.frameTranslation_y;
        v2_z = ( v2_z_c * frameHeader.frameScaling_z ) + frameHeader.frameTranslation_z;

        v3_x = ( v3_x_c * frameHeader.frameScaling_x ) + frameHeader.frameTranslation_x;
        v3_y = ( v3_y_c * frameHeader.frameScaling_y ) + frameHeader.frameTranslation_y;
        v3_z = ( v3_z_c * frameHeader.frameScaling_z ) + frameHeader.frameTranslation_z;

        //----------------------------------------------------------------------
        // Store the vertices in the vertex array
        //----------------------------------------------------------------------

        /* WRONG LAYOUT: MD2 Uses Z for what OpenGL uses as the Y axis and Y for Z

        vertexArray[ t * 9 + 0 ] = v1_x;
        vertexArray[ t * 9 + 1 ] = v1_y;
        vertexArray[ t * 9 + 2 ] = v1_z;
        vertexArray[ t * 9 + 3 ] = v2_x;
        vertexArray[ t * 9 + 4 ] = v2_y;
        vertexArray[ t * 9 + 5 ] = v2_z;
        vertexArray[ t * 9 + 6 ] = v3_x;
        vertexArray[ t * 9 + 7 ] = v3_y;
        vertexArray[ t * 9 + 8 ] = v3_z;

        */

        vertexArray[ t * 9 + 0 ] = v1_x;
        vertexArray[ t * 9 + 1 ] = v1_z;
        vertexArray[ t * 9 + 2 ] = v1_y;
        vertexArray[ t * 9 + 3 ] = v2_x;
        vertexArray[ t * 9 + 4 ] = v2_z;
        vertexArray[ t * 9 + 5 ] = v2_y;
        vertexArray[ t * 9 + 6 ] = v3_x;
        vertexArray[ t * 9 + 7 ] = v3_z;
        vertexArray[ t * 9 + 8 ] = v3_y;

        //----------------------------------------------------------------------
        // Get the Indices of the three texture coordinates of the triangle
        //----------------------------------------------------------------------

        unsigned short t1_i = triangles[t].texCoord1_index;
        unsigned short t2_i = triangles[t].texCoord2_index;
        unsigned short t3_i = triangles[t].texCoord3_index;

        //----------------------------------------------------------------------
        // Get the short representation of the texture coordinates
        //----------------------------------------------------------------------

        unsigned short t1_u_s = texCoords[t1_i].u;
        unsigned short t1_v_s = texCoords[t1_i].v;
        unsigned short t2_u_s = texCoords[t2_i].u;
        unsigned short t2_v_s = texCoords[t2_i].v;
        unsigned short t3_u_s = texCoords[t3_i].u;
        unsigned short t3_v_s = texCoords[t3_i].v;

        //----------------------------------------------------------------------
        // Convert short representation to floating point between 1 and 0. The
        // shorts give a pixel value of where the tex coord is. We want floats
        // for OpenGLs usage.
        //----------------------------------------------------------------------

        float t1_u = float(t1_u_s) / float(md2FileHeader.skinWidth  );
        float t1_v = float(t1_v_s) / float(md2FileHeader.skinHeight );
        float t2_u = float(t2_u_s) / float(md2FileHeader.skinWidth  );
        float t2_v = float(t2_v_s) / float(md2FileHeader.skinHeight );
        float t3_u = float(t3_u_s) / float(md2FileHeader.skinWidth  );
        float t3_v = float(t3_v_s) / float(md2FileHeader.skinHeight );

        //----------------------------------------------------------------------
        // Now store texture coordinates in the texture coordinate array
        //----------------------------------------------------------------------

        texCoordArray[ t * 6 + 0 ] = t1_u;
        texCoordArray[ t * 6 + 1 ] = t1_v;
        texCoordArray[ t * 6 + 2 ] = t2_u;
        texCoordArray[ t * 6 + 3 ] = t2_v;
        texCoordArray[ t * 6 + 4 ] = t3_u;
        texCoordArray[ t * 6 + 5 ] = t3_v;
    }

    //--------------------------------------------------------------------------
    // Finally generate the surface normals for every vertex in the model.
    // Also generate edges and edge normals.
    //--------------------------------------------------------------------------

    generateNormals();
    generateEdges();
    generateEdgeNormals();

    //--------------------------------------------------------------------------
    // Generate mesh bounding sphere
    //--------------------------------------------------------------------------

    generateBoundingSphere();

    //--------------------------------------------------------------------------
    // Cleanup tempoarary data
    //--------------------------------------------------------------------------

    delete[] triangles;
    delete[] texCoords;
    delete[] vertices;

    //--------------------------------------------------------------------------
    // Undo these macros
    //--------------------------------------------------------------------------

    #undef ABORT_LOAD
    #undef EVERYTHING_OK
}
