glDrawElementBaseVertex problem with second mesh

I’m trying to draw two different meshes that belong to the same VBO using glDrawElementsBaseVertex. I’m drawing the first mesh (name “object”) successfully, but the second mesh (named “plane”) is no where to be found.

Object

glDrawElementsBaseVertex ( GL_TRIANGLES, objectIndexSize, GL_UNSIGNED_INT, ( void * ) ( objectVertexBytes ), 0 );

Plane

glDrawElementsBaseVertex ( GL_TRIANGLES, objectIndexSize, GL_UNSIGNED_INT, ( void * ) ( planeVertexBytes ), 0 );

I realize the last parameter (aka basevertex) for the subsequent (read: anything other than the first mesh in the VBO) should not be zero, but left it 0 here. As I understand it, basevertex should an offset so that the draw call starts at indices[i] + basevertex. To comform to this, I’ve tried a basevertex = objectIndexSize for the plane and had no success.

I’m wondering if this has to do with how I setup my buffer sources:


	// Setup the buffer sources
	glVertexArrayElementBuffer ( vertexArray, buffer );
	glVertexArrayVertexBuffer ( vertexArray, positionLocation, buffer, 0, 3 * sizeof ( float ) );
	glVertexArrayVertexBuffer ( vertexArray, normalLocation, buffer, (objectVertexBytes + objectIndexBytes), 3 * sizeof ( float ) );

In fact, I’m almost certain it’s a problem, if not THE problem, but I’m not sure how to glVertexArrayVertexBuffer the same VBO with two different meshes with two different lengths of vertices and indices.

Source: http://pastebin.com/PDuk3fDu

glVertexArrayVertexBuffer is on line 83.
glDrawElementsBaseVertex is on line 168.

This should presumably be

glDrawElementsBaseVertex ( GL_TRIANGLES, planeIndexSize, GL_UNSIGNED_INT, ( void * ) ( objectSizeBytes + objectVertexBytes ), 0 );

Note; objectVertexBytes probably “should” be planeVertexBytes, but that’s not where you’re actually storing the indices (see line 65 of your code).

More generally, I suggest that you adopt a less “ad hoc” approach to handling sizes and offsets. You already appear to have some kind of data structure for managing the client-side data for a group of meshes, consider creating something similar for managing a group of meshes stored in a buffer object.

Actually, I think the index pointer should be objectSizeBytes + planeVertexBytes.

objectSizeBytes = objectVertexBytes + objectIndexBytes + objectNormalBytes, so it’s that plus the planeVertex, and then immediately after that in the VBO is the planeIndex.

Still no plane appearing though (I also tried it your way too). Did the glVertexArrayVertexBuffer on line 83 raise any concerns? It has to, since objectVertexBytes + objectIndexBytes is different than planeVertexBytes + planeIndexBytes.

I thought I could stuff two dissimilar meshes in the same VBO? How is that possible if I can only set the state for seemingly one when setting up the buffer sources?

More generally, I suggest that you adopt a less “ad hoc” approach to handling sizes and offsets. You already appear to have some kind of data structure for managing the client-side data for a group of meshes, consider creating something similar for managing a group of meshes stored in a buffer object.

Most definitely. I’ve got everything in this wall of code right now as I’m learning. I’m slowly starting to give my engine some structure, but I want to make sure I can get things working this way first.

Line 65 of your code is


glBufferSubData ( GL_ARRAY_BUFFER, objectSizeBytes + objectVertexBytes, planeIndexBytes, ms->GetIndices ( ms->Lookup ( plane ) ).data () );

Regardless of where you meant to store the indices for the plane, objectSizeBytes + objectVertexBytes is where you appear to be actually storing them.

Ah! Dang it. Fixed the SubData. I see a small face fragment of the plane now, but I think the glVertexArrayVertexBuffer on line 83 might be the issue now. I thought I could stuff two dissimilar meshes in the same VBO? How is that possible if I can only set the state for seemingly one when setting up the buffer sources?

Ah, figured it out…

Don’t mix your vertex + index VBO’s with your attribute VBO’s. The vertex and index one’s will change accordingly to your mesh, but the attribute VBO’s should stay the same for each because they belong to the same VAO. If the attribute layouts aren’t the same, then you’ll need another VAO.

In summary:

VBO 1

  • Mesh 1 vertices
  • Mesh 1 indices
  • Mesh 2 vertices
  • Mesh 2 indices

(Of course you could separate out Mesh 1 and Mesh 2 here if you’d like)

VBO 2

  • Mesh 1 Attributes
  • Mesh 2 Attributes

What do you mean by “vertices”, “indices” and “attributes” here? Because the term “vertex attribute” include things like positions and normals. Given that, what do you mean by “vertices”?

Sorry, I meant attributes as shader attributes. By vertices, indices, and normals I meant all the properties of a mesh whose size would change depending on the model.

Now that I sit down to write the code, I’m still kind of lost. Made a lot of sense a few minutes ago. I think I’m closer though.

Well, as you might be alluding too… I’m not making sense. Lol. What I was told was: “Don’t mix indices and attribute data into a single VBO”. Since I haven’t looked at shaders in a while, I forgot vertex shaders are per vertex (duh). So that naturally means vertices are a part of the attribute data. So, indices must need to be a VBO of it’s own, and that’s it.

Well, the term “VBO” is often used to refer specifically to a buffer used to hold a vertex attribute array (GL_ARRAY_BUFFER), which excludes indices (GL_ELEMENT_ARAY_BUFFER).

There’s no fundamental reason why indices can’t go into the same buffer as attributes, although that doesn’t mean that they should. The fact that their access patterns often differ may mean that you can’t decide upon an appropriate value for the usage parameter to glBufferData() or the flags parameter to glBufferStorage(), but that can also be true for different attributes within the same mesh. Additionally, the specification states that the implementation may optimise storage according to the target to which the buffer is first bound, so a buffer which will be bound to more than one target may be sub-optimal for all but one target.

And you can’t use glMapBufferRange() with GL_MAP_INVALIDATE_BUFFER_BIT unless you’re able to invalidate the entire buffer. You can use GL_MAP_INVALIDATE_RANGE_BIT, but the implementation may be unable to gain as much utility from that as from invalidating the entire buffer.

Additionally, the specification states that the implementation may optimise storage according to the target to which the buffer is first bound, so a buffer which will be bound to more than one target may be sub-optimal for all but one target.

It should be noted that, per issue #2 from ARB_direct_state_access, this is apparently not used by implementations.