3
votes

I have a custom file format that has all the needed information for a 3D mesh (exported from 3ds Max). I've extracted the data for vertices, vertex indices and normals.

I pass to OpenGL the vertex data, vertex indices and normals data and I render the mesh with a call to glDrawElements(GL_TRIANGLES,...)

Everything looks right but the normals. The problem is that the normals have different indices. And because OpenGL can use only one index buffer, it uses that index buffer for both the vertices and the normals.

I would be really grateful if you could suggest me how to go about that problem.

Important thing to note is that the vertex/normal data is not "sorted" and therefore I am unable to use the functionality of glDrawArrays(GL_TRIANGLES,...) - the mesh doesn't render correctly.

Is there a way/algorithm that I can use to sort the data so the mesh can be drawn correctly with glDrawArrays(GL_TRIANGLES,..) ? But even if there is an algorithm, there is one more problem - I will have to duplicate some vertices (because my vertex buffer consists of unique vertices - for example if you have cube my buffer will only have 8 vertices) and I am not sure how to do that.

2
"The problem is that the normals have different indices." Why? Re-arrange the index buffer so that the normals match up with the vertices. Better yet, interleave the vertex info.Colonel Thirty Two
In some meshes I get more normals than vertices, so there is no way to match the indices. Is that usual ?Geto
Oh, yea, for some shading effects, you might get vertices at the same point but with different normals. You're going to have to re-arrange data so that everything lines up, depending on what your attributes are and what file format you are using. But right now, you haven't actually asked a question, so I don't know what to answer.Colonel Thirty Two

2 Answers

12
votes

File types that use separate indices for vertices and normals do not match to the OpenGL vertex model very directly. As you noticed, OpenGL uses a single set of indices.

What you need to do is create an OpenGL vertex for each unique (vertex index, normal index) pair in your input. That takes a little work, but is not terribly difficult, particularly if you use available data structures. A STL map works well for this, with the (vertex index, normal index) pair as the key. I'm not going to provide full C++ code, but I can sketch it out.

Let's say you have already read your vertices into some kind of array/vector data structure inVertices, where the coordinates for vertex with index vertexIdx are stored in inVertices[vertexIdx]. Same thing for the normals, where the normal vector with index normalIdx is stored in inNormals[normalIdx].

Now you get to read a list of triangles, with each corner of each triangle given by both a vertexIdx and a normalIdx. We'll build a new combinedVertices array/vector that contains both vertex and normal coordinates, plus a new combinedIndices index list. Pseudo code:

nextCombinedIdx = 0
indexMap = empty
loop over triangles in input file
    loop over 3 corners of triangle
        read vertexIdx and normalIdx for the corner
        if indexMap.contains(key(vertexIdx, normalIdx)) then
            combinedIdx = indexMap.get(key(vertexIdx, normalIdx))
        else
            combinedIdx = nextCombinedIdx
            indexMap.add(key(vertexIdx, normalIdx), combinedIdx)
            nextCombinedIdx = nextCombinedIdx + 1
            combinedVertices.add(inVertices[vertexIdx], inNormals[normalIdx])
        end if
        combinedIndices.add(combinedIdx)
    end loop
end loop
-2
votes

I managed to do it without passing index buffer to OpenGL with glDrawArrays(GL_TRIANGLES,..) What I did is the following: Fill a vertex array, vertex indices array, normals array and normals indices array. Then I created new vertex and normal arrays with sorted data and passed them to OpenGL.

for i = 0; i < vertexIndices.size(); ++i
    newVertexArray[i] = oldVertexArray[vertexIndices[i]];

for i = 0; i < normalsIndices.size(); ++i
    newNormalsArray[i] = oldNormalsArray[normalsIndices[i]];

I optimized it a bit, without filling indices arrays at all. But the optimization depends on the way the programmer is reading the mesh data.