4
\$\begingroup\$

I am trying to write my own OBJ importer and renderer in OpenGL (yes, I know, reinventing the wheel) and I found a problem that I don't knwo how to handle.

In an OBJ file faces are defined by a list of indices that refers to the vertices IDs that belongs to that face (and the same for texture coordinates IDs), an example of a face could be:

f 2411/905 2414/906 2913/1463

which would mean that this face is created by vertices 2411, 2414, 3913 and texture coordinates IDs 905, 906, 1463. And this leads to different index buffers, if I am not mistaken...

Now the question is, having both a buffer of vertices and a buffer of texture coordinates, how can I use both "at the same time", if I am only able to bing one index buffer and the indices to be used for a particular face are different for vertices and texture coordinates? Is there any trick to bind two index buffers, one for vertices and another one for texture coordinates?

And finally, just to give some code of what I've tried and that obviously doesn't work because I am binding two different GL_ELEMENT_ARRAY_BUFFER_ARB before drawing..., so it ends up with random texture coodinates.

// TEXTURE VBO
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texCoordBuffer);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, texIndicesBuffer);         
glIndexPointer(GL_INT, 0, 0);

// GEOMETRY VBO
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);
glVertexPointer(3, GL_FLOAT, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);

glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalBuffer);
glNormalPointer(GL_FLOAT, 0, 0);
glEnableClientState(GL_NORMAL_ARRAY);                                   

glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indicesBuffer);
glIndexPointer(GL_INT, 0, 0);

// Bind texture and draw...
\$\endgroup\$
3

2 Answers 2

8
\$\begingroup\$

You cannot use separate index buffers in the way you intend to. But since you are writing your own importer, you can very well reorganise the in-memory data so that both the position and texture information are indexed the same way. This possibly means duplicating information in the process, but that cost must be put in regard to the overall bandwidth gain provided by the use of index buffers.

The recently released book OpenGL Insights has a chapter about how to do this efficiently: Indexing Multiple Vertex Arrays, by Arnaud Masserann. Even if you do not have the book or the article, the author provides the full source code on GitHub.

\$\endgroup\$
8
  • \$\begingroup\$ Thanks Sam! Reorganising the data was one of the solutions I was thinking of, but I thought there might be an easier way to get around this. It looked to me too complicated for just an OBJ importer... ;) \$\endgroup\$
    – Dan
    Jan 4, 2013 at 10:51
  • \$\begingroup\$ Also - glIndexPointer does not do what you think it does. opengl.org/sdk/docs/man2/xhtml/glIndexPointer.xml \$\endgroup\$ Jan 4, 2013 at 12:23
  • 1
    \$\begingroup\$ @Dan Unfortunately, an OBJ importer is fairly complicated :( \$\endgroup\$
    – Luke B.
    Jan 4, 2013 at 13:12
  • \$\begingroup\$ @Sam I finally got it working, reorganising the UV coordinates array in order to match the vertex index array as you suggested. Thanks! \$\endgroup\$
    – Dan
    Jan 4, 2013 at 15:22
  • \$\begingroup\$ @sam What about if you have multiple UV for the same vertex based on the triangle you're about to render? \$\endgroup\$
    – Emanuele
    Apr 10, 2016 at 17:24
1
\$\begingroup\$

What you have to do is "dupe up the data".

Say for (a contrived) example you have a square, but for some reason all 4 points of the square use the same normal:

v 0 0 0
v 1 0 0
v 1 1 0
v 0 1 0

vn 0 0 1

f 1//1 2//1 3//1
f 1//1 3//1 4//1

Great! Now what do you do?

When you're building your vertex array, you just duplicate vn 4 times. So your vertex array would contain:

struct Vertex{
  Vector3f pos, norm ;
}  ;

vector<Vertex> verts(4) ;
verts[0] = Vertex( Vector3f( 0, 0, 0 ), Vector3f( 0, 0, 1 ) ) 
verts[1] = Vertex( Vector3f( 1, 0, 0 ), Vector3f( 0, 0, 1 ) ) 
verts[2] = Vertex( Vector3f( 1, 1, 0 ), Vector3f( 0, 0, 1 ) ) 

verts[3] = Vertex( Vector3f( 0, 0, 0 ), Vector3f( 0, 0, 1 ) ) 
verts[4] = Vertex( Vector3f( 1, 1, 0 ), Vector3f( 0, 0, 1 ) ) 
verts[5] = Vertex( Vector3f( 0, 1, 0 ), Vector3f( 0, 0, 1 ) ) 

So you see, the normal Vector3f( 0,0,1 ) has got to be duplicated all over the place. If you use an index buffer, you can reduce this to 4 verts. But that's the best you can do with a situation like this.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks @bobobobo, this is similar to what I ended up doing. I basically reordered the array of UVs in order to match the order of the vertices. In the case of having vertices with multiple UVs, I duplicated that vertex, adding an extra vertex in the list of vertices and assigning to it its original UV. \$\endgroup\$
    – Dan
    Jan 6, 2013 at 11:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .