Skip to content

Commit

Permalink
v0.2 ALPHA
Browse files Browse the repository at this point in the history
Signed Distance Field Text Rendering is here!

doa::text namespace added.
Offers font loading and text rendering.

Basic Procedure:
Read font and create a Font object,
Create a Text object with the Font object
Render the text!!!
  • Loading branch information
aeris170 committed Jan 28, 2020
1 parent 08e4ed1 commit f5eb638
Show file tree
Hide file tree
Showing 14 changed files with 596 additions and 138 deletions.
17 changes: 13 additions & 4 deletions Engine/DoaEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ namespace doa {
return 1;
}
}
{ //GLFW INIT
if (FT_Init_FreeType(&internal::text::ft)) {
std::cout << "Fucked up while initializing FreeType!\n";
return 1;
}
}
return 0;
}

Expand Down Expand Up @@ -68,14 +74,16 @@ namespace doa {
glfwDestroyWindow(m_window);
glfwTerminate();
}
primitives::Primitive::generate_buffers();
shader::CreateShaderProgram("primitives-shader", "Shaders/vShader", "Shaders/fShader");
primitive::Primitive::generate_buffers();
shader::CreateShaderProgram("primitive-shader", "Shaders/primitiveVShader.vert", "Shaders/primitiveFShader.frag");
shader::CreateShaderProgram("text-shader", "Shaders/textVShader.vert", "Shaders/textFShader.frag");
internal::ezrender::instantiate_primitives();
}

glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return m_window;
}

Expand Down Expand Up @@ -125,7 +133,8 @@ namespace doa {
internal::scene::purge();
internal::shader::purge();
internal::ezrender::purge();
primitives::Primitive::purge();
internal::text::purge();
primitive::Primitive::purge();
glfwDestroyWindow(m_window);
glfwTerminate();
}
Expand Down
299 changes: 299 additions & 0 deletions Engine/DoaText.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
// NeoDoa 2019, v0.1 ALPHA
#include "doa.h"

namespace doa::text {
using namespace internal::text;

std::map<std::string, Font*> FONTS;

Font* CreateANSIFont(const char* name, const char* path, int atlasSize, int charCount) {
Font* f{ new Font() };
SDF::render_signed_distance_font(ft, path, atlasSize, charCount);
//SDF TEX GEN START
texture::Texture* texture{ new texture::Texture };

glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, *texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.f);

//stbi__vertical_flip(SDF::pdata.data(), atlasSize, atlasSize, 4);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, atlasSize, atlasSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, SDF::pdata.data());
glGenerateMipmap(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, 0);

f->m_sdf_size = atlasSize;
f->m_sdf_tex = texture;
//SDF TEX GEN END
//SDF CHAR GEN START
for (auto itr{ SDF::all_glyphs.begin() }; itr != SDF::all_glyphs.end(); ++itr) {
(*f->m_chars)[(*itr)->ID] = *itr;
}
//SDF CHAR GEN END
if (FONTS[name]) {
delete FONTS[name];
}
FONTS[name] = f;
return f;
}

Font* CreateUnicodeFont(const char* name, const char* path, int atlasSize, int charCount) {
Font* f{ new Font() };
SDF::render_signed_distance_font(ft, path, atlasSize, charCount);
//SDF TEX GEN START
texture::Texture* texture{ new texture::Texture };

glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, *texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.f);

stbi__vertical_flip(SDF::pdata.data(), atlasSize, atlasSize, 4);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, atlasSize, atlasSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, SDF::pdata.data());
glGenerateMipmap(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, 0);

f->m_sdf_size = atlasSize;
f->m_sdf_tex = texture;
//SDF TEX GEN END
//SDF CHAR GEN START
for (auto itr{ SDF::all_glyphs.begin() }; itr != SDF::all_glyphs.end(); ++itr) {
(*f->m_chars)[(*itr)->ID] = *itr;
}
//SDF CHAR GEN END
if (FONTS[name]) {
delete FONTS[name];
}
FONTS[name] = f;
return f;
}

Font* Get(const std::string& name) {
return FONTS[name];
}

Font::Font() {}

Font::~Font() {
glDeleteTextures(1, m_sdf_tex);
delete m_sdf_tex;
for (auto itr{ m_chars->begin() }; itr != m_chars->end(); ++itr) {
delete itr->second;
}
m_chars->clear();
delete m_chars;
}

