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


TerrainNodeClass::TerrainNodeClass()
{
}


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


TerrainNodeClass::~TerrainNodeClass()
{
}


void TerrainNodeClass::Initialize(OpenGLClass* OpenGL, void* terrainModelPtr, int terrainWidth, int nodeWidth, int nodeHeight, int nodeIndexX, int nodeIndexY)
{
    VertexType* terrainModel;


    // Coerce the pointer to the terrain model data into the vertex type of the model.
	terrainModel = (VertexType*)terrainModelPtr;

    // Copy just the node subset from the terrain model data.
	InitializeBuffers(OpenGL, terrainModel, terrainWidth, nodeWidth, nodeHeight, nodeIndexX, nodeIndexY);

    // Release the pointer to the terrain model.
	terrainModel = 0;

	// Build the debug lines for the node's bounding box.
	BuildLines(OpenGL);

    return;
}


void TerrainNodeClass::Shutdown(OpenGLClass* OpenGL)
{
    // Release the debug bounding box lines rendering buffers.
    ReleaseLines(OpenGL);

    // Release the buffers used to render the terrain node.
    ShutdownBuffers(OpenGL);

    return;
}


void TerrainNodeClass::Render(OpenGLClass* OpenGL)
{
    // Render the terrain node's buffers.
    RenderBuffers(OpenGL);

    return;
}


