-1

I'm learning opengl and following the tutorial on a 2D game from https://learnopengl.com/In-Practice/2D-Game/Breakout and once I made it to the part where something was supposed to show up on the screen, nothing was drawn. After a few frustrating days, I eventually was able to narrow the problem down to the Shader class destructor being called before the program stopped. But when I put all of the drawing code in one file, the destructor was not called and everything was drawn fine. I have recently fixed the destructor problem by returning pointers from the getters in the resource manager. However, it still does not draw. My code is here:

Main.cpp

#include <iostream>
#include <GL/glew.h>
#include <SFML/Graphics.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Game.hpp"

int main() {
    sf::RenderWindow window(sf::VideoMode(800, 600), "Test", sf::Style::Default, sf::ContextSettings(3, 3));

    if (!glewInit() == GLEW_OK) {
        std::cout << "Failed to initialize GLEW!";
        return -1;
    }

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

    Game::Game game(800.f, 600.f);
    game.__init__();

    sf::Clock clock;
    float deltaTime = 0.f;
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }
        glClearColor(0.1f, 0.1f, 0.1f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);

        game.render();

        window.display();
    }
    Resource::ResourceManager::__delete__();
    return 0;
}

Game.hpp

#pragma once

#include "Resources/ResourceManager.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <map>

namespace Game {

    enum class GameState {
        TITLE,
        ACTIVE,
        PAUSED,
        WIN,
        LOOSE
    };

    class Game
    {
    public:
        Game(float w_width, float w_height);
        ~Game();

        void __init__();
        void setKey(sf::Keyboard::Key key, bool isPressed);
        void update();
        void render();

    private:
        GLuint _vao;
        float _w_width;
        float _w_height;
        std::map<sf::Keyboard::Key, bool> _key_states;
    };
};

Game.cpp

#include "Game.hpp"

namespace Game {

    Game::Game(float w_width, float w_height)
    {
        _w_width = w_width;
        _w_height = w_height;
    }

    Game::~Game()
    {

    }

    void Game::__init__()
    {
        glm::mat4 Projection = glm::ortho(0.f, _w_width, _w_height, 0.f, -1.f, 1.f);
        glm::mat4 Model = glm::scale(glm::mat4(1.f), glm::vec3(100.f, 70.f, 1.f));

        Resource::ResourceManager::addShader(0, "Shaders/simpleVertexShader.glsl", "Shaders/simpleFragmentShader.glsl");

        Resource::ResourceManager::addTexture(0, "Textures/block.png");
        Resource::ResourceManager::addTexture(1, "Textures/block_solid.png");
        Resource::ResourceManager::addTexture(2, "Textures/background.jpg");

        Resource::Shader* main_shader = Resource::ResourceManager::getShader(0);
        main_shader->setMat4("Projection", Projection);
        main_shader->setMat4("Model", Model);
        main_shader->setVec3("Color", glm::vec3(0.1, 0.8, 0.1));

        glGenVertexArrays(1, &_vao);

        GLfloat vertex_data[] = {
            0.f, 0.f, 0.f, 0.f,
            1.f, 0.f, 1.f, 0.f,
            1.f, 1.f, 1.f, 1.f,

            1.f, 1.f, 1.f, 1.f,
            0.f, 1.f, 0.f, 1.f,
            0.f, 0.f, 0.f, 0.f
        };

        GLuint vbo;
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);

        glBindVertexArray(_vao);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }

    void Game::setKey(sf::Keyboard::Key key, bool isPressed)
    {
        _key_states[key] = isPressed;
    }

    void Game::update()
    {

    }

    void Game::render()
    {
        Resource::Texture* texture = Resource::ResourceManager::getTexture(0);
        Resource::Shader* shader = Resource::ResourceManager::getShader(0);
        texture->bind();
        shader->use();
        glBindVertexArray(_vao);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        glBindVertexArray(0);
    }
};

ResourceManager.hpp

#pragma once

