#include "CooneyMesh.h"
#include "CooneyMath.h"
using namespace Cooney::Math;

// VBOs not always standard.
#include "GLee.h"

// File IO
#include <iostream>
#include <fstream>
using namespace std;

namespace Cooney
{
	Mesh::Mesh()
	{
		// Set all the VBO links to disabled.
		for(unsigned int i=0; i<VBO_NUM; ++i)
		{
			initiatedvbo[i] = false;
			vertexbufferobject[i] = 0;
			
			vboStyle[i] = VBO_STATIC;
		}
	}
	
	Mesh::~Mesh()
	{
		// Go through each VBO and pop it from OpenGL
		// if it's loaded.
		for(unsigned int i=0; i<VBO_NUM; ++i)
		{
			if( initiatedvbo[i] )
				glDeleteBuffers(1, &vertexbufferobject[i]);
		}
	}
	
	int Mesh::AddVertex(const MeshVertex &vertex)
	{
		int vertIndex = -1;
		
		// Looks for an existing matching vertex.
		vertIndex = FindVertex(vertex);
		
		// If the vertex does not exist, add it.
		if( vertIndex < 0 )
		{
			vertIndex = NumVertexes(); // Once we push_back(), this is the index of all the new data.
			
			positions.push_back(vertex.position);
			normals.push_back(vertex.normal);
			tangents.push_back(vertex.tangent);
			colors.push_back(vertex.color);
			texCoords.push_back(vertex.texCoord);
		}
		
		return vertIndex;
	}
	bool Mesh::SetVertex(const MeshVertex &vertex, const unsigned int vertIndex)
	{
		if( vertIndex >= NumVertexes() )
			return false; // Bad Index.

		positions[vertIndex] = vertex.position;
		normals[vertIndex] = vertex.normal;
		tangents[vertIndex] = vertex.tangent;
		colors[vertIndex] = vertex.color;
		texCoords[vertIndex] = vertex.texCoord;
		
		return true;
	}
	int Mesh::FindVertex(const MeshVertex &vertex)
	{
		// Look for an existing vertex by comparing
		// its components.
		for(unsigned int i=0; i<NumVertexes(); ++i)
		{
			if( positions[i] == vertex.position &&
					normals[i] == vertex.normal &&
					tangents[i] == vertex.tangent &&
					colors[i] == vertex.color &&
					texCoords[i] == vertex.texCoord )
			{
				// Return its index.
				return i;
			}
		}

		// The vertex does not exist.
		return -1;
	}
	MeshVertex Mesh::GetVertex(const unsigned int vertIndex)
	{
		MeshVertex outVert;
		
		if( vertIndex >= NumVertexes() )
			return outVert; // Bad Index, Empty vertex.
		
		outVert.position = positions[vertIndex];
		outVert.normal = normals[vertIndex];
		outVert.tangent = tangents[vertIndex];
		outVert.color = colors[vertIndex];
		outVert.texCoord = texCoords[vertIndex];
		
		return outVert;
	}
	bool Mesh::GetVertex(const unsigned int vertIndex, MeshVertex& outVert)
	{	
		if( vertIndex >= NumVertexes() )
			return false; // Bad index.
		
		outVert.position = positions[vertIndex];
		outVert.normal = normals[vertIndex];
		outVert.tangent = tangents[vertIndex];
		outVert.color = colors[vertIndex];
		outVert.texCoord = texCoords[vertIndex];
		
		return true;
	}

	int	Mesh::AddFace(const MeshVertFace &face)
	{
		int outIndex = -1;
		
		// Uniquely add all of the vertexes to the stored
		// list and store their indexes in a MeshIndexFace.
		MeshIndexFace indFace;
		indFace.indexA = AddVertex(face.vertA);
		indFace.indexB = AddVertex(face.vertB);
		indFace.indexC = AddVertex(face.vertC);
		
		// Loop through and check for a duplicate face by comparing
		// indexes. If it exists, add the existing face.
		for(unsigned int i=0; i<(unsigned int)faces.size(); ++i)
		{
			if( faces[i].indexA == indFace.indexA &&
				faces[i].indexB == indFace.indexB &&
				faces[i].indexC == indFace.indexC )
				return (outIndex = (int)i);
		}
		
		// Add the face and grab it's index.
		outIndex = (int)faces.size();
		faces.push_back(indFace);
		
		return outIndex;
	}

	int	Mesh::AddFace(const MeshIndexFace &face)
	{
		int outIndex = -1;
		
		// Validate that the indexes supplied are in range.
		unsigned int numVertexes = NumVertexes();
		if( face.indexA >= numVertexes ||
			face.indexB >= numVertexes ||
			face.indexC >= numVertexes )
			return outIndex;
		
		// Loop through and check for a duplicate face by comparing
		// indexes. If it exists, add the existing face.
		for(unsigned int i=0; i<(unsigned int)faces.size(); ++i)
		{
			if( faces[i].indexA == face.indexA &&
			   faces[i].indexB == face.indexB &&
			   faces[i].indexC == face.indexC )
				return (outIndex = (int)i);
		}
		
		// Add the face and grab it's index.
		outIndex = (int)faces.size();
		faces.push_back(face);
		
		return outIndex;
	}

	int Mesh::AddFace(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC)
	{
		int outIndex = -1;
		
		// Uniquely add all of the vertexes to the stored
		// list and store their indexes in a MeshIndexFace.
		MeshIndexFace indFace;
		indFace.indexA = AddVertex(vertA);
		indFace.indexB = AddVertex(vertB);
		indFace.indexC = AddVertex(vertC);
		
		// Loop through and check for a duplicate face by comparing
		// indexes. If it exists, add the existing face.
		for(unsigned int i=0; i<(unsigned int)faces.size(); ++i)
		{
			if( faces[i].indexA == indFace.indexA &&
			   faces[i].indexB == indFace.indexB &&
			   faces[i].indexC == indFace.indexC )
				return (outIndex = (int)i);
		}
		
		// Add the face and grab it's index.
		outIndex = (int)faces.size();
		faces.push_back(indFace);
		
		return outIndex;
	}

	int Mesh::AddFace(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC)
	{
		int outIndex = -1;
		
		// Validate that the indexes supplied are in range.
		unsigned int numVertexes = NumVertexes();
		if( indexA >= numVertexes ||
		   indexB >= numVertexes ||
		   indexC >= numVertexes )
			return outIndex;
		
		// Loop through and check for a duplicate face by comparing
		// indexes. If it exists, add the existing face.
		for(unsigned int i=0; i<(unsigned int)faces.size(); ++i)
		{
			if( faces[i].indexA == indexA &&
			   faces[i].indexB == indexB &&
			   faces[i].indexC == indexC )
				return (outIndex = (int)i);
		}
		
		MeshIndexFace indFace;
		indFace.indexA = indexA;
		indFace.indexB = indexB;
		indFace.indexC = indexC;
		
		// Add the face and grab it's index.
		outIndex = (int)faces.size();
		faces.push_back(indFace);
		
		return outIndex;
	}

	bool Mesh::SetFace(const MeshVertFace &face, const unsigned int indexFace)
	{
		if( indexFace >= NumFaces() )
			return false; // Bad index.
		
		SetVertex(face.vertA, faces[indexFace].indexA);
		SetVertex(face.vertB, faces[indexFace].indexB);
		SetVertex(face.vertC, faces[indexFace].indexC);
		
		return true;
	}

	bool Mesh::SetFace(const MeshIndexFace &face, const unsigned int indexFace)
	{
		if( indexFace >= NumFaces() )
			return false; // Bad index.
		
		faces[indexFace] = face;
		
		return true;
	}

	bool Mesh::SetFace(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC, const unsigned int indexFace)
	{
		if( indexFace >= NumFaces() )
			return false; // Bad Index.
		
		SetVertex(vertA, faces[indexFace].indexA);
		SetVertex(vertB, faces[indexFace].indexB);
		SetVertex(vertC, faces[indexFace].indexC);
		
		return true;
	}

	bool Mesh::SetFace(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC, const unsigned int indexFace)
	{
		if( indexFace >= NumFaces() )
			return false; // Bad Index.
		
		faces[indexFace].indexA = indexA;
		faces[indexFace].indexB = indexB;
		faces[indexFace].indexC = indexC;
		
		return true;
	}

	MeshVertFace Mesh::GetVertFace(const unsigned int index)
	{
		MeshVertFace outFace;

		if( index >= NumFaces() )
			return outFace; // Bad Index, empty vertex face.
		
		outFace.vertA = GetVertex(faces[index].indexA);
		outFace.vertB = GetVertex(faces[index].indexB);
		outFace.vertC = GetVertex(faces[index].indexC);
		
		return outFace;
	}

	MeshIndexFace Mesh::GetIndexFace(const unsigned int index)
	{
		if( index >= NumFaces() )
			return MeshIndexFace(); // Bad Index, empty index face.
		
		return faces[index];
	}
	
	int Mesh::AddQuad(const MeshVertQuad &quad)
	{
		int outIndex = -1;
		
		// Uniquely add all of the vertexes to the stored
		// list and store their indexes in a MeshIndexQuad.
		MeshIndexQuad indQuad;
		indQuad.indexA = AddVertex(quad.vertA);
		indQuad.indexB = AddVertex(quad.vertB);
		indQuad.indexC = AddVertex(quad.vertC);
		indQuad.indexD = AddVertex(quad.vertD);
		
		// Loop through and check for a duplicate quad by comparing
		// indexes. If it exists, add the existing quad.
		for(unsigned int i=0; i<(unsigned int)quads.size(); ++i)
		{
			if( quads[i].indexA == indQuad.indexA &&
				quads[i].indexB == indQuad.indexB &&
				quads[i].indexC == indQuad.indexC &&
				quads[i].indexD == indQuad.indexD )
				return (outIndex = (int)i);
		}
		
		// Add the quad and grab it's index.
		outIndex = (int)quads.size();
		quads.push_back(indQuad);
		
		return outIndex;
	}
	
	int	Mesh::AddQuad(const MeshIndexQuad &quad)
	{
		int outIndex = -1;
		
		// Validate that the indexes supplied are in range.
		unsigned int numVertexes = NumVertexes();
		if( quad.indexA >= numVertexes ||
			quad.indexB >= numVertexes ||
			quad.indexC >= numVertexes ||
			quad.indexD >= numVertexes )
			return outIndex;
		
		// Loop through and check for a duplicate quad by comparing
		// indexes. If it exists, add the existing quad.
		for(unsigned int i=0; i<(unsigned int)quads.size(); ++i)
		{
			if( quads[i].indexA == quad.indexA &&
				quads[i].indexB == quad.indexB &&
				quads[i].indexC == quad.indexC &&
				quads[i].indexD == quad.indexD)
				return (outIndex = (int)i);
		}
		
		// Add the quad and grab it's index.
		outIndex = (int)quads.size();
		quads.push_back(quad);
		
		return outIndex;
	}
	
