💿🐜 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.

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