0

I've got a training app written in winapi So, I've got GL initialized there and I've got node-based system, that can be described by couple of classes

class mesh
{
GLuint vbo_index; //this is for having unique vbo
float *vertex_array;
float *normal_array;
unsigned int vertex_count;

etc.. //all those mesh things.
....
}

class node
{
bool is_mesh; //the node may or may not represent a mesh
mesh * mesh_ptr; //if it does then this pointer is a valid address
}

I've also got 2 global variables for keeping record of renderable mesh..

mesh **mesh_table;
unsigned int mesh_count;

Right now I'm experimenting on 2 objects. So I create 2 nodes of type mesh::cube with customizable number of x y and z segments. Expected behaviour of my app is let the user click between 2 of the nodes CUBE0, CUBE1 and show their customizable attributes - segments x, segments y, segments z. The user tweaks both objecs' parameters and they are being rendered out on top of each other in wireframe mode, so we can see the changing in their topology in real time.

When the node is being created for the first time, if the node type is mesh, then the mesh object is generated and it's mesh_ptr is written into the mesh_table and mesh_count increments. After that my opengl window class creates a unique vertex buffer object for the new mesh and stores it's index in the mesh_ptr.vbo_index

void window_glview::add_mesh_to_GPU(mesh* mesh_data)
{
    glGenBuffers(1,&mesh_data->vbo_index);
    glBindBuffer(GL_ARRAY_BUFFER ,mesh_data->vbo_index);
    glBufferData(GL_ARRAY_BUFFER ,mesh_data->vertex_count*3*4,mesh_data->vertex_array,GL_DYNAMIC_DRAW);
    glVertexAttribPointer(5,3,GL_FLOAT,GL_FALSE,0,NULL);//set vertex attrib (0)
    glEnableVertexAttribArray(5);
}

After that the user is able to tweak the parameters and each time the parameter value changes the object's mesh information is being re-evaluated based on the new parameter values, while still being the same mesh instance, after that VBO data is being updated by

void window_glview::update_vbo(mesh *_mesh)
{
    glBindBuffer(GL_ARRAY_BUFFER,_mesh->vbo_vertex);
    glBufferData(GL_ARRAY_BUFFER,_mesh->vertex_count*12,_mesh->vertex_array,GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER,0);
}

and the whole scene redrawn by

for (unsigned short i=0;i<mesh_count;i++)
    draw_mesh(mesh_table[i],GL_QUADS,false);
SwapBuffers(hDC);

The function for a single mesh is

bool window_glview::draw_mesh(mesh* mesh_data,unsigned int GL_DRAW_METHOD,bool indices)
{
    glUseProgram(id_program);
    glBindBuffer(GL_ARRAY_BUFFER,mesh_data->vbo_index);
    GLuint id_matrix_loc = glGetUniformLocation(id_program, "in_Matrix");
    glUniformMatrix4fv(id_matrix_loc,1,GL_TRUE,cam.matrixResult.get());
    GLuint id_color_loc=glGetUniformLocation(id_program,"uColor");

    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    glUniform3f(id_color_loc,mesh_color[0],mesh_color[1],mesh_color[2]);
    glDrawArrays(GL_DRAW_METHOD,0,mesh_data->vertex_count);
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glUseProgram(0);
    return true;
}

The problem is that only the last object in stack is being drawn that way, and the other object's points are all in 0 0 0, so in the viewport it's rendered one cube with proper parameters and one cube just as a DOT enter image description here enter image description here

QUESTION: Where did I go wrong?

Antiusninja
  • 157
  • 1
  • 17

2 Answers2

1

You have a fundamental misunderstanding of what glBindBuffer(GL_ARRAY_BUFFER,mesh_data->vbo_vertex); does.

That sets the bound array buffer, which is actually only used by a handful of commands (mostly glVertexAttrib{I|L}Pointer (...)), binding the buffer itself is not going to do anything useful.

What you need to do is something along the lines of this:

bool window_glview::draw_mesh(mesh* mesh_data,unsigned int GL_DRAW_METHOD,bool indices)
{
    glUseProgram(id_program);

    //
    // Setup Vertex Pointers in addition to binding a VBO
    //
    glBindBuffer(GL_ARRAY_BUFFER,mesh_data->vbo_vertex);
    glVertexAttribPointer(5,3,GL_FLOAT,GL_FALSE,0,NULL);//set vertex attrib (0)
    glEnableVertexAttribArray(5);

    GLuint id_matrix_loc = glGetUniformLocation(id_program, "in_Matrix");
    glUniformMatrix4fv(id_matrix_loc,1,GL_TRUE,cam.matrixResult.get());
    GLuint id_color_loc=glGetUniformLocation(id_program,"uColor");

    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    glUniform3f(id_color_loc,mesh_color[0],mesh_color[1],mesh_color[2]);
    glDrawArrays(GL_DRAW_METHOD,0,mesh_data->vertex_count);
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glUseProgram(0);
    return true;
}

Now, if you really want to make this simple and be able to do this just by changing a single object binding, I would suggest you look into Vertex Array Objects. They will persistently store the vertex pointer state.

