Search code examples
c++opengldrawingmeshvao

c++ OpenGL Issue drawing mesh with vbo and vao stored within an object


I am trying to make a 3D chess game using c++ and OpenGL and while doing this I have come across a very strange bug where the triangle (a stand in for the chess piece) wont draw and I am left with a blank screen. When I create the mesh object in the main function rather than in the piece class it works without a problem but when it is in the piece class it does not. Is this an issue with pointers?

Here is my code:

So in the main I create a chess peice

Piece piece = Piece(vec3(0, 0, 0)); //Create a chess peice

and later on in the main method the mesh is drawn in a loop (the draw method will be shown later on in this post)

piece.GetMesh().Draw(); //Draw the chess peice

in the chess peice header I create the mesh information and object:

//Mesh information
vector <vec3> verticies;
vector <unsigned int> indicies;
Mesh mesh;

In the .cpp file for the chess peice I create the mesh object - verticies is a vec3 vector and indicies is an unsigned int vector.

mesh = Mesh(verticies, indicies); //Create the mesh object

This is the mesh constructor

Mesh::Mesh(vector <vec3> verticies, vector <unsigned int> indicies)
{
    unsigned int numVerticies = verticies.size();
    unsigned int numIndicies = indicies.size();

    vector <vec2> texCoords;

    //set the tex coords
    unsigned int texCoordsIndex = 0;

    for (unsigned int i = 0; i < numIndicies; i++)
    {
        switch (texCoordsIndex)
        {
        case 0:
            texCoords.push_back(vec2(0, 0));
            texCoordsIndex++;
            break;
        case 1:
            texCoords.push_back(vec2(0, 1));
            texCoordsIndex++;
            break;
        case 2:
            texCoords.push_back(vec2(1, 1));
            texCoordsIndex = 0;
            break;
        }
    }

    //Sends the mesh to the buffer
    drawCount = indicies.size();

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(NUM_BUFFERS, vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[POSITION_VB]);
    glBufferData(GL_ARRAY_BUFFER, verticies.size() * sizeof(verticies[0]), &verticies[0], GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vbo[TEXCOORD_VB]);
    glBufferData(GL_ARRAY_BUFFER, texCoords.size() * sizeof(texCoords[0]), &texCoords[0], GL_STATIC_DRAW);

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[INDEX_VB]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(indicies[0]), &indicies[0], GL_STATIC_DRAW);

    glBindVertexArray(0);
}

and here is the draw function whitch is a method within the Mesh class

void Mesh::Draw()
{
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

If the code I have provided is insufficient just ask and I can add more code sections to the thread.

Thank you.


Solution

  • Maybe that's a shot from the hip, but since you use glGenVertexArrays in the constructor of the class Mesh, I assume that you use glDeleteVertexArrays in the destructor of the class Mesh.
    In this line:

    mesh = Mesh(verticies, indicies);
    

    the following happens:

    You create a temporary Mesh object, which generates the vertex array object in its constructor. At this point all the data are valid and the vertex array object is generated (GPU). When you assign the object, the members of the temporary object are copied to the target object. Now 2 identical objects exists. Immediately after this is done, the temporary Mesh object gets destroyed and the vertex array object get deleted (glDeleteVertexArrays) by the destructor Mesh::~Mesh of the temporary object. At this point all the data are gone.

    Put a breakpoint in the destrutor Mesh::~Mesh, then you can simply track the expiration.

    Make the class not copyable and not copy constructable, but specify a move constructor and move operator.

    class Mesh 
    {
        Mesh(const Mesh &) = delete;
        Mesh & operator = (const Mesh &) = delete;
    
        Mesh( Mesh && );
        Mesh & operator = ( Mesh && );
    
        ....
    };
    

    See: