///////////////////////////////////////////////////////////////////////////////
// Filename: terrainclass.cpp
///////////////////////////////////////////////////////////////////////////////
#include "terrainclass.h"


TerrainClass::TerrainClass()
{
    m_heightMap = 0;
    m_terrainModel = 0;
    m_Nodes = 0;
    m_Texture = 0;
    m_NormalMap = 0;
    m_QuadTree = 0;
    m_renderList = 0;
}


TerrainClass::TerrainClass(const TerrainClass& other)
{
}


TerrainClass::~TerrainClass()
{
}


bool TerrainClass::Initialize(OpenGLClass* OpenGL, char* setupFilename)
{
    char terrainFilename[256], textureFilename[256], colorMapFilename[256], normalFilename[256];
    float heightScale;
    bool result;


    // Get the terrain filename, dimensions, and so forth from the setup file.
    result = LoadSetupFile(setupFilename, terrainFilename, heightScale, textureFilename, colorMapFilename, normalFilename);
    if(!result)
    {
        return false;
    }

    // Initialize the terrain height map with the data from the raw file.
    result = LoadRawHeightMap(terrainFilename);
    if(!result)
    {
        return false;
    }

    // Setup the X and Z coordinates for the height map as well as scale the terrain height by the height scale value.
    SetTerrainCoordinates(heightScale);

    // Calculate the normals for the terrain data.
    CalculateNormals();

    // Load in the color map for the terrain.
    result = LoadColorMap(colorMapFilename);
    if(!result)
    {
        return false;
    }

    // Now build the 3D model of the terrain.
    BuildTerrainModel();

    // We can now release the height map since it is no longer needed in memory once the 3D terrain model has been built.
    ReleaseHeightMap();

    // Calculate the tangent and binormal for the terrain model.
    CalculateTerrainVectors();

    // Calculate the dimensions of the mesh for the quad tree's usage.
    CalculateMeshDimensions();

    // Copy the terrain model data into the individual terrain nodes.
    result = LoadTerrainNodes(OpenGL);
    if(!result)
    {
        return false;
    }

    // Release the terrain model now that the nodes have been loaded.
    ReleaseTerrainModel();

    // Create and initialize the diffuse texture object.
    m_Texture = new TextureClass;

    result = m_Texture->Initialize(OpenGL, textureFilename, false);
    if(!result)
    {
        return false;
    }

    // Create and initialize the normal map texture object.
    m_NormalMap = new TextureClass;

    result = m_NormalMap->Initialize(OpenGL, normalFilename, false);
    if(!result)
    {
        return false;
    }

    return true;
}


void TerrainClass::Shutdown(OpenGLClass* OpenGL)
{
    // Release the normal map texture object.
    if(m_NormalMap)
    {
        m_NormalMap->Shutdown();
        delete m_NormalMap;
        m_NormalMap = 0;
    }

    // Release the diffuse texture object.
    if(m_Texture)
    {
        m_Texture->Shutdown();
        delete m_Texture;
        m_Texture = 0;
    }

    // Release the terrain nodes.
    ReleaseTerrainNodes(OpenGL);

    return;
}


bool TerrainClass::Render(OpenGLClass* OpenGL, ShaderManagerClass* ShaderManager, LightClass* Light, FrustumClass* Frustum, float* worldMatrix, float* viewMatrix, float* projectionMatrix)
{
    float diffuseLightColor[4], lightDirection[3];
    int i, j, renderCount;
    bool result, wireFrame, renderNodeLines;


    // Get the light properties.
    Light->GetDirection(lightDirection);
    Light->GetDiffuseColor(diffuseLightColor);

    // Set wireframe mode off.
    wireFrame = false;

    // Set node bounding box line rendering on.
    renderNodeLines = true;

    // Enable wireframe mode to see triangles composing the terrain clearly.
    if(wireFrame)
    {
        OpenGL->EnableWireframe();
    }

    // Get the list of nodes to render.
    m_QuadTree->Render(Frustum, m_minHeight, m_maxHeight);
    m_QuadTree->GetRenderList(m_renderList, renderCount);

    // Set the terrain shader as the current shader program and set the matrices that it will use for rendering.
    result = ShaderManager->RenderTerrainShader(worldMatrix, viewMatrix, projectionMatrix, lightDirection, diffuseLightColor);
    if(!result)
    {
        return false;
    }

    // Set the diffuse texture for the terrain in the pixel shader texture unit 0.
    m_Texture->SetTexture(OpenGL, 0);

    // Set the normal map texture for the terrain in the pixel shader texture unit 1.
    m_NormalMap->SetTexture(OpenGL, 1);

    // Put the node vertex and index buffers on the graphics pipeline to prepare them for drawing.
    for(i=0; i<renderCount; i++)
    {
        // Only render the list of nodes that the quad tree determined are visible.
        j = m_renderList[i];
        m_Nodes[j].Render(OpenGL);
    }

    // Disable wireframe mode after rendering terrain.
    if(wireFrame)
    {
        OpenGL->DisableWireframe();
    }

    // Render the node bounding box lines.
    if(renderNodeLines)
    {
        result = ShaderManager->RenderColorShader(worldMatrix, viewMatrix, projectionMatrix);
        if(!result)
        {
            return false;
        }

        for(i=0; i<renderCount; i++)
        {
            // Only render the node bounding boxes that the quad tree determined are visible.
            j = m_renderList[i];
            m_Nodes[j].RenderLines(OpenGL);
        }
    }

    return true;
}


