#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 ©)
{
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);
}
}