1
\$\begingroup\$

So, as the title might suggets, I'm currently trying to port a gui stack for my game engine from LWJGL(Java) to C++.

I originally had this following code in java:

        if(guiFramebuffer != 0 && colorTexture != 0)
        {           
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            
            GL30.glDisable(GL_CULL_FACE);
            GL30.glDisable(GL_DEPTH_TEST);
            
            GL30.glBlendFunc(GL30.GL_SRC_ALPHA, GL30.GL_ONE_MINUS_SRC_ALPHA);
            GL30.glEnable( GL30.GL_BLEND );
            
            imageShaderProgram.start();                     
            GL30.glBindVertexArray(screenFillingQuad.getVAOId());
            GL30.glEnableVertexAttribArray(0);
            GL30.glEnableVertexAttribArray(1);
            
            GL30.glActiveTexture(GL30.GL_TEXTURE0);
            GL30.glBindTexture(GL_TEXTURE_2D, colorTexture);
            
            // Returns 0
            System.out.println("Error before: " + GL30.glGetError());
            GL11.glDrawElements(GL11.GL_TRIANGLES, screenFillingQuad.getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
            // Returns 0
            System.out.println("Error after: " + GL30.glGetError());
            
            GL30.glDisableVertexAttribArray(1);
            GL30.glDisableVertexAttribArray(0);
            GL30.glBindVertexArray(0);
            
            imageShaderProgram.stop();
            GL30.glDisable(GL30.GL_BLEND);
        }
        else
            throw new NullPointerException("The framebuffer doesn't exist");        

Now, let's get to the method that ports this whole thing to C++ (note that we use a nullptr as the last argument for glDrawElements here too, this will later be important.)

C++ code:

    basicImageShader.bindShader();

    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    glBindVertexArray(planeVAO->getVAO());
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, colorTexture);

    std::cout << "BEFORE: ";
    GLint beforeError = glGetError();
    OUTPUT_OPENGLERR(beforeError); // Returns 0 (no error)
    
    // The issue seems to be that the indices buffer isn't being recognized.
    // If we take a look, we see the nullptr passed in as the last argument. If we change
    // it, we can achieve a similar effect as having a indices buffer, but only to an extend,
    // as this feature might be heavily slowing down the IO bus between graphics card and CPU.
    glDrawElements(GL_TRIANGLES, planeVAO->getVerticesAmount(), GL_UNSIGNED_INT, nullptr);

    GLint error = glGetError();
    OUTPUT_OPENGLERR(error); // Returns 1281 (GL_INVALID_VALUE)

    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);

    basicImageShader.unbindShader();

As you can see, the problematic gl call is equivalent to the java lwjgl code, but this call returns an error value of 1281.
Now, to an important observation:
This is not the case if we replace the nullptr in the draw call with an actual array. I'm not sure why this is the case. I'd be happy if you could provide any information and point out what I did wrong.

Edit: The user @MaximusMinimus pointed out that I should try glDrawArrays to narrow down the problem, so I've called glDrawArrays(GL_TRIANGLES, 0, 3), and this indeed worked. I should maybe add that he/she isn't proposing glDrawArrays as a solution, but rather as a step to narrow down the issue I have.

So, here is the code that loads the indices buffer:
IndicesVBOLoader.h

#pragma once

#include "AbstractVBOLoader.h"
#include <vector>

class IndicesVBOLoader : public AbstractVBOLoader
{
private:
    std::vector<GLuint> indices;

public:
    IndicesVBOLoader(std::vector<GLuint>& indices);

    virtual GLuint loadVBO() override;
};

IndicesVBOLoader.cpp:
#include "IndicesVBOLoader.h"

IndicesVBOLoader::IndicesVBOLoader(std::vector<GLuint>& indices) : AbstractVBOLoader(-1, -1)
{
    this->indices = indices;
}

GLuint IndicesVBOLoader::loadVBO()
{
    GLuint vbo;
    glGenBuffers(1, &vbo);

    glBindBuffer(1, vbo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size(), indices.data(), GL_STATIC_DRAW);

    return vbo;
}

Also, here is my old code, written in Java:
package de.budschie.engine.assets_management.geometry;

import org.lwjgl.opengl.GL30;

public class IndicesVBOAssignment extends ArrayVBOAssignment<int[]>
{

    public IndicesVBOAssignment(int[] array)
    {
        super(-1, -1, array);
    }

    @Override
    public int loadVBO()
    {
        int vbo = GL30.glGenBuffers();
        
        GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, vbo);
        GL30.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, array, GL30.GL_STATIC_DRAW);
        
        return vbo;
    }
}


And, finally, here is the way I load VAOs:

#include "VAOLoader.h"

VAO VAOLoader::loadVAO(std::vector<AbstractVBOLoader*>& loader)
{
    std::vector<GLuint> vbos = std::vector<GLuint>();

    GLuint vao;
    glCreateVertexArrays(1, &vao);

    std::optional<GLuint> currentVerticesAmount = std::optional<GLuint>();

    for (size_t i = 0; i < loader.size(); i++)
    {
        AbstractVBOLoader* currentVBOLoader = loader[i];

        glBindVertexArray(vao);

        if (!currentVerticesAmount.has_value())
        {
            std::optional<GLuint> optVerticesAmount = currentVBOLoader->getAmountPerVertex();
            if (optVerticesAmount.has_value())
                currentVerticesAmount = optVerticesAmount;
        }

        vbos.push_back(currentVBOLoader->loadVBO());
    }

    return VAO(vbos, vao, currentVerticesAmount.value());
}
\$\endgroup\$
8
  • \$\begingroup\$ The docs for glDrawElements says that GL_INVALID_VALUE is returned if count, so planeVAO->getVerticesAmount() is negative. Could this be the case? \$\endgroup\$
    – Tyyppi_77
    Feb 15 at 19:13
  • \$\begingroup\$ @Tyyppi_77 Yeah I saw that too... But unfortunately, after a quick debugging session, I could be sure that my vertices amount is not negative. In fact, the vertices amount is 3. \$\endgroup\$
    – Budschie
    Feb 15 at 20:01
  • \$\begingroup\$ Since you're only drawing a single triangle here, could you try it with glDrawArrays (GL_TRIANGLES, 0, 3) and see what happens? I'm not proposing this as a solution (otherwise it would be an answer not a comment) but as a troubleshooting step. If glDrawArrays works you have a problem in your index buffer setup. If glDrawArrays also fails your problem is most likely elsewhere. That should then help you narrow it down and get your glDrawElements call working. \$\endgroup\$ Feb 16 at 8:25
  • \$\begingroup\$ @MaximusMinimus Yes, indeed, if I call glDrawArrays like you've said, everything works fine. \$\endgroup\$
    – Budschie
    Feb 16 at 9:23
  • \$\begingroup\$ Can you check and confirm that you've actually got an index buffer bound when you draw? I see that you're using VAOs and the element array buffer binding is part of VAO state, not global state, so the glBindBuffer you do on creation won't hold after a VAO change. \$\endgroup\$ Feb 16 at 9:54
1
\$\begingroup\$

Alright, I found the issue: When I loaded the indices in, I (for whatever reason) decided to put a 1 instead of an enum (more precicely, GL_ELEMENT_ARRAY_BUFFER) to the glBindBuffer call that was being made whilest loading the indices. So, this is the corrected code:

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size(), indices.data(), GL_STATIC_DRAW);


Luckily, everything works fine now (or at least without any errors).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.