	int Mesh::AddQuad(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC, const MeshVertex &vertD)
	{
		int outIndex = -1;
		
		// Uniquely add all of the vertexes to the stored
		// list and store their indexes in a MeshIndexQuad.
		MeshIndexQuad indQuad;
		indQuad.indexA = AddVertex(vertA);
		indQuad.indexB = AddVertex(vertB);
		indQuad.indexC = AddVertex(vertC);
		indQuad.indexD = AddVertex(vertD);
		
		// Loop through and check for a duplicate quad by comparing
		// indexes. If it exists, add the existing quad.
		for(unsigned int i=0; i<(unsigned int)quads.size(); ++i)
		{
			if( quads[i].indexA == indQuad.indexA &&
				quads[i].indexB == indQuad.indexB &&
				quads[i].indexC == indQuad.indexC &&
				quads[i].indexD == indQuad.indexD )
				return (outIndex = (int)i);
		}
		
		// Add the quad and grab it's index.
		outIndex = (int)quads.size();
		quads.push_back(indQuad);
		
		return outIndex;
	}
	
	int Mesh::AddQuad(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC, const unsigned int indexD)
	{
		int outIndex = -1;
		
		// Validate that the indexes supplied are in range.
		unsigned int numVertexes = NumVertexes();
		if( indexA >= numVertexes ||
			indexB >= numVertexes ||
			indexC >= numVertexes ||
			indexD >= numVertexes )
			return outIndex;
		
		// Loop through and check for a duplicate quad by comparing
		// indexes. If it exists, add the existing quad.
		for(unsigned int i=0; i<(unsigned int)quads.size(); ++i)
		{
			if( quads[i].indexA == indexA &&
				quads[i].indexB == indexB &&
				quads[i].indexC == indexC &&
				quads[i].indexD == indexD )
				return (outIndex = (int)i);
		}
		
		MeshIndexQuad indQuad;
		indQuad.indexA = indexA;
		indQuad.indexB = indexB;
		indQuad.indexC = indexC;
		indQuad.indexD = indexD;
		
		// Add the quad and grab it's index.
		outIndex = (int)quads.size();
		quads.push_back(indQuad);
		
		return outIndex;
	}
	
	bool Mesh::SetQuad(const MeshVertQuad &quad, const unsigned int indexQuad)
	{
		if( indexQuad >= NumQuads() )
			return false; // Bad index.
		
		SetVertex(quad.vertA, quads[indexQuad].indexA);
		SetVertex(quad.vertB, quads[indexQuad].indexB);
		SetVertex(quad.vertC, quads[indexQuad].indexC);
		SetVertex(quad.vertD, quads[indexQuad].indexD);
		
		return true;
	}
	
	bool Mesh::SetQuad(const MeshIndexQuad &quad, const unsigned int indexQuad)
	{
		if( indexQuad >= NumQuads() )
			return false; // Bad index.
		
		quads[indexQuad] = quad;
		
		return true;
	}
	
	bool Mesh::SetQuad(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC, const MeshVertex &vertD, const unsigned int indexQuad)
	{
		if( indexQuad >= NumQuads() )
			return false; // Bad index.
		
		SetVertex(vertA, quads[indexQuad].indexA);
		SetVertex(vertB, quads[indexQuad].indexB);
		SetVertex(vertC, quads[indexQuad].indexC);
		SetVertex(vertD, quads[indexQuad].indexD);
		
		return true;
	}
	
	bool Mesh::SetQuad(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC, const unsigned int indexD, const unsigned int indexQuad)
	{
		if( indexQuad >= NumQuads() )
			return false; // Bad Index.
		
		MeshIndexQuad indQuad;
		indQuad.indexA = indexA;
		indQuad.indexB = indexB;
		indQuad.indexC = indexC;
		indQuad.indexD = indexD;
		
		quads[indexQuad] = indQuad;
		
		return true;
	}
	
	MeshVertQuad Mesh::GetVertQuad(const unsigned int index)
	{
		MeshVertQuad outQuad;

		if( index >= NumQuads() )
			return outQuad; // Bad index, empty vertex quad.
		
		outQuad.vertA = GetVertex(quads[index].indexA);
		outQuad.vertB = GetVertex(quads[index].indexB);
		outQuad.vertC = GetVertex(quads[index].indexC);
		outQuad.vertD = GetVertex(quads[index].indexD);
		
		return outQuad;
	}
	
	MeshIndexQuad Mesh::GetIndexQuad(const unsigned int index)
	{
		if( index >= NumQuads() )
			return MeshIndexQuad(); // Bad Index, empty index quad.
		
		return quads[index];
	}

