//********************************************************************************//
//                                                                                //
//      - "Talk to me like I'm a 3 year old!" Programming Lessons -               //
//                                                                                //
//      $Author:        DigiBen     digiben@gametutorials.com                     //
//                                                                                //
//      $Program:       GT Loader Part 3                                       //
//                                                                                //
//      $Description:   Loads the GT Skeletal Animation format .GTF                 //
//                                                                                //
//********************************************************************************//

#include "main.h"
#include "gtf.h"


//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////

// Below we have a bunch of functions that we need for some basic
// calculations when dealing with 3D vectors and quaternions.  Below
// those are some matrix-math functions, as well as the complicated
// SLERP (Spherical Linear Interpolation) function.  The rest of the
// additions are for loading the animation data, rendering the animations,
// then handling the timing and updating of the animation frames.

////////////////////////////// OPERATOR * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This multiplies a scalar by a CVector3 and returns a CVector3
/////
////////////////////////////// OPERATOR * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*


CVector3 operator*(float scalar, const CVector3& v) 
{
    // Return the multiplied scalar by the vector
    return CVector3(v.x * scalar, v.y * scalar, v.z * scalar);
}


////////////////////////////// OPERATOR * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This multiplies a quaternion by a CVector3
/////
////////////////////////////// OPERATOR * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CVector3 CQuaternion::operator*(CVector3 v)
{
    // This function is a complex function that calculates the
    // result of multiplying a quaternion by a vector.  We perform
    // three calculations for different parts of the whole equation
    // and then add them all up to return the result:
    //
    // ResultVector = 
    // ( vec * ( 2w^2 - 1) ) + (2 * (q_xyz dot vec) * q_xyz) + (2w * (q_xyz cross vec))

    // First we store the x,y,z of the quaternion in a CVector3
    CVector3 q(x, y, z);
    
    // The is the first part of the equation: ( vec * ( 2w^2 - 1) )
    CVector3 t1 = v * (2.0f * w * w - 1.0f);

    // This is the second part of the equation: (2 * (q_xyz dot vec) * q_xyz)
    CVector3 t2 = 2.0f * q.Dot(v) * q;

    // This is the third and final part of the equation: (2 * w * (q_xyz cross vec))
    CVector3 t3 = 2.0f * w * q.Cross(v);

    // Now add up all the vectors and return the final resultant vector
    return t1 + t2 + t3;
}


////////////////////////////// OPERATOR * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This multiplies 2 quaternions and returns the resultant quaternion
/////
////////////////////////////// OPERATOR * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CQuaternion CQuaternion::operator*(const CQuaternion& q)
{
    // This is how to multiply a quaternion by a quaternion
    return CQuaternion(w * q.x + x * q.w + y * q.z - z * q.y,
                       w * q.y + y * q.w + z * q.x - x * q.z,
                       w * q.z + z * q.w + x * q.y - y * q.x,
                       w * q.w - x * q.x - y * q.y - z * q.z);
}


////////////////////////////// OPERATOR + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This adds 2 quaternions and returns the resultant quaternion
/////
////////////////////////////// OPERATOR + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CQuaternion CQuaternion::operator+(const CQuaternion& q)
{
    // Add the quaternion to the current quaternion and return the result
    return CQuaternion(x + q.x, y + q.y, z + q.z, w + q.w);
}



////////////////////////////// CREATE MATRIX \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This function converts a quaternion to a rotation matrix
/////
////////////////////////////// CREATE MATRIX \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*  

