[OpenGL and C++] Problems with Mesh class

Hi all!

This is my first post on the forum :slight_smile:
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.
");
        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.
");
        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 :open_mouth: 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? :slight_smile:
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;
}

For A, try to store pointers to your meshes in the std vector.
For B, enable depth testing in your OpenGL initialization function.

[QUOTE=Silence;1287985]For A, try to store pointers to your meshes in the std vector.
For B, enable depth testing in your OpenGL initialization function.[/QUOTE]

Thanks a lot for your answer!
Your solution for A indeed worked :slight_smile: 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 :slight_smile:

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 :frowning:

[QUOTE=Silence;1287985]For A, try to store pointers to your meshes in the std vector.
For B, enable depth testing in your OpenGL initialization function.[/QUOTE]

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

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.

Okay :slight_smile:
Thanks a lot for your help!