PDA

View Full Version : [OpenGL and C++] Problems with Mesh class



Charogne
07-26-2017, 01:41 AM
Hi all!

This is my first post on the forum :)
I am a PhD student in Physics, passionate in videogames, and I am trying to experiment a bit with OpenGL and C++ to build from scratch a very basic and naive ''game engine'' for some very small application.
After managing to write correctly working shaders and draw a triangle and rotate it in space in the simplest possible way, I wanted to start to build a class framework to automatize some of the procedures.

For instance, I wrote the following class for meshes to be drawn on screen:


class Mesh
{
private:
int Nv;
GLfloat *Positions;
GLfloat *Colors;

bool OnDraw;

protected:
GLuint vao;
GLuint vbo;

public:
//Constructors
Mesh(void);
Mesh(int nv);
Mesh(int nv, GLfloat positions[][4], GLfloat colors[][4]);

//Set methods
void SetPositions(GLfloat positions[][4]);
void SetColors(GLfloat colors[][4]);
void setOnDraw(bool onDraw);

//Get methods
bool getOnDraw(void);

//Methods to vary positions and colors

//Methods to output positions and colors
void PrintPositions(void);
void PrintColors(void);

//Drawing methods
void Initialize(void);
void Draw(void);
void Update(void);

//Destructor
~Mesh(void);
};


whose these are the full declarations of the interesting methods:



Mesh::Mesh(int nv, GLfloat positions[][4], GLfloat colors[][4])
{
int i, j;

//Set the number of vertices of the mesh
this->Nv = nv;

//And consequently allocate the memory for the positions and the colors
//For the positions
if((this->Positions = (GLfloat*)std::malloc(this->Nv * 4 * sizeof(GLfloat))) == NULL)
{
printf("Error allocating the memory for the vertices' position of the mesh, program will be arrested.\n");
exit(EXIT_FAILURE);
}
//And for the colors
if((this->Colors = (GLfloat*)std::malloc(this->Nv * 4 * sizeof(GLfloat))) == NULL)
{
printf("Error allocating the memory for the vertices' position of the mesh, program will be arrested.\n");
exit(EXIT_FAILURE);
}


//Then, set the positions and the colors
for(i=0;i<this->Nv;i++)
{
for(j=0;j<4;j++)
{
this->Positions[4*i+j] = positions[i][j];
this->Colors[4*i+j] = colors[i][j];
}
}


//And set the mesh not to be drawn
OnDraw = false;
}




void Mesh::Initialize(void)
{
GLuint VertexPosition, VertexColor;


//Generate the vertex array object
glGenVertexArrays(1, &(this->vao));
//And select it
glBindVertexArray(this->vao);

//Then generate the buffer for the verteces' data
glGenBuffers(1, &(this->vbo));
//And select it
glBindBuffer(GL_ARRAY_BUFFER, this->vbo);


//Let's then allocate in the buffer the memory for the data
glBufferData(GL_ARRAY_BUFFER, ((this->Nv * 4 * sizeof(GLfloat)) + (this->Nv * 4 * sizeof(GLfloat))), NULL, GL_STATIC_DRAW);
//And send the data to the buffer
glBufferSubData(GL_ARRAY_BUFFER, 0, (this->Nv * 4 * sizeof(GLfloat)), this->Positions);
glBufferSubData(GL_ARRAY_BUFFER, (this->Nv * 4 * sizeof(GLfloat)), (this->Nv * 4 * sizeof(GLfloat)), this->Colors);


//Then, associate shader variables with buffer's positions
//For vertex positions
VertexPosition = glGetAttribLocation(ShaderProgram, "vPosition");
glEnableVertexAttribArray(VertexPosition);
glVertexAttribPointer(VertexPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

//And vertex colors
VertexColor = glGetAttribLocation(ShaderProgram, "vColor");
glEnableVertexAttribArray(VertexColor);
glVertexAttribPointer(VertexColor, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET((this->Nv * 4 * sizeof(GLfloat))));


//Then, unset the vertex array object and vertex buffer objects
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);


//In the end, set this mesh to be drawn
OnDraw = true;
}




void Mesh::Draw(void)
{
//Select the vertex array of the mesh
glBindVertexArray(this->vao);

//Draw the data stored in this vertex array
glDrawArrays(GL_TRIANGLES, 0, this->Nv);

//And unbind the vertex array object
glBindVertexArray(0);
}




void Mesh::Update(void)
{
//Select the mesh's vertex array object
glBindVertexArray(this->vao);


//Select the mesh's vertex buffer object
glBindBuffer(GL_ARRAY_BUFFER, this->vbo);


//And send the updated data to the buffer
glBufferSubData(GL_ARRAY_BUFFER, 0, (this->Nv * 4 * sizeof(GLfloat)), this->Positions);
glBufferSubData(GL_ARRAY_BUFFER, (this->Nv * 4 * sizeof(GLfloat)), (this->Nv * 4 * sizeof(GLfloat)), this->Colors);


//And unbind the vertex array object and vertex buffer object
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}


So, the idea of the class is that after having instanced an object of the class, having specified the positions and colors of the vertices, calling its Initialize() method a vertex array object and vertex buffer object for the mesh are created, and the data of the vertices are sent to the correct buffers. Then, by calling the Draw() method (typically in the onDisplay callback) the mesh is drawn on the screen. The method Update allows to change the data about the vertices in the buffer after having changed the positions and colors of the mesh's colors.
If I just represent one single mesh with this method, it correctly works. However, as soon as I try to create more meshes, I get weird behaviours that I do not fully understand. Here they are.