void CQuaternion::CreateMatrix(float *pMatrix)
{
    // Make sure the matrix has allocated memory to store the rotation data
    if(!pMatrix) return;
    
    // Fill in the rows of the 4x4 matrix, according to the quaternion to matrix equations

    // First row
    pMatrix[ 0] = 1.0f - 2.0f * ( y * y + z * z );  
    pMatrix[ 1] = 2.0f * ( x * y - w * z );  
    pMatrix[ 2] = 2.0f * ( x * z + w * y );  
    pMatrix[ 3] = 0.0f;  

    // Second row
    pMatrix[ 4] = 2.0f * ( x * y + w * z );  
    pMatrix[ 5] = 1.0f - 2.0f * ( x * x + z * z );  
    pMatrix[ 6] = 2.0f * ( y * z - w * x );  
    pMatrix[ 7] = 0.0f;  

    // Third row
    pMatrix[ 8] = 2.0f * ( x * z - w * y );  
    pMatrix[ 9] = 2.0f * ( y * z + w * x );  
    pMatrix[10] = 1.0f - 2.0f * ( x * x + y * y );  
    pMatrix[11] = 0.0f;  

    // Fourth row
    pMatrix[12] = 0;  
    pMatrix[13] = 0;  
    pMatrix[14] = 0;  
    pMatrix[15] = 1.0f;

    // Now pMatrix[] is a 4x4 homogeneous matrix that can be applied to an OpenGL Matrix
}


///////////////////////////////// CREATE FROM MATRIX \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This creates a quaternion from a 3x3 or a 4x4 matrix, depending on rowColumnCount
/////
///////////////////////////////// CREATE FROM MATRIX \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CQuaternion::CreateFromMatrix(float *pTheMatrix, int rowColumnCount)
{
    // Make sure the matrix has valid memory and it's not expected that we allocate it.
    // Also, we do a check to make sure the matrix is a 3x3 or a 4x4 (must be 3 or 4).
    if(!pTheMatrix || ((rowColumnCount != 3) && (rowColumnCount != 4))) return;

    // This function is used to take in a 3x3 or 4x4 matrix and convert the matrix
    // to a quaternion.  If rowColumnCount is a 3, then we need to convert the 3x3
    // matrix passed in to a 4x4 matrix, otherwise we just leave the matrix how it is.
    // Since we want to apply a matrix to an OpenGL matrix, we need it to be 4x4.

    // Point the matrix pointer to the matrix passed in, assuming it's a 4x4 matrix
    float *pMatrix = pTheMatrix;

    // Create a 4x4 matrix to convert a 3x3 matrix to a 4x4 matrix (If rowColumnCount == 3)
    float m4x4[16] = {0};

    // If the matrix is a 3x3 matrix (which it is for Quake3), then convert it to a 4x4
    if(rowColumnCount == 3)
    {
        // Set the 9 top left indices of the 4x4 matrix to the 9 indices in the 3x3 matrix.
        // It would be a good idea to actually draw this out so you can visualize it.
        m4x4[0]  = pTheMatrix[0];    m4x4[1]  = pTheMatrix[1];    m4x4[2]  = pTheMatrix[2];
        m4x4[4]  = pTheMatrix[3];    m4x4[5]  = pTheMatrix[4];    m4x4[6]  = pTheMatrix[5];
        m4x4[8]  = pTheMatrix[6];    m4x4[9]  = pTheMatrix[7];    m4x4[10] = pTheMatrix[8];

        // Since the bottom and far right indices are zero, set the bottom right corner to 1.
        // This is so that it follows the standard diagonal line of 1's in the identity matrix.
        m4x4[15] = 1;

        // Set the matrix pointer to the first index in the newly converted matrix
        pMatrix = &m4x4[0];
    }

    // The next step, once we made sure we are dealing with a 4x4 matrix, is to check the
    // diagonal of the matrix.  This means that we add up all of the indices that comprise
    // the standard 1's in the identity matrix.  If you draw out the identity matrix of a
    // 4x4 matrix, you will see that the 1's form a diagonal line.  Notice we just assume
    // that the last index (15) is 1 because it is not effected in the 3x3 rotation matrix.

    // Find the diagonal of the matrix by adding up it's diagonal indices.
    // This is also known as the "trace", but I will call the variable diagonal.
    float diagonal = pMatrix[0] + pMatrix[5] + pMatrix[10] + 1;
    float scale = 0.0f;

    // Below we check if the diagonal is greater than zero.  To avoid accidents with
    // floating point numbers, we substitute 0 with 0.00000001.  If the diagonal is
    // great than zero, we can perform an "instant" calculation, otherwise we will need
    // to identify which diagonal element has the greatest value.  Note, that it appears
    // that %99 of the time, the diagonal IS greater than 0 so the rest is rarely used.

    // If the diagonal is greater than zero
    if(diagonal > 0.00000001)
    {
        // Calculate the scale of the diagonal
        scale = float(sqrt(diagonal ) * 2);

        // Calculate the x, y, x and w of the quaternion through the respective equation
        x = ( pMatrix[9] - pMatrix[6] ) / scale;
        y = ( pMatrix[2] - pMatrix[8] ) / scale;
        z = ( pMatrix[4] - pMatrix[1] ) / scale;
        w = 0.25f * scale;
    }
    else 
    {
        // If the first element of the diagonal is the greatest value
        if ( pMatrix[0] > pMatrix[5] && pMatrix[0] > pMatrix[10] )  
        {   
            // Find the scale according to the first element, and double that value
            scale  = (float)sqrt( 1.0f + pMatrix[0] - pMatrix[5] - pMatrix[10] ) * 2.0f;

            // Calculate the x, y, x and w of the quaternion through the respective equation
            x = 0.25f * scale;
            y = (pMatrix[4] + pMatrix[1] ) / scale;
            z = (pMatrix[2] + pMatrix[8] ) / scale;
            w = (pMatrix[9] - pMatrix[6] ) / scale;  
        } 
        // Else if the second element of the diagonal is the greatest value
        else if ( pMatrix[5] > pMatrix[10] ) 
        {
            // Find the scale according to the second element, and double that value
            scale  = (float)sqrt( 1.0f + pMatrix[5] - pMatrix[0] - pMatrix[10] ) * 2.0f;
            
            // Calculate the x, y, x and w of the quaternion through the respective equation
            x = (pMatrix[4] + pMatrix[1] ) / scale;
            y = 0.25f * scale;
            z = (pMatrix[9] + pMatrix[6] ) / scale;
            w = (pMatrix[2] - pMatrix[8] ) / scale;
        } 
        // Else the third element of the diagonal is the greatest value
        else 
        {   
            // Find the scale according to the third element, and double that value
            scale  = (float)sqrt( 1.0f + pMatrix[10] - pMatrix[0] - pMatrix[5] ) * 2.0f;

            // Calculate the x, y, x and w of the quaternion through the respective equation
            x = (pMatrix[2] + pMatrix[8] ) / scale;
            y = (pMatrix[9] + pMatrix[6] ) / scale;
            z = 0.25f * scale;
            w = (pMatrix[4] - pMatrix[1] ) / scale;
        }
    }
}



