Note that the code I'm posting 'works' so the problem can be in
the mechanism or in the implementation (drivers?)
This is the simple VBO managment class (italian comment but it should be simple
to understand)(in the .cpp file I only define the static variable
m_currentBufferID)
#ifndef AC_GL_VBO#define AC_GL_VBO#include "lib/glee/glee.h"#include <gl/gl.h>#include "ac_types.h"namespace AC{ namespace GL{class VertexBufferObject{public: /// @brief void ctor VertexBufferObject(void){ m_BufferID=0; } /// @brief Dealloca e distrugge ~VertexBufferObject(void){ Delete(); } /// @brief Dealloca void Delete(void){ if(m_BufferID){ if(m_currentBufferID==m_BufferID) m_currentBufferID=0; glDeleteBuffers(1,&m_BufferID); m_BufferID=0; } } /// @brief Genera nuovo buffer bool Gen(void){ Delete(); glGenBuffers(1,&m_BufferID); return m_BufferID!=0; } /// @brief Verifica se il buffer esiste bool IsBuffer(void)const{ return GetBuffer()!=0; } /// @brief Ritorna ID del buffer GLuint GetBuffer(void)const{ return m_BufferID; } /// @brief BufferData /// target : /// - ARRAY_BUFFER /// - ELEMENT_ARRAY_BUFFER /// usage : /// - STREAM_DRAW /// - STREAM_READ /// - STREAM_COPY /// - STATIC_DRAW /// - STATIC_READ /// - STATIC_COPY /// - DYNAMIC_DRAW /// - DYNAMIC_READ /// - DYNAMIC_COPY void Data(GLenum target, SIZET size, LPCVOID data, GLenum usage)const{ if(IsBuffer()){ Bind(target); glBufferData(target,size,data,usage); } } /// @brief BindBuffer /// /// target può essere /// - ARRAY_BUFFER /// - ELEMENT_ARRAY_BUFFER /// void Bind(GLenum target)const{ if(m_BufferID&&(m_currentBufferID!=m_BufferID)){ glBindBuffer(target, m_BufferID); m_currentBufferID=m_BufferID; } } /// @brief Estensione supportata ? static bool IsSupported(void){ return glGenBuffers !=NULL && glBufferData !=NULL && glDeleteBuffers!=NULL; }private: // id del buffer GLuint m_BufferID; static GLuint m_currentBufferID; };}} // end of ns GL,AC#endif // ens of #ifndef AC_GL_VBO
This is the inner loop of the MeshObject 'object'
The MeshObject is a vector of Mesh
I also have a variable with the DrawMode needed (combination of
LIGHTING,TEXTURE,TEXTURE1,WIREFRAME,...)
I use also glee.h so I can use extension more easily...
The relevant functions in the code are
Mesh::DeclareVertexArray() and
Mesh::DrawElements()
void MeshObject::glset(void)const{ Material currentMaterial; glPushAttrib(GL_COLOR_BUFFER_BIT|GL_TEXTURE_BIT); // save attrib glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); // save client attrib bool bFirstMesh = true; for(MESHES::const_iterator mesh=m_Meshes.begin(); mesh!=m_Meshes.end(); mesh++) { // setup material if((m_DrawMode&LIGHTING) && (bFirstMesh || currentMaterial!=mesh->m_Material)){ bFirstMesh=false; mesh->m_Material.glset(GL_FRONT_AND_BACK); } glEnableClientState(GL_VERTEX_ARRAY); mesh->DeclareVertexArray(); // I use the same code for VA and VBO // this is for wireframe only and for wireframe 'overlay' if(m_DrawMode&WIREFRAME){ glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT | GL_LIGHTING_BIT | GL_LINE_BIT); glDisable(GL_LIGHTING); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDepthFunc(GL_LEQUAL); glDisable(GL_TEXTURE_2D); glPolygonMode( GL_FRONT_AND_BACK,GL_LINE); mesh->m_colorWireframe.glset(); mesh->DrawElements(); // also in this case I use the same code! glPopAttrib(); } glEnableClientState(GL_NORMAL_ARRAY); mesh->DeclareNormalArray(); if(m_DrawMode&TEXTURE){ // NOTE : this part contains the routine to set up the texture // and multitexture with and witout lighting // I removed it for brevity: if you need I can post it! } // this is to force something to be drawn if everything is disabled! bool bForce = !((m_DrawMode&WIREFRAME)|| (m_DrawMode&SOLID) || (m_DrawMode&TEXTURE) ); if(bForce || ((m_DrawMode&SOLID)||(m_DrawMode&TEXTURE))){ mesh->m_colorSolid.glset(); mesh->DrawElements(); } }// altra mesh glPopClientAttrib(); glPopAttrib();}
Now I post (they are very short) the code I use to setup VBOs
As I posted in my first post I have these structures in the single Mesh object
Vector3f/2f are vector of 3 and 2 floats I use to store GL data with limited
functions (I use them as storage structure)
/// @brief Vertice struct MeshVertex{ Vector3f vertex; Vector3f normal; Vector2f texcoord; Vector2f texcoord1; }; /// @brief Vertici typedef std::vector<MeshVertex> VERTICES; /// @brief Vertici VERTICES m_Vertices; /// @brief Triangolo /// /// I suoi vertici sono indici relativi ai vertici globali /// The indices refer to m_Vertices struct Triangle{ GLuint a; GLuint b; GLuint c; }; /// @brief Triangoli typedef std::vector<Triangle> TRIANGLES; /// @brief Triangoli TRIANGLES m_Triangles;
This is the function used to generate VBOs and it is called by an extern
function every time the object is changed (few times at the beginning)
usage is a param that can be as you know GL_STATIC_DRAW or GL_DYNAMIC_DRAW
vboVertices and vboElements are VertexBufferObject (see above)
// i generate VBO for every mesh in the vectorvoid MeshObject::GenerateVBO(GLenum usage){ for(MESHES::iterator mesh=m_Meshes.begin();mesh!=m_Meshes.end();mesh++) mesh->GenerateVBO(usage);}// generate VBO for the single meshvoid Mesh::GenerateVBO(GLenum usage){ if(!VertexBufferObject::IsSupported()) return; // non supportato! m_vboVertices.Gen(); m_vboElements.Gen(); m_vboVertices.Data(GL_ARRAY_BUFFER, m_Vertices.size()*sizeof(Mesh::MeshVertex), m_Vertices.begin(), usage); m_vboElements.Data(GL_ELEMENT_ARRAY_BUFFER, m_Triangles.size()*sizeof(Mesh::Triangle), m_Triangles.begin(), usage);}
Now the
tricky part : how I can use the same code with VA and VBO
If I call MeshObject::GenerateVBO() I will use VBO else VA
Note the use of BUFFER_OFFSET to resolve the interleaving: it works!
// if i previously called GenerateVBO() this function will return truebool Mesh::VBOEnabled(void)const{ return m_vboVertices.IsBuffer()&&m_vboElements.IsBuffer();} // macro to define 'starting' offset in VBOs// NOTE: the forum script hide the + plus sign!!! #define BUFFER_OFFSET(i) ((char*)NULL plus (i))void Mesh::DeclareVertexArray(void)const{ if(!VBOEnabled()) glVertexPointer(3,GL_FLOAT,sizeof(MeshVertex),&(m_Vertices[0].vertex)); else{ m_vboVertices.Bind(GL_ARRAY_BUFFER); // if it is in use this is a no op glVertexPointer(3,GL_FLOAT,sizeof(MeshVertex),BUFFER_OFFSET(0)); }}void Mesh::DeclareNormalArray(void)const{ if(!VBOEnabled()) glNormalPointer(GL_FLOAT,sizeof(MeshVertex),&(m_Vertices[0].normal)); else{ m_vboVertices.Bind(GL_ARRAY_BUFFER); glNormalPointer(GL_FLOAT,sizeof(MeshVertex), BUFFER_OFFSET((const char*)&(m_Vertices[0].normal)- (const char*)&(m_Vertices[0].vertex))); }}// very similarvoid Mesh::DeclareTextureArray(void)const{...}void Mesh::DeclareTextureArray1(void)const{...}
I know that this is a lot of code (in fact I tried to avoid to post it :)
However it may be useful for me to understand the problem or useful to someone
to implement similar (and better) classes.
I remember that the problem is that the code works with VA and VBO but if I use VBO it is very very slow.