bool TerrainClass::LoadSetupFile(char* filename, char* terrainFilename, float& heightScale, char* textureFilename, char* colorMapFilename, char* normalFilename)
{
    ifstream fin;
    char input;


    // Open the setup file.  If it could not open the file then exit.
    fin.open(filename);
    if(fin.fail())
    {
        return false;
    }

    // Read up to the terrain file name.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }

    // Read in the terrain file name.
    fin >> terrainFilename;

    // Read up to the color map file name.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }

    // Read in the color map file name.
    fin >> colorMapFilename;

    // Read up to the value of terrain height.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }

    // Read in the terrain height.
    fin >> m_terrainHeight;

    // Read up to the value of terrain width.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }

    // Read in the terrain width.
    fin >> m_terrainWidth;

    // Read up to the value of terrain height scaling.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }

    // Read in the terrain height scaling.
    fin >> heightScale;

    // Read up to the texture file name.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }

    // Read in the texture file name.
    fin >> textureFilename;

    // Read up to the normal map texture file name.
    fin.get(input);
    while(input != ':')
    {
        fin.get(input);
    }

    // Read in the normal map file name.
    fin >> normalFilename;

    // Close the setup file.
    fin.close();

    return true;
}


bool TerrainClass::LoadRawHeightMap(char* terrainFilename)
{
    FILE* filePtr;
    unsigned short* rawImage;
    unsigned int imageSize, count;
    int error, i, j, index;


    // Create the float array to hold the height map data.
    m_heightMap = new HeightMapType[m_terrainWidth * m_terrainHeight];

    // Open the 16 bit raw height map file for reading in binary.
    filePtr = fopen(terrainFilename, "rb");
    if(filePtr == NULL)
    {
        return false;
    }

    // Calculate the size of the raw image data.
    imageSize = m_terrainHeight * m_terrainWidth;

    // Allocate memory for the raw image data.
    rawImage = new unsigned short[imageSize];

    // Read in the raw image data.
    count = fread(rawImage, sizeof(unsigned short), imageSize, filePtr);
    if(count != imageSize)
    {
        return false;
    }

    // Close the file.
    error = fclose(filePtr);
    if(error != 0)
    {
        return false;
    }

    // Copy the image data into the height map array.
    for(j=0; j<m_terrainHeight; j++)
    {
        for(i=0; i<m_terrainWidth; i++)
        {
            index = (m_terrainWidth * j) + i;

            // Store the height at this point in the height map array.
            m_heightMap[index].y = (float)rawImage[index];
        }
    }

    // Release the bitmap image data.
    delete [] rawImage;
    rawImage = 0;

    return true;
}


void TerrainClass::SetTerrainCoordinates(float heightScale)
{
    int i, j, index;


    // Loop through all the elements in the height map array and adjust their coordinates correctly.
    for(j=0; j<m_terrainHeight; j++)
    {
        for(i=0; i<m_terrainWidth; i++)
        {
            index = (m_terrainWidth * j) + i;

            // Set the X and Z coordinates.
            m_heightMap[index].x = (float)i;
            m_heightMap[index].z = -(float)j;

            // Move the terrain depth into the positive range.  For example from (0, -256) to (256, 0).
            m_heightMap[index].z += (float)(m_terrainHeight - 1);

            // Scale the height.
            m_heightMap[index].y /= heightScale;
        }
    }

    return;
}