/////////////////////////////////////// SLERP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   Returns a spherical linear interpolated quaternion between q1 and q2, with respect to t
/////
/////////////////////////////////////// SLERP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CQuaternion CQuaternion::Slerp(CQuaternion &q1, CQuaternion &q2, float t)
{
    // Create a local quaternion to store the interpolated quaternion
    CQuaternion qInterpolated;

    // This function is the milk and honey of our quaternion code, the rest of
    // the functions are an appendage to what is done here.  Everyone understands
    // the terms, "matrix to quaternion", "quaternion to matrix", "create quaternion matrix",
    // "quaternion multiplication", etc.. but "SLERP" is the stumbling block, even a little 
    // bit after hearing what it stands for, "Spherical Linear Interpolation".  What that
    // means is that we have 2 quaternions (or rotations) and we want to interpolate between 
    // them.  The reason what it's called "spherical" is that quaternions deal with a sphere.  
    // Linear interpolation just deals with 2 points primarily, where when dealing with angles
    // and rotations, we need to use sin() and cos() for interpolation.  If we wanted to use
    // quaternions for camera rotations, which have much more instant and jerky changes in 
    // rotations, we would use Spherical-Cubic Interpolation.  The equation for SLERP is this:
    //
    // q = (((b.a)^-1)^t)a
    //
    // Go here for an a detailed explanation and proofs of this equation:
    //
    // http://www.magic-software.com/Documentation/quat.pdf
    //
    // Now, Let's code it

    // Here we do a check to make sure the 2 quaternions aren't the same, return q1 if they are
    if(q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w) 
        return q1;

    // Following the (b.a) part of the equation, we do a dot product between q1 and q2.
    // We can do a dot product because the same math applied for a 3D vector as a 4D vector.
    float result = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z) + (q1.w * q2.w);

    // If the dot product is less than 0, the angle is greater than 90 degrees
    if(result < 0.0f)
    {
        // Negate the second quaternion and the result of the dot product
        q2 = CQuaternion(-q2.x, -q2.y, -q2.z, -q2.w);
        result = -result;
    }

    // Set the first and second scale for the interpolation
    float scale0 = 1 - t, scale1 = t;

    // Next, we want to actually calculate the spherical interpolation.  Since this
    // calculation is quite computationally expensive, we want to only perform it
    // if the angle between the 2 quaternions is large enough to warrant it.  If the
    // angle is fairly small, we can actually just do a simpler linear interpolation
    // of the 2 quaternions, and skip all the complex math.  We create a "delta" value
    // of 0.1 to say that if the cosine of the angle (result of the dot product) between
    // the 2 quaternions is smaller than 0.1, then we do NOT want to perform the full on 
    // interpolation using.  This is because you won't really notice the difference.

    // Check if the angle between the 2 quaternions was big enough to warrant such calculations
    if(1 - result > 0.1f)
    {
        // Get the angle between the 2 quaternions, and then store the sin() of that angle
        float theta = (float)acos(result);
        float sinTheta = (float)sin(theta);

        // Calculate the scale for q1 and q2, according to the angle and it's sine value
        scale0 = (float)sin( ( 1 - t ) * theta) / sinTheta;
        scale1 = (float)sin( ( t * theta) ) / sinTheta;
    }   

    // Calculate the x, y, z and w values for the quaternion by using a special
    // form of linear interpolation for quaternions.
    qInterpolated.x = (scale0 * q1.x) + (scale1 * q2.x);
    qInterpolated.y = (scale0 * q1.y) + (scale1 * q2.y);
    qInterpolated.z = (scale0 * q1.z) + (scale1 * q2.z);
    qInterpolated.w = (scale0 * q1.w) + (scale1 * q2.w);

    // Return the interpolated quaternion
    return qInterpolated;
}

