glDrawElements with texture pointer problem

Hey guys whats up. So I have 4 vectors:


vector<Vertex3> m_vertexCoordinates; // a list of all the vertices in the model
vector<int> m_triangleIndices; // list storing the order that vertices are drawn
vector<Vertex3> m_textureCoordinates; // a list of all the textures in the model
vector<int> m_textureIndices; // list storing the order that textures are drawn

Simple enough. I can draw my models without error by doing:

int count = 0;
for each (int i in m_triangleIndices)
{
        float s = m_textureCoordinates[m_textureIndices[count]].x();
        float t = m_textureCoordinates[m_textureIndices[count++]].y();
	glTexCoord2f(s,t);
	glVertex3fv(m_vertexCoordinates[i]);
}

I now want to adopt the same thing using VBOs. I have two methods, one to create my buffer, the other to draw it:

void createOpenGLVertexBufferObject()
{
	// Deal with ARRAY_BUFFER
	glGenBuffers(1, &mBufferVertList);
	glBindBuffer(GL_ARRAY_BUFFER, mBufferVertList);
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*m_vertexCoordinates.size()*3+sizeof(GLfloat)*m_textureCoordinates.size()*2, NULL, GL_STATIC_DRAW);
	GLfloat *vertBuff = (GLfloat *)glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY);

	int count = 0;
	for (vector<Vertex3>::iterator v = m_vertexCoordinates.begin(); v < m_vertexCoordinates.end(); v++)
	{
		vertBuff[count++] = v->x();
		vertBuff[count++] = v->y();
		vertBuff[count++] = v->z();
	}
	int index = 0;
	for (int i = 0; i < m_textureCoordinates.size(); i++)
	{
		int tex_ind = m_textureIndices[index++];
		float s = m_textureCoordinates[tex_ind].x();
		float t = m_textureCoordinates[tex_ind].y();
		vertBuff[count++] = s;
		vertBuff[count++] = t;
	}
	glUnmapBuffer(GL_ARRAY_BUFFER);

	// Deal with ELEMENT_ARRAY_BUFFER

	glGenBuffers(1, &mBufferTriList);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferTriList);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*m_triangleIndices.size(), NULL, GL_STATIC_DRAW);
	GLuint *triBuff = (GLuint *)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
	for (int i = 0; i < m_triangleIndices.size(); i++)
	    triBuff[i] = m_triangleIndices[i];
	glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
}
void drawOpenGLVertexBufferObject()
{
	glBindBuffer(GL_ARRAY_BUFFER, mBufferVertList);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferTriList);

	// enable vertex coordinates
	glEnableClientState(GL_VERTEX_ARRAY);
	// enable textures
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		
	glVertexPointer(3, GL_FLOAT, 0, (float *)NULL);
	glTexCoordPointer(2, GL_FLOAT, 0, (float *)NULL + m_vertexCoordinates.size()*3);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, m_material);
	glColor3d(1,1,1);

	glDrawElements(GL_TRIANGLES, m_triangleIndices.size(), GL_UNSIGNED_INT, 0);
}

The problem is, when I do this, the textures are not being drawn correctly. The models are correct, the textures seem to be drawn in the wrong order but I cant see the reason why. Anybody got a clue?

You have to match texture coordinates to vertex coordinates. createOpenGLVertexBufferObject() appears to be trying to do that, but it’s not doing it correctly. Essentially, each distinct vertex/texture coordinate pair needs to be a separate vertex. One way to do this is (untested; treat as pseudo-code):


map<pair<int,int>, int> map;
vector<Vertex3> m_vertexCoordinates2;
vector<Vertex3> m_textureCoordinates2;
vector<int> indices;
assert(m_triangleIndices.size() == m_textureIndices.size());
for (int i = 0; i < m_triangleIndices.size(); i++) {
    int vi = m_triangleIndices[i];
    int ti = m_textureIndices[i];
    auto key = make_pair(vi,ti);
    auto it = map.find(key);
    if (it == map.end()) {
        int ix = m_vertexCoordinates2.size();
        map[key] = ix;
        m_vertexCoordinates2.append(m_vertexCoordinates[vi]);
        m_textureCoordinates2.append(m_textureCoordinates[ti]);
        indices.push_back(ix);
    }
    else
        indices.push_back(it->second);
}

Thanks for the response! Your solution seems a little over my head… to be honest I cant really follow it. I still dont see whats actually wrong with the way I have done it. Isn’t the VBO method I used identical to the way I did it using glBegin/glEnd or am I not properly understanding how glDrawElements works?

For example, lets say I have 9 coordinates making up 3 vertices, x1,y1,z1, x2,y2,z2, x3,y3,z3 and I have textures s1,t1, s2,t2, s3,t3. If the order to draw the vertices is backward, how are the textures applied?

The number of elements in each attribute array must be the same. For each vertex, an index from the array passed to glDrawElements() is used to select an element from each array.

With the glBegin() code, you have separate index arrays for the vertex coordinates and texture coordinates. That isn’t possible when drawing arrays; you have one set of indices shared between all attributes.

The code which uses glDrawElements() copies the vertex coordinates to the array in their existing order, copies the texture coordinates based on the indices in m_textureIndices, then passes m_triangleIndices to glDrawElements (via a buffer object).

If you want the simplest possible conversion your glBegin code to use VBOs, you need to copy both vertex coordinates and texture coordinates based upon their index arrays then use glDrawArrays() (you could use glDrawElements() with an index array of [0,1,2,…], but there’s no point).

However: that will typically create arrays which are larger than is strictly necessary, as each vertex will only be used once. If you have vertices which are used multiple times, with both the vertex coordinates and texture coordinates the same each time, you only need one set of coordinates and can just duplicate the index.

But that requires identifying when you need to create a new vertex and when you can re-use an existing vertex. That is what the map is for in my example. As each pair of a vertex index and texture index is extracted from the index arrays, the code checks to see whether that pair already exists in the map. If it does, it simply adds the index of the merged vertex to the index array. If it doesn’t, it creates a new merged vertex, copying the vertex coordinates and texture coordinates to the corresponding arrays, adds the index of the new vertex to the index array, and stores the index in the map, associated with the vertex/texture-index pair.

This procedure is fairly standard when reading data from formats such as OBJ, where each face vertex can have separate indices into the vertex coordinate and texture coordinate arrays.