diff --git a/src/7.in_practice/2.text_rendering/text.fs b/src/7.in_practice/2.text_rendering/text.fs new file mode 100644 index 0000000..bbd0c17 --- /dev/null +++ b/src/7.in_practice/2.text_rendering/text.fs @@ -0,0 +1,12 @@ +#version 330 core +in vec2 TexCoords; +out vec4 color; + +uniform sampler2D text; +uniform vec3 textColor; + +void main() +{ + vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r); + color = vec4(textColor, 1.0) * sampled; +} \ No newline at end of file diff --git a/src/7.in_practice/2.text_rendering/text.vs b/src/7.in_practice/2.text_rendering/text.vs new file mode 100644 index 0000000..94b6118 --- /dev/null +++ b/src/7.in_practice/2.text_rendering/text.vs @@ -0,0 +1,11 @@ +#version 330 core +layout (location = 0) in vec4 vertex; // +out vec2 TexCoords; + +uniform mat4 projection; + +void main() +{ + gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); + TexCoords = vertex.zw; +} \ No newline at end of file diff --git a/src/7.in_practice/2.text_rendering/text_rendering.cpp b/src/7.in_practice/2.text_rendering/text_rendering.cpp new file mode 100644 index 0000000..aebf199 --- /dev/null +++ b/src/7.in_practice/2.text_rendering/text_rendering.cpp @@ -0,0 +1,274 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include "Shader.h" + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void processInput(GLFWwindow *window); +void RenderText(Shader &shader, std::string text, float x, float y, float scale, glm::vec3 color); + +// settings +const unsigned int SCR_WIDTH = 800; +const unsigned int SCR_HEIGHT = 600; + +/// Holds all state information relevant to a character as loaded using FreeType +struct Character { + unsigned int TextureID; // ID handle of the glyph texture + glm::ivec2 Size; // Size of glyph + glm::ivec2 Bearing; // Offset from baseline to left/top of glyph + unsigned int Advance; // Horizontal offset to advance to next glyph +}; + +std::map Characters; +unsigned int VAO, VBO; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // OpenGL state + // ------------ + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // compile and setup the shader + // ---------------------------- + Shader shader("text.vs", "text.fs"); + glm::mat4 projection = glm::ortho(0.0f, static_cast(SCR_WIDTH), 0.0f, static_cast(SCR_HEIGHT)); + shader.Use(); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); + + // FreeType + // -------- + FT_Library ft; + // All functions return a value different than 0 whenever an error occurred + if (FT_Init_FreeType(&ft)) + std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl; + + // load font as face + FT_Face face; + if (FT_New_Face(ft, "fonts/arial.ttf", 0, &face)) + std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; + + // set size to load glyphs as + FT_Set_Pixel_Sizes(face, 0, 48); + + // disable byte-alignment restriction + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // load first 128 characters of ASCII set + for (unsigned char c = 0; c < 128; c++) + { + // Load character glyph + if (FT_Load_Char(face, c, FT_LOAD_RENDER)) + { + std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl; + continue; + } + // generate texture + unsigned int texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RED, + face->glyph->bitmap.width, + face->glyph->bitmap.rows, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + face->glyph->bitmap.buffer + ); + // set texture options + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // now store character for later use + Character character = { + texture, + glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), + glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), + face->glyph->advance.x + }; + Characters.insert(std::pair(c, character)); + } + glBindTexture(GL_TEXTURE_2D, 0); + // destroy FreeType once we're finished + FT_Done_Face(face); + FT_Done_FreeType(ft); + + + // configure VAO/VBO for texture quads + // ----------------------------------- + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + RenderText(shader, "This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f)); + RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3, 0.7f, 0.9f)); + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + glfwTerminate(); + return 0; +} + +// The MAIN function, from here we start our application and run the Game loop +int main() +{ + // Init GLFW + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); + + GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed + glfwMakeContextCurrent(window); + + // Initialize GLEW to setup the OpenGL Function pointers + glewExperimental = GL_TRUE; + glewInit(); + + // Define the viewport dimensions + glViewport(0, 0, WIDTH, HEIGHT); + + // Set OpenGL options + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// render line of text +// ------------------- +void RenderText(Shader &shader, std::string text, float x, float y, float scale, glm::vec3 color) +{ + // activate corresponding render state + shader.Use(); + glUniform3f(glGetUniformLocation(shader.Program, "textColor"), color.x, color.y, color.z); + glActiveTexture(GL_TEXTURE0); + glBindVertexArray(VAO); + + // iterate through all characters + std::string::const_iterator c; + for (c = text.begin(); c != text.end(); c++) + { + Character ch = Characters[*c]; + + float xpos = x + ch.Bearing.x * scale; + float ypos = y - (ch.Size.y - ch.Bearing.y) * scale; + + float w = ch.Size.x * scale; + float h = ch.Size.y * scale; + // update VBO for each character + float vertices[6][4] = { + { xpos, ypos + h, 0.0f, 0.0f }, + { xpos, ypos, 0.0f, 1.0f }, + { xpos + w, ypos, 1.0f, 1.0f }, + + { xpos, ypos + h, 0.0f, 0.0f }, + { xpos + w, ypos, 1.0f, 1.0f }, + { xpos + w, ypos + h, 1.0f, 0.0f } + }; + // render glyph texture over quad + glBindTexture(GL_TEXTURE_2D, ch.TextureID); + // update content of VBO memory + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // be sure to use glBufferSubData and not glBufferData + + glBindBuffer(GL_ARRAY_BUFFER, 0); + // render quad + glDrawArrays(GL_TRIANGLES, 0, 6); + // now advance cursors for next glyph (note that advance is number of 1/64 pixels) + x += (ch.Advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels)) + } + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); +} +