Text::Text(Font* font, const char* data) : GameObject(), m_font{ font }, m_data{ data } {
m_shader = doa::shader::SHADERS["text-shader"];
m_is_fixed = true;

while (m_data[m_data_length] != 0) {
++m_data_length;
}

int vertices_count = m_data_length * 18;
int tex_coords_count = m_data_length * 12;
GLfloat* vertices = new GLfloat[vertices_count];
GLfloat* tex_coords = new GLfloat[tex_coords_count];

float cur_pos{ 0 };
int vi{ 0 }, ti{ 0 };
for (int i{ 0 }; i < m_data_length; ++i) {
SDF::sdf_glyph& character{ *m_font->m_chars[0][m_data[i]] };
float x{ cur_pos + character.xoff };
float y{ character.yoff };

float xPlusCharWidth = x + character.width;
float yMinusCharHeight = y - character.height;

vertices[vi++] = x;
vertices[vi++] = y;
vertices[vi++] = 0;

vertices[vi++] = xPlusCharWidth;
vertices[vi++] = y;
vertices[vi++] = 0;

vertices[vi++] = xPlusCharWidth;
vertices[vi++] = yMinusCharHeight;
vertices[vi++] = 0;

vertices[vi++] = xPlusCharWidth;
vertices[vi++] = yMinusCharHeight;
vertices[vi++] = 0;

vertices[vi++] = x;
vertices[vi++] = yMinusCharHeight;
vertices[vi++] = 0;

vertices[vi++] = x;
vertices[vi++] = y;
vertices[vi++] = 0;

m_data_max_x = xPlusCharWidth;

if (yMinusCharHeight < m_data_min_y) {
m_data_min_y = yMinusCharHeight;
}
if (y > m_data_max_y) {
m_data_max_y = y;
}

tex_coords[ti++] = character.x;
tex_coords[ti++] = character.y;

tex_coords[ti++] = character.x + character.width;
tex_coords[ti++] = character.y;

tex_coords[ti++] = character.x + character.width;
tex_coords[ti++] = character.y + character.height;

tex_coords[ti++] = character.x + character.width;
tex_coords[ti++] = character.y + character.height;

tex_coords[ti++] = character.x;
tex_coords[ti++] = character.y + character.height;

tex_coords[ti++] = character.x;
tex_coords[ti++] = character.y;

cur_pos += character.xadv;
}

for (int i{ 0 }; i < tex_coords_count; ++i) {
tex_coords[i] /= m_font->m_sdf_size;
}

glGenVertexArrays(1, &m_VAO);
glBindVertexArray(m_VAO);

glGenBuffers(1, &m_vertices_VBO);
glBindBuffer(GL_ARRAY_BUFFER, m_vertices_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertices_count, vertices, GL_STATIC_DRAW);
GLint pos = glGetAttribLocation(*m_shader, "vertex_coords");
glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, 0);
glEnableVertexAttribArray(pos);

glGenBuffers(1, &m_tex_coords_VBO);
glBindBuffer(GL_ARRAY_BUFFER, m_tex_coords_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * tex_coords_count, tex_coords, GL_STATIC_DRAW);
GLint tex_coords_position = glGetAttribLocation(*m_shader, "texture_coords");
glVertexAttribPointer(tex_coords_position, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, 0);
glEnableVertexAttribArray(tex_coords_position);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

delete[] vertices;
delete[] tex_coords;
}

Text::~Text() {
delete m_color;
delete m_outline_color;
delete m_outline_offset;
}

void Text::update(const scene::Scene& parent, const std::vector<scene::GameObject*>& objects, const std::vector<scene::Light*>& lights) {}