//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////


///////////////////////////////// CMODEL GTF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This is our CModelGTF constructor
/////
///////////////////////////////// CMODEL GTF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CModelGTF::CModelGTF()
{
}


///////////////////////////////// ~CMODEL GTF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This is our CModelGTF deconstructor
/////
///////////////////////////////// ~CMODEL GTF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

CModelGTF::~CModelGTF()
{
    // Free all the model data
    DestroyModel();
}   


///////////////////////////////// DESTROY MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This frees our model and all it's associated data
/////
///////////////////////////////// DESTROY MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CModelGTF::DestroyModel()
{
    // Go through all the objects in the model
    for(int i = 0; i < m_Model.numOfObjects; i++)
    {
        // Free the faces and vertices
        if(m_Model.pObject[i].pFaces)       delete [] m_Model.pObject[i].pFaces;
        if(m_Model.pObject[i].pVerts)       delete [] m_Model.pObject[i].pVerts;


//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////
        
        // Go through all of our weight lists and free the pointers
        for(int w = 0; w < (int)m_Model.pObject[i].vWeightInfo.size(); w++)
        {
            // Check if there is a valid pointer for this weight list
            if(m_Model.pObject[i].vWeightInfo[w].pWeightInfo)
            {
                // Delete the pointer and set it to NULL
                delete [] m_Model.pObject[i].vWeightInfo[w].pWeightInfo;
                m_Model.pObject[i].vWeightInfo[w].pWeightInfo = NULL;
            }
        }

        // Clear our STL list for the weight influences
        m_Model.pObject[i].vWeightInfo.clear();

        // Go through all of the bone data and free the lists
        for(int b = 0; b < (int)m_Model.pObject[i].vBoneInfo.size(); b++)
        {
            // Check if the matrice pointer is valid
            if(m_Model.pObject[i].vBoneInfo[b].pBoneMatrices)
            {
                // Delete and NULL the matrix list
                delete [] m_Model.pObject[i].vBoneInfo[b].pBoneMatrices;
                m_Model.pObject[i].vBoneInfo[b].pBoneMatrices = NULL;
            }
        }

        // Clear the bone list
        m_Model.pObject[i].vBoneInfo.clear();

//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////


    }
}
    