void TerrainClass::CalculateNormals()
{
    int i, j, index1, index2, index3, index;
    float vertex1[3], vertex2[3], vertex3[3], vector1[3], vector2[3], sum[3], length;
    VectorType* normals;


    // Create a temporary array to hold the face normal vectors.
    normals = new VectorType[(m_terrainHeight-1) * (m_terrainWidth-1)];

    // Go through all the faces in the mesh and calculate their normals.
    for(j=0; j<(m_terrainHeight-1); j++)
    {
        for(i=0; i<(m_terrainWidth-1); i++)
        {
            index1 = ((j+1) * m_terrainWidth) + i;      // Bottom left vertex.
            index2 = ((j+1) * m_terrainWidth) + (i+1);  // Bottom right vertex.
            index3 = (j * m_terrainWidth) + i;          // Upper left vertex.

            // Get three vertices from the face.
            vertex1[0] = m_heightMap[index1].x;
            vertex1[1] = m_heightMap[index1].y;
            vertex1[2] = m_heightMap[index1].z;

            vertex2[0] = m_heightMap[index2].x;
            vertex2[1] = m_heightMap[index2].y;
            vertex2[2] = m_heightMap[index2].z;

            vertex3[0] = m_heightMap[index3].x;
            vertex3[1] = m_heightMap[index3].y;
            vertex3[2] = m_heightMap[index3].z;

            // Calculate the two vectors for this face.
            vector1[0] = vertex1[0] - vertex3[0];
            vector1[1] = vertex1[1] - vertex3[1];
            vector1[2] = vertex1[2] - vertex3[2];
            vector2[0] = vertex3[0] - vertex2[0];
            vector2[1] = vertex3[1] - vertex2[1];
            vector2[2] = vertex3[2] - vertex2[2];

            index = (j * (m_terrainWidth - 1)) + i;

            // Calculate the cross product of those two vectors to get the un-normalized value for this face normal.
            normals[index].x = (vector1[1] * vector2[2]) - (vector1[2] * vector2[1]);
            normals[index].y = (vector1[2] * vector2[0]) - (vector1[0] * vector2[2]);
            normals[index].z = (vector1[0] * vector2[1]) - (vector1[1] * vector2[0]);

            // Calculate the length.
            length = (float)sqrt((normals[index].x * normals[index].x) + (normals[index].y * normals[index].y) +
                     (normals[index].z * normals[index].z));

            // Normalize the final value for this face using the length.
            normals[index].x = (normals[index].x / length);
            normals[index].y = (normals[index].y / length);
            normals[index].z = (normals[index].z / length);
        }
    }

    // Now go through all the vertices and take a sum of the face normals that touch this vertex.
    for(j=0; j<m_terrainHeight; j++)
    {
        for(i=0; i<m_terrainWidth; i++)
        {
            // Initialize the sum.
            sum[0] = 0.0f;
            sum[1] = 0.0f;
            sum[2] = 0.0f;

            // Bottom left face.
            if(((i-1) >= 0) && ((j-1) >= 0))
            {
                index = ((j-1) * (m_terrainWidth-1)) + (i-1);

                sum[0] += normals[index].x;
                sum[1] += normals[index].y;
                sum[2] += normals[index].z;
            }

            // Bottom right face.
            if((i<(m_terrainWidth-1)) && ((j-1) >= 0))
            {
                index = ((j - 1) * (m_terrainWidth - 1)) + i;

                sum[0] += normals[index].x;
                sum[1] += normals[index].y;
                sum[2] += normals[index].z;
            }

            // Upper left face.
            if(((i-1) >= 0) && (j<(m_terrainHeight-1)))
            {
                index = (j * (m_terrainWidth-1)) + (i-1);

                sum[0] += normals[index].x;
                sum[1] += normals[index].y;
                sum[2] += normals[index].z;
            }

            // Upper right face.
            if((i < (m_terrainWidth-1)) && (j < (m_terrainHeight-1)))
            {
                index = (j * (m_terrainWidth-1)) + i;

                sum[0] += normals[index].x;
                sum[1] += normals[index].y;
                sum[2] += normals[index].z;
            }

            // Calculate the length of this normal.
            length = (float)sqrt((sum[0] * sum[0]) + (sum[1] * sum[1]) + (sum[2] * sum[2]));

            // Get an index to the vertex location in the height map array.
            index = (j * m_terrainWidth) + i;

            // Normalize the final shared normal for this vertex and store it in the height map array.
            m_heightMap[index].nx = (sum[0] / length);
            m_heightMap[index].ny = (sum[1] / length);
            m_heightMap[index].nz = (sum[2] / length);
        }
    }

    // Release the temporary normals.
    delete [] normals;
    normals = 0;

    return;
}


