diff --git a/Callbacks.cpp b/Callbacks.cpp new file mode 100644 index 0000000..6d10a4a --- /dev/null +++ b/Callbacks.cpp @@ -0,0 +1,25 @@ +#include "Callbacks.h" +#include + +#include "Game.h" + + +void errorCallback(int error, const char* description) { + fputs(description, stderr); +} + +void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { + Game& game = Game::getInstance(); + game.m_textArea.keyInput(key, scancode, action, mods); +} + +void charCallback(GLFWwindow* window, unsigned int codepoint) { + Game& game = Game::getInstance(); + game.m_textArea.charInput(codepoint); +} + +void windowResizeCallback(GLFWwindow* window, int width, int height) { + if (height == 0) return; + glViewport(0, 0, width, height); +} + diff --git a/Callbacks.h b/Callbacks.h new file mode 100644 index 0000000..402a524 --- /dev/null +++ b/Callbacks.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + + +void errorCallback(int error, const char* description); + +void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); + +void charCallback(GLFWwindow* window, unsigned int codepoint); + +void windowResizeCallback(GLFWwindow* window, int width, int height); \ No newline at end of file diff --git a/Game.cpp b/Game.cpp new file mode 100644 index 0000000..2111cd8 --- /dev/null +++ b/Game.cpp @@ -0,0 +1,115 @@ +#include "Game.h" +#include "Callbacks.h" +#include "Utils.h" + +void Game::initOpenGL() { + + glfwSetErrorCallback(errorCallback); + + if (!glfwInit()) { + fprintf(stderr, "Failed to initialize GLFW.\n"); + exit(EXIT_FAILURE); + } + + glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + + int width = 800; + int height = 800; + + m_window = glfwCreateWindow(width, height, "OpenGL", NULL, NULL); + + if (!m_window) + { + fprintf(stderr, "Failed to create window.\n"); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(m_window); + glfwSwapInterval(1); + + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to initialize GLEW.\n"); + glfwTerminate(); + exit(EXIT_FAILURE); + } +} + +void Game::init() { + + glClearColor(0.8f, 0.8f, 0.8f, 1); + glEnable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + + glfwSetWindowSizeCallback(m_window, windowResizeCallback); + glfwSetKeyCallback(m_window, keyCallback); + glfwSetCharCallback(m_window, charCallback); + + m_sp2d = new ShaderProgram("shaders/v_2d.glsl", "shaders/g_2d.glsl", "shaders/f_2d.glsl"); + m_sp2dPost = new ShaderProgram("shaders/v_post.glsl", nullptr, "shaders/f_post.glsl"); + + m_asciiTexture = loadTexture("assets/ascii.png"); + + m_frameColorBuffer = createColorBuffer(m_window); + m_frameDepthBuffer = createDepthBuffer(m_window); + m_frameBuffer = createFrameBuffer(m_frameColorBuffer, m_frameDepthBuffer); + +} + +void Game::update(double delta) { + m_textArea.update(delta); + +} + +void Game::draw() { + glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + m_sp2d->use(); + glEnableVertexAttribArray(m_sp2d->a("pos")); + glEnableVertexAttribArray(m_sp2d->a("dim")); + glEnableVertexAttribArray(m_sp2d->a("tex")); + glEnableVertexAttribArray(m_sp2d->a("inv")); + + bindTilemap(m_sp2d, m_asciiTexture, 2, glm::ivec2(16, 16)); + + int width, height; + glfwGetWindowSize(m_window, &width, &height); + glUniform2f(m_sp2d->u("window"), width, height); + glUniform1f(m_sp2d->u("layer"), 0.0f); + + m_textArea.draw(m_sp2d); + + // The value zero is reserved to represent the default framebuffer provided by the windowing system + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDisable(GL_DEPTH_TEST); + glClear(GL_COLOR_BUFFER_BIT); + + m_sp2dPost->use(); + glActiveTexture(GL_TEXTURE0 + 3); + glBindTexture(GL_TEXTURE_2D, m_frameColorBuffer); + glDrawArrays(GL_TRIANGLES, 0, 6); + glUniform1i(m_sp2dPost->u("colorBuffer"), 3); + glfwSwapBuffers(m_window); +} + +void Game::clean() { + delete m_sp2d; + delete m_sp2dPost; + + glDeleteFramebuffers(1, &m_frameBuffer); + glDeleteTextures(1, &m_frameColorBuffer); + glDeleteRenderbuffers(1, &m_frameDepthBuffer); +} + +void Game::cleanOpenGL() { + glfwDestroyWindow(m_window); + glfwTerminate(); +} + + diff --git a/Game.h b/Game.h new file mode 100644 index 0000000..a13c943 --- /dev/null +++ b/Game.h @@ -0,0 +1,64 @@ +#pragma once + +#include "Callbacks.h" +#include "ShaderProgram.h" +#include "GraphicalTextArea.h" + + +class Game{ + +private: + void initOpenGL(); + void init(); + void update(double delta); + void draw(); + void clean(); + void cleanOpenGL(); + +public: + static Game& getInstance() { + static Game instance; + return instance; + } + + void run() { + initOpenGL(); + init(); + + double lastFrameTime = glfwGetTime(); + + while (!glfwWindowShouldClose(m_window)) + { + double newFrameTime = glfwGetTime(); + double delta = newFrameTime - lastFrameTime; + + update(delta); + draw(); + + lastFrameTime = newFrameTime; + glfwPollEvents(); + } + + clean(); + cleanOpenGL(); + } + +private: + GLFWwindow* m_window; + GraphicalTextArea m_textArea{ }; + + ShaderProgram* m_sp2d{ nullptr }; + ShaderProgram* m_sp2dPost{ nullptr }; + + GLuint m_frameColorBuffer; + GLuint m_frameDepthBuffer; + GLuint m_frameBuffer; + + GLuint m_asciiTexture; + + friend void errorCallback(int error, const char* description); + friend void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); + friend void charCallback(GLFWwindow* window, unsigned int codepoint); + friend void windowResizeCallback(GLFWwindow* window, int width, int height); +}; + diff --git a/GraphicalTextArea.cpp b/GraphicalTextArea.cpp index 47dac95..e9bdecc 100644 --- a/GraphicalTextArea.cpp +++ b/GraphicalTextArea.cpp @@ -87,14 +87,16 @@ bool GraphicalTextArea::charInput(unsigned int codepoint) { return true; } -void GraphicalTextArea::draw(ShaderProgram* shader, double delta) { - +void GraphicalTextArea::update(double delta) { // update cursor blink cursorBlinkTimer.update(delta); if (cursorBlinkTimer.done()) { cursorBlinkState = !cursorBlinkState; cursorBlinkTimer.restart(); } +} + +void GraphicalTextArea::draw(ShaderProgram* shader) { data.clear(); diff --git a/GraphicalTextArea.h b/GraphicalTextArea.h index 1104d41..4fbe87e 100644 --- a/GraphicalTextArea.h +++ b/GraphicalTextArea.h @@ -20,7 +20,8 @@ class GraphicalTextArea : public TextArea { bool keyInput(int key, int scancode, int action, int mods); bool charInput(unsigned int codepoint); - void draw(ShaderProgram* shader, double delta); + void update(double delta); + void draw(ShaderProgram* shader); private: std::vector data; diff --git a/Main.cpp b/Main.cpp index 43c4f5a..24aa668 100644 --- a/Main.cpp +++ b/Main.cpp @@ -6,281 +6,13 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lodepng.h" - -#include "LoadOBJ.h" -#include "ShaderProgram.h" -#include "Timer.h" -#include "GraphicalTextArea.h" - - -ShaderProgram* sp2D; -ShaderProgram* spPost; - -GLuint frameColorBuffer; -GLuint frameDepthBuffer; -GLuint frameBuffer; - -GLuint asciiTexture; - -GraphicalTextArea textArea("The quick brown\nfox jumps over\nthe lazy dog"); - - -GLuint createColorBuffer(GLFWwindow* window) { - - int width, height; - glfwGetFramebufferSize(window, &width, &height); - - unsigned int colorBuffer; - glGenTextures(1, &colorBuffer); - glBindTexture(GL_TEXTURE_2D, colorBuffer); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); - - return colorBuffer; -} - -GLuint createDepthBuffer(GLFWwindow* window) { - - int width, height; - glfwGetFramebufferSize(window, &width, &height); - - unsigned int depthBuffer; - glGenRenderbuffers(1, &depthBuffer); - glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - - return depthBuffer; -} - -GLuint createFrameBuffer(GLuint color_buffer, GLuint depth_buffer) { - GLuint frameBuffer; - - glGenFramebuffers(1, &frameBuffer); - glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); - - glFramebufferTexture2D( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, color_buffer, 0); - - glFramebufferRenderbuffer( - GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, depth_buffer); - - return frameBuffer; -} - -void errorCallback(int error, const char* description) { - fputs(description, stderr); -} - -void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { - textArea.keyInput(key, scancode, action, mods); -} - -void charCallback(GLFWwindow* window, unsigned int codepoint) { - textArea.charInput(codepoint); -} - -void windowResizeCallback(GLFWwindow* window, int width, int height) { - if (height == 0) return; - glViewport(0, 0, width, height); -} - -GLuint loadTexture(const char* filename) { - GLuint tex; - glActiveTexture(GL_TEXTURE0); - - std::vector image; - unsigned width, height; - - unsigned error = lodepng::decode(image, width, height, filename); // TODO: handle error - - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)image.data()); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - return tex; -} - -GLuint createColor(glm::vec3 rgb, GLuint textureId=-1) { - - unsigned char image[] = { - (unsigned char)(rgb.r * 255.0f), - (unsigned char)(rgb.g * 255.0f), - (unsigned char)(rgb.b * 255.0f) - }; - - if (textureId == -1) glGenTextures(1, &textureId); - - glBindTexture(GL_TEXTURE_2D, textureId); - - GLint mipmapLevel = 0; - GLint componentNumber = 4; // rgba - - GLint width = 1; - GLint height = 1; - - glTexImage2D(GL_TEXTURE_2D, mipmapLevel, componentNumber, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); - - return textureId; -} - -void initOpenGLProgram(GLFWwindow* window) { - glClearColor(0.8f,0.8f,0.8f,1); - glEnable(GL_DEPTH_TEST); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - - glfwSetWindowSizeCallback(window, windowResizeCallback); - glfwSetKeyCallback(window, keyCallback); - glfwSetCharCallback(window, charCallback); - - sp2D = new ShaderProgram("shaders/v_2d.glsl", "shaders/g_2d.glsl", "shaders/f_2d.glsl"); - spPost = new ShaderProgram("shaders/v_post.glsl", nullptr, "shaders/f_post.glsl"); - - asciiTexture = loadTexture("assets/ascii.png"); - - frameColorBuffer = createColorBuffer(window); - frameDepthBuffer = createDepthBuffer(window); - frameBuffer = createFrameBuffer(frameColorBuffer, frameDepthBuffer); - -} - -void freeOpenGLProgram(GLFWwindow* window) { - delete sp2D; - delete spPost; - - glDeleteFramebuffers(1, &frameBuffer); - glDeleteTextures(1, &frameColorBuffer); - glDeleteRenderbuffers(1, &frameDepthBuffer); -} - -void bindTexture(ShaderProgram* shader, GLuint textureId, unsigned textureIndex) { - glActiveTexture(GL_TEXTURE0 + textureIndex); - glBindTexture(GL_TEXTURE_2D, textureId); - std::string address = "textures[" + std::to_string(textureIndex) + "]"; - glUniform1i(shader->u(address.c_str()), textureIndex); - -} - -void bindTilemap(ShaderProgram* shader, GLuint textureId, unsigned textureIndex, glm::ivec2 tilemapSize) { - bindTexture(shader, textureId, textureIndex); - std::string tilemapSizeAddress = "tilemapSize[" + std::to_string(textureIndex) + "]"; - glUniform2i(shader->u(tilemapSizeAddress.c_str()), tilemapSize.x, tilemapSize.y); -} - -void drawScene(GLFWwindow* window, double delta) { - - glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - sp2D->use(); - glEnableVertexAttribArray(sp2D->a("pos")); - glEnableVertexAttribArray(sp2D->a("dim")); - glEnableVertexAttribArray(sp2D->a("tex")); - glEnableVertexAttribArray(sp2D->a("inv")); - - bindTilemap(sp2D, asciiTexture, 2, glm::ivec2(16, 16)); - - int width, height; - glfwGetWindowSize(window, &width, &height); - glUniform2f(sp2D->u("window"), width, height); - glUniform1f(sp2D->u("layer"), 0.0f); - - textArea.draw(sp2D, delta); - - // The value zero is reserved to represent the default framebuffer provided by the windowing system - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDisable(GL_DEPTH_TEST); - glClear(GL_COLOR_BUFFER_BIT); - - spPost->use(); - glActiveTexture(GL_TEXTURE0 + 3); - glBindTexture(GL_TEXTURE_2D, frameColorBuffer); - glDrawArrays(GL_TRIANGLES, 0, 6); - glUniform1i(spPost->u("colorBuffer"), 3); - glfwSwapBuffers(window); -} +#include "Game.h" int main(void) { - GLFWwindow* window; - - glfwSetErrorCallback(errorCallback); - - if (!glfwInit()) { - fprintf(stderr, "Failed to initialize GLFW.\n"); - exit(EXIT_FAILURE); - } - - glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); - glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); - - int width = 800; - int height = 800; - - window = glfwCreateWindow(width, height, "OpenGL", NULL, NULL); - - if (!window) - { - fprintf(stderr, "Failed to create window.\n"); - glfwTerminate(); - exit(EXIT_FAILURE); - } - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - if (glewInit() != GLEW_OK) { - fprintf(stderr, "Failed to initialize GLEW.\n"); - exit(EXIT_FAILURE); - } - - initOpenGLProgram(window); - - double lastFrameTime = glfwGetTime(); - - - while (!glfwWindowShouldClose(window)) - { - double newFrameTime = glfwGetTime(); - double delta = newFrameTime - lastFrameTime; - std::string fpsText = std::to_string(1 / delta); - - glfwSetWindowTitle(window, fpsText.c_str()); - - drawScene(window, delta); - - lastFrameTime = newFrameTime; - - glfwPollEvents(); - } - - freeOpenGLProgram(window); - - glfwDestroyWindow(window); - glfwTerminate(); - exit(EXIT_SUCCESS); -} + Game& game = Game::getInstance(); + game.run(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/ProgrammingGame.vcxproj b/ProgrammingGame.vcxproj index cea7a60..ee6a0ca 100644 --- a/ProgrammingGame.vcxproj +++ b/ProgrammingGame.vcxproj @@ -19,19 +19,25 @@ + + + + + + diff --git a/Utils.cpp b/Utils.cpp new file mode 100644 index 0000000..3c93d74 --- /dev/null +++ b/Utils.cpp @@ -0,0 +1,85 @@ +#include "Utils.h" +#include + +#include "ShaderProgram.h" + + +GLuint loadTexture(const char* filename) { + GLuint tex; + glActiveTexture(GL_TEXTURE0); + + std::vector image; + unsigned width, height; + + unsigned error = lodepng::decode(image, width, height, filename); // TODO: handle error + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*)image.data()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return tex; +} + +GLuint createColorBuffer(GLFWwindow* window) { + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + unsigned int colorBuffer; + glGenTextures(1, &colorBuffer); + glBindTexture(GL_TEXTURE_2D, colorBuffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); + + return colorBuffer; +} + +GLuint createDepthBuffer(GLFWwindow* window) { + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + unsigned int depthBuffer; + glGenRenderbuffers(1, &depthBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + + return depthBuffer; +} + +GLuint createFrameBuffer(GLuint color_buffer, GLuint depth_buffer) { + GLuint frameBuffer; + + glGenFramebuffers(1, &frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); + + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, color_buffer, 0); + + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depth_buffer); + + return frameBuffer; +} + +void bindTexture(ShaderProgram* shader, GLuint textureId, unsigned textureIndex) { + glActiveTexture(GL_TEXTURE0 + textureIndex); + glBindTexture(GL_TEXTURE_2D, textureId); + std::string address = "textures[" + std::to_string(textureIndex) + "]"; + glUniform1i(shader->u(address.c_str()), textureIndex); + +} + +void bindTilemap(ShaderProgram* shader, GLuint textureId, unsigned textureIndex, glm::ivec2 tilemapSize) { + bindTexture(shader, textureId, textureIndex); + std::string tilemapSizeAddress = "tilemapSize[" + std::to_string(textureIndex) + "]"; + glUniform2i(shader->u(tilemapSizeAddress.c_str()), tilemapSize.x, tilemapSize.y); +} \ No newline at end of file diff --git a/Utils.h b/Utils.h new file mode 100644 index 0000000..4dbeb0e --- /dev/null +++ b/Utils.h @@ -0,0 +1,24 @@ +#pragma once + + +#include +#include +#include + +#include "ShaderProgram.h" + +#include +#include + + +GLuint loadTexture(const char* filename); + +GLuint createColorBuffer(GLFWwindow* window); + +GLuint createDepthBuffer(GLFWwindow* window); + +GLuint createFrameBuffer(GLuint color_buffer, GLuint depth_buffer); + +void bindTexture(ShaderProgram* shader, GLuint textureId, unsigned textureIndex); + +void bindTilemap(ShaderProgram* shader, GLuint textureId, unsigned textureIndex, glm::ivec2 tilemapSize); \ No newline at end of file