///////////////////////////////// LOAD MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This loads our model from the given path and file name
/////
///////////////////////////////// LOAD MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

bool CModelGTF::LoadModel(LPSTR strPath, LPSTR strModel)
{
    // Create a loader class instance
    CLoadGTF loadGTF;

    // Make sure valid model name was passed in
    if(!strModel) return false;

    char szFullPath[MAX_PATH] = {0};

    // If there is a valid path we want to add the file name to it,
    // otherwise just use the file name with no path.
    if(strPath)
        sprintf(szFullPath, "%s\\%s", strPath, strModel);
    else
        strcpy(szFullPath, strModel);

    // Here we load the .GTF file.  We also make sure it loaded correctly.
    if(!loadGTF.ImportGTF(&m_Model, szFullPath))
    {
        // Display an error message telling us the file could not be found
        MessageBox(g_hWnd, "Unable to load the model!", "Error", MB_OK);
        return false;
    }

    // After all the data is loaded we can go through and load all the textures.
    LoadModelTextures(strPath);

    // The model was loaded correctly so return true
    return true;
}


///////////////////////////////// LOAD MODEL TEXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This loads the textures for the model in a specified directory
/////
///////////////////////////////// LOAD MODEL TEXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CModelGTF::LoadModelTextures(LPSTR strPath)
{
    // Grab a pointer to the model
    t3DModel *pModel = &m_Model;

    // Go through all the materials that are assigned to this model
    for(int i = 0; i < (int)pModel->pMaterials.size(); i++)
    {
        // Check to see if there is a file name to load in this material
        if(strlen(pModel->pMaterials[i].strFile) > 0)
        {
            // Create a boolean to tell us if we have a new texture to load
            bool bNewTexture = true;

            // Go through all the textures in our string list to see if it's already loaded
            for(unsigned int j = 0; j < strTextures.size(); j++)
            {
                string strTemp = pModel->pMaterials[i].strFile;
                string strTemp2 = strTextures[j];

                // If the texture name is already in our list of texture, don't load it again.
                if(strstr(pModel->pMaterials[i].strFile, strTextures[j].c_str()) )
                {
                    // We don't need to load this texture since it's already loaded
                    bNewTexture = false;

                    // Assign the texture index to our current material textureID.
                    // This ID will then be used as an index into m_Textures[].
                    pModel->pMaterials[i].texureId = j;
                }
            }

            // Make sure before going any further that this is a new texture to be loaded
            if(bNewTexture == falsecontinue;
            
            char strFullPath[255] = {0};

            // Check if there is a valid path
            if(strlen(strPath) > 0)
            {
                // Add the file name and path together so we can load the texture
                sprintf(strFullPath, "%s\\%s", strPath, pModel->pMaterials[i].strFile);
            }
            else
            {
                // If there is no path, don't attach a directory to our file name
                sprintf(strFullPath, "%s", strPath, pModel->pMaterials[i].strFile);
            }

            // We pass in a reference to an index into our texture array member variable.
            CreateTexture(m_Textures[strTextures.size()], strFullPath);                                

            // Set the texture ID for this material by getting the current loaded texture count
            pModel->pMaterials[i].texureId = (int)strTextures.size();

            // Now we increase the loaded texture count by adding the texture name to our list
            strTextures.push_back(pModel->pMaterials[i].strFile);
        }
    }
}


//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////

///////////////////////////////// SET CURRENT TIME \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This sets time t for the interpolation between the current and next key frame
/////
///////////////////////////////// SET CURRENT TIME \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CModelGTF::SetCurrentTime()
{
    float elapsedTime   = 0.0f;

    // This function is very similar to finding the frames per second.
    // Instead of checking when we reach a second, we check if we reach
    // 1 second / our animation speed. (1000 ms / animationSpeed).
    // That's how we know when we need to switch to the next key frame.
    // In the process, we get the t value for how far we are at to going to the
    // next animation key frame.  We use time to do the interpolation, that way
    // it runs the same speed on any persons computer, regardless of their specs.
    // It might look choppier on a junky computer, but the key frames still be
    // changing the same time as the other persons, it will just be not as smooth
    // of a transition between each frame.  The more frames per second we get, the
    // smoother the animation will be.  Since we are working with multiple models 
    // we don't want to create static variables, so the t and elapsedTime data are 
    // stored in the model's structure.

    // Get the current time in milliseconds
    float time = (float)GetTickCount();

    // Find the time that has elapsed since the last time that was stored
    elapsedTime = time - m_Model.lastTime;

    // Store the animation speed for this animation in a local variable
    float animationSpeed = (float)m_Model.animSpeed;

    // To find the current t we divide the elapsed time by the ratio of:
    //
    // (1_second / the_animation_frames_per_second)
    //
    // Since we are dealing with milliseconds, we need to use 1000
    // milliseconds instead of 1 because we are using GetTickCount(), which is in 
    // milliseconds. 1 second == 1000 milliseconds.  The t value is a value between 
    // 0 to 1.  It is used to tell us how far we are from the current key frame to 
    // the next key frame.
    float t = elapsedTime / (1000.0f / animationSpeed);
    
    // If our elapsed time goes over the desired time segment, start over and go 
    // to the next key frame.
    if (elapsedTime >= (1000.0f / animationSpeed) )
    {
        // Set our current frame to the next key frame (which could be the start of the anim)
        m_Model.currentFrame = m_Model.nextFrame;
        m_Model.nextFrame = (m_Model.currentFrame + 1) % m_Model.pObject[0].numFrames;

        // Set our last time for the model to the current time
        m_Model.lastTime = time;

        t = 0.0f;
    }

    // Set the t for the model to be used in interpolation
    m_Model.t = t;
}


///////////////////////////////// UPDATE MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This sets the current frame of animation, depending on it's FPS and t
/////
///////////////////////////////// UPDATE MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 

void CModelGTF::UpdateModel()
{
    // Grab a pointer to the model
    t3DModel *pModel = &m_Model;

    // Make sure there are objects and animation speed in the model
    if(!pModel->pObject.size() || pModel->animSpeed <= 0) return;

    // Initialize a start and end frame.  In this tutorial we don't add a way
    // to keep track of each animation, so we just play all of the animations
    // in a sequence.  If we wanted to only play certain animations the start
    // frame and end frame would need to be changed for each animation.
    int startFrame = 0;
    int endFrame   = pModel->pObject[0].numFrames;
    
    // This gives us the next frame we are going to.  We mod the current frame plus
    // 1 by the current animations end frame to make sure the next frame is valid.
    pModel->nextFrame = (pModel->currentFrame + 1) % endFrame;

    // If the next frame is zero, that means that we need to start the animation over.
    // To do this, we set nextFrame to the starting frame of this animation.
    if(pModel->nextFrame == 0) 
        pModel->nextFrame =  startFrame;

    // Next, we want to get the current time that we are interpolating by.  Remember,
    // if t = 0 then we are at the beginning of the animation, where if t = 1 we are at the end.
    // Anything from 0 to 1 can be thought of as a percentage from 0 to 100 percent complete.
    SetCurrentTime();
}

//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////


///////////////////////////////// DRAW MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This recursively draws all the character nodes, starting with the legs.
/////
///////////////////////////////// DRAW MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CModelGTF::DrawModel()
{


//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////

    // Before we render the model we need to update the animation frames
    UpdateModel();

//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////


    // Render the model and all it's objects
    RenderModel();
}


///////////////////////////////// RENDER MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////   This goes through all the meshes and renders them to the screen
/////
///////////////////////////////// RENDER MODEL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CModelGTF::RenderModel()
{
    // Go through all of the objects stored in this model
    for(int i = 0; i < m_Model.numOfObjects; i++)
    {
        // Get the current object that we are displaying
        t3DObject *pObject = &m_Model.pObject[i];
    &nb