font rendering works now
parent
29a7923269
commit
b84d29ee9b
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
include(FetchContent)
|
||||
|
||||
set(BLT_GRAPHICS_VERSION 1.0.7)
|
||||
set(BLT_GRAPHICS_VERSION 1.0.8)
|
||||
set(BLT_GRAPHICS_TEST_VERSION 0.0.1)
|
||||
|
||||
project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION})
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace blt::gfx
|
|||
struct bounded_t
|
||||
{
|
||||
font::glyph_t glyph;
|
||||
// starting point
|
||||
// starting point inside texture atlas
|
||||
blt::vec2ui min;
|
||||
// ending point of the glyph, inside texture atlas
|
||||
blt::vec2ui max;
|
||||
|
@ -49,18 +49,18 @@ namespace blt::gfx
|
|||
public:
|
||||
explicit font_texture_atlas(blt::i32 dimensions);
|
||||
|
||||
added_font_results_t add_font(const font::font_file_t& file);
|
||||
added_font_results_t add_font(const font::font_file_t& file, float size);
|
||||
|
||||
added_font_results_t add_font(const font::font_file_t& file, blt::u64 start);
|
||||
added_font_results_t add_font(const font::font_file_t& file, blt::u64 start, float size);
|
||||
|
||||
bounded_t& get_glyph(blt::u64 c)
|
||||
{ return glyphs.at(c); }
|
||||
bounded_t& get_glyph(blt::u64 c, float size)
|
||||
{ return glyphs.at(size).at(c); }
|
||||
|
||||
[[nodiscard]] const bounded_t& get_glyph(blt::u64 c) const
|
||||
{ return glyphs.at(c); }
|
||||
[[nodiscard]] const bounded_t& get_glyph(blt::u64 c, float size) const
|
||||
{ return glyphs.at(size).at(c); }
|
||||
|
||||
private:
|
||||
blt::hashmap_t<blt::u64, bounded_t> glyphs;
|
||||
blt::hashmap_t<float, blt::hashmap_t<blt::u64, bounded_t>> glyphs;
|
||||
blt::u32 used_width = 0;
|
||||
blt::u32 used_height = 0;
|
||||
};
|
||||
|
@ -90,11 +90,14 @@ namespace blt::gfx
|
|||
{
|
||||
if (!(size >= atlas.min_size && size <= atlas.max_size))
|
||||
continue;
|
||||
if (atlas.min_char > c && atlas.max_char < c)
|
||||
if (c >= atlas.min_char && c <= atlas.max_char)
|
||||
return *atlas.atlas;
|
||||
}
|
||||
throw std::runtime_error("Character not found inside atlases at this size");
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::i32 get_dimensions() const
|
||||
{ return dimensions; }
|
||||
|
||||
private:
|
||||
blt::i32 dimensions;
|
||||
|
@ -111,27 +114,89 @@ namespace blt::gfx
|
|||
public:
|
||||
explicit compiled_text_t(font_generator_t& generator);
|
||||
|
||||
void change_text(std::string_view str);
|
||||
void change_text(std::string_view str, float size);
|
||||
|
||||
compiled_text_t& setPosition(const blt::vec2f& pos)
|
||||
{
|
||||
position = pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
compiled_text_t& setScale(const blt::vec2f& s)
|
||||
{
|
||||
scale = s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
compiled_text_t& setColor(const blt::vec4f& c)
|
||||
{
|
||||
color = c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
compiled_text_t& setPosition(float x, float y)
|
||||
{
|
||||
position = {x, y};
|
||||
return *this;
|
||||
}
|
||||
|
||||
compiled_text_t& setScale(float x, float y)
|
||||
{
|
||||
scale = {x, y};
|
||||
return *this;
|
||||
}
|
||||
|
||||
compiled_text_t& setColor(float r, float g, float b, float a = 1)
|
||||
{
|
||||
color = {r, g, b, a};
|
||||
return *this;
|
||||
}
|
||||
|
||||
compiled_text_t& setZIndex(float s)
|
||||
{
|
||||
z_index = s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const vec2f& getPosition() const
|
||||
{ return position; }
|
||||
|
||||
[[nodiscard]] const vec2f& getScale() const
|
||||
{ return scale; }
|
||||
|
||||
[[nodiscard]] const vec4f& getColor() const
|
||||
{ return color; }
|
||||
|
||||
[[nodiscard]] float getZIndex() const
|
||||
{ return z_index; }
|
||||
|
||||
private:
|
||||
void draw();
|
||||
|
||||
struct text_render_info_t
|
||||
{
|
||||
vertex_array_t vao{};
|
||||
font_texture_atlas* texture{};
|
||||
font_texture_atlas* texture = nullptr;
|
||||
blt::size_t render_start = 0;
|
||||
blt::size_t render_count = 0;
|
||||
|
||||
text_render_info_t() = default;
|
||||
text_render_info_t(font_texture_atlas* texture, size_t renderStart, size_t renderCount):
|
||||
texture(texture), render_start(renderStart), render_count(renderCount)
|
||||
{}
|
||||
};
|
||||
|
||||
font_generator_t& generator;
|
||||
std::vector<std::unique_ptr<text_render_info_t>> renders;
|
||||
vertex_array_t vao{};
|
||||
std::vector<text_render_info_t> renders;
|
||||
blt::vec2f position;
|
||||
blt::vec2f scale = {1, 1};
|
||||
blt::vec4 color = blt::make_color(1, 1, 1);
|
||||
float z_index = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit font_renderer_t();
|
||||
|
||||
void create_default(blt::i32 dimensions = 0);
|
||||
void create(const std::vector<float>& generated_font_sizes, blt::i32 dimensions = 0);
|
||||
|
||||
void cleanup();
|
||||
|
@ -141,9 +206,15 @@ namespace blt::gfx
|
|||
generator->add_font(file);
|
||||
}
|
||||
|
||||
compiled_text_t* create_text(std::string_view str);
|
||||
void add_default_font(std::string_view path);
|
||||
|
||||
void add_default_font(const blt::u8* data, blt::size_t size, bool compressed = false);
|
||||
|
||||
compiled_text_t* create_text(std::string_view str, float size);
|
||||
|
||||
void destroy_text(compiled_text_t* text);
|
||||
|
||||
void render();
|
||||
|
||||
private:
|
||||
std::unique_ptr<font_generator_t> generator;
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
const std::string shader_2d_font_frag = R"("
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
layout (location = 0) out vec4 FragColor;
|
||||
in vec2 uv;
|
||||
layout (location = 0) out vec4 FragColor;
|
||||
in vec2 uv;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
|
||||
void main() {
|
||||
FragColor = texture(tex, uv).r * color;
|
||||
}
|
||||
void main() {
|
||||
FragColor = texture(tex, uv).r * color;
|
||||
if (FragColor.a < 0.2)
|
||||
discard;
|
||||
}
|
||||
|
||||
")";
|
||||
#endif
|
|
@ -18,9 +18,10 @@ layout (std140) uniform GlobalMatrices
|
|||
};
|
||||
|
||||
uniform float z_index;
|
||||
uniform mat4 transform;
|
||||
|
||||
void main() {
|
||||
gl_Position = ovm * vec4(vertex.xy, z_index, 1.0);
|
||||
gl_Position = ovm * transform * vec4(vertex.xy, z_index, 1.0);
|
||||
uv = vertex.zw;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,18 +25,27 @@
|
|||
namespace blt::gfx
|
||||
{
|
||||
font_texture_atlas::font_texture_atlas(blt::i32 dimensions): texture_gl2D(dimensions, dimensions, GL_R8)
|
||||
{}
|
||||
|
||||
font_texture_atlas::added_font_results_t font_texture_atlas::add_font(const font::font_file_t& file)
|
||||
{
|
||||
return add_font(file, file.character_min_index);
|
||||
bind();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
|
||||
}
|
||||
|
||||
font_texture_atlas::added_font_results_t font_texture_atlas::add_font(const font::font_file_t& file, blt::u64 start)
|
||||
font_texture_atlas::added_font_results_t font_texture_atlas::add_font(const font::font_file_t& file, float size)
|
||||
{
|
||||
return add_font(file, file.character_min_index, size);
|
||||
}
|
||||
|
||||
font_texture_atlas::added_font_results_t font_texture_atlas::add_font(const font::font_file_t& file, blt::u64 start, float size)
|
||||
{
|
||||
blt::u32 max_height = 0;
|
||||
blt::u64 min_index_used = file.character_max_index;
|
||||
blt::u64 max_index_used = start;
|
||||
file.font.set_pixel_sizes(0, static_cast<blt::u32>(size));
|
||||
|
||||
const blt::u32 padding = 2;
|
||||
|
||||
for (auto i = start; i < file.character_max_index; i++)
|
||||
{
|
||||
// returns an empty optional if there is no error code
|
||||
|
@ -46,10 +55,14 @@ namespace blt::gfx
|
|||
|
||||
auto width = face->glyph->bitmap.width;
|
||||
auto height = face->glyph->bitmap.rows;
|
||||
max_height = std::max(max_height, height);
|
||||
|
||||
auto texture_width = width + padding;
|
||||
auto texture_height = height + padding;
|
||||
|
||||
max_height = std::max(max_height, texture_height);
|
||||
|
||||
// if adding this width overflows, we need to move to the next row.
|
||||
if (used_width + width > static_cast<blt::u32>(getWidth()))
|
||||
if (used_width + texture_width > static_cast<blt::u32>(getWidth()))
|
||||
{
|
||||
used_height += max_height;
|
||||
max_height = 0;
|
||||
|
@ -66,14 +79,14 @@ namespace blt::gfx
|
|||
auto begin_height = used_height;
|
||||
auto end_width = used_width + width;
|
||||
auto end_height = used_height + height;
|
||||
used_width += width;
|
||||
used_width += texture_width;
|
||||
|
||||
// BLT_TRACE("%d %d %d %d", begin_width, begin_height, width, height);
|
||||
// upload the texture
|
||||
upload(face->glyph->bitmap.buffer, GL_RED, 0, static_cast<blt::i32>(begin_width), static_cast<blt::i32>(begin_height),
|
||||
static_cast<blt::i32>(width), static_cast<blt::i32>(height), GL_UNSIGNED_BYTE);
|
||||
|
||||
glyphs.insert(std::pair{i, bounded_t{
|
||||
glyphs[size].insert(std::pair{i, bounded_t{
|
||||
font::glyph_t{
|
||||
{width, height},
|
||||
{face->glyph->bitmap_left, face->glyph->bitmap_top},
|
||||
|
@ -81,6 +94,8 @@ namespace blt::gfx
|
|||
{begin_width, begin_height}, {end_width, end_height}}
|
||||
});
|
||||
}
|
||||
bind();
|
||||
generateMipmaps();
|
||||
return {-1ul, min_index_used, max_index_used};
|
||||
}
|
||||
|
||||
|
@ -98,7 +113,6 @@ namespace blt::gfx
|
|||
min_size = std::min(size, min_size);
|
||||
max_size = std::max(size, max_size);
|
||||
auto& vec = atlases;
|
||||
file.font.set_pixel_sizes(0, static_cast<blt::u32>(size));
|
||||
|
||||
if (vec.empty())
|
||||
vec.emplace_back(std::make_unique<font_texture_atlas>(dimensions), min_size, max_size, file.character_min_index, 0);
|
||||
|
@ -106,15 +120,17 @@ namespace blt::gfx
|
|||
blt::u64 start_index = file.character_min_index;
|
||||
while (start_index != -1ul)
|
||||
{
|
||||
auto results = vec.back().atlas->add_font(file, start_index);
|
||||
auto results = vec.back().atlas->add_font(file, start_index, size);
|
||||
vec.back().min_char = results.min_index_used;
|
||||
vec.back().max_char = results.max_index_used;
|
||||
vec.back().min_size = min_size;
|
||||
vec.back().max_size = max_size;
|
||||
min_size = size;
|
||||
max_size = size;
|
||||
if (results.last_inserted_index != -1ul)
|
||||
{
|
||||
vec.emplace_back(std::make_unique<font_texture_atlas>(dimensions), min_size, max_size, start_index, 0);
|
||||
min_size = size;
|
||||
max_size = size;
|
||||
}
|
||||
start_index = results.last_inserted_index;
|
||||
// BLT_INFO("%f, %ld", size, start_index);
|
||||
}
|
||||
|
@ -126,12 +142,13 @@ namespace blt::gfx
|
|||
extern float square_vertices[20];
|
||||
extern const unsigned int square_indices[6];
|
||||
|
||||
font_renderer_t::font_renderer_t()
|
||||
{}
|
||||
font_renderer_t::font_renderer_t() = default;
|
||||
|
||||
void font_renderer_t::create(const std::vector<float>& generated_font_sizes, blt::i32 dimensions)
|
||||
{
|
||||
font_shader = shader_t::make_unique(shader_2d_font_vert, shader_2d_font_frag);
|
||||
font_shader->bind();
|
||||
font_shader->bindAttribute(0, "vertex");
|
||||
font::create();
|
||||
if (dimensions == 0)
|
||||
{
|
||||
|
@ -145,11 +162,19 @@ namespace blt::gfx
|
|||
|
||||
void font_renderer_t::cleanup()
|
||||
{
|
||||
for (auto& n : added_texts)
|
||||
n = nullptr;
|
||||
for (auto& c : cached_text_objects)
|
||||
c = nullptr;
|
||||
added_texts.clear();
|
||||
cached_text_objects.clear();
|
||||
text_ptr_to_index.clear();
|
||||
font_shader = nullptr;
|
||||
generator = nullptr;
|
||||
blt::gfx::font::cleanup();
|
||||
}
|
||||
|
||||
font_renderer_t::compiled_text_t* font_renderer_t::create_text(std::string_view str)
|
||||
font_renderer_t::compiled_text_t* font_renderer_t::create_text(std::string_view str, float size)
|
||||
{
|
||||
std::unique_ptr<font_renderer_t::compiled_text_t> ptr;
|
||||
if (!cached_text_objects.empty())
|
||||
|
@ -160,7 +185,7 @@ namespace blt::gfx
|
|||
{
|
||||
ptr = std::make_unique<compiled_text_t>(*generator);
|
||||
}
|
||||
ptr->change_text(str);
|
||||
ptr->change_text(str, size);
|
||||
auto index = added_texts.size();
|
||||
added_texts.push_back(std::move(ptr));
|
||||
text_ptr_to_index[added_texts.back().get()] = index;
|
||||
|
@ -176,21 +201,108 @@ namespace blt::gfx
|
|||
added_texts.erase(added_texts.begin() + static_cast<blt::ptrdiff_t>(index));
|
||||
}
|
||||
|
||||
void font_renderer_t::compiled_text_t::change_text(std::string_view str)
|
||||
void font_renderer_t::render()
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
font_shader->bind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
for (auto& text : added_texts)
|
||||
{
|
||||
blt::mat4x4 transform;
|
||||
transform.translate(text->position);
|
||||
transform.scale(text->scale);
|
||||
font_shader->setMatrix("transform", transform);
|
||||
font_shader->setFloat("z_index", text->z_index);
|
||||
font_shader->setVec4("color", text->color);
|
||||
text->draw();
|
||||
}
|
||||
}
|
||||
|
||||
void font_renderer_t::create_default(blt::i32 dimensions)
|
||||
{
|
||||
create({9, 11, 12, 13, 14, 16, 18, 24, 32, 36, 40, 48, 52, 64, 72, 96, 106}, dimensions);
|
||||
|
||||
add_default_font(reinterpret_cast<const blt::u8*>(font::default_font_compressed_data), font::default_font_compressed_size, true);
|
||||
}
|
||||
|
||||
void font_renderer_t::add_default_font(std::string_view path)
|
||||
{
|
||||
font::font_face_t default_font_face{path};
|
||||
font::font_file_t default_font{default_font_face, 0, 128};
|
||||
add_font(default_font);
|
||||
}
|
||||
|
||||
void font_renderer_t::add_default_font(const blt::u8* data, blt::size_t size, bool compressed)
|
||||
{
|
||||
font::font_face_t default_font_face{data, size, compressed};
|
||||
font::font_file_t default_font{default_font_face, 0, 128};
|
||||
add_font(default_font);
|
||||
}
|
||||
|
||||
void font_renderer_t::compiled_text_t::change_text(std::string_view str, float size)
|
||||
{
|
||||
static std::vector<float> vertices;
|
||||
vertices.clear();
|
||||
renders.clear();
|
||||
|
||||
font_texture_atlas* last_texture = nullptr;
|
||||
blt::size_t draw_start = 0;
|
||||
blt::size_t draw_count = 0;
|
||||
|
||||
float x = 0;
|
||||
// TODO: parker UTF8
|
||||
for (const auto& c : str)
|
||||
{
|
||||
|
||||
auto& texture = generator.get_texture(size, c);
|
||||
|
||||
if (last_texture == nullptr)
|
||||
last_texture = &texture;
|
||||
else if (last_texture != &texture)
|
||||
{
|
||||
renders.emplace_back(last_texture, draw_start, draw_count);
|
||||
draw_start += draw_count;
|
||||
draw_count = 0;
|
||||
}
|
||||
|
||||
auto& ch = texture.get_glyph(c, size);
|
||||
auto dims = static_cast<float>(generator.get_dimensions());
|
||||
|
||||
float x_pos = x + static_cast<float>(ch.glyph.bearing.x());
|
||||
float y_pos = -static_cast<float>(ch.glyph.size.y() - ch.glyph.bearing.y());
|
||||
|
||||
auto w = static_cast<float>(ch.glyph.size.x());
|
||||
auto h = static_cast<float>(ch.glyph.size.y());
|
||||
|
||||
auto texture_min_x = static_cast<float>(ch.min.x()) / dims;
|
||||
auto texture_min_y = static_cast<float>(ch.min.y()) / dims;
|
||||
auto texture_max_x = static_cast<float>(ch.max.x()) / dims;
|
||||
auto texture_max_y = static_cast<float>(ch.max.y()) / dims;
|
||||
|
||||
// BLT_TRACE("%c: %f | xy[%f %f] wh[%f %f] | min[%f %f] max[%f %f] |", c, x, x_pos, y_pos, w, h, texture_min_x, texture_min_y, texture_max_x, texture_max_y);
|
||||
|
||||
std::array<float, 6 * 4> local_vert = {
|
||||
x_pos, y_pos + h, texture_min_x, texture_min_y,
|
||||
x_pos, y_pos, texture_min_x, texture_max_y,
|
||||
x_pos + w, y_pos, texture_max_x, texture_max_y,
|
||||
|
||||
x_pos, y_pos + h, texture_min_x, texture_min_y,
|
||||
x_pos + w, y_pos, texture_max_x, texture_max_y,
|
||||
x_pos + w, y_pos + h, texture_max_x, texture_min_y
|
||||
};
|
||||
vertices.insert(vertices.end(), local_vert.begin(), local_vert.end());
|
||||
draw_count += 6;
|
||||
x += static_cast<float>(ch.glyph.advance >> 6);
|
||||
}
|
||||
|
||||
// BLT_TRACE("size: %ld %ld %ld", draw_count, vertices.size(), vertices.size() / 4);
|
||||
|
||||
renders.emplace_back(last_texture, draw_start, draw_count);
|
||||
vao.getBuffer(0).update(static_cast<long>(vertices.size() * sizeof(float)), vertices.data());
|
||||
}
|
||||
|
||||
font_renderer_t::compiled_text_t::compiled_text_t(font_generator_t& generator): generator(generator)
|
||||
{
|
||||
renders.emplace_back();
|
||||
auto& vao = renders.back()->vao;
|
||||
auto vbo = vertex_array_t::make_vbo({});
|
||||
vbo->vbo.create();
|
||||
vbo->vbo.bind();
|
||||
|
@ -201,6 +313,17 @@ namespace blt::gfx
|
|||
|
||||
void font_renderer_t::compiled_text_t::draw()
|
||||
{
|
||||
|
||||
font_texture_atlas* last_texture = nullptr;
|
||||
vao.bind();
|
||||
for (const auto& render : renders)
|
||||
{
|
||||
if (last_texture != render.texture)
|
||||
{
|
||||
last_texture = render.texture;
|
||||
render.texture->bind();
|
||||
}
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, static_cast<int>(render.render_start), static_cast<int>(render.render_count));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue