Antkeeper  0.0.1
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
text.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <engine/scene/text.hpp>
23 #include <engine/scene/camera.hpp>
24 #include <engine/debug/log.hpp>
25 #include <cstddef>
26 
27 namespace scene {
28 
29 namespace {
30 
32  constexpr gl::vertex_input_attribute text_vertex_attributes[3] =
33  {
34  {
36  0,
38  0
39  },
40  {
42  0,
44  2 * sizeof(float)
45  },
46  {
48  0,
50  4 * sizeof(float)
51  }
52  };
53 
55  constexpr std::size_t text_vertex_stride = (2 + 2 + 4) * sizeof(float);
56 }
57 
59 {
60  // Construct vertex array
61  m_vertex_array = std::make_unique<gl::vertex_array>(text_vertex_attributes);
62 
63  // Construct empty vertex buffer
64  m_vertex_buffer = std::make_unique<gl::vertex_buffer>();
65 
66  // Init render operation
68  m_render_op.vertex_array = m_vertex_array.get();
69  m_render_op.vertex_buffer = m_vertex_buffer.get();
70  m_render_op.vertex_offset = 0;
71  m_render_op.vertex_stride = text_vertex_stride;
72  m_render_op.first_vertex = 0;
73  m_render_op.vertex_count = 0;
74  m_render_op.first_instance = 0;
75  m_render_op.instance_count = 1;
76 }
77 
78 void text::render(render::context& ctx) const
79 {
80  if (m_render_op.vertex_count)
81  {
82  m_render_op.depth = ctx.camera->get_view_frustum().near().distance(get_translation());
83  m_render_op.layer_mask = get_layer_mask();
84  ctx.operations.push_back(&m_render_op);
85  }
86 }
87 
89 {
90  update_content();
91 }
92 
93 void text::set_material(std::shared_ptr<render::material> material)
94 {
95  m_render_op.material = material;
96 }
97 
99 {
100  if (m_font != font)
101  {
102  m_font = font;
103  update_content();
104  }
105 }
106 
108 {
109  if (m_direction != direction)
110  {
111  m_direction = direction;
112  update_content();
113  }
114 }
115 
116 void text::set_content(const std::string& content)
117 {
118  if (m_content_u8 != content)
119  {
120  m_content_u8 = content;
121  m_content_u32 = type::unicode::u32(m_content_u8);
122  update_content();
123  }
124 }
125 
127 {
128  m_color = color;
129  update_color();
130 }
131 
132 void text::transformed()
133 {
134  // Naive algorithm: transform each corner of the AABB
135  m_world_bounds = {math::fvec3::infinity(), -math::fvec3::infinity()};
136  for (std::size_t i = 0; i < 8; ++i)
137  {
138  m_world_bounds.extend(get_transform() * m_local_bounds.corner(i));
139  }
140 
141  m_render_op.transform = get_transform().matrix();
142 }
143 
144 void text::update_content()
145 {
146  // If no valid font or no text, clear vertex count
147  if (!m_font || m_content_u32.empty())
148  {
149  m_render_op.vertex_count = 0;
150  m_local_bounds = {{0, 0, 0}, {0, 0, 0}};
151  transformed();
152  return;
153  }
154 
155  // Reserve vertex data
156  auto vbo_min_size = m_content_u32.length() * text_vertex_stride * 6;
157  if (m_vertex_data.size() < vbo_min_size)
158  {
159  m_vertex_data.resize(vbo_min_size);
160  }
161 
162  std::uint32_t visible_character_count = static_cast<std::uint32_t>(m_content_u32.length());
163 
164  // Get font metrics and texture
165  const type::font_metrics& font_metrics = m_font->get_font_metrics();
166  const auto& font_texture = m_font->get_texture();
167  const auto& font_texture_dimensions = font_texture->get_image_view()->get_image()->get_dimensions();
168 
169  // Init pen position
170  math::fvec2 pen_position = {0.0f, 0.0f};
171 
172  // Reset local-space bounds
173  m_local_bounds.min = {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), 0.0f};
174  m_local_bounds.max = {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), 0.0f};
175 
176  // Generate vertex data
177  char32_t previous_code = 0;
178  float* v = reinterpret_cast<float*>(m_vertex_data.data());
179  for (char32_t code: m_content_u32)
180  {
181  // Apply kerning
182  if (previous_code)
183  {
184  pen_position.x() += m_font->get_kerning(previous_code, code).x();
185  }
186 
187  // Get glyph
188  const type::bitmap_glyph* glyph = m_font->get_glyph(code);
189  if (glyph)
190  {
191  // Calculate vertex positions
192  math::fvec2 positions[6];
193  positions[0] = pen_position + glyph->metrics.horizontal_bearing;
194  positions[1] = {positions[0].x(), positions[0].y() - glyph->metrics.height};
195  positions[2] = {positions[0].x() + glyph->metrics.width, positions[1].y()};
196  positions[3] = {positions[2].x(), positions[0].y()};
197  positions[4] = positions[0];
198  positions[5] = positions[2];
199 
200  // Calculate vertex UVs
201  math::fvec2 uvs[6];
202  uvs[0] = {static_cast<float>(glyph->position.x()), static_cast<float>(glyph->position.y())};
203  uvs[1] = {uvs[0].x(), uvs[0].y() + glyph->metrics.height};
204  uvs[2] = {uvs[0].x() + glyph->metrics.width, uvs[1].y()};
205  uvs[3] = {uvs[2].x(), uvs[0].y()};
206  uvs[4] = uvs[0];
207  uvs[5] = uvs[2];
208 
209  for (int i = 0; i < 6; ++i)
210  {
211  // Round positions
212  positions[i].x() = std::round(positions[i].x());
213  positions[i].y() = std::round(positions[i].y());
214 
215  // Normalize UVs
216  uvs[i].x() = uvs[i].x() / static_cast<float>(font_texture_dimensions[0]);
217  uvs[i].y() = uvs[i].y() / static_cast<float>(font_texture_dimensions[1]);
218  }
219 
220  // Add vertex to vertex data buffer
221  for (int i = 0; i < 6; ++i)
222  {
223  *(v++) = positions[i].x();
224  *(v++) = positions[i].y();
225  *(v++) = uvs[i].x();
226  *(v++) = uvs[i].y();
227  *(v++) = m_color[0];
228  *(v++) = m_color[1];
229  *(v++) = m_color[2];
230  *(v++) = m_color[3];
231  }
232 
233  // Advance pen position
234  pen_position.x() += glyph->metrics.horizontal_advance;
235 
236  // Update local-space bounds
237  for (int i = 0; i < 4; ++i)
238  {
239  const math::fvec2& position = positions[i];
240  for (int j = 0; j < 2; ++j)
241  {
242  m_local_bounds.min[j] = std::min<float>(m_local_bounds.min[j], position[j]);
243  m_local_bounds.max[j] = std::max<float>(m_local_bounds.max[j], position[j]);
244  }
245  }
246  }
247  else
248  {
249  --visible_character_count;
250  }
251 
252  // Handle newlines
253  if (code == U'\n')
254  {
255  pen_position.x() = 0.0f;
256  pen_position.y() -= font_metrics.linegap;
257  }
258 
259  // Update previous UTF-32 character code
260  previous_code = code;
261  }
262 
263  // Adjust min VBO size
264  vbo_min_size = visible_character_count * text_vertex_stride * 6;
265 
266  // Upload vertex data to VBO, growing VBO size if necessary
267  if (vbo_min_size > m_vertex_buffer->size())
268  {
269  m_vertex_buffer->resize(vbo_min_size, {m_vertex_data.data(), vbo_min_size});
270  }
271  else
272  {
273  m_vertex_buffer->write({m_vertex_data.data(), vbo_min_size});
274  }
275 
276  // Update render op
277  m_render_op.vertex_count = visible_character_count * 6;
278 
279  // Update world-space bounds
280  transformed();
281 }
282 
283 void text::update_color()
284 {
285  std::byte* v = m_vertex_data.data();
286 
287  // Skip position UV
288  v += (2 + 2) * sizeof(float);
289 
290  for (std::size_t i = 0; i < m_render_op.vertex_count; ++i)
291  {
292  // Update vertex color
293  std::memcpy(v, m_color.data(), sizeof(float) * 4);
294 
295  v += text_vertex_stride;
296  }
297 
298  // Update VBO
299  m_vertex_buffer->write({m_vertex_data.data(), m_render_op.vertex_count * text_vertex_stride});
300 }
301 
302 } // namespace scene
constexpr const view_frustum_type & get_view_frustum() const noexcept
Returns the camera's view frustum.
Definition: camera.hpp:271
constexpr std::uint32_t get_layer_mask() const noexcept
Returns the layer mask of the object.
Definition: object.hpp:121
constexpr const vector_type & get_translation() const noexcept
Returns the translation of the object.
Definition: object.hpp:133
constexpr const transform_type & get_transform() const noexcept
Returns the transform of the object.
Definition: object.hpp:127
void set_content(const std::string &content)
Sets the text content.
Definition: text.cpp:116
void render(render::context &ctx) const override
Adds render operations to a render context.
Definition: text.cpp:78
void set_direction(type::text_direction direction)
Sets the direction of the text.
Definition: text.cpp:107
void set_color(const math::fvec4 &color)
Sets the text color.
Definition: text.cpp:126
void refresh()
Manually updates the text object if its font has been updated or altered in any way.
Definition: text.cpp:88
void set_material(std::shared_ptr< render::material > material)
Sets the text material.
Definition: text.cpp:93
text()
Constructs a text object.
Definition: text.cpp:58
void set_font(const type::bitmap_font *font)
Sets the text font.
Definition: text.cpp:98
Raster font in which glyphs are stored as arrays of pixels.
Definition: bitmap-font.hpp:39
const std::shared_ptr< gl::texture_2d > & get_texture() const noexcept
Returns a pointer to the glyph corresponding to a UTF-32 character code, or nullptr if no such glyph ...
const bitmap_glyph * get_glyph(char32_t code) const
Returns a pointer to the glyph corresponding to a UTF-32 character code, or nullptr if no such glyph ...
const math::fvec2 & get_kerning(char32_t first, char32_t second) const
Returns the kerning offset for a pair of glyphs.
Definition: font.cpp:44
const font_metrics & get_font_metrics() const
Returns metrics describing the font.
Definition: font.hpp:109
Color science.
Definition: aces.hpp:27
@ r32g32b32a32_sfloat
@ triangle_list
Separate triangle primitives.
constexpr vector< T, N > round(const vector< T, N > &x)
Performs a element-wise round operation.
Definition: vector.hpp:1489
@ uv
Vertex UV texture coordinates (vec2)
3D scene.
Definition: context.hpp:28
std::u32string u32(const std::string &u8)
Converts a UTF-8 string to UTF-32.
Definition: convert.cpp:26
text_direction
Text writing direction.
constexpr T distance(const vector_type &point) const noexcept
Calculates the signed distance from the hyperplane to a point.
Definition: hyperplane.hpp:78
vector_type min
Minimum extent of the hyperrectangle.
vector_type max
Maximum extent of the hyperrectangle.
void extend(const vector_type &point) noexcept
Extends the hyperrectangle to include a point.
constexpr vector_type corner(std::size_t index) const noexcept
Returns the nth corner of the hyperrectangle.
constexpr const plane_type & near() const noexcept
Returns the near clipping plane.
constexpr matrix_type matrix() const noexcept
Constructs a matrix representing the transformation.
Definition: transform.hpp:87
n-dimensional vector.
Definition: vector.hpp:44
constexpr element_type & x() noexcept
Returns a reference to the first element.
Definition: vector.hpp:164
constexpr element_type & y() noexcept
Returns a reference to the second element.
Definition: vector.hpp:180
constexpr element_type * data() noexcept
Returns a pointer to the element array.
Definition: vector.hpp:152
static constexpr vector infinity() noexcept
Returns a vector of infinities, where every element is equal to infinity.
Definition: vector.hpp:342
Context of a renderer.
Definition: context.hpp:40
const scene::camera * camera
Pointer to the camera.
Definition: context.hpp:42
std::vector< const operation * > operations
Render operations generated by visible objects.
Definition: context.hpp:60
std::uint32_t vertex_count
Definition: operation.hpp:45
std::uint32_t instance_count
Definition: operation.hpp:47
std::size_t vertex_offset
Definition: operation.hpp:42
std::size_t vertex_stride
Definition: operation.hpp:43
std::uint32_t layer_mask
Definition: operation.hpp:56
std::uint32_t first_instance
Definition: operation.hpp:46
math::fmat4 transform
Definition: operation.hpp:51
gl::primitive_topology primitive_topology
Definition: operation.hpp:39
std::uint32_t first_vertex
Definition: operation.hpp:44
const gl::vertex_buffer * vertex_buffer
Definition: operation.hpp:41
std::shared_ptr< render::material > material
Definition: operation.hpp:49
const gl::vertex_array * vertex_array
Definition: operation.hpp:40
Single glyph in a bitmap font.
glyph_metrics metrics
Metrics describing the glyph.
math::uvec2 position
Position of the packed glyph bitmap within the font bitmap.
Metrics describing properties of a font.
float linegap
Distance that must be placed between two lines of text.
float height
Vertical extent of the glyph.
float width
Horizontal extent of the glyph.
math::fvec2 horizontal_bearing
Offset from the pen position to the glyph's top-left edge, in horizontal layouts.
float horizontal_advance
Distance to move the pen position after the glyph has been rendered, in horizontal layouts.