#include <unordered_map>
#include <fstream>
#include <string>
#include <iostream>
#include "Shader.hpp"
#include "Texture.hpp"

namespace Resource {

    class ResourceManager {
    public:
        static void addShader(uint8_t id, std::string vertexShaderSource, std::string fragmentShaderSource);
        static void addTexture(uint8_t id, std::string imageSource);

        static Shader* getShader(uint8_t id);
        static Texture* getTexture(uint8_t id);

        static void __delete__();

    private:
        ResourceManager() {}
        static std::string _loadShaderFromFile(std::string shaderSource);

    public:
        static std::unordered_map<uint8_t, Shader*> _shaders;
        static std::unordered_map<uint8_t, Texture*> _textures;
    };
};

ResourceManager.cpp

#include "ResourceManager.hpp"

namespace Resource {
    std::unordered_map<uint8_t, Shader*> ResourceManager::_shaders;
    std::unordered_map<uint8_t, Texture*> ResourceManager::_textures;

    void ResourceManager::addShader(uint8_t id, std::string vertexShaderSource, std::string fragmentShaderSource)
    {
        _shaders[id] = new Shader(_loadShaderFromFile(vertexShaderSource).c_str(), _loadShaderFromFile(fragmentShaderSource).c_str());
    }

    void ResourceManager::addTexture(uint8_t id, std::string imageSource)
    {
        _textures[id] = new Texture(imageSource);
    }

    Shader* ResourceManager::getShader(uint8_t id)
    {
        return _shaders[id];
    }

    Texture* ResourceManager::getTexture(uint8_t id)
    {
        return _textures[id];
    }

    void ResourceManager::__delete__()
    {
        for (auto iter : _shaders) {
            delete iter.second;
        }
        for (auto iter : _textures) {
            delete iter.second;
        }
    }

    std::string ResourceManager::_loadShaderFromFile(std::string shaderSource)
    {
        std::ifstream file(shaderSource);
        if (!file.is_open()) {
            std::cout << "Failed to load shader: " << shaderSource << "!" << std::endl;
            return "";
        }
        std::string line;
        std::string lines;
        while (std::getline(file, line)) {
            lines += line + '\n';
        }
        return lines;
    }
};

Shader.hpp

#pragma once

#include <GL/glew.h>
#include <glm/glm.hpp>

namespace Resource {

    class Shader {
    public:
        Shader(const char* vertexShaderSource, const char* fragmentShaderSource);
        Shader();
        ~Shader();

        void use();

        void setMat4(const char* location, glm::mat4 matrix);
        void setVec3(const char* location, glm::vec3 vector);
        void setVec4(const char* location, glm::vec4 vector);

        void printID();

        Shader(const Shader&) = delete;
        Shader& operator=(const Shader&) = delete;

    private:
        GLuint _id;
    };
};

Shader.cpp

#include "Shader.hpp"

#include <iostream>

namespace Resource {

    Shader::Shader(const char* vertexShaderSource, const char* fragmentShaderSource)
    {
        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);

        _id = glCreateProgram();
        glAttachShader(_id, vertexShader);
        glAttachShader(_id, fragmentShader);
        glLinkProgram(_id);

        glDetachShader(_id, vertexShader);
        glDetachShader(_id, fragmentShader);
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    }

    Shader::Shader()
    {
    }

    Shader::~Shader()
    {
        glDeleteProgram(_id);
    }

    void Shader::use()
    {
        glUseProgram(_id);
    }

    void Shader::setMat4(const char* location, glm::mat4 matrix)
    {
        glUniformMatrix4fv(glGetUniformLocation(_id, location), 1, GL_FALSE, &matrix[0][0]);
    }

    void Shader::setVec3(const char* location, glm::vec3 vector)
    {
        glUniform3fv(glGetUniformLocation(_id, location), 1, &vector[0]);
    }

    void Shader::setVec4(const char* location, glm::vec4 vector)
    {
        glUniform4fv(glGetUniformLocation(_id, location), 1, &vector[0]);
    }

    void Shader::printID()
    {
        std::cout << _id << std::endl;
    }
};