void Text::render(const scene::Scene& parent, const std::vector<scene::GameObject*>& objects, const std::vector<scene::Light*>& lights) const {
glUseProgram(*m_shader);
glBindVertexArray(m_VAO);

glm::mat4 modelMatrix = glm::mat4(1); // A Camera space transforms are complete.
if (!m_is_fixed) { // |
modelMatrix = glm::translate(modelMatrix, glm::vec3(camera::x, camera::y, 0)); // | Third, translate the object back to it's position.
modelMatrix = glm::scale(modelMatrix, glm::vec3(camera::z, camera::z, 1)); // | Second, zoom into the camera origin.
modelMatrix = glm::translate(modelMatrix, glm::vec3(-camera::x, -camera::y, 0)); // | First, translate the object to camera origin.
} // |
// | Object space transfroms are complete.
modelMatrix = glm::translate(modelMatrix, *m_position); // | Third, translate the object.
modelMatrix = glm::rotate(modelMatrix, (float)(m_rotation->x / 180.f * M_PI), glm::vec3(1, 0, 0)); // | and round X.
modelMatrix = glm::rotate(modelMatrix, (float)(m_rotation->y / 180.f * M_PI), glm::vec3(0, 1, 0)); // | round Y,
modelMatrix = glm::rotate(modelMatrix, (float)(m_rotation->z / 180.f * M_PI), glm::vec3(0, 0, 1)); // | Second, rotate the object round Z,
modelMatrix = glm::scale(modelMatrix, glm::vec3(*m_scale, 1)); // | First, scale the object.
// | Reference point transforms are complete.
if (m_ref_point == TOP_LEFT || m_ref_point == TOP_RIGHT) { // | If Text is referenced from top,
modelMatrix = glm::translate(modelMatrix, glm::vec3(0, -m_data_max_y, 0)); // | Translate to top,
} else { // | Else, Text is referenced from bottom,
modelMatrix = glm::translate(modelMatrix, glm::vec3(0, -m_data_min_y, 0)); // | Translate to bottom.
} // |
if (m_ref_point == BOTTOM_RIGHT || m_ref_point == TOP_RIGHT) { // | If Text is referenced from right,
modelMatrix = glm::translate(modelMatrix, glm::vec3(-m_data_max_x, 0, 0)); // | Translate to right.
} // |
if (m_ref_point == CENTER) { // | Text is referenced from center.
modelMatrix = glm::translate(modelMatrix, glm::vec3(-m_data_max_x / 2, -(m_data_min_y + m_data_max_y) / 2, 0)); // | Translate to center.
} // |

GLint modelMatrixLocation = glGetUniformLocation(*m_shader, "modelMatrix");
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, glm::value_ptr(modelMatrix));

GLint viewMatrixLocation = glGetUniformLocation(*m_shader, "viewMatrix");
if (m_is_fixed) {
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, glm::value_ptr(internal::m_fixed_view_matrix));
} else {
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, glm::value_ptr(internal::m_view_matrix));
}

GLint projectionMatrixLocation = glGetUniformLocation(*m_shader, "projectionMatrix");
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, glm::value_ptr(internal::m_projection_matrix));

GLint sampler2DLocation = glGetUniformLocation(*m_shader, "characterTextureAtlas");
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, *m_font->m_sdf_tex);
glUniform1i(sampler2DLocation, 0);

GLint colorLocation = glGetUniformLocation(*m_shader, "color");
GLint outlineColorLocation = glGetUniformLocation(*m_shader, "outlineColor");
GLint outlineOffsetLocation = glGetUniformLocation(*m_shader, "outlineOffset");
glUniform3fv(colorLocation, 1, glm::value_ptr(*m_color));
glUniform3fv(outlineColorLocation, 1, glm::value_ptr(*m_outline_color));
glUniform2fv(outlineOffsetLocation, 1, glm::value_ptr(*m_outline_offset));

GLint widthLocation = glGetUniformLocation(*m_shader, "width");
GLint edgeLocation = glGetUniformLocation(*m_shader, "edge");
glUniform1f(widthLocation, m_char_width);
glUniform1f(edgeLocation, m_char_edge);

GLint borderWidthLocation = glGetUniformLocation(*m_shader, "borderWidth");
GLint borderEdgeLocation = glGetUniformLocation(*m_shader, "borderEdge");
glUniform1f(borderWidthLocation, m_char_border_width);
glUniform1f(borderEdgeLocation, m_char_border_edge);

glDepthMask(false);
glDrawArrays(GL_TRIANGLES, 0, 6 * m_data_length);
glDepthMask(true);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glUseProgram(0);
}
}

namespace internal::text {

FontLoader ft;

void purge() {
for (auto itr{ doa::text::FONTS.begin() }; itr != doa::text::FONTS.end(); ++itr) {
delete itr->second;
}
doa::text::FONTS.clear();
FT_Done_FreeType(ft);
}
}
Loading

0 comments on commit f5eb638

Please sign in to comment.