c++macosqtopenglvertex-array-object

glGenVertexArrays Not creating unique VAO when in Main loop but works otherwise


I'm currently working on a small editor for a game engine I have been writing. I've set up a file menu with Qt 5.6 that has an option to add a model which creates a new model and adds it to the rendering engine manager. On Mac OSX 10.11 (with OpenGL 3 or 4) this works fine. On Ubuntu 16.04 it only slightly works. I can initialize as many models as I want outside of the main loop. However inside the main loop I can only initialize as many models as I initialized outside of the main loop. When I pull up gdb inside of my IDE the issue seems to be related to VAO's. On OSX a new VAO is initialized for each model regardless of where I create a mesh. On Ubuntu, outside the main loop new VAO's are generated for each mesh. Inside the main loop the meshes that draw all have the same VAO as a previously created mesh outside the main loop. Once I create more models than have been created outside the main loop and a new VAO gets generated the model does not draw.

To summarize, in Ubuntu all models outside the main loop get their own VAO. Inside the main loop the VAO GLuint handle starts recounting from 2 again. Once the handle/tag counts past however many models were initialized outside the main loop, the models stop drawing. It works just fine one Mac OSX. I suspect this is an OpenGL/Qt issue so even though there's a lot of extra code for the engine I think the question is fair to ask. Link: https://github.com/BennetLeff/engine

Since there are quite a few files I'll only include what I think is necessary but I'll link to the github project as well. Sorry for the large code dump but tell me if there's anything else I can provide.

Mesh.cpp

#include "Mesh.h"
#include "Transform.h"

#include <stdio.h>

Mesh::Mesh(std::vector<glm::vec3> vertices, std::vector<glm::vec3> normals, std::vector<glm::vec2> textures, std::vector<GLuint> indices)
{
    drawCount_ = indices.size();

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

    glGenBuffers(NUMBUFFERS, vertexBufferObject_);

    // Position Attrib
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject_[POSITION_VB]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

    // Texture Attrib
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject_[TEXCOORD_VB]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(textures[0]) * textures.size(), textures.data(), GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

  // Normals Attrib
  glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject_[NORMAL_VB]);
  glBufferData(GL_ARRAY_BUFFER, sizeof(normals[0]) * normals.size(), normals.data(), GL_STATIC_DRAW);
  glEnableVertexAttribArray(2);
  glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexBufferObject_[INDEX_VB]);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), indices.data(), GL_STATIC_DRAW);

    glBindVertexArray(0);

  fprintf(stderr, "vao val %d\n", vertexArrayObject_);
}

Mesh::~Mesh()
{
    glDeleteVertexArrays(1, &vertexArrayObject_);
}

void Mesh::draw()
{
  glBindVertexArray(vertexArrayObject_);
    glDrawElements(GL_TRIANGLES, drawCount_, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
  if (glGetError())
      printf("GL error %d", glGetError());
}

Main.cpp

#include <stdio.h>

#ifdef __APPLE__
    #include <OpenGL/gl3.h>
#else
    #include <GL/glew.h>
#endif

#include "Camera.h"
#include "Model.h"

#include "Editor.h"
#include "RenderEngine.h"

#include <QApplication>

bool quit = false;

int main(int argc, char* argv[])
{
    auto WIDTH = 1024;
    auto HEIGHT = 800;

    /* 
     * Sets up a QApplication
     * with the proper formatting. This allows
     * GL versions to be set and so forth. Then
     * the QApplication is polled in the main loop
     * for events.
    */

    QApplication app(argc, argv);
    QSurfaceFormat format;
    format.setSamples(16);
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(4, 3);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);

    // Sets up Rendering Engine and Editor.
    auto cam = new Camera(glm::vec3(0, 6, -20), 70.0f, (float) WIDTH / (float) HEIGHT, 0.01f, 1000.0f);
    RenderEngine* engine = new RenderEngine(cam);
    Editor editor(engine, WIDTH, HEIGHT);

    editor.showEditor();

    /*
     * Must call Editor.show() before any other
     * OpenGL calls. This is mostly because of Qt.
    */
    // auto house = Model("./res/farm house/OBJ/Farmhouse OBJ.obj", "./res/farm house/Textures/Farmhouse Texture.jpg");
    // house.transform->getPosition()->z = 40;

    // auto model = Model("./res/Alfred/Alfred.obj", "./res/Alfred/alfred_dif.png");
    // auto model2 = Model("./res/Alfred/Alfred.obj", "./res/Alfred/alfred_dif.png");

    float counter = 0.0f;

    while (editor.isVisible())
    {
        app.processEvents();
        // model.transform->getRotation()->y = float(editor.getSliderValue()) / 10;
        // model.transform->getPosition()->z = editor.getManValue();
        counter += 0.1f;

        /*
         * Just updating window for now because
         * it may be faster. Need to benchmark this
         * and determine what is necessary.
        */
        editor.getWindow()->update();
    }

    return 0;
}

