From b9dd6c78a37a51767343bfac06a09a7acf7c64a9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 25 Feb 2021 14:17:24 +0300 Subject: [PATCH] Implement glyph rendering in painter --- libs/ui/include/psemek/ui/painter.hpp | 1 + libs/ui/include/psemek/ui/painter_impl.hpp | 1 + libs/ui/source/painter_impl.cpp | 198 +++++++++++++++++---- 3 files changed, 165 insertions(+), 35 deletions(-) diff --git a/libs/ui/include/psemek/ui/painter.hpp b/libs/ui/include/psemek/ui/painter.hpp index 0f470d78..e857ae96 100644 --- a/libs/ui/include/psemek/ui/painter.hpp +++ b/libs/ui/include/psemek/ui/painter.hpp @@ -10,6 +10,7 @@ namespace psemek::ui struct painter { virtual void draw_rect(geom::box const & rect, gfx::color_rgba const & color) = 0; + virtual void draw_glyph(font const & f, char32_t c, geom::box const & rect, gfx::color_rgba const & color) = 0; virtual ~painter() {} }; diff --git a/libs/ui/include/psemek/ui/painter_impl.hpp b/libs/ui/include/psemek/ui/painter_impl.hpp index 8fd3280a..e35f3d4a 100644 --- a/libs/ui/include/psemek/ui/painter_impl.hpp +++ b/libs/ui/include/psemek/ui/painter_impl.hpp @@ -16,6 +16,7 @@ namespace psemek::ui ~painter_impl(); void draw_rect(geom::box const & rect, gfx::color_rgba const & color) override; + void draw_glyph(font const & f, char32_t c, geom::box const & rect, gfx::color_rgba const & color) override; void render(geom::matrix const & transform); diff --git a/libs/ui/source/painter_impl.cpp b/libs/ui/source/painter_impl.cpp index e425fce5..28d23333 100644 --- a/libs/ui/source/painter_impl.cpp +++ b/libs/ui/source/painter_impl.cpp @@ -1,8 +1,11 @@ #include #include -#include -#include +#include + +#include + +#include namespace psemek::ui { @@ -13,7 +16,7 @@ R"(#version 330 uniform mat4 u_transform; layout (location = 0) in vec2 in_position; -layout (location = 1) in uint in_depth; +layout (location = 1) in float in_depth; layout (location = 2) in vec4 in_color; out vec4 color; @@ -38,6 +41,46 @@ void main() } )"; + static char const textured_vs[] = +R"(#version 330 + +uniform mat4 u_transform; + +uniform vec2 u_texture_size; + +layout (location = 0) in vec2 in_position; +layout (location = 1) in float in_depth; +layout (location = 2) in vec4 in_color; +layout (location = 3) in vec2 in_texcoord; + +out vec4 color; +out vec2 texcoord; + +void main() +{ + gl_Position = u_transform * vec4(in_position, 1.0 - float(in_depth) / 16777216.0 * 2.0, 1.0); + color = in_color; + texcoord = in_texcoord / u_texture_size; +} +)"; + + static char const textured_fs[] = +R"(#version 330 + +uniform sampler2D u_texture; + +in vec2 texcoord; +in vec4 color; + +out vec4 out_color; + +void main() +{ + out_color = texture(u_texture, texcoord) * color; +} +)"; + + struct colored_vertex { geom::point position; @@ -46,33 +89,75 @@ void main() }; static_assert(sizeof(colored_vertex) == 16); + struct colored_batch + { + std::vector vertices; + std::vector indices; + }; + + bool operator == (colored_batch const &, colored_batch const &) + { + return true; + } + + struct textured_vertex + { + geom::point position; + std::uint32_t depth; + gfx::color_rgba color; + geom::vector texcoords; + }; + static_assert(sizeof(textured_vertex) == 20); + + struct textured_batch + { + gfx::texture_2d const * texture = nullptr; + std::vector vertices{}; + std::vector indices{}; + }; + + bool operator == (textured_batch const & b1, textured_batch const & b2) + { + return b1.texture == b2.texture; + } + struct painter_impl::impl { std::uint32_t depth = 0; gfx::program colored_program; - gfx::array colored_vao; - gfx::buffer colored_vbo; - gfx::buffer colored_ebo; + gfx::mesh colored_mesh; - std::vector colored_vertices; - std::vector colored_indices; + gfx::program textured_program; + gfx::mesh textured_mesh; + + std::vector> batches; + + template + Batch & batch(Batch && n) + { + if (!batches.empty()) + { + auto b = std::get_if(&batches.back()); + if (b && *b == n) return *b; + } + + batches.push_back(std::move(n)); + return std::get(batches.back()); + } impl(); }; painter_impl::impl::impl() : colored_program{colored_vs, colored_fs} + , textured_program{textured_vs, textured_fs} { - colored_vao.bind(); - colored_vbo.bind(); - gl::EnableVertexAttribArray(0); - gl::VertexAttribPointer(0, 2, gl::FLOAT, gl::FALSE, sizeof(colored_vertex), reinterpret_cast(0)); - gl::EnableVertexAttribArray(1); - gl::VertexAttribIPointer(1, 1, gl::UNSIGNED_INT, sizeof(colored_vertex), reinterpret_cast(8)); - gl::EnableVertexAttribArray(2); - gl::VertexAttribPointer(2, 4, gl::UNSIGNED_BYTE, gl::TRUE, sizeof(colored_vertex), reinterpret_cast(12)); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, colored_ebo.id()); + textured_program.bind(); + textured_program["u_texture"] = 0; + + colored_mesh.setup, std::uint32_t, gfx::normalized>(); + textured_mesh.setup, std::uint32_t, gfx::normalized, geom::vector>(); } painter_impl::painter_impl() @@ -83,33 +168,76 @@ void main() void painter_impl::draw_rect(geom::box const & rect, gfx::color_rgba const & color) { + auto & batch = impl().batch({}); + std::uint32_t const depth = impl().depth++; - std::uint32_t const base = impl().colored_vertices.size(); + std::uint32_t const base = batch.vertices.size(); - impl().colored_vertices.push_back({rect.corner(0.f, 0.f), depth, color}); - impl().colored_vertices.push_back({rect.corner(1.f, 0.f), depth, color}); - impl().colored_vertices.push_back({rect.corner(0.f, 1.f), depth, color}); - impl().colored_vertices.push_back({rect.corner(1.f, 1.f), depth, color}); + batch.vertices.push_back({rect.corner(0.f, 0.f), depth, color}); + batch.vertices.push_back({rect.corner(1.f, 0.f), depth, color}); + batch.vertices.push_back({rect.corner(0.f, 1.f), depth, color}); + batch.vertices.push_back({rect.corner(1.f, 1.f), depth, color}); - impl().colored_indices.insert(impl().colored_indices.end(), {base + 0, base + 1, base + 2, base + 2, base + 1, base + 3}); + batch.indices.insert(batch.indices.end(), {base + 0, base + 1, base + 2, base + 2, base + 1, base + 3}); + } + + void painter_impl::draw_glyph(font const & f, char32_t c, geom::box const & rect, gfx::color_rgba const & color) + { + auto tbox = f.texcoords(c); + if (!tbox) return; + + auto size = f.atlas().size(); + + auto const tc = [&](float x, float y) + { + auto t = tbox->corner(x, y); + geom::vector r; + r[0] = geom::clamp(t[0], {0, size[0]}); + r[1] = geom::clamp(t[1], {0, size[1]}); + return r; + }; + + auto & batch = impl().batch(textured_batch{&f.atlas()}); + + std::uint32_t const depth = impl().depth++; + std::uint32_t const base = batch.vertices.size(); + + batch.vertices.push_back({rect.corner(0.f, 0.f), depth, color, tc(0.f, 0.f)}); + batch.vertices.push_back({rect.corner(1.f, 0.f), depth, color, tc(1.f, 0.f)}); + batch.vertices.push_back({rect.corner(0.f, 1.f), depth, color, tc(0.f, 1.f)}); + batch.vertices.push_back({rect.corner(1.f, 1.f), depth, color, tc(1.f, 1.f)}); + + batch.indices.insert(batch.indices.end(), {base + 0, base + 1, base + 2, base + 2, base + 1, base + 3}); } void painter_impl::render(geom::matrix const & transform) { - impl().colored_vbo.load(impl().colored_vertices); - impl().colored_ebo.load(impl().colored_indices); - - int const colored_count = impl().colored_indices.size(); - - impl().colored_vertices.clear(); - impl().colored_indices.clear(); - - impl().depth = 0; - impl().colored_program.bind(); impl().colored_program["u_transform"] = transform; - impl().colored_vao.bind(); - gl::DrawElements(gl::TRIANGLES, colored_count, gl::UNSIGNED_INT, nullptr); + impl().textured_program.bind(); + impl().textured_program["u_transform"] = transform; + + gl::ActiveTexture(gl::TEXTURE0); + + auto batch_visitor = util::overload([&](colored_batch const & b){ + impl().colored_program.bind(); + impl().colored_mesh.load(b.vertices, b.indices, gl::TRIANGLES); + impl().colored_mesh.draw(); + }, + [&](textured_batch const & b){ + if (!b.texture) return; + impl().textured_program.bind(); + impl().textured_program["u_texture_size"] = geom::cast(b.texture->size()); + b.texture->bind(); + impl().textured_mesh.load(b.vertices, b.indices, gl::TRIANGLES); + impl().textured_mesh.draw(); + }); + + for (auto const & b : impl().batches) + std::visit(batch_visitor, b); + + impl().depth = 0; + impl().batches.clear(); } }