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

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