summaryrefslogtreecommitdiffstats
path: root/lib/VBOMesh.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/VBOMesh.cpp')
-rw-r--r--lib/VBOMesh.cpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/lib/VBOMesh.cpp b/lib/VBOMesh.cpp
new file mode 100644
index 0000000..a6acb10
--- /dev/null
+++ b/lib/VBOMesh.cpp
@@ -0,0 +1,208 @@
+/*
+ * VBOMesh.cpp
+ *
+ * Copyright 2007 Richard S. Wright Jr.. All rights reserved.
+ * This class allows you to simply add triangles as if this class were a
+ * container. The AddTriangle() function searches the current list of triangles
+ * and determines if the vertex/normal/texcoord is a duplicate. If so, it addes
+ * an entry to the index array instead of the list of vertices.
+ * When finished, call EndMesh() to free up extra unneeded memory that is reserved
+ * as workspace when you call BeginMesh().
+ *
+ * This class can easily be extended to contain other vertex attributes, and to
+ * save itself and load itself from disk (thus forming the beginnings of a custom
+ * model file format).
+ *
+ * Very similiar to CTriangleMesh, except the final arrays are stored in Vertex buffer objects
+ * Before anybody flames me, bear in mind this is not a book on object oriented programming,
+ * and I'm trying to be nice to the C people ;-)
+ * Yes, I know all about inheritance, and it's all good....
+ */
+
+#include "VBOMesh.h"
+
+///////////////////////////////////////////////////////////
+// Constructor, does what constructors do... set everything to zero or NULL
+CVBOMesh::CVBOMesh(void)
+{
+ pIndexes = NULL;
+ pVerts = NULL;
+ pNorms = NULL;
+ pTexCoords = NULL;
+
+ nMaxIndexes = 0;
+ nNumIndexes = 0;
+ nNumVerts = 0;
+}
+
+////////////////////////////////////////////////////////////
+// Free any dynamically allocated memory. For those C programmers
+// coming to C++, it is perfectly valid to delete a NULL pointer.
+CVBOMesh::~CVBOMesh(void)
+{
+ // Just in case these still are allocated when the object is destroyed
+ delete [] pIndexes;
+ delete [] pVerts;
+ delete [] pNorms;
+ delete [] pTexCoords;
+
+ // Delete buffer objects
+ glDeleteBuffers(4, bufferObjects);
+}
+
+////////////////////////////////////////////////////////////
+// Start assembling a mesh. You need to specify a maximum amount
+// of indexes that you expect. The EndMesh will clean up any uneeded
+// memory. This is far better than shreading your heap with STL containers...
+// At least that's my humble opinion.
+void CVBOMesh::BeginMesh(GLuint nMaxVerts)
+{
+ // Just in case this gets called more than once...
+ delete [] pIndexes;
+ delete [] pVerts;
+ delete [] pNorms;
+ delete [] pTexCoords;
+
+ nMaxIndexes = nMaxVerts;
+ nNumIndexes = 0;
+ nNumVerts = 0;
+
+ // Allocate new blocks
+ pIndexes = new GLushort[nMaxIndexes];
+ pVerts = new M3DVector3f[nMaxIndexes];
+ pNorms = new M3DVector3f[nMaxIndexes];
+ pTexCoords = new M3DVector2f[nMaxIndexes];
+}
+
+/////////////////////////////////////////////////////////////////
+// Add a triangle to the mesh. This searches the current list for identical
+// (well, almost identical - these are floats you know...) verts. If one is found, it
+// is added to the index array. If not, it is added to both the index array and the vertex
+// array grows by one as well.
+void CVBOMesh::AddTriangle(M3DVector3f verts[3], M3DVector3f vNorms[3], M3DVector2f vTexCoords[3])
+{
+ const float e = 0.000001; // How small a difference to equate
+
+ // First thing we do is make sure the normals are unit length!
+ // It's almost always a good idea to work with pre-normalized normals
+ m3dNormalizeVector(vNorms[0]);
+ m3dNormalizeVector(vNorms[1]);
+ m3dNormalizeVector(vNorms[2]);
+
+
+ // Search for match - triangle consists of three verts
+ for(GLuint iVertex = 0; iVertex < 3; iVertex++)
+ {
+ GLuint iMatch = 0;
+ for(iMatch = 0; iMatch < nNumVerts; iMatch++)
+ {
+ // If the vertex positions are the same
+ if(m3dCloseEnough(pVerts[iMatch][0], verts[iVertex][0], e) &&
+ m3dCloseEnough(pVerts[iMatch][1], verts[iVertex][1], e) &&
+ m3dCloseEnough(pVerts[iMatch][2], verts[iVertex][2], e) &&
+
+ // AND the Normal is the same...
+ m3dCloseEnough(pNorms[iMatch][0], vNorms[iVertex][0], e) &&
+ m3dCloseEnough(pNorms[iMatch][1], vNorms[iVertex][1], e) &&
+ m3dCloseEnough(pNorms[iMatch][2], vNorms[iVertex][2], e) &&
+
+ // And Texture is the same...
+ m3dCloseEnough(pTexCoords[iMatch][0], vTexCoords[iVertex][0], e) &&
+ m3dCloseEnough(pTexCoords[iMatch][1], vTexCoords[iVertex][1], e))
+ {
+ // Then add the index only
+ pIndexes[nNumIndexes] = iMatch;
+ nNumIndexes++;
+ break;
+ }
+ }
+
+ // No match for this vertex, add to end of list
+ if(iMatch == nNumVerts)
+ {
+ memcpy(pVerts[nNumVerts], verts[iVertex], sizeof(M3DVector3f));
+ memcpy(pNorms[nNumVerts], vNorms[iVertex], sizeof(M3DVector3f));
+ memcpy(pTexCoords[nNumVerts], &vTexCoords[iVertex], sizeof(M3DVector2f));
+ pIndexes[nNumIndexes] = nNumVerts;
+ nNumIndexes++;
+ nNumVerts++;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////
+// Compact the data. This is a nice utility, but you should really
+// save the results of the indexing for future use if the model data
+// is static (doesn't change).
+void CVBOMesh::EndMesh(void)
+{
+ // Create the buffer objects
+ glGenBuffers(4, bufferObjects);
+
+ // Copy data to video memory
+ // Vertex data
+ glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[VERTEX_DATA]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*nNumVerts*3, pVerts, GL_STATIC_DRAW);
+
+ // Normal data
+ glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[NORMAL_DATA]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*nNumVerts*3, pNorms, GL_STATIC_DRAW);
+
+ // Texture coordinates
+ glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[TEXTURE_DATA]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*nNumVerts*2, pTexCoords, GL_STATIC_DRAW);
+
+ // Indexes
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects[INDEX_DATA]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort)*nNumIndexes, pIndexes, GL_STATIC_DRAW);
+
+ // Free older, larger arrays
+ delete [] pIndexes;
+ delete [] pVerts;
+ delete [] pNorms;
+ delete [] pTexCoords;
+
+ // Reasign pointers so they are marked as unused
+ pIndexes = NULL;
+ pVerts = NULL;
+ pNorms = NULL;
+ pTexCoords = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Draw - make sure you call glEnableClientState for these arrays
+void CVBOMesh::Draw(void) {
+ // Here's where the data is now
+ glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[VERTEX_DATA]);
+ glVertexPointer(3, GL_FLOAT,0, 0);
+
+ // Normal data
+ glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[NORMAL_DATA]);
+ glNormalPointer(GL_FLOAT, 0, 0);
+
+ // Texture coordinates
+ glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[TEXTURE_DATA]);
+ glTexCoordPointer(2, GL_FLOAT, 0, 0);
+
+ // Indexes
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects[INDEX_DATA]);
+ glDrawElements(GL_TRIANGLES, nNumIndexes, GL_UNSIGNED_SHORT, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Scale of the vertices. The only way to do this is to map the VBO back
+// into client memory, then back again
+void CVBOMesh::Scale(GLfloat fScaleValue)
+{
+ glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[VERTEX_DATA]);
+ M3DVector3f *pVertexData = (M3DVector3f *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
+
+ if(pVertexData != NULL)
+ {
+ for(int i = 0; i < nNumVerts; i++)
+ m3dScaleVector3(pVertexData[i], fScaleValue);
+
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ }
+}
+