Texture.hpp

#pragma once

#include <GL/glew.h>
#include <SFML/Graphics/Image.hpp>

namespace Resource {

    class Texture {
    public:
        Texture(std::string image_src);
        Texture();
        ~Texture();

        void bind();

        Texture(const Texture&) = delete;
        Texture& operator=(const Texture&) = delete;

    private:
        GLuint _id;
    };
}

Texture.cpp

#include "Texture.hpp"

namespace Resource {

    Texture::Texture(std::string image_src)
    {
        glGenTextures(1, &_id);
        bind();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glGenerateMipmap(GL_TEXTURE_2D);
        sf::Image texture;
        texture.loadFromFile(image_src);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.getSize().x, texture.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.getPixelsPtr());
    }

    Texture::Texture()
    {
    }

    Texture::~Texture()
    {
        glDeleteTextures(1, &_id);
    }

    void Texture::bind()
    {
        glBindTexture(GL_TEXTURE_2D, _id);
    }
};

I am also somewhat new to c++ so sorry in advance if there are any obvious improvements that could be made.

  • 1
    Please include the minimum relevant code needed to reproduce the problem **in the question itself**. Links to off-site resources tend to break which makes the answer useless for future visitors. – BDL Aug 05 '18 at 06:23
  • I changed the question a little bit. It is now different from their problem. – That_one_guy Aug 05 '18 at 06:42
  • Not the asnwer, but identifiers with double underscores in them are reserved and shouldn't be used: https://stackoverflow.com/a/228797/2752075 – HolyBlackCat Aug 05 '18 at 11:21

1 Answers1

1

glUniform* specifies the value of a uniform variable for the current program object.

This means you have to call glUseProgram to installs the propper program object as part of current rendering state, befaor you can set the value of the uniform by glUniform*.

This means that you have to installs main_shader, befor you set the values of the uniforms in the method Game::__init__ to solve the issue:

main_shader->use();
main_shader->setMat4("Projection", Projection);
main_shader->setMat4("Model", Model);
main_shader->setVec3("Color", glm::vec3(0.1, 0.8, 0.1));

Alternatively you can use glProgramUniform* (since OpenGL 4.1), which allows you to specify the program object, where the value should be set to.


The 2nd issue is, that GL_ARRAY_BUFFER is not a valid enumeration constant for glDrawArrays. If you would check for OpenGL erros by glGetError you would get GL_INVALID_ENUM error.

In your case you have to use GL_TRIANGLES:

glDrawArrays(GL_TRIANGLES, 0, 6);
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • I tried your suggestion and it still did not work.And when you call glUniform* it will need an id for the variable in the shader which you get with glGetUniformLocation. Therefore, I do not think you need to bind the shader program. – That_one_guy Aug 05 '18 at 07:04
  • Yes. That was the way it was intended to be used in my comment. For example, glUniformMatrix4fv(glGetUniformLocation(programID, "vairable_name"), 1, GL_FALSE, &matrix[0][0]). Sorry if it did not come across that way. – That_one_guy Aug 05 '18 at 07:08
  • I feel stupid for letting that happen. I fixed that now, and for whatever reason, it still does not draw. – That_one_guy Aug 05 '18 at 14:49
  • #version 330 core layout (location = 0) in vec4 vertex; out vec2 uv; uniform mat4 Projection; uniform mat4 Model; void main(){ uv = vertex.zw; gl_Position = Projection * Model * vec4(vertex.xy, 0.0, 1.0); } – That_one_guy Aug 05 '18 at 15:06
  • Sadly that does not wok either. – That_one_guy Aug 05 '18 at 15:35
  • Interesting... I stopped multiplying the model and projection matrices and it displayed something. It must be something with the model or projection matrix. – That_one_guy Aug 05 '18 at 15:59
  • Thanks for the help you did give me. I finally fixed the problem! – That_one_guy Aug 05 '18 at 16:08