Community
  • 1
  • 1
Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • Ok, I'll try to understand. It's pretty tough to see the whole picture out of all the info I've read. As I understood before - glBindBuffer binds the buffer that has the exact vertices that I wish to render, that are already in the video memory. And vertex array object is just a set of all those different states and locations that I could dispatch in one small command. – Antiusninja Oct 29 '14 at 16:38
  • 1
    @Antiusninja: That is partially correct, but what `glBindBuffer` really does is setup the memory space for the `glVertexAttrib{I|L}Pointer (...)` command. It tells that command where the memory it points to is relative to. `glDrawArrays (...)` uses your vertex pointers, and those aren't setup until you actually call a `Pointer` command. – Andon M. Coleman Oct 29 '14 at 16:41
  • Thanks, it worked. I'm still not getting the picture)) Some really detailed explanation article would do the job.. – Antiusninja Oct 29 '14 at 16:42
  • So by your words I could think that glGenBuffers gives me a pointer of a token-type to video memory, glBufferData finds a place for my data in video memory, allocates it and points my pointer there. After that I call my pointer by bindbuffer and it's some kind of an active video-memory address. glVertexAttribPointer shows the offset and stride to work with this address in a specific manner to extract the right data out of this memory address. And glEnableVertexAttribArray somehow tells GLSL shader to use this data for a particular input variable? – Antiusninja Oct 29 '14 at 16:57
  • @Antiusninja: What `glGenBuffers` really does is reserve a name (ID) for a buffer object. `glBufferData` allocates memory for and fills the *bound* buffer with data. `glVertexAttribPointer` sets up the pointer for vertex attribute N relative to whatever is bound to `GL_ARRAY_BUFFER` at the time. The one important thing in your description here that you left out was actually binding the buffer you generated to `GL_ARRAY_BUFFER`. Also keep in mind that a Vertex Array Object will store the vertex attribute pointer and enable/disable state for you so you don't have to do that everytime you draw. – Andon M. Coleman Oct 29 '14 at 17:02
  • @Antiusninja: I have a more thorough explanation of the differences between a VAO and a VBO in another answer I wrote [here](http://stackoverflow.com/questions/23314787/use-of-vertex-array-objects-and-vertex-buffer-objects/23315318#23315318) – Andon M. Coleman Oct 29 '14 at 17:07
1

in your draw glBindBuffer(GL_ARRAY_BUFFER,mesh_data->vbo_index); doesn't actually do anything; the information about the vertex attribute is not bound to the buffer at all. it is set in the glVertexAttribPointer(5,3,GL_FLOAT,GL_FALSE,0,NULL); call which gets overwritten each time a new mesh is uploaded.

either create and use a VAO or move that call from add_mesh_to_GPU to draw_mesh:

for the VAO you would do:

void window_glview::add_mesh_to_GPU(mesh* mesh_data)
{
    glGenVertexArrays(1, &mesh_data->vao_index);//new GLInt field
    glBindVertexArray(mesh_data->vao_index);
    glGenBuffers(1,&mesh_data->vbo_index);
    glBindBuffer(GL_ARRAY_BUFFER ,mesh_data->vbo_index);
    glBufferData(GL_ARRAY_BUFFER ,mesh_data->vertex_count*3*4,mesh_data->vertex_array,GL_DYNAMIC_DRAW);
    glVertexAttribPointer(5,3,GL_FLOAT,GL_FALSE,0,NULL);//set vertex attrib (0)
    glEnableVertexAttribArray(5);
    glBindVertexArray(0);
}

bool window_glview::draw_mesh(mesh* mesh_data,unsigned int GL_DRAW_METHOD,bool indices)
{
    glBindVertexArray(mesh_data->vao_index);
    glUseProgram(id_program);
    GLuint id_matrix_loc = glGetUniformLocation(id_program, "in_Matrix");
    glUniformMatrix4fv(id_matrix_loc,1,GL_TRUE,cam.matrixResult.get());
    GLuint id_color_loc=glGetUniformLocation(id_program,"uColor");

    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    glUniform3f(id_color_loc,mesh_color[0],mesh_color[1],mesh_color[2]);
    glDrawArrays(GL_DRAW_METHOD,0,mesh_data->vertex_count);
    glUseProgram(0);
    glBindVertexArray(0);
    return true;
}
ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • You are wrong. glBindBuffer(GL_ARRAY_BUFFER,mesh_data->vbo_index) chooses which of several available buffers gpu should use to source vertex data. I've got lots of mesh objects and each got it's own unique array of floats that describe vertices. Each mesh is assigned it's own buffer object, the only way to access this buffer object is via glBindBuffer... That's the logical part. The actual part is that if I test your statement and comment glBindBuffer command in my code, then it does not work! – Antiusninja Oct 30 '14 at 12:51
  • no `bindBuffer` just selects which specific buffer to use but **not** how to interpret the data inside it, that is what `glVertexAttribPointer` is for; glVertexAttribPointer stores "for attribute 5 there are 3 GL_FLOATS per vertex non-normalized starting at the beginning of glGet(GL_ARRAY_BUFFER_BINDING​) tightly packed" in the currently bound VAO – ratchet freak Oct 30 '14 at 13:00
  • So you actually agree that glBindBuffer() does something usefull in the function. First you told it was meaningless. Now I see how glVertexAttribPointer works. Still you confuse me with VAO) – Antiusninja Oct 30 '14 at 14:54
  • There is an extension with the function `VertexArrayVertexOffsetEXT` where you don't need to bind the buffer or vao at all, it instead just passes them in as parameters. – ratchet freak Oct 30 '14 at 15:23