bool TerrainClass::LoadColorMap(char* colorMapFilename)
{
    FILE* filePtr;
    TargaHeader targaFileHeader;
    unsigned char* targaImage;
    unsigned long count, imageSize;
    int height, width, bpp, error, i, j, k, index;


    // Open the color map file in binary.
    filePtr = fopen(colorMapFilename, "rb");
    if(filePtr == NULL)
    {
        return false;
    }

    // Read in the file header.
    count = fread(&targaFileHeader, sizeof(TargaHeader), 1, filePtr);
    if(count != 1)
    {
        return false;
    }

    // Get the important information from the header.
    width = (int)targaFileHeader.width;
    height = (int)targaFileHeader.height;
    bpp = (int)targaFileHeader.bpp;

    // Make sure the color map dimensions are the same as the terrain dimensions for easy 1 to 1 mapping.
    if((height != m_terrainHeight) || (width != m_terrainWidth))
    {
        return false;
    }

    // Make sure we are dealing with 24 bit color maps.
    if(bpp != 24)
    {
        return false;
    }

    // Calculate the size of the 24 bit targa image data.
    imageSize = width * height * 3;

    // Allocate memory for the targa image data.
    targaImage = new unsigned char[imageSize];

    // Read in the targa image data.
    count = fread(targaImage, 1, imageSize, filePtr);
    if(count != imageSize)
    {
        return false;
    }

    // Close the file.
    error = fclose(filePtr);
    if(error != 0)
    {
        return false;
    }

    // Initialize the position in the targa image data buffer.
    k=0;

    // Read the image data into the color map portion of the height map structure.
    for(j=0; j<m_terrainHeight; j++)
    {
        for(i=0; i<m_terrainWidth; i++)
        {
            // Targa are saved upside down by default in most image editors, so load bottom to top into the array.
            index = (m_terrainWidth * (m_terrainHeight - 1 - j)) + i;

            m_heightMap[index].r = (float)targaImage[k+2] / 255.0f;
            m_heightMap[index].g = (float)targaImage[k+1] / 255.0f;
            m_heightMap[index].b = (float)targaImage[k+0] / 255.0f;
            k+=3;
        }
    }

    // Release the targa image data.
    delete [] targaImage;
    targaImage = 0;

    return true;
}