A) If I try to create a std::vector of Meshes in the main.cpp file of the OpenGL application, adding (for example) two meshes and initializing them with the following code:



//Initialize the first (coloured) triangle
GLfloat MyTriangle_Positions[3][4] = {{1.f,-1.f,-0.5f,1.f}, {1.f,1.f,-0.5f,1.f}, {1.f,0.f,0.5f,1.f}};
GLfloat MyTriangle_Colors[3][4] = {{1.f,0.f,0.f,1.f}, {0.f,1.f,0.f,1.f}, {0.f,0.f,1.f,1.f}};
Meshes.push_back(Mesh(3, MyTriangle_Positions, MyTriangle_Colors));
Meshes[0].Initialize();

//Initialize the second (white) triangle
GLfloat MyTriangle_Positions2[3][4] = {{-1.f,-1.f,-0.5f,1.f}, {-1.f,1.f,-0.5f,1.f}, {-1.f,0.f,0.5f,1.f}};
GLfloat MyTriangle_Colors2[3][4] = {{1.f,1.f,1.f,1.f}, {1.f,1.f,1.f,1.f}, {1.f,1.f,1.f,1.f}};
Meshes.push_back(Mesh(3, MyTriangle_Positions2, MyTriangle_Colors2));
Meshes[1].Initialize();


and then I draw them on screen with the following code in the onDisplay callback:



for(int i=0;i<Meshes.size();i++)
{
Meshes[i].Draw();
}


only the second one of the declared meshes (the white one) is represented, and both of the mesh objects have vao (the vertex array object index) equal to one (so I guess that somehow what is happening is that I use the same vertex array object for both, and so when initializing the second one I overwrite the vertex data of the first one and I only represent the second one).
However, if I initialize them with the following code



//Initialize the first (coloured) triangle
GLfloat MyTriangle_Positions[3][4] = {{1.f,-1.f,-0.5f,1.f}, {1.f,1.f,-0.5f,1.f}, {1.f,0.f,0.5f,1.f}};
GLfloat MyTriangle_Colors[3][4] = {{1.f,0.f,0.f,1.f}, {0.f,1.f,0.f,1.f}, {0.f,0.f,1.f,1.f}};
Meshes.push_back(Mesh(3, MyTriangle_Positions, MyTriangle_Colors));

//Initialize the second (white) triangle
GLfloat MyTriangle_Positions2[3][4] = {{-1.f,-1.f,-0.5f,1.f}, {-1.f,1.f,-0.5f,1.f}, {-1.f,0.f,0.5f,1.f}};
GLfloat MyTriangle_Colors2[3][4] = {{1.f,1.f,1.f,1.f}, {1.f,1.f,1.f,1.f}, {1.f,1.f,1.f,1.f}};
Meshes.push_back(Mesh(3, MyTriangle_Positions2, MyTriangle_Colors2));

Meshes[0].Initialize();
Meshes[1].Initialize();


both the meshes are perfectly represented :O Why is this happening?


B) If instead of using a std::vector of meshes I just declare two spare meshes as:



Mesh1 = Mesh(3, MyTriangle_Positions, MyTriangle_Colors);
Mesh1.Initialize();
Mesh2 = Mesh(3, MyTriangle_Positions2, MyTriangle_Colors2);
Mesh2.Initialize();


and I draw them like this:



Mesh1.Draw();
Mesh2.Draw();


both the meshes are displayed. However, no matter how I rotate the camera, and which is the relative position of the meshes one with respect to the other and with respect to the camera, the Mesh2 is always displayed on top of the Mesh1. If I draw them in inverted order, namely with the code:



Mesh2.Draw();
Mesh1.Draw();


the Mesh1 is always represented on top of mesh 1.


So, in the end, I would really like not only to solve this problems, but to really understand where they originate. Can you help me guys? :)
For instance, these are the very basic vertex and fragment shaders I am using:

- Vertex shader:


# version 410

uniform mat4 ProjectionMatrix;
uniform mat4 ModelViewMatrix;
in vec4 vPosition;
in vec4 vColor;

out vec4 color;

void main()
{
color = vColor;
//gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vPosition;
gl_Position = ProjectionMatrix * ModelViewMatrix * vPosition;
}


- Fragment shader:


# version 410

in vec4 color;

out vec4 fColor;

void main()
{
fColor = color;
}

Silence
07-26-2017, 02:41 AM
For A, try to store pointers to your meshes in the std vector.
For B, enable depth testing in your OpenGL initialization function.

Charogne
07-26-2017, 06:54 AM
For A, try to store pointers to your meshes in the std vector.
For B, enable depth testing in your OpenGL initialization function.

Thanks a lot for your answer!
Your solution for A indeed worked :) But could you be so gentle to also give me an idea of which was the problem, or some reference for it? Thanks in advance :)

However, I have not solved the B problem yet. Indeed, if - just before initializing the meshes and starting the GL main loop - I use the code


glEnable(GL_DEPTH_TEST)

I have the exact same problem :(

Charogne
07-26-2017, 06:58 AM
For A, try to store pointers to your meshes in the std vector.
For B, enable depth testing in your OpenGL initialization function.

Googling a bit I found a solution. It works as soon as I add the option GLUT_DEPTH in glut initialization.

Silence
07-26-2017, 07:29 AM
But could you be so gentle to also give me an idea of which was the problem, or some reference for it?

I simply guessed that you were expecting to play directly with the variables in the vector while in fact you were working with a copy (returned by a function or so), at some points of your code. This can be a common issue with C++. Using pointers in common C++ containers will avoid such issues.

Charogne
07-26-2017, 07:58 AM
Okay :)
Thanks a lot for your help!