PDA

View Full Version : glDrawElements not working with STL vectors



the_Seppi
07-27-2014, 06:21 AM
Hello, and straight to the point:

I'm stuck in the middle of a OpenGL/C++/SDL project, where I just replaced the arrays containing vertices, texcoordes, indices, etc. by STL vectors due to unexpectable behavior with STL vectors (push_back() after passing pointer to first element as array.) No my original problems, meaning lots of segfaults, are solved, but another problem turned up:
The cube I created for testing purposes is not drawn any more (since I moved from C-style arrays (pointers to first element) to std::vector).

I've reduced my rendering function to the following until I can fix the problem:


glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//replacement for the deprecated glLookAt, which is unavailable in Mesa 10.1 (which I use)
util_compat_gluLookAt(posX+cx,posY+cy,posZ+cz, posX,posY,posZ, 0,1,0);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glPushMatrix();

glTranslatef(posX,posY,posZ);
glRotatef(spectatorLongitude*180/M_PI, 0,1,0);

glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

glColorPointer(4, GL_UNSIGNED_BYTE, 0, &cube.getColors().front());
glVertexPointer(3, GL_FLOAT, 0, &cube.getVertices().front());
glDrawElements(GL_QUADS, cube.getIndices().size(), GL_UNSIGNED_INT, &cube.getIndices().front());

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);

glPopMatrix();

//These WCS axes are drawn without problem
glLineWidth(3);
glBegin(GL_LINES);
glColor3f(1,0,0);
glVertex3f(0,0,0);
glVertex3f(100,0,0);
glColor3f(0,1,0);
glVertex3f(0,0,0);
glVertex3f(0,100,0);
glColor3f(0,0,1);
glVertex3f(0,0,0);
glVertex3f(0,0,100);
glEnd();

SDL_GL_SwapWindow(screen);

The only thing that changed in the code are the arguments passed to glVertexPointer, glColorPointer and glDrawElements. I.e. I replaced cube.getVertices(), which returned a GLfloat array with &cube.getVertices().front(), which returns a pointer to the first element of the vector. This should be the same as passing an C-style array to the GL functions, since I'm compiling with --std=c++11, which guarantees that vector elements are stored contiguously in memory.

According to the GDB debugger, the vectors contain and keep the values they are intended to have. Since the values are hardcoded into a header file, a change of them isn't even possible, because this file is included in any file using them.

The hardcoded vectors:

// vertices
std::vector<GLfloat> vertices {
-5,0,-5,
5,0,-5,
-5,10,-5,
-5,0,5,
5,10,-5,
5,0,5,
-5,10,5,
5,10,5
};
// Color buffer
std::vector<GLubyte> colors {
0,0,0,255,
255,0,0,255,
0,255,0,255,
0,0,255,255,
255,255,0,255,
255,0,255,255,
0,255,255,255,
255,255,255,255
};
// Vertex index buffer
std::vector<GLuint> indices {
0,1,4,2,
0,2,6,3,
0,3,5,1,
1,4,7,5,
5,7,6,3,
2,6,7,4
};

The class SolidObject, from which Player (currently a cube for debugging purposes) inherits: (reduced to important parts)

class SolidObject {

protected:
std::vector<GLfloat> vertices, texcoords, normals;
std::vector<GLuint> indices;
std::vector<GLubyte> colors;

public:
SolidObject(std::vector<GLfloat>& vertices, std::vector<GLuint>& indices,
std::vector<GLfloat>& normals, std::vector<GLubyte>& colors,
std::vector<GLfloat>& texcoords) {
this->vertices = vertices;
this->indices = indices;
this->normals = normals;
this->colors = colors;
this->texcoords = texcoords;
}
//other get... functions declared analogous
std::vector<GLfloat> SolidObject::getVertices() {
return this->vertices;
}
}

What I tried so far:

Ensure it's not a driver problem (tried both xserver-xorg-video-ati (using OpenG L3.0) and fglrx (OpenGL 4.3), same problem with both)
Ensure the vectors contain the values intended (GDB)
Ensure the vector elements are stored contiguously in memory (*(&cube.getVertices().front() + x))
Set GDB watchpoints on the vectors -> no changes after initialisation


However, I could not yet find a solution. Can someone of you find the error in my code?

Thanks in advance.

Edit: I replaced the hardcoded quad faces by triangle faces, as I read using quads can cause problems with newer OpenGL versions (although I wouldn't call 3.0 "new" and it worked when I still used arrays...)

std::vector<GLuint> indices {
0,1,4, 4,2,0,
0,2,6, 6,3,0,
0,3,5, 5,1,0,
1,4,7, 7,5,1,
5,7,6, 6,3,5,
2,6,7, 7,4,2
};

glDrawElements(GL_TRIANGLES, cube.getIndices().size(), GL_UNSIGNED_INT, &cube.getIndices().front());

Now I can see the cube again, but it's misformed and there's some strange color fighting (both happening at the same vertex):
https://dl.dropboxusercontent.com/u/326576/2014-07-27_15-45-40.bmp
https://dl.dropboxusercontent.com/u/326576/2014-07-27_15-45-41.bmp
https://dl.dropboxusercontent.com/u/326576/2014-07-27_15-45-42.bmp

carsten neumann
07-27-2014, 08:49 AM
Your problem are the getter functions for e.g. vertices:



//other get... functions declared analogous
std::vector<GLfloat> SolidObject::getVertices()
{
return this->vertices;
}


This copies the vertices vector into a temporary (whose address you pass to glVertexPointer) and then it gets deleted immediately, meaning glVertexPointer points to garbage. Change the return type std::vector<GLfloat> const& to avoid the copy. Also, you can use vector<T>::data() to get a pointer to the stored values.

the_Seppi
07-27-2014, 09:13 AM
Thank you, the const& solved it... Does your last sentence mean that std::vector::data() and &std::vector::front() are analogous?

carsten neumann
07-27-2014, 11:57 AM
Yes, for most cases it'll be the same, but using data() makes the intention clearer IMHO. When storing some very strange types that have an overloaded operator& in the vector the result could be different, but that's a fairly made up edge case ;)