glDrawElements noob

Hi guys,

I was wondering if could get some help please?

I’ve exported an OBJ file from 3ds max with the vertex, vertex normal and texture coord data included. In the exported file, each entry is listed in its respective list, thus:


v  -10.0000 0.0000 10.0000 //vertex 1; x,y,z
v  10.0000 0.0000 10.0000 // vertex 2
v  -10.0000 0.0000 -10.0000 // etc

vn 0.0000 -1.0000 -0.0000 // normal 1; x,y,z
vn 0.0000 1.0000 -0.0000 // normal 2
vn 0.0000 1.0000 -0.0000 // etc

and then underneath the triangles are specfied using the format:


f v(x)/vt(x)/vn(x), v(x)/vt(x)/vn(x), v(x)/vt(x)/vn(x)

…where x is the list lookup index, and v,t and n are the vertex, texture coord and normal lists.

I’ve filtered out the irrelevant data using another program, in order to provide 3x 3d-coordinate arrays for OpenGL, called vertlist, normlist and texlist. I’ve also transferred the index list into an array of GLunits called trilist.

When it comes to glDrawElements I’m having trouble, because it only accepts one set of indices, and I need 3, because my model has 8 verts, 12 vt’s and 36 vn’s and therefore the values don’t correspond in any way.

My code looks like this:


glEnableClientState(GL_VERTEX_ARRAY);	
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);	

glVertexPointer(3, GL_FLOAT, 0, vertList);
glNormalPointer(3, GL_FLOAT, 0, normList);
glTexCoordPointer(3, GL_FLOAT, 0, texList);

glDrawElements(GL_TRIANGLES, TRIANGLECOUNT * 3 , GL_UNSIGNED_INT, triList); // < problem here

The only other way I can imagine to do it is to avoid indices altogether, and have three separate same-size arrays with corresponding v, vt and vn data. But this would seem wasteful as I would have to duplicate vertex and texture information to match the number of normals in the scene. Not to mention that this would be slower.

Is this a common problem in OpenGL? Any suggestions for a workaround would be very welcome.

Many thanks,

Neil

You can definitely do the latter – i.e. just use glDrawArrays and forget the indices.

Or you can expand out v/n/tc permutations per vertex, sort out dups, assign indices, optimize for best vertex and triangle ordering, and use DrawElements.

The reason there is one index list (AFAIK) is that it makes it easier for the GPU to “reuse” vertex computations. For instance, if you have one triangle with indicies 0,1,2 and another right next to it with indices 2,1,3, then it doesn’t need to run the vtx shader again for vtx indices 1 and 2 – they’re already “in the vertex cache” from the first triangle.

I suppose the hardware could accept multiple indices but then the chance of reuse is less likely. And besides, with most normal 3D tessellated objects, the vertex position, normal, and texcoord for a single point (vertex) on that object is unique, regardless of how many triangles that point participates in. The exceptions of course being texture edges or areas where the curvature changes abruptly (different face normals) such as a corner of a building.

Okay, thanks for your help.

I think I will need to write a custom exporter for 3ds Max, which for each triangle, lists its vertices, normals and texcoords, regardless of duplication. To be honest I don’t mind if it is slow at the moment - I just want to get it doing SOMETHING lol.

Thanks again,

Neil

If you are wanting to learn how to import 3D files that is excellent you are learning by writing your own import library. However, if you just want to load 3D objects – I have been using a library that supports more filetypes – taking a look at the Open Asset Import Library may be helpful.

They have an example that unfortunately uses the immediate glVertex, glNormal etc calls. However, the library itself really gives you blocks of memory that are easily mapped into vertex Buffer objects. The code naturally fits into something like


... in initialization section
      glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
      glBufferData(GL_ARRAY_BUFFER, (max_index+1) * 3 * sizeof(GLfloat), &mesh->mVertices[0].x, GL_STATIC_DRAW);
... in rendering section
      glBindVertexArray(P->vao);
      glDrawElements(GL_TRIANGLES, P->nElements, GL_UNSIGNED_INT, 0);


where the library gives you the mesh->mVertices array (and normals, colors, material props etc) with a simple call


scene = aiImportFile(filenemae,...);

which determines the filetype automatically and chooses the 3ds, obj, ply etc loader internally.