void TerrainClass::BuildTerrainModel()
{
    int i, j, index, index1, index2, index3, index4;


    // Calculate the number of vertices in the 3D terrain model.
    m_vertexCount = (m_terrainHeight - 1) * (m_terrainWidth - 1) * 6;

    // Create the 3D terrain model array.
    m_terrainModel = new ModelType[m_vertexCount];

    // Initialize the index into the height map array.
    index = 0;

    // Load the 3D terrain model with the height map terrain data.
    // We will be creating 2 triangles for each of the four points in a quad.
    for(j=0; j<(m_terrainHeight-1); j++)
    {
        for(i=0; i<(m_terrainWidth-1); i++)
        {
            // Get the indexes to the four points of the quad.
            index1 = (m_terrainWidth * j) + i;          // Upper left.
            index2 = (m_terrainWidth * j) + (i+1);      // Upper right.
            index3 = (m_terrainWidth * (j+1)) + i;      // Bottom left.
            index4 = (m_terrainWidth * (j+1)) + (i+1);  // Bottom right.

            // Now create two triangles for that quad.
            // Triangle 1 - Upper left.
            m_terrainModel[index].x = m_heightMap[index1].x;
            m_terrainModel[index].y = m_heightMap[index1].y;
            m_terrainModel[index].z = m_heightMap[index1].z;
            m_terrainModel[index].tu = 0.0f;
            m_terrainModel[index].tv = 1.0f;
            m_terrainModel[index].nx = m_heightMap[index1].nx;
            m_terrainModel[index].ny = m_heightMap[index1].ny;
            m_terrainModel[index].nz = m_heightMap[index1].nz;
            m_terrainModel[index].r = m_heightMap[index1].r;
            m_terrainModel[index].g = m_heightMap[index1].g;
            m_terrainModel[index].b = m_heightMap[index1].b;
            index++;

            // Triangle 1 - Upper right.
            m_terrainModel[index].x = m_heightMap[index2].x;
            m_terrainModel[index].y = m_heightMap[index2].y;
            m_terrainModel[index].z = m_heightMap[index2].z;
            m_terrainModel[index].tu = 1.0f;
            m_terrainModel[index].tv = 1.0f;
            m_terrainModel[index].nx = m_heightMap[index2].nx;
            m_terrainModel[index].ny = m_heightMap[index2].ny;
            m_terrainModel[index].nz = m_heightMap[index2].nz;
            m_terrainModel[index].r = m_heightMap[index2].r;
            m_terrainModel[index].g = m_heightMap[index2].g;
            m_terrainModel[index].b = m_heightMap[index2].b;
            index++;

            // Triangle 1 - Bottom left.
            m_terrainModel[index].x = m_heightMap[index3].x;
            m_terrainModel[index].y = m_heightMap[index3].y;
            m_terrainModel[index].z = m_heightMap[index3].z;
            m_terrainModel[index].tu = 0.0f;
            m_terrainModel[index].tv = 0.0f;
            m_terrainModel[index].nx = m_heightMap[index3].nx;
            m_terrainModel[index].ny = m_heightMap[index3].ny;
            m_terrainModel[index].nz = m_heightMap[index3].nz;
            m_terrainModel[index].r = m_heightMap[index3].r;
            m_terrainModel[index].g = m_heightMap[index3].g;
            m_terrainModel[index].b = m_heightMap[index3].b;
            index++;

            // Triangle 2 - Bottom left.
            m_terrainModel[index].x = m_heightMap[index3].x;
            m_terrainModel[index].y = m_heightMap[index3].y;
            m_terrainModel[index].z = m_heightMap[index3].z;
            m_terrainModel[index].tu = 0.0f;
            m_terrainModel[index].tv = 0.0f;
            m_terrainModel[index].nx = m_heightMap[index3].nx;
            m_terrainModel[index].ny = m_heightMap[index3].ny;
            m_terrainModel[index].nz = m_heightMap[index3].nz;
            m_terrainModel[index].r = m_heightMap[index3].r;
            m_terrainModel[index].g = m_heightMap[index3].g;
            m_terrainModel[index].b = m_heightMap[index3].b;
            index++;

            // Triangle 2 - Upper right.
            m_terrainModel[index].x = m_heightMap[index2].x;
            m_terrainModel[index].y = m_heightMap[index2].y;
            m_terrainModel[index].z = m_heightMap[index2].z;
            m_terrainModel[index].tu = 1.0f;
            m_terrainModel[index].tv = 1.0f;
            m_terrainModel[index].nx = m_heightMap[index2].nx;
            m_terrainModel[index].ny = m_heightMap[index2].ny;
            m_terrainModel[index].nz = m_heightMap[index2].nz;
            m_terrainModel[index].r = m_heightMap[index2].r;
            m_terrainModel[index].g = m_heightMap[index2].g;
            m_terrainModel[index].b = m_heightMap[index2].b;
            index++;

            // Triangle 2 - Bottom right.
            m_terrainModel[index].x = m_heightMap[index4].x;
            m_terrainModel[index].y = m_heightMap[index4].y;
            m_terrainModel[index].z = m_heightMap[index4].z;
            m_terrainModel[index].tu = 1.0f;
            m_terrainModel[index].tv = 0.0f;
            m_terrainModel[index].nx = m_heightMap[index4].nx;
            m_terrainModel[index].ny = m_heightMap[index4].ny;
            m_terrainModel[index].nz = m_heightMap[index4].nz;
            m_terrainModel[index].r = m_heightMap[index4].r;
            m_terrainModel[index].g = m_heightMap[index4].g;
            m_terrainModel[index].b = m_heightMap[index4].b;
            index++;
        }
    }

    return;
}


void TerrainClass::ReleaseHeightMap()
{
    // Release the height map array.
    if(m_heightMap)
    {
        delete [] m_heightMap;
        m_heightMap = 0;
    }

    return;
}


void TerrainClass::ReleaseTerrainModel()
{
    // Release the terrain model data.
    if(m_terrainModel)
    {
        delete [] m_terrainModel;
        m_terrainModel = 0;
    }

    return;
}