GUIWindow.cpp (inherits from QOpenGLWidget, QOpenGLFunctions)

#include <GL/glew.h>
#include "GUIWindow.h"

GUIWindow::GUIWindow(QWidget* parent, RenderEngine* engine) :
    QOpenGLWidget(parent), engine(engine) { }

void GUIWindow::initializeGL()
{
    // Set up the rendering context, load shaders and other resources, etc.:
    initializeOpenGLFunctions();

    // If not on OSX we need to include
    // OpenGL as an extension
    #ifndef __APPLE__
        glewExperimental = GL_TRUE;

        GLenum err = glewInit();
        if (GLEW_OK != err) {
            /* Problem: glewInit failed, something is seriously wrong. */
            fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        }
    #endif

    // Depth test not enabled by default.
    glEnable(GL_DEPTH_TEST);
}

void GUIWindow::resizeGL(int w, int h) {  }

void GUIWindow::paintGL()
{
    // Draw the scene
    clear(0.1, 0.4, 0.6, 1.0);

    // Draw all Models
    engine->draw();
}

void GUIWindow::clear(float r, float g, float b, float a)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glClearColor(r, g, b, a);
}

GUIWindow::~GUIWindow() { }

Model.cpp (excluding model loading which just gets data from AssImp)

void Model::draw(Camera* cam)
{
    shader.draw();
    shader.update(transform, cam);
    tex.bind(0);
    modelMesh->draw();
}

void Model::bindTexture(Texture tex)
{
    this->tex = tex;
}

RenderingEngine.cpp

#include "RenderEngine.h"

RenderEngine::RenderEngine(Camera* cam)
    : cam(cam)
{
    this->init();
}

void RenderEngine::init()
{
    // If not on OSX we need to include
    // OpenGL as an extension
    #ifndef __APPLE__
        glewExperimental = GL_TRUE;

        GLenum err = glewInit();
        if (GLEW_OK != err) {
            /* Problem: glewInit failed, something is seriously wrong. */
            fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        }

        glEnable(GL_DEPTH_TEST);
    #endif
}

void RenderEngine::addModel(Model model)
{
    printf("added model \n");
    this->models.push_back(model);
    for (int i = 0; i < this->models.size(); i++)
        printf("Model Pos: (%g, %g, %g) \n", models[i].transform->getPosition()->x,
               models[i].transform->getPosition()->y,
               models[i].transform->getPosition()->z);
    printf("there are %d models now \n", this->models.size());
}

void RenderEngine::draw()
{
     for (int i = 0; i < this->models.size(); i++)
        models[i].draw(this->cam);
}

Editor.cpp (minus most Qt Calls which just set up the window)

#include "Editor.h"

Editor::Editor(RenderEngine* renderEngine, int width, int height)
{
    frame = 0;
    this->width = width;
    this->height = height;
    this->engine = renderEngine;
    this->window = new GUIWindow(0, renderEngine);
}

void Editor::initialize()
{
    // Set up the rendering context, load shaders and other resources, etc.:
    // initializeOpenGLFunctions();
    glViewport(0, 0, width, height);
    setupWidgets();
}


float Editor::getRandNum()
{
    srand(time(0));
    float num = rand() % 10 + 1;
    fprintf(stderr, "%d \n", num);
    return num;
}

void Editor::addModelToScene()
{
    srand (time(NULL));

    auto trans = new Transform();
    trans->getPosition()->x = rand() % 10 + 1;
    trans->getPosition()->y = rand() % 10 + 1;
    trans->getPosition()->z = rand() % 10 + 1;
    engine->addModel(Model("./res/Alfred/Alfred.obj", "./res/Alfred/alfred_dif.png", trans));
    fprintf(stderr, "Add a Model \n");
}

void Editor::showEditor()
{
    // Sets up the rest of the widgets locations.
    setupWidgets();
    // Sets up the QMainWindow.
    this->show();
}

void Editor::addModel(Model model)
{
    engine->addModel(model);
}

Solution

  • I don't know why I was down voted but I've finally figured out the issue. In Editor::addModelToScene() I needed to call window.makeCurrent() right before I called addModel(). I have zero idea why this would work on OSX regardless. Also thanks to this answer for giving me the idea to try this.