💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
7.9 KiB

  1. /*
  2. * Copyright (C) 2021 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. #include "scene/text.hpp"
  20. #include "renderer/material.hpp"
  21. #include "renderer/vertex-attribute.hpp"
  22. #include "math/vector-operators.hpp"
  23. #include <cstddef>
  24. #include <codecvt>
  25. namespace scene {
  26. text::text():
  27. local_bounds{{0, 0, 0}, {0, 0, 0}},
  28. world_bounds{{0, 0, 0}, {0, 0, 0}},
  29. material(nullptr),
  30. font(nullptr),
  31. direction(type::text_direction::ltr),
  32. content_u8(std::string()),
  33. content_u32(std::u32string()),
  34. color({0.0f, 0.0f, 0.0f, 1.0f}),
  35. vertex_stride(0),
  36. vertex_count(0),
  37. vao(nullptr),
  38. vbo(nullptr)
  39. {
  40. // Allocate VBO and VAO
  41. vbo = new gl::vertex_buffer(0, nullptr, gl::buffer_usage::static_draw);
  42. vao = new gl::vertex_array();
  43. // Calculate vertex stride
  44. vertex_stride = (3 + 2 + 4) * sizeof(float);
  45. // Init vertex attribute offset
  46. std::size_t attribute_offset = 0;
  47. // Define vertex position attribute
  48. gl::vertex_attribute position_attribute;
  49. position_attribute.buffer = vbo;
  50. position_attribute.offset = attribute_offset;
  51. position_attribute.stride = vertex_stride;
  52. position_attribute.type = gl::vertex_attribute_type::float_32;
  53. position_attribute.components = 3;
  54. attribute_offset += position_attribute.components * sizeof(float);
  55. // Define vertex UV attribute
  56. gl::vertex_attribute uv_attribute;
  57. uv_attribute.buffer = vbo;
  58. uv_attribute.offset = attribute_offset;
  59. uv_attribute.stride = vertex_stride;
  60. uv_attribute.type = gl::vertex_attribute_type::float_32;
  61. uv_attribute.components = 2;
  62. attribute_offset += uv_attribute.components * sizeof(float);
  63. // Define vertex color attribute
  64. gl::vertex_attribute color_attribute;
  65. color_attribute.buffer = vbo;
  66. color_attribute.offset = attribute_offset;
  67. color_attribute.stride = vertex_stride;
  68. color_attribute.type = gl::vertex_attribute_type::float_32;
  69. color_attribute.components = 4;
  70. attribute_offset += color_attribute.components * sizeof(float);
  71. // Bind vertex attributes to VAO
  72. vao->bind(render::vertex_attribute::position, position_attribute);
  73. vao->bind(render::vertex_attribute::uv, uv_attribute);
  74. vao->bind(render::vertex_attribute::color, color_attribute);
  75. }
  76. text::~text()
  77. {
  78. // Free VAO and VBO
  79. delete vao;
  80. delete vbo;
  81. }
  82. void text::set_material(::material* material)
  83. {
  84. this->material = material;
  85. }
  86. void text::set_font(const type::bitmap_font* font)
  87. {
  88. if (this->font != font)
  89. {
  90. this->font = font;
  91. // Update text in VBO
  92. update_content();
  93. }
  94. }
  95. void text::set_direction(type::text_direction direction)
  96. {
  97. if (this->direction != direction)
  98. {
  99. this->direction = direction;
  100. // Update text in VBO
  101. update_content();
  102. }
  103. }
  104. void text::set_content(const std::string& content)
  105. {
  106. // If content has changed
  107. if (content_u8 != content)
  108. {
  109. // Update UTF-8 content
  110. content_u8 = content;
  111. // Convert UTF-8 content to UTF-32
  112. std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
  113. content_u32 = convert.from_bytes(content_u8.c_str());
  114. // Update text in VBO
  115. update_content();
  116. }
  117. }
  118. void text::set_color(const float4& color)
  119. {
  120. this->color = color;
  121. // Update color in VBO
  122. update_color();
  123. }
  124. void text::transformed()
  125. {
  126. world_bounds = aabb_type::transform(local_bounds, get_transform());
  127. }
  128. void text::update_tweens()
  129. {
  130. object_base::update_tweens();
  131. if (material)
  132. {
  133. material->update_tweens();
  134. }
  135. }
  136. void text::update_content()
  137. {
  138. // If no valid font or no text, clear vertex count
  139. if (!font || content_u32.empty())
  140. {
  141. vertex_count = 0;
  142. return;
  143. }
  144. // Calculate new vertex count and minimum vertex buffer size
  145. std::size_t vertex_count = content_u32.length() * 6;
  146. std::size_t min_vertex_buffer_size = vertex_count * vertex_stride;
  147. // Expand vertex data buffer to accommodate vertices
  148. if (vertex_data.size() < min_vertex_buffer_size)
  149. vertex_data.resize(min_vertex_buffer_size);
  150. // Get font metrics and bitmap
  151. const type::font_metrics& font_metrics = font->get_font_metrics();
  152. const image& font_bitmap = font->get_bitmap();
  153. // Init pen position
  154. float2 pen_position = {0.0f, 0.0f};
  155. // Reset local-space bounds
  156. local_bounds = {{0, 0, 0}, {0, 0, 0}};
  157. // Generate vertex data
  158. char32_t previous_code = 0;
  159. float* v = reinterpret_cast<float*>(vertex_data.data());
  160. for (char32_t code: content_u32)
  161. {
  162. // Apply kerning
  163. if (previous_code)
  164. {
  165. pen_position.x += font->get_kerning(previous_code, code).x;
  166. }
  167. if (font->contains(code))
  168. {
  169. // Get glyph
  170. const type::bitmap_glyph& glyph = font->get_glyph(code);
  171. // Calculate vertex positions
  172. float2 positions[6];
  173. positions[0] = pen_position + glyph.metrics.horizontal_bearing;
  174. positions[1] = {positions[0].x, positions[0].y - glyph.metrics.height};
  175. positions[2] = {positions[0].x + glyph.metrics.width, positions[1].y};
  176. positions[3] = {positions[2].x, positions[0].y};
  177. positions[4] = positions[0];
  178. positions[5] = positions[2];
  179. // Calculate vertex UVs
  180. float2 uvs[6];
  181. uvs[0] = {static_cast<float>(glyph.position.x), static_cast<float>(glyph.position.y)};
  182. uvs[1] = {uvs[0].x, uvs[0].y + glyph.metrics.height};
  183. uvs[2] = {uvs[0].x + glyph.metrics.width, uvs[1].y};
  184. uvs[3] = {uvs[2].x, uvs[0].y};
  185. uvs[4] = uvs[0];
  186. uvs[5] = uvs[2];
  187. for (int i = 0; i < 6; ++i)
  188. {
  189. // Round positions
  190. positions[i].x = std::round(positions[i].x);
  191. positions[i].y = std::round(positions[i].y);
  192. // Normalize UVs
  193. uvs[i].x = uvs[i].x / static_cast<float>(font_bitmap.get_width());
  194. uvs[i].y = uvs[i].y / static_cast<float>(font_bitmap.get_height());
  195. }
  196. // Add vertex to vertex data buffer
  197. for (int i = 0; i < 6; ++i)
  198. {
  199. *(v++) = positions[i].x;
  200. *(v++) = positions[i].y;
  201. *(v++) = 0.0f;
  202. *(v++) = uvs[i].x;
  203. *(v++) = uvs[i].y;
  204. *(v++) = color.x;
  205. *(v++) = color.y;
  206. *(v++) = color.z;
  207. *(v++) = color.w;
  208. }
  209. // Advance pen position
  210. pen_position.x += glyph.metrics.horizontal_advance;
  211. // Update local-space bounds
  212. for (int i = 0; i < 4; ++i)
  213. {
  214. const float2& position = positions[i];
  215. for (int j = 0; j < 2; ++j)
  216. {
  217. local_bounds.min_point[j] = std::min<float>(local_bounds.min_point[j], position[j]);
  218. local_bounds.max_point[j] = std::max<float>(local_bounds.max_point[j], position[j]);
  219. }
  220. }
  221. }
  222. else
  223. {
  224. // Glyph not in font, zero vertex data
  225. for (std::size_t i = 0; i < (6 * 9); ++i)
  226. *(v++) = 0.0f;
  227. }
  228. // Handle newlines
  229. if (code == static_cast<char32_t>('\n'))
  230. {
  231. pen_position.x = 0.0f;
  232. pen_position.y -= font_metrics.linegap;
  233. }
  234. // Update previous UTF-32 character code
  235. previous_code = code;
  236. }
  237. // Resize VBO, if necessary, and upload vertex data
  238. if (vertex_count > this->vertex_count)
  239. {
  240. this->vertex_count = vertex_count;
  241. vbo->resize(min_vertex_buffer_size, vertex_data.data());
  242. }
  243. else
  244. {
  245. vbo->write(0, min_vertex_buffer_size, vertex_data.data());
  246. }
  247. // Update vertex count
  248. this->vertex_count = vertex_count;
  249. // Update world-space bounds
  250. transformed();
  251. }
  252. void text::update_color()
  253. {
  254. float* v = reinterpret_cast<float*>(vertex_data.data());
  255. for (std::size_t i = 0; i < vertex_count; ++i)
  256. {
  257. // Skip vertex position (vec3) and vertex UV (vec2)
  258. v += (3 + 2);
  259. // Update vertex color
  260. *(v++) = color.x;
  261. *(v++) = color.y;
  262. *(v++) = color.z;
  263. *(v++) = color.w;
  264. }
  265. // Update VBO
  266. vbo->write(0, vertex_count * vertex_stride, vertex_data.data());
  267. }
  268. } // namespace scene