0

I have the following global variables:

vector<vector<unsigned int> > faces; 
vector<float> vertexCoords; 
unsigned int modelList;

In my initialization there is this code:

glEnableClientState(GL_VERTEX_ARRAY);   // vertex array
glVertexPointer(3, GL_FLOAT, 0, &vertexCoords[0]);

modelList = glGenLists(1);  // generate display list
glNewList(modelList, GL_COMPILE); 
for (unsigned int i = 0; i < faces.size(); i++) {
    glBegin(GL_TRIANGLE_STRIP); 
    for (vector<unsigned int>::iterator it = faces[i].begin();
            it != faces[i].end(); it++)
        glArrayElement(*it - 1); 
    glEnd();
}
glEndList(); // End display list.

which in my glutDisplayFunc, I call:

glCallList(modelList); // Execute display list.

Instead of a DisplayList, I would like to now use a VBO. How can I go about converting this code? Thanks in advance.

1 Answers1

1

You're in luck. The structure of your program already has everything in place. We can even get around that annoying 1-based indexing.

First create a VBO:

GLuint vbo_id;
GLuint eabo_id; /* EIB = Element Array Buffer */
size_t eabo_n_elements;

void make_vbo()
{
    GLuint genbuf_ids;
    glGenBuffers(2, genbuf_ids);

    vbo_id  = genbuf_ids[0];
    eabo_id = genbuf_ids[1];

    glBindBuffer(GL_ARRAY_BUFFER, vbo_id);

Allocate space for the data, any copy the vertex data into the buffer. Since your vertex element index array is shifted by one, we allocate for one vertex more and copy the data into it with an offset (we could as well use the GL_ARB_draw_elements_base_vertex extensions, but I'd like to show it this way:

    glBufferData(
        GL_ARRAY_BUFFER,
        (vertexCoords.size() + 3)*sizeof(vertexCoords[0]),
        NULL,
        GL_STATIC_DRAW );

    glBufferSubData(
        GL_ARRAY_BUFFER,
        sizeof(vertexCoords[0])*3, /* offset by 1 vertex */
        (vertexCoords.size())*sizeof(vertexCoords[0],
        &vertexCoords[0]);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

The same for the element array buffer; you "stacked" two vectors into each other… which is not very efficient by the way. Let's unroll this. First again allocate the memory for tha buffer, but don't copy the data into it. Since each subvector may have a different length (although if used for a specific primitive draw mode they should be all the same size) first determine the total number of elements (using the C++11 feature auto to save some typing and so that we can use iterators); also later on we need to know that number to draw them:

    eabo_n_elements = 0;
    for(auto i = faces.begin(); i != faces.end(); i++) {
        eabo_n_elements += i.size();
    }

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eabo_id);
    glBufferData(
        GL_ELEMENT_ARRAY_BUFFER,
        sizeof(faces[0][0]) * eabo_n_elements,
        NULL,
        GL_STATIC_DRAW );

Unroll-copy the face data into the EAB

    size_t offset = 0;
    for(auto i = faces.begin(); i != faces.end(); i++) {
        size_t const len = i.size() * sizeof(i[0]);
        glBufferSubData(
            GL_ELEMENT_ARRAY_BUFFER,
            offset,
            len,
            &i[0] );
        offset += len;
    }

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

Now we can draw that. Technically we could use a VAO (and with OpenGL-4 we have to have one), but let's safe that. Drawing VBOs is very similar to drawing from client side vertex arrays. Now you used glVertexPointer which indicates you're using the fixed function pipeline. We can use that. But then VBOs became OpenGL core functionality only with OpenGL-3 (before that it was available for a long time as an extension). So it's glVertexAttribPointer plus a matching shader instead. To keep the transition as small as possible, let's recycle your use of glVertexPointer. The main difference is, that we bind the VBO before making the glVertexPointer call and pass a integer-cast-to-a-pointer (which actually may invoke UB for anything other than 0) offset into the data parameter:

void draw_vbo()
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
    glVertexPointer(3, GL_FLOAT, 0, (void*)0);

Once the VBO has been "linked" into a vertex array access, we can unbind it. The data will still be fetched from the VBO.

    glBindBuffer(GL_ARRAY_BUFFER, 0);

And finally make the draw call. Remember that we applied the 1-element offset when copying the vertex data to the VBO. So we just pass through the face vertex element indices. Same pattern as for the VBO: Bind, cast integer offset to pointer and pass that to glDrawElements

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eabo_id);
    glDrawElements(GL_TRIANGLE_STRIP, eabo_n_elements, GL_UNSIGNED_INT, (void*)0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Community
  • 1
  • 1
datenwolf
  • 159,371
  • 13
  • 185
  • 298