Reputation: 851
I'm working on a scientific project, where we have to make the camera upside down, and flip everything else. But I can't seem to find a way to flip Freetype text vertically. Here's how it looks like now:
And this is how I create my texts:
// Text.cpp
#include "Text.h"
using namespace OpenGL::Rendering::Models;
Text::Text(const std::string& text, OpenGL::Container::Position position,
int font_size, OpenGL::Container::Color color) {
m_font_size = font_size;
m_scale = 1.0;
m_text = text;
this->color.r = color.r;
this->color.g = color.g;
this->color.b = color.b;
this->color.a = color.a;
this->position.x = position.x;
this->position.y = position.y;
this->position.z = position.z;
if (FT_Init_FreeType(&font)) {
Log()->critical("Could not initalize Freetype library for fonts.");
}
if (FT_New_Face(font, "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf", 0,
&face)) {
Log()->critical("Could not load font. File is missing maybe?");
}
FT_Set_Char_Size(face, 0, m_font_size * 64, 300, 300);
FT_Set_Pixel_Sizes(face, 0, m_font_size);
if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) {
Log()->critical(
"Could not load a test glyph. The font is corrupted maybe?");
}
float angle = 0;
matrix.xx = (FT_Fixed)(cos(angle) * 0x10000L);
matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L);
matrix.yx = (FT_Fixed)(sin(angle) * 0x10000L);
matrix.yy = (FT_Fixed)(cos(angle) * 0x10000L);
FT_Set_Transform(face, &matrix, 0);
for (GLubyte c = 0; c < 128; ++c) {
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
Log()->critical("Could not load glyph \"{}\"", c);
continue;
}
GLuint 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);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
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);
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<GLchar, Character>(c, character));
}
FT_Done_Face(face);
FT_Done_FreeType(font);
}
void Text::create() {
GLuint vao;
GLuint vbo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Container::Vertex) * 6, NULL,
GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Container::Vertex),
(void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(
1, 2, GL_FLOAT, GL_FALSE, sizeof(Container::Vertex),
(void*)(offsetof(Container::Vertex, Container::Vertex::m_texcoord)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
this->vao = vao;
this->vbos.push_back(vbo);
this->set_program(OpenGL::Managers::ShaderManager::get_program("text"));
this->set_position(position.x, position.y, position.z);
//this->set_rotation(180, 0, 0, 1);
}
void Text::draw() {
glUseProgram(this->program);
glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
false, &model_matrix[0][0]);
glUniform4f(glGetUniformLocation(this->program, "text_color"), color.r,
color.g, color.b, color.a);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(this->vao);
GLfloat temp_x = 0;
GLfloat temp_y = 0;
std::string::const_iterator c;
for (c = m_text.begin(); c != m_text.end(); c++) {
Character ch = characters[*c];
GLfloat xpos = temp_x + ch.bearing.x * m_scale;
GLfloat ypos = temp_y - (ch.size.y - ch.bearing.y) * m_scale;
GLfloat zpos = position.z;
GLfloat w = ch.size.x * m_scale;
GLfloat h = ch.size.y * m_scale;
std::vector<Container::Vertex> vertices;
vertices.push_back(Container::Vertex(glm::vec3(xpos, ypos + h, zpos),
glm::vec2(0.0, 0.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos, ypos, zpos),
glm::vec2(0.0, 1.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos + w, ypos, zpos),
glm::vec2(1.0, 1.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos, ypos + h, zpos),
glm::vec2(0.0, 0.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos + w, ypos, zpos),
glm::vec2(1.0, 1.0)));
vertices.push_back(Container::Vertex(
glm::vec3(xpos + w, ypos + h, zpos), glm::vec2(1.0, 0.0)));
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
glBindBuffer(GL_ARRAY_BUFFER, this->vbos[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Container::Vertex) * vertices.size(), &vertices[0]);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
temp_x += (ch.advance >> 6) * m_scale;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Text::set_text(const std::string& a_text) {
if (!a_text.empty()) {
m_text = a_text;
} else {
Log()->info("Cannot set the text. Input seems to be empty.");
}
}
std::string Text::get_text() { return m_text; }
void Text::set_color(const Container::Color a_color) {
color.r = a_color.r;
color.g = a_color.g;
color.b = a_color.b;
color.a = a_color.a;
}
The ideal solution is to flip each glyph while keeping bearing and stuff, but I have no idea how to do that. My coordinates are all in pixels, and I'm using OpenGL 3.3 core profile.
Upvotes: 0
Views: 1564
Reputation: 19213
Looking at you code you render each character as two textured triangles so its very easy to flip the text. All that's needed is to change the UV coordinates for the texture:
//Flip UV's second value
vertices.push_back(Container::Vertex(glm::vec3(xpos, ypos + h, zpos),
glm::vec2(0.0, 1.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos, ypos, zpos),
glm::vec2(0.0, 0.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos + w, ypos, zpos),
glm::vec2(1.0, 0.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos, ypos + h, zpos),
glm::vec2(0.0, 1.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos + w, ypos, zpos),
glm::vec2(1.0, 0.0)));
vertices.push_back(Container::Vertex(glm::vec3(xpos + w, ypos + h, zpos),
glm::vec2(1.0, 1.0)));
Aside from that, this is not very efficient method to render lots of text. Usually its done by packing all the characters into one buffer and also collect the glyphs into one texture atlas. So there's only one draw call and one texture bound and UVs are then used to map specific glyph to the triangles.
Upvotes: 1