void TerrainClass::CalculateTerrainVectors()
{
    int vertexCount, faceCount, i, index;
    TempVertexType vertex1, vertex2, vertex3;
    VectorType tangent, binormal;


    // Calculate the number of vertices in the terrain.
    vertexCount = (m_terrainHeight - 1) * (m_terrainWidth - 1) * 6;

    // Calculate the number of faces in the terrain model.
    faceCount = vertexCount / 3;

    // Initialize the index to the model data.
    index = 0;

    // Go through all the faces and calculate the the tangent, binormal, and normal vectors.
    for(i=0; i<faceCount; i++)
    {
        // Get the three vertices for this face from the terrain model.
        vertex1.x = m_terrainModel[index].x;
        vertex1.y = m_terrainModel[index].y;
        vertex1.z = m_terrainModel[index].z;
        vertex1.tu = m_terrainModel[index].tu;
        vertex1.tv = m_terrainModel[index].tv;
        vertex1.nx = m_terrainModel[index].nx;
        vertex1.ny = m_terrainModel[index].ny;
        vertex1.nz = m_terrainModel[index].nz;
        index++;

        vertex2.x = m_terrainModel[index].x;
        vertex2.y = m_terrainModel[index].y;
        vertex2.z = m_terrainModel[index].z;
        vertex2.tu = m_terrainModel[index].tu;
        vertex2.tv = m_terrainModel[index].tv;
        vertex2.nx = m_terrainModel[index].nx;
        vertex2.ny = m_terrainModel[index].ny;
        vertex2.nz = m_terrainModel[index].nz;
        index++;

        vertex3.x = m_terrainModel[index].x;
        vertex3.y = m_terrainModel[index].y;
        vertex3.z = m_terrainModel[index].z;
        vertex3.tu = m_terrainModel[index].tu;
        vertex3.tv = m_terrainModel[index].tv;
        vertex3.nx = m_terrainModel[index].nx;
        vertex3.ny = m_terrainModel[index].ny;
        vertex3.nz = m_terrainModel[index].nz;
        index++;

        // Calculate the tangent and binormal of that face.
        CalculateTangentBinormal(vertex1, vertex2, vertex3, tangent, binormal);

        // Store the tangent and binormal for this face back in the model structure.
        m_terrainModel[index-1].tx = tangent.x;
        m_terrainModel[index-1].ty = tangent.y;
        m_terrainModel[index-1].tz = tangent.z;
        m_terrainModel[index-1].bx = binormal.x;
        m_terrainModel[index-1].by = binormal.y;
        m_terrainModel[index-1].bz = binormal.z;

        m_terrainModel[index-2].tx = tangent.x;
        m_terrainModel[index-2].ty = tangent.y;
        m_terrainModel[index-2].tz = tangent.z;
        m_terrainModel[index-2].bx = binormal.x;
        m_terrainModel[index-2].by = binormal.y;
        m_terrainModel[index-2].bz = binormal.z;

        m_terrainModel[index-3].tx = tangent.x;
        m_terrainModel[index-3].ty = tangent.y;
        m_terrainModel[index-3].tz = tangent.z;
        m_terrainModel[index-3].bx = binormal.x;
        m_terrainModel[index-3].by = binormal.y;
        m_terrainModel[index-3].bz = binormal.z;
    }

    return;
}


