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.