	void Mesh::CalculateNormals()
	{
		// We're calculating normals from scratch and part
		// of this operation includes accumulating face normals.
		// To that end, we need to zero out the current normals
		// so they won't be included in the normal calculation.
		for(unsigned int i=0; i<(unsigned int)normals.size(); ++i)
		{
			MakeZero3(normals[i]);
		}
		
		// For every face, calculate the face normal and accumulate
		// it by summing it with each of it's vertex normals.
		for(unsigned int i=0; i<(unsigned int)NumFaces(); ++i)
		{
			unsigned int indexA, indexB, indexC;
			indexA = faces[i].indexA;
			indexB = faces[i].indexB;
			indexC = faces[i].indexC;
			
			Vec3 edge_ab, edge_bc;
			edge_ab = positions[indexB] - positions[indexA];
			edge_bc = positions[indexC] - positions[indexB];
			
			// We don't normalize below because the size
			// of the face scales the direction of the normal.
			// We normalize later.
			Vec3 normal = Cross3(edge_ab, edge_bc);
			normals[indexA] += normal;
			normals[indexB] += normal;
			normals[indexC] += normal;
		}
		
		// For every quad, calculate the face normal and accumulate
		// it by summing it with each of it's vertex normals.
		for(unsigned int i=0; i<(unsigned int)NumQuads(); ++i)
		{
			// TODO: Do two triangle normal calculations
			// for quads, don't just carry over the values
			// to the d'th vert. (not really necessary).
			
			unsigned int indexA, indexB, indexC, indexD;
			indexA = quads[i].indexA;
			indexB = quads[i].indexB;
			indexC = quads[i].indexC;
			indexD = quads[i].indexD;
			
			Vec3 edge_ab, edge_bc;
			edge_ab = positions[indexB] - positions[indexA];
			edge_bc = positions[indexC] - positions[indexB];
			
			// Note: we don't normalize below because the size
			// of the face scales the direction of the normal.
			// We normalize later.
			Vec3 normal = Cross3(edge_ab, edge_bc);
			normals[indexA] += normal;
			normals[indexB] += normal;
			normals[indexC] += normal;
			normals[indexD] += normal;
		}
		
		// Now that we've accumulated the normals of all the faces,
		// normalize them.
		for(unsigned int i=0; i<(unsigned int)normals.size(); ++i)
		{
			normals[i] = Normalized3(normals[i]);
		}
	}

	void Mesh::CalculateTangents()
	{
		const unsigned int numVertexes = NumVertexes();
		const unsigned int numFaces = NumFaces();
		const unsigned int numQuads = NumQuads();
		
		// create a tangent cluster
		Vec3 *tangentsA = new Vec3[numVertexes * 2];
		Vec3 *tangentsB = tangentsA + numVertexes;
		memset(tangentsA, 0, numVertexes * sizeof(Vec3) * 2);
		
		// Loop through every face and determine the direction
		// in space of the U and V coordinates.
		for(unsigned int i=0; i<(unsigned int)numFaces; ++i)
		{
			unsigned int indexA, indexB, indexC;
			indexA = faces[i].indexA;
			indexB = faces[i].indexB;
			indexC = faces[i].indexC;
			
			const Vec3 &vertA = positions[indexA];
			const Vec3 &vertB = positions[indexB];
			const Vec3 &vertC = positions[indexC];
			
			const Vec2 &texCoordA = texCoords[indexA];
			const Vec2 &texCoordB = texCoords[indexB];
			const Vec2 &texCoordC = texCoords[indexC];
			
			float x_ab = vertB.x - vertA.x;
			float x_ac = vertC.x - vertA.x;
			float y_ab = vertB.y - vertA.y;
			float y_ac = vertC.y - vertA.y;
			float z_ab = vertB.z - vertA.z;
			float z_ac = vertC.z - vertA.z;
			
			float tex_u_ab = texCoordB.x - texCoordA.x;
			float tex_u_ac = texCoordC.x - texCoordA.x;
			float tex_v_ab = texCoordB.y - texCoordA.y;
			float tex_v_ac = texCoordC.y - texCoordA.y;
			
			// Compute the directions.
			float r = 1.0f / (tex_u_ab * tex_v_ac - tex_u_ac * tex_v_ab);
			Vec3 udir((tex_v_ac * x_ab - tex_v_ab * x_ac) * r,
					  (tex_v_ac * y_ab - tex_v_ab * y_ac) * r,
					  (tex_v_ac * z_ab - tex_v_ab * z_ac) * r);
			Vec3 vdir((tex_u_ab * x_ac - tex_u_ac * x_ab) * r,
					  (tex_u_ab * y_ac - tex_u_ac * y_ab) * r,
					  (tex_u_ab * z_ac - tex_u_ac * z_ab) * r);
			
			// Accumulate the u direction.
			tangentsA[indexA] += udir;
			tangentsA[indexB] += udir;
			tangentsA[indexC] += udir;
			// Accumulate the v direction.
			tangentsB[indexA] += vdir;
			tangentsB[indexB] += vdir;
			tangentsB[indexC] += vdir;
		}

		// Loop through every quad and determine the direction
		// in space of the U and V coordinates.
		for(unsigned int i=0; i<(unsigned int)numQuads; ++i)
		{
			// TODO: Do two triangle tangent calculations
			// for quads, don't just carry over the values
			// to the d'th vert. (not really necessary, UV
			// quads are generally planar).
			
			unsigned int indexA, indexB, indexC, indexD;
			indexA = quads[i].indexA;
			indexB = quads[i].indexB;
			indexC = quads[i].indexC;
			indexD = quads[i].indexD;
			
			const Vec3 &vertA = positions[indexA];
			const Vec3 &vertB = positions[indexB];
			const Vec3 &vertC = positions[indexC];
			
			const Vec2 &texCoordA = texCoords[indexA];
			const Vec2 &texCoordB = texCoords[indexB];
			const Vec2 &texCoordC = texCoords[indexC];
			
			float x_ab = vertB.x - vertA.x;
			float x_ac = vertC.x - vertA.x;
			float y_ab = vertB.y - vertA.y;
			float y_ac = vertC.y - vertA.y;
			float z_ab = vertB.z - vertA.z;
			float z_ac = vertC.z - vertA.z;
			
			float tex_u_ab = texCoordB.x - texCoordA.x;
			float tex_u_ac = texCoordC.x - texCoordA.x;
			float tex_v_ab = texCoordB.y - texCoordA.y;
			float tex_v_ac = texCoordC.y - texCoordA.y;
			
			// Calculate the u and v direction.
			float r = 1.0f / (tex_u_ab * tex_v_ac - tex_u_ac * tex_v_ab);
			Vec3 udir((tex_v_ac * x_ab - tex_v_ab * x_ac) * r,
					  (tex_v_ac * y_ab - tex_v_ab * y_ac) * r,
					  (tex_v_ac * z_ab - tex_v_ab * z_ac) * r);
			Vec3 vdir((tex_u_ab * x_ac - tex_u_ac * x_ab) * r,
					  (tex_u_ab * y_ac - tex_u_ac * y_ab) * r,
					  (tex_u_ab * z_ac - tex_u_ac * z_ab) * r);
			
			// Accumulate the u direction.
			tangentsA[indexA] += udir;
			tangentsA[indexB] += udir;
			tangentsA[indexC] += udir;
			tangentsA[indexD] += udir;
			
			// Accumulate the v direction.
			tangentsB[indexA] += vdir;
			tangentsB[indexB] += vdir;
			tangentsB[indexC] += vdir;
			tangentsB[indexD] += vdir;
		}
		
		// Normalize the u direction and accumulate it.
		for(unsigned int i=0; i<(unsigned int)numVertexes; ++i)
		{
			const Vec3 &normal = normals[i];
			const Vec3 &tangentA = tangentsA[i];
			
			// Gram-Schmidt orthogonalize.
			tangents[i] = Normalized3((tangentA - normal * Dot3(normal, tangentA)));
		}
		
		delete [] tangentsA;
		delete [] tangentsB;
	}