void TerrainNodeClass::InitializeBuffers(OpenGLClass* OpenGL, VertexType* terrainModel, int terrainWidth, int nodeHeight, int nodeWidth, int nodeIndexX, int nodeIndexY)
{
    VertexType* vertices;
    unsigned int* indices;
    int vertexCount, modelIndex, index, i, j;


    // Calculate the number of vertices in this terrain node.
	vertexCount = (nodeHeight - 1) * (nodeWidth - 1) * 6;

	// Set the index count to the same as the vertex count.
	m_indexCount = vertexCount;

	// Create the vertex array.
	vertices = new VertexType[vertexCount];

	// Create the index array.
	indices = new unsigned int[m_indexCount];

	// Setup the indexes into the terrain model data we are using to build this smaller node subset.
	modelIndex = ((nodeIndexX * (nodeWidth - 1)) + (nodeIndexY * (nodeHeight - 1) * (terrainWidth - 1))) * 6;

	// Load the vertex array and index array with data.
	index = 0;
	for(j=0; j<(nodeHeight - 1); j++)
	{
		for(i=0; i<((nodeWidth - 1) * 6); i++)
		{
            vertices[index].x  = terrainModel[modelIndex].x;
            vertices[index].y  = terrainModel[modelIndex].y;
            vertices[index].z  = terrainModel[modelIndex].z;
            vertices[index].tu = terrainModel[modelIndex].tu;
            vertices[index].tv = terrainModel[modelIndex].tv;
            vertices[index].nx = terrainModel[modelIndex].nx;
            vertices[index].ny = terrainModel[modelIndex].ny;
            vertices[index].nz = terrainModel[modelIndex].nz;
            vertices[index].r  = terrainModel[modelIndex].r;
            vertices[index].g  = terrainModel[modelIndex].g;
            vertices[index].b  = terrainModel[modelIndex].b;
            vertices[index].tx = terrainModel[modelIndex].tx;
            vertices[index].ty = terrainModel[modelIndex].ty;
            vertices[index].tz = terrainModel[modelIndex].tz;
            vertices[index].bx = terrainModel[modelIndex].bx;
            vertices[index].by = terrainModel[modelIndex].by;
            vertices[index].bz = terrainModel[modelIndex].bz;
			indices[index] = index;
			modelIndex++;
			index++;
		}
		modelIndex += (terrainWidth * 6) - (nodeWidth * 6);
	}

    // Allocate an OpenGL vertex array object.
    OpenGL->glGenVertexArrays(1, &m_vertexArrayId);

    // Bind the vertex array object to store all the buffers and vertex attributes we create here.
    OpenGL->glBindVertexArray(m_vertexArrayId);

    // Generate an ID for the vertex buffer.
    OpenGL->glGenBuffers(1, &m_vertexBufferId);

    // Bind the vertex buffer and load the vertex data into the vertex buffer.
    OpenGL->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
    OpenGL->glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexType), vertices, GL_STATIC_DRAW);

    // Enable the vertex array attributes.
    OpenGL->glEnableVertexAttribArray(0);  // Vertex position.
    OpenGL->glEnableVertexAttribArray(1);  // Texture coordinates.
    OpenGL->glEnableVertexAttribArray(2);  // Normals.
    OpenGL->glEnableVertexAttribArray(3);  // Tangent
    OpenGL->glEnableVertexAttribArray(4);  // Binormal
    OpenGL->glEnableVertexAttribArray(5);  // Color.

    // Specify the location and format of the position portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexType), 0);

    // Specify the location and format of the texture portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(VertexType), (unsigned char*)NULL + (3 * sizeof(float)));

    // Specify the location and format of the normal vector portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(VertexType), (unsigned char*)NULL + (5 * sizeof(float)));

    // Specify the location and format of the tangent vector portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(3, 3, GL_FLOAT, false, sizeof(VertexType), (unsigned char*)NULL + (8 * sizeof(float)));

    // Specify the location and format of the binormal vector portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(4, 3, GL_FLOAT, false, sizeof(VertexType), (unsigned char*)NULL + (11 * sizeof(float)));

    // Specify the location and format of the color vector portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(5, 3, GL_FLOAT, false, sizeof(VertexType), (unsigned char*)NULL + (14 * sizeof(float)));

    // Generate an ID for the index buffer.
    OpenGL->glGenBuffers(1, &m_indexBufferId);

    // Bind the index buffer and load the index data into it.
    OpenGL->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferId);
    OpenGL->glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount* sizeof(unsigned int), indices, GL_STATIC_DRAW);

    // Calculate the node dimensions so we can build bounding box lines.
    m_maxX = vertices[0].x;
    m_minX = vertices[0].x;
    m_maxY = vertices[0].y;
    m_minY = vertices[0].y;
    m_maxZ = vertices[0].z;
    m_minZ = vertices[0].z;

    for(i=1; i<vertexCount; i++)
    {
        if(vertices[i].x > m_maxX)  { m_maxX = vertices[i].x; }
        if(vertices[i].x < m_minX)  { m_minX = vertices[i].x; }

        if(vertices[i].y > m_maxY)  { m_maxY = vertices[i].y; }
        if(vertices[i].y < m_minY)  { m_minY = vertices[i].y; }

        if(vertices[i].z > m_maxZ)  { m_maxZ = vertices[i].z; }
        if(vertices[i].z < m_minZ)  { m_minZ = vertices[i].z; }
    }

    // Now that the buffers have been loaded we can release the array data.
    delete [] vertices;
    vertices = 0;

    delete [] indices;
    indices = 0;

    return;
}


void TerrainNodeClass::ShutdownBuffers(OpenGLClass* OpenGL)
{
    // Release the vertex array object.
    OpenGL->glBindVertexArray(0);
    OpenGL->glDeleteVertexArrays(1, &m_vertexArrayId);

    // Release the vertex buffer.
    OpenGL->glBindBuffer(GL_ARRAY_BUFFER, 0);
    OpenGL->glDeleteBuffers(1, &m_vertexBufferId);

    // Release the index buffer.
    OpenGL->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    OpenGL->glDeleteBuffers(1, &m_indexBufferId);

    return;
}


void TerrainNodeClass::RenderBuffers(OpenGLClass* OpenGL)
{
    // Bind the vertex array object that stored all the information about the vertex and index buffers.
    OpenGL->glBindVertexArray(m_vertexArrayId);

    // Render the vertex buffer as triangles using the index buffer.
    glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, 0);

    return;
}


void TerrainNodeClass::BuildLines(OpenGLClass* OpenGL)
{
    LineVertexType* vertices;
    unsigned int* indices;
    float red, green, blue;
    int vertexCount2, index;


    // Set the color of the bound box lines to yellow.
    red = 1.0f;
    green = 1.0f;
    blue = 0.0f;

    // Set the number of vertices for the 12 lines in the box.
    vertexCount2 = 24;

    // Set the index count to the same as the vertex count.
    m_indexCount2 = vertexCount2;

    // Create the vertex array.
    vertices = new LineVertexType[vertexCount2];

    // Create the index array.
    indices = new unsigned int[m_indexCount2];

    // Initialize the index into the vertices.
    index = 0;

    // Base 4 lines.
    vertices[index].x = m_minX;
    vertices[index].y = m_minY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_minY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_minY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_minY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_minY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_minY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_minY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_minY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    // Top 4 lines.
    vertices[index].x = m_minX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    // Vertical 4 lines.
    vertices[index].x = m_minX;
    vertices[index].y = m_minY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_minY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_minZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_minY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_minX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_minY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    vertices[index].x = m_maxX;
    vertices[index].y = m_maxY;
    vertices[index].z = m_maxZ;
    vertices[index].r = red;
    vertices[index].g = green;
    vertices[index].b = blue;
    indices[index] = index;
    index++;

    // Allocate an OpenGL vertex array object.
    OpenGL->glGenVertexArrays(1, &m_vertexArrayId2);

    // Bind the vertex array object to store all the buffers and vertex attributes we create here.
    OpenGL->glBindVertexArray(m_vertexArrayId2);

    // Generate an ID for the vertex buffer.
    OpenGL->glGenBuffers(1, &m_vertexBufferId2);

    // Bind the vertex buffer and load the vertex data into the vertex buffer.
    OpenGL->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId2);
    OpenGL->glBufferData(GL_ARRAY_BUFFER, vertexCount2 * sizeof(LineVertexType), vertices, GL_STATIC_DRAW);

    // Enable the two vertex array attributes.
    OpenGL->glEnableVertexAttribArray(0);  // Vertex position.
    OpenGL->glEnableVertexAttribArray(1);  // Vertex color.

    // Specify the location and format of the position portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(LineVertexType), 0);

    // Specify the location and format of the color portion of the vertex buffer.
    OpenGL->glVertexAttribPointer(1, 3, GL_FLOAT, false, sizeof(LineVertexType), (unsigned char*)NULL + (3 * sizeof(float)));

    // Generate an ID for the index buffer.
    OpenGL->glGenBuffers(1, &m_indexBufferId2);

    // Bind the index buffer and load the index data into it.
    OpenGL->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferId2);
    OpenGL->glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount2 * sizeof(unsigned int), indices, GL_STATIC_DRAW);

    // Now that the buffers have been loaded we can release the array data.
    delete [] vertices;
    vertices = 0;

    delete [] indices;
    indices = 0;

    return;
}


void TerrainNodeClass::ReleaseLines(OpenGLClass* OpenGL)
{
    // Release the vertex array object.
    OpenGL->glBindVertexArray(0);
    OpenGL->glDeleteVertexArrays(1, &m_vertexArrayId2);

    // Release the vertex buffer.
    OpenGL->glBindBuffer(GL_ARRAY_BUFFER, 0);
    OpenGL->glDeleteBuffers(1, &m_vertexBufferId2);

    // Release the index buffer.
    OpenGL->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    OpenGL->glDeleteBuffers(1, &m_indexBufferId2);

    return;
}


void TerrainNodeClass::RenderLines(OpenGLClass* OpenGL)
{
    // Bind the vertex array object that stored all the information about the vertex and index buffers.
    OpenGL->glBindVertexArray(m_vertexArrayId2);

    // Render the vertex buffer as lines using the index buffer.
    glDrawElements(GL_LINES, m_indexCount2, GL_UNSIGNED_INT, 0);

    return;
}
