diff --git a/include/engine/font.hpp b/include/engine/font.hpp index 25489d9..efcc541 100644 --- a/include/engine/font.hpp +++ b/include/engine/font.hpp @@ -29,20 +29,33 @@ namespace egn{ + struct font_character{ + GLuint atlas_texture; + math::vec2 atlas_offset; + math::vec2 size; + math::vec2 bearing; + math::vec2 advance; + }; + class font { private: - //ascii texture atlas - const char* m_bitmap_handle; - //When rendering unicode, load up new textures per code page to create many atlases - std::map m_extra_codepages; - //stb or freetype font handle + std::optional m_atlas; + std::map m_atlas_data; public: - //load the font file and process it with stb/freetype lib - //render out basic ascii characters to bitmap texture atlas - //maybe do signed distance fonts? example: https://github.com/ShoYamanishi/SDFont - //dynamically add new codepage textures for rendering non-ascii + font(void) = default; + font(gfx::texture&& tex, std::map&& metadata); + ~font(void) = default; + + font_character& operator[](int character); + const font_character& operator[](int character)const; + + gfx::texture release_atlas(void); + std::map release_metadata(void); + + bool valid(void)const; + }; class font_generator @@ -65,7 +78,7 @@ namespace egn{ // glyph_height: requested pixel height of glyph // atlas_width: requested pixel width of atlas // atlas_height: requested pixel height of atlas - std::optional generate_atlas(int start_codepoint, int count, int glyph_width, int glyph_height, int atlas_width, int atlas_height); + font generate_atlas(int start_codepoint, int count, size_t glyph_width, size_t glyph_height); std::optional generate_glyph(char character, int width, int height); private: diff --git a/src/board.cpp b/src/board.cpp index 620b80a..11a32cc 100644 --- a/src/board.cpp +++ b/src/board.cpp @@ -24,12 +24,12 @@ gfx::unified_mesh square_mesh(const gfx::material& blank, const gfx::material& o, const gfx::material& x){ static constexpr gfx::vertex s_vertices[] = { - {{-1.0f, 1.0f, 0.0f}, {}, {0.0f, 1.0f}}, - {{-1.0f, -1.0f, 0.0f}, {}, {0.0f, 0.0f}}, - {{ 1.0f, -1.0f, 0.0f}, {}, {1.0f, 0.0f}}, - {{ 1.0f, -1.0f, 0.0f}, {}, {1.0f, 0.0f}}, - {{ 1.0f, 1.0f, 0.0f}, {}, {1.0f, 1.0f}}, - {{-1.0f, 1.0f, 0.0f}, {}, {0.0f, 1.0f}} + {{-1.0f, 1.0f, 0.0f}, {}, {0.0f, 0.0f}}, + {{-1.0f, -1.0f, 0.0f}, {}, {0.0f, 1.0f}}, + {{ 1.0f, -1.0f, 0.0f}, {}, {1.0f, 1.0f}}, + {{ 1.0f, -1.0f, 0.0f}, {}, {1.0f, 1.0f}}, + {{ 1.0f, 1.0f, 0.0f}, {}, {1.0f, 0.0f}}, + {{-1.0f, 1.0f, 0.0f}, {}, {0.0f, 0.0f}} }; const gfx::material* mats[] = {&blank, &o, &x}; @@ -61,6 +61,7 @@ board::board(egn::resource_manager& resman): { gfx::material* blank, *o, *x; gfx::model* sq; + blank = resman.emplace_material("assets/images/blank.jpg", egn::deferred_image("assets/images/blank.jpg", true)).first; o = resman.emplace_material("assets/images/o.jpg", egn::deferred_image("assets/images/o.jpg", true)).first; x = resman.emplace_material("assets/images/x.jpg", egn::deferred_image("assets/images/x.jpg", true)).first; diff --git a/src/engine/font.cpp b/src/engine/font.cpp index f5accbe..4406484 100644 --- a/src/engine/font.cpp +++ b/src/engine/font.cpp @@ -17,9 +17,32 @@ */ #include "engine/font.hpp" +#include "math/vec.hpp" +#include //sqrt +#include //max +#include //move namespace egn{ + font::font(gfx::texture&& tex, std::map&& metadata): + m_atlas(std::move(tex)), + m_atlas_data(std::move(metadata)){} + font_character& font::operator[](int character){ + return m_atlas_data[character]; + } + const font_character& font::operator[](int character)const{ + return m_atlas_data.at(character); + } + gfx::texture font::release_atlas(void){ + return gfx::texture(std::move(*m_atlas)); + } + std::map font::release_metadata(void){ + return std::map(std::move(m_atlas_data)); + } + bool font::valid(void)const{ + return m_atlas.has_value(); + } + font_generator::font_generator(const char* file){ initialize_freetype_(); if(FT_New_Face(s_ft, file, 0, &m_face)){ @@ -30,6 +53,48 @@ namespace egn{ FT_Done_Face(m_face); } + font font_generator::generate_atlas(int start_codepoint, int count, size_t glyph_width, size_t glyph_height){ + FT_Set_Pixel_Sizes(m_face, glyph_width, glyph_height); + + size_t atlas_height = std::sqrt(count) * glyph_height; + size_t atlas_width = atlas_height; + + debug_print("atlas size: (%lu, %lu)\n", atlas_width, atlas_height); + + gfx::texture atlas(GL_RED, atlas_width, atlas_height, GL_UNSIGNED_BYTE, false); + std::map atlas_metadata; + + math::vec2 atlas_pos = {0, 0}; + size_t tallest_in_row = 0; + + for(int i = start_codepoint;i < start_codepoint + count;++i){ + if(FT_Load_Char(m_face, i, FT_LOAD_RENDER)){ + debug_print_error("Failed to load character '%c', from font '%s'\n", i, m_face->family_name); + continue; + } + auto& bitmap = m_face->glyph->bitmap; + const math::vec2 bitmap_size = {bitmap.width, bitmap.rows}; + debug_print("Loaded character %c: (%d, %d)\n", i, bitmap.width, bitmap.rows); + + font_character character = {atlas.raw(), + atlas_pos, + bitmap_size, + {m_face->glyph->bitmap_left, m_face->glyph->bitmap_top}, + {m_face->glyph->advance.x, m_face->glyph->advance.y}}; + + atlas_metadata.emplace(i, character); + if(atlas_pos.x() + bitmap_size.x() > atlas_width){ + atlas_pos.x() = 0; + atlas_pos.y() += tallest_in_row; + tallest_in_row = 0; + } + atlas.set_subimage(bitmap.buffer, GL_RED, GL_UNSIGNED_BYTE, atlas_pos.x(), atlas_pos.y(), bitmap_size.x(), bitmap_size.y()); + tallest_in_row = rexy::max(tallest_in_row, bitmap_size.y()); + atlas_pos.x() += bitmap_size.x(); + } + return {std::move(atlas), std::move(atlas_metadata)}; + } + std::optional font_generator::generate_glyph(char character, int width, int height){ FT_Set_Pixel_Sizes(m_face, width, height); if(FT_Load_Char(m_face, character, FT_LOAD_RENDER)){ diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index b684a81..d55d5d5 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -82,17 +82,11 @@ namespace gfx{ m_mipmapped(mipmap) { glCreateTextures(GL_TEXTURE_2D, 1, &m_tex_id); - GLint old_alignment = save_unpack_alignment_(); - if(!create_texture_storage_()){ debug_print_error("Failed to create texture storage\n"); return; } - glTextureSubImage2D(m_tex_id, 0, 0, 0, m_width, m_height, m_format, m_type, data); - if(m_mipmapped) - generate_mipmap(); - - glPixelStorei(GL_UNPACK_ALIGNMENT, old_alignment); + set_image(data, format, w, h, type); } texture::texture(GLenum format, GLsizei w, GLsizei h, GLenum type, bool mipmap): texture(nullptr, format, w, h, type, mipmap){} @@ -153,7 +147,6 @@ namespace gfx{ bool texture::set_image(const unsigned char* data, GLenum format, GLsizei w, GLsizei h, GLenum type){ if(!data){ - debug_print_error("Image is invalid\n"); return false; } GLint old_alignment = save_unpack_alignment_(); @@ -204,7 +197,8 @@ namespace gfx{ w = rexy::min(w, m_width - xoffset); glTextureSubImage2D(m_tex_id, 0, xoffset, yoffset, w, h, format, type, data); - generate_mipmap(); + if(m_mipmapped) + generate_mipmap(); glPixelStorei(GL_UNPACK_ALIGNMENT, old_alignment); return true;