	void Mesh::Clear()
	{
		faces.clear();
		quads.clear();
		
		positions.clear();
		normals.clear();
		tangents.clear();
		colors.clear();
		texCoords.clear();
	}

	void Mesh::Resize(unsigned int numVertexes, unsigned int numFaces, unsigned int numQuads)
	{
		faces.resize(numFaces);
		quads.resize(numQuads);
		
		positions.resize(numVertexes);
		normals.resize(numVertexes);
		tangents.resize(numVertexes);
		colors.resize(numVertexes);
		texCoords.resize(numVertexes);
	}
	
	void Mesh::Reserve(unsigned int numVertexes, unsigned int numFaces, unsigned int numQuads)
	{
		faces.reserve(numFaces);		
		quads.reserve(numQuads);
		
		positions.reserve(numVertexes);
		normals.reserve(numVertexes);
		tangents.reserve(numVertexes);
		colors.reserve(numVertexes);
		texCoords.reserve(numVertexes);
	}
	
	void Mesh::Copy(Cooney::Mesh &copy)
	{
		Resize(copy.NumVertexes(), copy.NumFaces(), copy.NumQuads());
		
		memcpy(VertPositions(), copy.VertPositions(), NumVertexes() * sizeof(Cooney::Math::Vec3));
		memcpy(VertNormals(), copy.VertNormals(), NumVertexes() * sizeof(Cooney::Math::Vec3));
		memcpy(VertTangents(), copy.VertTangents(), NumVertexes() * sizeof(Cooney::Math::Vec3));
		memcpy(VertColors(), copy.VertColors(), NumVertexes() * sizeof(Cooney::Math::Vec3));
		memcpy(VertTexCoords(), copy.VertTexCoords(), NumVertexes() * sizeof(Cooney::Math::Vec2));
		memcpy(Faces(), copy.Faces(), NumFaces() * sizeof(MeshIndexFace));
		memcpy(Quads(), copy.Quads(), NumQuads() * sizeof(MeshIndexQuad));
		
		// Note, you may need to invalidate the OpenGL data!
		// PopGLData();
	}
	
	void Mesh::SetGLStreaming(bool bPositions, bool bNormals, bool bTangents, bool bColors, bool bTexCoords, bool bFaceIndexes, bool bQuadIndexes)
	{
		vboStyle[VBO_POSITION] = bPositions;
		vboStyle[VBO_NORMAL] = bNormals;
		vboStyle[VBO_TANGENT] = bTangents;
		vboStyle[VBO_COLOR] = bColors;
		vboStyle[VBO_TEXCOORD] = bTexCoords;
		vboStyle[VBO_FACEINDEX] = bFaceIndexes;
		vboStyle[VBO_QUADINDEX] = bQuadIndexes;
	}

