Implement simple text rendering in gfx::painter
This commit is contained in:
parent
3136c3b239
commit
eb67ee846d
2 changed files with 193 additions and 0 deletions
|
|
@ -16,6 +16,34 @@ namespace psemek::gfx
|
|||
{
|
||||
using color = color_rgba;
|
||||
|
||||
enum class font
|
||||
{
|
||||
font_9x12,
|
||||
};
|
||||
|
||||
enum class x_align
|
||||
{
|
||||
left,
|
||||
center,
|
||||
right,
|
||||
};
|
||||
|
||||
enum class y_align
|
||||
{
|
||||
top,
|
||||
center,
|
||||
bottom,
|
||||
};
|
||||
|
||||
struct text_options
|
||||
{
|
||||
font f = font::font_9x12;
|
||||
float scale = 1.f;
|
||||
x_align x = x_align::center;
|
||||
y_align y = y_align::center;
|
||||
color c = {255, 255, 255, 255};
|
||||
};
|
||||
|
||||
painter();
|
||||
~painter();
|
||||
|
||||
|
|
@ -25,6 +53,9 @@ namespace psemek::gfx
|
|||
void circle(geom::point<float, 2> const & center, float radius, color const & c);
|
||||
void line(geom::point<float, 2> const & p0, geom::point<float, 2> const & p1, float width, color const & c, bool smooth = true);
|
||||
|
||||
geom::vector<float, 2> text_size(std::string_view str, font f = font::font_9x12, float scale = 1.f);
|
||||
void text(geom::point<float, 2> const & p, std::string_view str, text_options const & opts);
|
||||
|
||||
// Should be called on each frame
|
||||
void render(geom::matrix<float, 4, 4> const & transform);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/gfx/program.hpp>
|
||||
#include <psemek/gfx/mesh.hpp>
|
||||
#include <psemek/gfx/texture.hpp>
|
||||
#include <psemek/gfx/resource/font_9x12.hpp>
|
||||
#include <psemek/geom/constants.hpp>
|
||||
#include <psemek/util/memory_stream.hpp>
|
||||
|
||||
static const char vertex_source[] =
|
||||
R"(#version 330
|
||||
|
|
@ -33,6 +36,42 @@ void main()
|
|||
}
|
||||
)";
|
||||
|
||||
static const char text_vertex_source[] =
|
||||
R"(#version 330
|
||||
|
||||
uniform mat4 u_transform;
|
||||
|
||||
layout (location = 0) in vec4 in_position;
|
||||
layout (location = 1) in vec4 in_color;
|
||||
layout (location = 2) in vec2 in_texcoord;
|
||||
|
||||
out vec4 color;
|
||||
out vec2 texcoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_transform * in_position;
|
||||
color = in_color;
|
||||
texcoord = in_texcoord;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char text_fragment_source[] =
|
||||
R"(#version 330
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
in vec4 color;
|
||||
in vec2 texcoord;
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = color * texture(u_texture, texcoord);
|
||||
}
|
||||
)";
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
|
|
@ -44,15 +83,39 @@ namespace psemek::gfx
|
|||
color_rgba color;
|
||||
};
|
||||
|
||||
struct text_vertex
|
||||
{
|
||||
geom::point<float, 2> position;
|
||||
color_rgba color;
|
||||
geom::point<std::uint16_t, 2> texcoord;
|
||||
};
|
||||
|
||||
gfx::program program{vertex_source, fragment_source};
|
||||
gfx::program text_program{text_vertex_source, text_fragment_source};
|
||||
gfx::indexed_mesh mesh;
|
||||
gfx::indexed_mesh text_mesh;
|
||||
|
||||
std::vector<vertex> vertices;
|
||||
std::vector<std::uint32_t> indices;
|
||||
|
||||
std::vector<text_vertex> text_vertices;
|
||||
std::vector<std::uint32_t> text_indices;
|
||||
|
||||
gfx::texture_2d font_texture;
|
||||
|
||||
impl()
|
||||
{
|
||||
mesh.setup<geom::vector<float, 3>, gfx::normalized<color_rgba>>();
|
||||
text_mesh.setup<geom::point<float, 2>, gfx::normalized<color_rgba>, gfx::normalized<geom::point<std::uint16_t, 2>>>();
|
||||
|
||||
util::memory_istream font_data(resource::font_9x12);
|
||||
font_texture.load(gfx::read_pbm(font_data));
|
||||
|
||||
font_texture.bind();
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_SWIZZLE_G, gl::RED);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_SWIZZLE_B, gl::RED);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_SWIZZLE_A, gl::RED);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -140,15 +203,114 @@ namespace psemek::gfx
|
|||
}
|
||||
}
|
||||
|
||||
geom::vector<float, 2> painter::text_size(std::string_view str, font f, float scale)
|
||||
{
|
||||
// TODO: multiline text
|
||||
geom::vector<float, 2> s;
|
||||
switch (f)
|
||||
{
|
||||
case font::font_9x12:
|
||||
s = {9.f, 12.f};
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown font");
|
||||
}
|
||||
|
||||
s[0] *= str.size() * scale;
|
||||
s[1] *= scale;
|
||||
return s;
|
||||
}
|
||||
|
||||
void painter::text(geom::point<float, 2> const & p, std::string_view str, text_options const & opts)
|
||||
{
|
||||
auto const size = text_size(str, opts.f, opts.scale);
|
||||
|
||||
auto pen = p;
|
||||
|
||||
switch (opts.x)
|
||||
{
|
||||
case x_align::left:
|
||||
break;
|
||||
case x_align::center:
|
||||
pen[0] -= size[0] / 2.f;
|
||||
break;
|
||||
case x_align::right:
|
||||
pen[0] -= size[0];
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown x alignment");
|
||||
}
|
||||
|
||||
switch (opts.y)
|
||||
{
|
||||
case y_align::top:
|
||||
break;
|
||||
case y_align::center:
|
||||
pen[1] -= size[1] / 2.f;
|
||||
break;
|
||||
case y_align::bottom:
|
||||
pen[1] -= size[1];
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown y alignment");
|
||||
}
|
||||
|
||||
geom::vector<float, 2> const sx = {9.f * opts.scale, 0.f};
|
||||
geom::vector<float, 2> const sy = {0.f, 12.f * opts.scale};
|
||||
|
||||
auto to_texcoord = [](int tx, int ty)
|
||||
{
|
||||
return geom::point<std::uint16_t, 2>{ (tx * 65535) / 16, (ty * 65535) / 6 };
|
||||
};
|
||||
|
||||
for (char c : str)
|
||||
{
|
||||
if (c < 32 || c > 128) c = '?';
|
||||
|
||||
int ty = (c - 32) / 16;
|
||||
int tx = (c - 32) % 16;
|
||||
|
||||
std::uint32_t const base = impl().text_vertices.size();
|
||||
|
||||
geom::point<float, 2> position;
|
||||
color_rgba color;
|
||||
geom::point<std::uint16_t, 2> texcoord;
|
||||
|
||||
impl().text_vertices.push_back({pen, opts.c, to_texcoord(tx, ty)});
|
||||
impl().text_vertices.push_back({pen + sx, opts.c, to_texcoord(tx + 1, ty)});
|
||||
impl().text_vertices.push_back({pen + sy, opts.c, to_texcoord(tx, ty + 1)});
|
||||
impl().text_vertices.push_back({pen + sx + sy, opts.c, to_texcoord(tx + 1, ty + 1)});
|
||||
|
||||
impl().text_indices.push_back(base + 0);
|
||||
impl().text_indices.push_back(base + 1);
|
||||
impl().text_indices.push_back(base + 3);
|
||||
impl().text_indices.push_back(base + 0);
|
||||
impl().text_indices.push_back(base + 3);
|
||||
impl().text_indices.push_back(base + 2);
|
||||
|
||||
pen += sx;
|
||||
}
|
||||
}
|
||||
|
||||
void painter::render(geom::matrix<float, 4, 4> const & transform)
|
||||
{
|
||||
impl().mesh.load(impl().vertices, impl().indices, gl::STREAM_DRAW);
|
||||
impl().vertices.clear();
|
||||
impl().indices.clear();
|
||||
|
||||
impl().text_mesh.load(impl().text_vertices, impl().text_indices, gl::STREAM_DRAW);
|
||||
impl().text_vertices.clear();
|
||||
impl().text_indices.clear();
|
||||
|
||||
impl().program.bind();
|
||||
impl().program["u_transform"] = transform;
|
||||
impl().mesh.draw(gl::TRIANGLES);
|
||||
|
||||
impl().text_program.bind();
|
||||
impl().text_program["u_transform"] = transform;
|
||||
impl().text_program["u_texture"] = 0;
|
||||
impl().font_texture.bind();
|
||||
impl().text_mesh.draw(gl::TRIANGLES);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue