Add basic font atlas loading

This commit is contained in:
rexy712 2022-01-15 15:26:21 -08:00
parent 1c4d5cd304
commit a49d24d61e
4 changed files with 98 additions and 25 deletions

View File

@ -29,20 +29,33 @@
namespace egn{
struct font_character{
GLuint atlas_texture;
math::vec2<size_t> atlas_offset;
math::vec2<size_t> size;
math::vec2<size_t> bearing;
math::vec2<size_t> 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<int, const char*> m_extra_codepages;
//stb or freetype font handle
std::optional<gfx::texture> m_atlas;
std::map<int, font_character> 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<int, font_character>&& metadata);
~font(void) = default;
font_character& operator[](int character);
const font_character& operator[](int character)const;
gfx::texture release_atlas(void);
std::map<int, font_character> 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<gfx::texture> 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<gfx::texture> generate_glyph(char character, int width, int height);
private:

View File

@ -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;

View File

@ -17,9 +17,32 @@
*/
#include "engine/font.hpp"
#include "math/vec.hpp"
#include <cmath> //sqrt
#include <rexy/utility.hpp> //max
#include <utility> //move
namespace egn{
font::font(gfx::texture&& tex, std::map<int, font_character>&& 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<int, font_character> font::release_metadata(void){
return std::map<int, font_character>(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<int, font_character> atlas_metadata;
math::vec2<size_t> 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<size_t> 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<gfx::texture> 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)){

View File

@ -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;