Add basic font atlas loading
This commit is contained in:
parent
1c4d5cd304
commit
a49d24d61e
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)){
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user