void TerrainClass::CalculateTangentBinormal(TempVertexType vertex1, TempVertexType vertex2, TempVertexType vertex3, VectorType& tangent, VectorType& binormal)
{
    float vector1[3], vector2[3];
    float tuVector[2], tvVector[2];
    float den;
    float length;


    // Calculate the two vectors for this face.
    vector1[0] = vertex2.x - vertex1.x;
    vector1[1] = vertex2.y - vertex1.y;
    vector1[2] = vertex2.z - vertex1.z;

    vector2[0] = vertex3.x - vertex1.x;
    vector2[1] = vertex3.y - vertex1.y;
    vector2[2] = vertex3.z - vertex1.z;

    // Calculate the tu and tv texture space vectors.
    tuVector[0] = vertex2.tu - vertex1.tu;
    tvVector[0] = vertex2.tv - vertex1.tv;

    tuVector[1] = vertex3.tu - vertex1.tu;
    tvVector[1] = vertex3.tv - vertex1.tv;

    // Calculate the denominator of the tangent/binormal equation.
    den = 1.0f / (tuVector[0] * tvVector[1] - tuVector[1] * tvVector[0]);

    // Calculate the cross products and multiply by the coefficient to get the tangent and binormal.
    tangent.x = (tvVector[1] * vector1[0] - tvVector[0] * vector2[0]) * den;
    tangent.y = (tvVector[1] * vector1[1] - tvVector[0] * vector2[1]) * den;
    tangent.z = (tvVector[1] * vector1[2] - tvVector[0] * vector2[2]) * den;

    binormal.x = (tuVector[0] * vector2[0] - tuVector[1] * vector1[0]) * den;
    binormal.y = (tuVector[0] * vector2[1] - tuVector[1] * vector1[1]) * den;
    binormal.z = (tuVector[0] * vector2[2] - tuVector[1] * vector1[2]) * den;

    // Calculate the length of the tangent.
    length = (float)sqrt((tangent.x * tangent.x) + (tangent.y * tangent.y) + (tangent.z * tangent.z));

    // Normalize the tangent and then store it.
    tangent.x = tangent.x / length;
    tangent.y = tangent.y / length;
    tangent.z = tangent.z / length;

    // Calculate the length of the binormal.
    length = (float)sqrt((binormal.x * binormal.x) + (binormal.y * binormal.y) + (binormal.z * binormal.z));

    // Normalize the binormal and then store it.
    binormal.x = binormal.x / length;
    binormal.y = binormal.y / length;
    binormal.z = binormal.z / length;

    return;
}


bool TerrainClass::LoadTerrainNodes(OpenGLClass* OpenGL)
{
    int nodeWidth, arrayHeight, arrayWidth, i, j, index;


    // Set the width of the node.  64 +1 (0-64) since dealing with 1025 sized terrain and need even division.
    nodeWidth = 65;

    // Set size of the node array that we are breaking the terrain up into.  Must alight with the terrain size and the node width.
    arrayHeight = 16;
    arrayWidth = 16;

    // Set the number of nodes in the 2D array.
    m_nodeCount = arrayHeight * arrayWidth;

    // Create the terrain node array.
	m_Nodes = new TerrainNodeClass[m_nodeCount];

	// Loop through and initialize all the terrain nodes.
	for(j=0; j<arrayHeight; j++)
	{
		for(i=0; i<arrayWidth; i++)
		{
			index = (arrayWidth * j) + i;

			m_Nodes[index].Initialize(OpenGL, m_terrainModel, m_terrainWidth, nodeWidth, nodeWidth, i, j);
		}
	}

	// Create and initialize the quad tree object.
    m_QuadTree = new QuadTreeClass;
    m_QuadTree->Initialize((m_terrainWidth-1), (nodeWidth - 1), 512.0f, 512.0f);

    // Create the render list for the quad tree.
    m_renderList = new int[m_nodeCount];

	return true;
}


void TerrainClass::ReleaseTerrainNodes(OpenGLClass* OpenGL)
{
    int i;


    // Release the render list.
    if(m_renderList)
    {
        delete [] m_renderList;
        m_renderList = 0;
    }

    // Release the quad tree object.
    if(m_QuadTree)
    {
        m_QuadTree->Shutdown();
        delete m_QuadTree;
        m_QuadTree = 0;
    }

	// Release the terrain node array.
	if(m_Nodes)
	{
		for(i=0; i<m_nodeCount; i++)
		{
			m_Nodes[i].Shutdown(OpenGL);
		}

		delete [] m_Nodes;
		m_Nodes = 0;
	}

	return;
}


void TerrainClass::CalculateMeshDimensions()
{
	int i;


	// Determine the minimum and maximum height of the terrain for the quad tree object.
	m_minHeight = m_terrainModel[0].y;
	m_maxHeight = m_terrainModel[0].y;

	for(i=1; i<m_vertexCount; i++)
    {
        if(m_terrainModel[i].y < m_minHeight)
        {
            m_minHeight = m_terrainModel[i].y;
        }

        if(m_terrainModel[i].y > m_maxHeight)
        {
            m_maxHeight = m_terrainModel[i].y;
        }
    }

	return;
}


void TerrainClass::GetNodesDrawn(int& nodesDrawn, int& nodesCulled)
{
    m_QuadTree->GetNodesDrawn(nodesDrawn, nodesCulled);
    return;
}