	void Mesh::PushGL(bool bPositions, bool bNormals, bool bTangents, bool bColors, bool bTexCoords)
	{
		// Depending on whether or not the data buffer is set to streaming or static,
		// we may push the data to OpenGL every time we render or not. Streaming is
		// constantly updated. Also, data that has not yet been uploaded will be uploaded
		// for the first time.
		PushGLData((!initiatedvbo[VBO_POSITION] || vboStyle[VBO_POSITION]) && bPositions,
				   (!initiatedvbo[VBO_NORMAL] || vboStyle[VBO_NORMAL]) && bNormals,
				   (!initiatedvbo[VBO_TANGENT] || vboStyle[VBO_TANGENT]) && bTangents,
				   (!initiatedvbo[VBO_COLOR] || vboStyle[VBO_COLOR]) && bColors,
				   (!initiatedvbo[VBO_TEXCOORD] || vboStyle[VBO_TEXCOORD]) && bTexCoords,
				   (!initiatedvbo[VBO_FACEINDEX] || vboStyle[VBO_FACEINDEX]),
				   (!initiatedvbo[VBO_QUADINDEX] || vboStyle[VBO_QUADINDEX]));
		
		// Render position data (this is pretty much the minimum).
		if( bPositions )
		{	
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_POSITION]);
			glEnableClientState(GL_VERTEX_ARRAY);
			glVertexPointer(3, GL_FLOAT, 0, 0);//(float *)VertPositions());
			glBindBuffer(GL_ARRAY_BUFFER, 0);
		}
		else
		{
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glDisableClientState(GL_VERTEX_ARRAY);
			glVertexPointer(3, GL_FLOAT, 0, 0);
		}
		
		// Push normal data.
		if( bNormals )
		{	
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_NORMAL]);
			glEnableClientState(GL_NORMAL_ARRAY);
			glNormalPointer(GL_FLOAT, 0, 0);// (float *)VertNormals());
			glBindBuffer(GL_ARRAY_BUFFER,0);
		}
		else
		{
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glDisableClientState(GL_NORMAL_ARRAY);
			glNormalPointer(GL_FLOAT, 0, 0);
		}
		
		// Push color data.
		if(  bColors )
		{
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_COLOR]);
			glEnableClientState(GL_COLOR_ARRAY);
			glColorPointer(3, GL_FLOAT, 0, 0);// (float*)VertColors());
			glBindBuffer(GL_ARRAY_BUFFER,0);
		}
		else
		{
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glDisableClientState(GL_COLOR_ARRAY);
			glColorPointer(3, GL_FLOAT, 0, 0);
		}
		
		// Push texture coordinate data to the first texture channel.
		glClientActiveTexture(GL_TEXTURE0);
		if( bTexCoords )
		{
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_TEXCOORD]);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glTexCoordPointer(2, GL_FLOAT, 0, 0);//(float *)VertTexCoords());
			glBindBuffer(GL_ARRAY_BUFFER, 0);
		}
		else
		{
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glTexCoordPointer(2, GL_FLOAT, 0, 0);
		}
		
		// Puch tangent data to the second texture channel
		glClientActiveTexture(GL_TEXTURE1);
		if( bTangents )
		{	
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_TANGENT]);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glTexCoordPointer(3, GL_FLOAT, 0, 0);//(float *)VertTangents());
			glBindBuffer(GL_ARRAY_BUFFER, 0);
		}
		else
		{
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glTexCoordPointer(3, GL_FLOAT, 0, 0);
		}
		
		// Going back to default client active texture.
		glClientActiveTexture(GL_TEXTURE0);

		// Draw all the faces.
		if( NumFaces() )
		{	
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexbufferobject[VBO_FACEINDEX]);
			glDrawElements(GL_TRIANGLES, NumFaces() * 3, GL_UNSIGNED_INT, 0);
		}
		
		// Draw all the quads.
		if( NumQuads() )
		{
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexbufferobject[VBO_QUADINDEX]);
			glDrawElements(GL_QUADS, NumQuads() * 4, GL_UNSIGNED_INT, 0);
		}
		
	//	Old style rendering, no VBOs... for reference...
	//	if( NumFaces() )
	//		glDrawElements(GL_TRIANGLES, NumFaces() * 3, GL_UNSIGNED_INT, (unsigned int *)Faces());
	//	if( NumQuads() )
	//		glDrawElements(GL_QUADS, NumQuads() * 4, GL_UNSIGNED_INT, (unsigned int *)Quads());
	//	std::cout << NumFaces() << " " << NumVertexes() << "\n";
		
		// Disable the data usage.
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_COLOR_ARRAY);
		glClientActiveTexture(GL_TEXTURE1);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glClientActiveTexture(GL_TEXTURE0);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	}
	
	void Mesh::PushGLData(bool bPositions, bool bNormals, bool bTangents, bool bColors, bool bTexCoords, bool bFaceIndexes, bool bQuadIndexes)
	{

		// Load the position data from host to OpenGL.
		if( bPositions )
		{	
			if( !initiatedvbo[VBO_POSITION] )
				glGenBuffers(1, &vertexbufferobject[VBO_POSITION]);
			
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_POSITION]);
			glBufferData(GL_ARRAY_BUFFER, NumVertexes() * 3 * sizeof(float), (float *)VertPositions(), vboStyle[VBO_POSITION] ? GL_STATIC_DRAW : GL_STREAM_DRAW);
			
			initiatedvbo[VBO_POSITION] = true;
		}
		
		// Load the normal data from host to OpenGL.
		if( bNormals )
		{
			if( !initiatedvbo[VBO_NORMAL] )
				glGenBuffers(1, &vertexbufferobject[VBO_NORMAL]);
			
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_NORMAL]);
			glBufferData(GL_ARRAY_BUFFER, NumVertexes() * 3 * sizeof(float), (float *)VertNormals(), vboStyle[VBO_NORMAL] ? GL_STATIC_DRAW : GL_STREAM_DRAW);
			
			initiatedvbo[VBO_NORMAL] = true;
		}
		
		// Load the color data from host to OpenGL.
		if( bColors )
		{
			if( !initiatedvbo[VBO_COLOR] )
				glGenBuffers(1, &vertexbufferobject[VBO_COLOR]);
			
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_COLOR]);
			glBufferData(GL_ARRAY_BUFFER, NumVertexes() * 3 * sizeof(float), (float *)VertColors(), vboStyle[VBO_COLOR] ? GL_STATIC_DRAW : GL_STREAM_DRAW);
			
			initiatedvbo[VBO_COLOR] = true;
		}
		
		// Load the texture coordinate data from host to OpenGL.
		if( bTexCoords )
		{
			if( !initiatedvbo[VBO_TEXCOORD] )
				glGenBuffers(1, &vertexbufferobject[VBO_TEXCOORD]);
			
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_TEXCOORD]);
			glBufferData(GL_ARRAY_BUFFER, NumVertexes() * 2 * sizeof(float), (float *)VertTexCoords(), vboStyle[VBO_TEXCOORD] ? GL_STATIC_DRAW : GL_STREAM_DRAW);
			
			initiatedvbo[VBO_TEXCOORD] = true;
		}
		
		// Load the tangent data from host to OpenGL.
		if( bTangents )
		{
			if( !initiatedvbo[VBO_TANGENT] )
				glGenBuffers(1, &vertexbufferobject[VBO_TANGENT]);
			
			glBindBuffer(GL_ARRAY_BUFFER, vertexbufferobject[VBO_TANGENT]);
			glBufferData(GL_ARRAY_BUFFER, NumVertexes() * 3 * sizeof(float), (float *)VertTangents(), vboStyle[VBO_TANGENT] ? GL_STATIC_DRAW : GL_STREAM_DRAW);
			
			initiatedvbo[VBO_TANGENT] = true;
		}
		
		// Load the face index data from host to OpenGL.
		if( bFaceIndexes )
		{
			if( !initiatedvbo[VBO_FACEINDEX] )			
				glGenBuffers(1, &vertexbufferobject[VBO_FACEINDEX]);
			
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexbufferobject[VBO_FACEINDEX]);
			glBufferData(GL_ELEMENT_ARRAY_BUFFER, NumFaces() * 3 * sizeof(unsigned int), (unsigned int *)Faces(), vboStyle[VBO_FACEINDEX] ? GL_STATIC_DRAW : GL_STREAM_DRAW);
			
			initiatedvbo[VBO_FACEINDEX] = true;
		}
		
		// Load the quad index data from host to OpenGL.
		if( bQuadIndexes )
		{
			if( !initiatedvbo[VBO_QUADINDEX] )
				glGenBuffers(1, &vertexbufferobject[VBO_QUADINDEX]);
			
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexbufferobject[VBO_QUADINDEX]);
			glBufferData(GL_ELEMENT_ARRAY_BUFFER, NumQuads() * 4 * sizeof(unsigned int), (unsigned int *)Quads(), vboStyle[VBO_QUADINDEX] ? GL_STATIC_DRAW : GL_STREAM_DRAW);
			
			initiatedvbo[VBO_QUADINDEX] = true;
		}
		
		// Clean up a potential mess.
		// Unbind the buffers.
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	}
	
	void Mesh::PopGLData(bool bPositions, bool bNormals, bool bTangents, bool bColors, bool bTexCoords, bool bFaceIndexes, bool bQuadIndexes)
	{
		// Remove the position data from OpenGL.
		if( bPositions )
		{
			glDeleteBuffers(1, &vertexbufferobject[VBO_POSITION]);
			vertexbufferobject[VBO_POSITION] = 0;
			initiatedvbo[VBO_POSITION] = false;
		}
		
		// Remove the normal data from OpenGL.
		if( bNormals )
		{
			glDeleteBuffers(1, &vertexbufferobject[VBO_NORMAL]);
			vertexbufferobject[VBO_NORMAL] = 0;
			initiatedvbo[VBO_NORMAL] = false;
		}
		
		// Remove the tangent data from OpenGL.
		if( bTangents )
		{
			glDeleteBuffers(1, &vertexbufferobject[VBO_TANGENT]);
			vertexbufferobject[VBO_TANGENT] = 0;
			initiatedvbo[VBO_TANGENT] = false;
		}
		
		// Remove the color data from OpenGL.
		if( bColors )
		{
			glDeleteBuffers(1, &vertexbufferobject[VBO_COLOR]);
			vertexbufferobject[VBO_COLOR] = 0;
			initiatedvbo[VBO_COLOR] = false;
		}
		
		// Remove the texture coordinate data from OpenGL.
		if( bTexCoords )
		{
			glDeleteBuffers(1, &vertexbufferobject[VBO_TEXCOORD]);
			vertexbufferobject[VBO_TEXCOORD] = 0;
			initiatedvbo[VBO_TEXCOORD] = false;
		}
		
		// Remove the face index data from OpenGL.
		if( bFaceIndexes )
		{
			glDeleteBuffers(1, &vertexbufferobject[VBO_FACEINDEX]);
			vertexbufferobject[VBO_FACEINDEX] = 0;
			initiatedvbo[VBO_FACEINDEX] = false;
		}
		
		// Remove the quad index data from OpenGL.
		if( bQuadIndexes )
		{
			glDeleteBuffers(1, &vertexbufferobject[VBO_QUADINDEX]);
			vertexbufferobject[VBO_QUADINDEX] = 0;
			initiatedvbo[VBO_QUADINDEX] = false;
		}
	}
	
	bool Mesh::Load(const char *filePath)
	{
		ifstream inFile(filePath, ios::in | ios::binary);
		
		if( !inFile.is_open() )
		{
			std::cout << "Could not open file: " << filePath << "\n";
			return false;
		}
		
		// Mesh expects the CNYMESH0 magic number.
		char inIdBuffer[8] = {0};
		char identifier[8] = {'C','N','Y','M','E','S','H','0'};
		inFile.read((char *)inIdBuffer, 8);
		
		bool bValidFile = false;
		if( !memcmp(inIdBuffer, identifier, 8) )
			bValidFile = true;
		if( !bValidFile )
		{
			// Bad file, quit.
			std::cout << "File is invalid: " << filePath << "\n";
			inFile.close();
			return false;
		}
		
		// Load all of the mesh data by streaming in through
		// the file stream object.
		StreamInMesh(inFile);
		
		inFile.close();
		
		return true;
	}
	
	bool Mesh::Save(const char *filePath)
	{
		std::ofstream outFile(filePath, ios::out | ios::binary | ios::trunc);
		
		if( !outFile.is_open() )
			return false;
		
		// The current magic number is CNYMESH0.
		char identifier[8] = {'C','N','Y','M','E','S','H','0'};
		outFile.write((char *)identifier, 8);
		
		// Stream out the mesh to the file-stream.
		StreamOutMesh(outFile);
		
		outFile.close();
		
		return true;
	}
	
	void Mesh::StreamInMesh(std::istream& inStream)
	{
		// First read the number of vertexes, faces and quads in.
		unsigned int numVertexes, numFaces, numQuads;
		inStream.read((char *)&numVertexes, sizeof(unsigned int));
		inStream.read((char *)&numFaces, sizeof(unsigned int));
		inStream.read((char *)&numQuads, sizeof(unsigned int));
		// Resize to fit the incoming data.
		// This is much faster than letting vector realloc
		// all the time.
		Resize(numVertexes, numFaces, numQuads);
		
		// For faster reading from stream, get direct access to the beginning
		// of the vertex data arrays.
		const Vec3 *directPositions = VertPositions();
		const Vec3 *directNormals = VertNormals();
		const Vec3 *directTangents = VertTangents();
		const Vec3 *directColors = VertColors();
		const Vec2 *directTexCoords = VertTexCoords();
		
		// Read the vertex data from stream.
		inStream.read((char *)directPositions, numVertexes * sizeof(Vec3));
		inStream.read((char *)directNormals, numVertexes * sizeof(Vec3));
		inStream.read((char *)directTangents, numVertexes * sizeof(Vec3));
		inStream.read((char *)directColors, numVertexes * sizeof(Vec3));
		inStream.read((char *)directTexCoords, numVertexes * sizeof(Vec2));
		
		// For faster reading from stream, use direct access to the beginning
		// of the triangle face data arrays.
		const MeshIndexFace *directFaces = Faces();
		// Read triangles from the stream.
		inStream.read((char *)directFaces, numFaces * sizeof(MeshIndexFace));
		
		// For faster reading from stream, use direct access to the beginning
		// of the quad face data arrays.
		const MeshIndexQuad *directQuads = Quads();
		// Read quads from the stream.
		inStream.read((char *)directQuads, numQuads * sizeof(MeshIndexQuad));
	}
	
	void Mesh::StreamOutMesh(std::ostream& outStream)
	{
		// First, write the number of vertexes, faces(tris) and quads to the stream.
		unsigned int numVertexes, numFaces, numQuads;
		numVertexes = NumVertexes();
		numFaces = NumFaces();
		numQuads = NumQuads();
		outStream.write((char *)&numVertexes, sizeof(unsigned int));
		outStream.write((char *)&numFaces, sizeof(unsigned int));
		outStream.write((char *)&numQuads, sizeof(unsigned int));
		
		// Get pointers to the vertex data arrays for faster writing.
		const Vec3 *directPositions = VertPositions();
		const Vec3 *directNormals = VertNormals();
		const Vec3 *directTangents = VertTangents();
		const Vec3 *directColors = VertColors();
		const Vec2 *directTexCoords = VertTexCoords();
		// Write the vertex data to the stream.
		outStream.write((char *)directPositions, sizeof(Vec3) * numVertexes);
		outStream.write((char *)directNormals, sizeof(Vec3) * numVertexes);
		outStream.write((char *)directTangents, sizeof(Vec3) * numVertexes);
		outStream.write((char *)directColors, sizeof(Vec3) * numVertexes);
		outStream.write((char *)directTexCoords, sizeof(Vec2) * numVertexes);
		
		// Write the tri face data to the stream.
		const MeshIndexFace *directFaces = Faces();
		outStream.write((char *)directFaces, sizeof(MeshIndexFace) * numFaces);
		
		// Write the quad data to the stream.
		const MeshIndexQuad *directQuads = Quads();
		outStream.write((char *)directQuads, sizeof(MeshIndexQuad) * numQuads);
	}
}