// Must be the first include #include // Other includes #include #include #include #include "Gedeng/Logger.h" #include "Gedeng/TextLabel.h" namespace Gedeng { TextLabel::TextLabel() : shader(Shader("Shader/text.vs", "Shader/text.fs")) { FT_Library ft; if (FT_Init_FreeType(&ft)) { GG_CORE_ERROR("Couldn't create font library!"); return; } FT_Face face; if (FT_New_Face(ft, "Resources/Fonts/Inter.ttf", 0, &face)) { GG_CORE_ERROR("Couldn't load font from path!"); return; } // Set size FT_Set_Pixel_Sizes(face, 0, 48); // Load first 128 ASCII Characters glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction for (unsigned char c = 0; c < 128; c++) { // load character glyph if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { GG_CORE_ERROR("Couldn't load character glyph!"); 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), static_cast(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); valid = true; } void TextLabel::render_text(String text, float x, float y, float scale, Vector3 color) { // activate corresponding render state shader.use(); glUniform3f(glGetUniformLocation(shader.ID, "textColor"), color.x, color.y, color.z); // FIXME: We need the screen size here... (or UI module size?) glm::mat4 projection = glm::ortho(0.0f, static_cast(500), 0.0f, static_cast(500)); glUniformMatrix4fv(glGetUniformLocation(shader.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glActiveTexture(GL_TEXTURE0); glBindVertexArray(vao); // iterate through all characters for (size_t i = 0; i < text.get_length(); i++) { Character ch = characters[text[i]]; 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.texture_id); // 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); } bool TextLabel::is_valid() { return valid; } } // namespace Gedeng