gui.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "core/math/color4.inl"
  6. #include "core/math/constants.h"
  7. #include "core/math/matrix4x4.inl"
  8. #include "core/math/vector2.inl"
  9. #include "core/math/vector3.inl"
  10. #include "core/memory/memory.inl"
  11. #include "core/strings/string.inl"
  12. #include "core/strings/string_id.inl"
  13. #include "core/strings/utf8.h"
  14. #include "resource/font_resource.inl"
  15. #include "resource/material_resource.h"
  16. #include "resource/resource_manager.h"
  17. #include "world/gui.h"
  18. #include "world/material_manager.h"
  19. #include "world/shader_manager.h"
  20. #include <bgfx/bgfx.h>
  21. #include <float.h>
  22. namespace crown
  23. {
  24. static inline u32 depth_u32(f32 depth)
  25. {
  26. return u32(depth * 100.0f * 1000.0f);
  27. }
  28. GuiBuffer::GuiBuffer(ShaderManager &sm)
  29. : _shader_manager(&sm)
  30. , _num_vertices(0)
  31. , _num_indices(0)
  32. , _vertex_buffer()
  33. , _index_buffer()
  34. {
  35. }
  36. void *GuiBuffer::vertex_buffer_end()
  37. {
  38. return _vertex_buffer.data + _num_vertices*24;
  39. }
  40. void *GuiBuffer::index_buffer_end()
  41. {
  42. return _index_buffer.data + _num_indices*2;
  43. }
  44. void GuiBuffer::create()
  45. {
  46. _pos_tex_col.begin();
  47. _pos_tex_col.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float);
  48. _pos_tex_col.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float, true);
  49. _pos_tex_col.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true);
  50. _pos_tex_col.end();
  51. }
  52. void GuiBuffer::reset()
  53. {
  54. _num_vertices = 0;
  55. _num_indices = 0;
  56. bgfx::allocTransientVertexBuffer(&_vertex_buffer, 4096, _pos_tex_col);
  57. bgfx::allocTransientIndexBuffer(&_index_buffer, 6144);
  58. }
  59. void GuiBuffer::submit(u32 num_vertices, u32 num_indices, const Matrix4x4 &world, ShaderData &shader, u8 view, u32 depth)
  60. {
  61. bgfx::setVertexBuffer(0, &_vertex_buffer, _num_vertices, num_vertices);
  62. bgfx::setIndexBuffer(&_index_buffer, _num_indices, num_indices);
  63. bgfx::setTransform(to_float_ptr(world));
  64. bgfx::setState(shader.state);
  65. bgfx::submit(view, shader.program, depth);
  66. _num_vertices += num_vertices;
  67. _num_indices += num_indices;
  68. }
  69. void GuiBuffer::submit_with_material(u32 num_vertices, u32 num_indices, const Matrix4x4 &world, u8 view, u32 depth, Material *material)
  70. {
  71. bgfx::setVertexBuffer(0, &_vertex_buffer, _num_vertices, num_vertices);
  72. bgfx::setIndexBuffer(&_index_buffer, _num_indices, num_indices);
  73. bgfx::setTransform(to_float_ptr(world));
  74. material->bind(view, depth);
  75. _num_vertices += num_vertices;
  76. _num_indices += num_indices;
  77. }
  78. Gui::Gui(GuiBuffer &gb
  79. , ResourceManager &rm
  80. , ShaderManager &sm
  81. , MaterialManager &mm
  82. , ShaderData *shader
  83. , u8 view
  84. )
  85. : _marker(DEBUG_GUI_MARKER)
  86. , _buffer(&gb)
  87. , _resource_manager(&rm)
  88. , _shader_manager(&sm)
  89. , _material_manager(&mm)
  90. , _world(MATRIX4X4_IDENTITY)
  91. , _gui_shader(shader)
  92. , _view(view)
  93. {
  94. _node.next = NULL;
  95. _node.prev = NULL;
  96. }
  97. Gui::~Gui()
  98. {
  99. _marker = 0;
  100. }
  101. void Gui::move(const Vector2 &pos)
  102. {
  103. set_translation(_world, { pos.x, pos.y, 0 });
  104. }
  105. void Gui::triangle_3d(const Matrix4x4 &local_pose, const Vector3 &a, const Vector3 &b, const Vector3 &c, const Color4 &color, f32 depth)
  106. {
  107. VertexData *vd = (VertexData *)_buffer->vertex_buffer_end();
  108. vd[0].pos.x = a.x;
  109. vd[0].pos.y = a.y;
  110. vd[0].pos.z = a.z;
  111. vd[0].uv.x = 0.0f;
  112. vd[0].uv.y = 0.0f;
  113. vd[0].col = to_abgr(color);
  114. vd[1].pos.x = b.x;
  115. vd[1].pos.y = b.y;
  116. vd[1].pos.z = b.z;
  117. vd[1].uv.x = 1.0f;
  118. vd[1].uv.y = 0.0f;
  119. vd[1].col = to_abgr(color);
  120. vd[2].pos.x = c.x;
  121. vd[2].pos.y = c.y;
  122. vd[2].pos.z = c.z;
  123. vd[2].uv.x = 1.0f;
  124. vd[2].uv.y = 1.0f;
  125. vd[2].col = to_abgr(color);
  126. u16 *inds = (u16 *)_buffer->index_buffer_end();
  127. inds[0] = 0;
  128. inds[1] = 1;
  129. inds[2] = 2;
  130. _buffer->submit(3, 3, local_pose*_world, *_gui_shader, _view, depth_u32(depth));
  131. }
  132. void Gui::triangle(const Vector2 &a, const Vector2 &b, const Vector2 &c, const Color4 &color, f32 depth)
  133. {
  134. triangle_3d(MATRIX4X4_IDENTITY
  135. , { a.x, a.y, 0.0f }
  136. , { b.x, b.y, 0.0f }
  137. , { c.x, c.y, 0.0f }
  138. , color
  139. , depth
  140. );
  141. }
  142. void Gui::rect_3d(const Matrix4x4 &local_pose, const Vector3 &pos, const Vector2 &size, const Color4 &color, f32 depth)
  143. {
  144. VertexData *vd = (VertexData *)_buffer->vertex_buffer_end();
  145. vd[0].pos.x = pos.x;
  146. vd[0].pos.y = pos.y;
  147. vd[0].pos.z = pos.z;
  148. vd[0].uv.x = 0.0f;
  149. vd[0].uv.y = 1.0f;
  150. vd[0].col = to_abgr(color);
  151. vd[1].pos.x = pos.x + size.x;
  152. vd[1].pos.y = pos.y;
  153. vd[1].pos.z = pos.z;
  154. vd[1].uv.x = 1.0f;
  155. vd[1].uv.y = 1.0f;
  156. vd[1].col = to_abgr(color);
  157. vd[2].pos.x = pos.x + size.x;
  158. vd[2].pos.y = pos.y + size.y;
  159. vd[2].pos.z = pos.z;
  160. vd[2].uv.x = 1.0f;
  161. vd[2].uv.y = 0.0f;
  162. vd[2].col = to_abgr(color);
  163. vd[3].pos.x = pos.x;
  164. vd[3].pos.y = pos.y + size.y;
  165. vd[3].pos.z = pos.z;
  166. vd[3].uv.x = 0.0f;
  167. vd[3].uv.y = 0.0f;
  168. vd[3].col = to_abgr(color);
  169. u16 *inds = (u16 *)_buffer->index_buffer_end();
  170. inds[0] = 0;
  171. inds[1] = 1;
  172. inds[2] = 2;
  173. inds[3] = 0;
  174. inds[4] = 2;
  175. inds[5] = 3;
  176. _buffer->submit(4, 6, local_pose*_world, *_gui_shader, _view, depth_u32(depth));
  177. }
  178. void Gui::rect(const Vector3 &pos, const Vector2 &size, const Color4 &color)
  179. {
  180. rect_3d(MATRIX4X4_IDENTITY
  181. , { pos.x, pos.y, 0.0f }
  182. , size
  183. , color
  184. , pos.z
  185. );
  186. }
  187. void Gui::image_3d_uv(const Matrix4x4 &local_pose, const Vector3 &pos, const Vector2 &size, const Vector2 &uv0, const Vector2 &uv1, StringId64 material, const Color4 &color, f32 depth)
  188. {
  189. VertexData *vd = (VertexData *)_buffer->vertex_buffer_end();
  190. vd[0].pos.x = pos.x;
  191. vd[0].pos.y = pos.y;
  192. vd[0].pos.z = pos.z;
  193. vd[0].uv.x = uv0.x;
  194. vd[0].uv.y = uv1.y;
  195. vd[0].col = to_abgr(color);
  196. vd[1].pos.x = pos.x + size.x;
  197. vd[1].pos.y = pos.y;
  198. vd[1].pos.z = pos.z;
  199. vd[1].uv.x = uv1.x;
  200. vd[1].uv.y = uv1.y;
  201. vd[1].col = to_abgr(color);
  202. vd[2].pos.x = pos.x + size.x;
  203. vd[2].pos.y = pos.y + size.y;
  204. vd[2].pos.z = pos.z;
  205. vd[2].uv.x = uv1.x;
  206. vd[2].uv.y = uv0.y;
  207. vd[2].col = to_abgr(color);
  208. vd[3].pos.x = pos.x;
  209. vd[3].pos.y = pos.y + size.y;
  210. vd[3].pos.z = pos.z;
  211. vd[3].uv.x = uv0.x;
  212. vd[3].uv.y = uv0.y;
  213. vd[3].col = to_abgr(color);
  214. u16 *inds = (u16 *)_buffer->index_buffer_end();
  215. inds[0] = 0;
  216. inds[1] = 1;
  217. inds[2] = 2;
  218. inds[3] = 0;
  219. inds[4] = 2;
  220. inds[5] = 3;
  221. const MaterialResource *mr = (MaterialResource *)_resource_manager->get(RESOURCE_TYPE_MATERIAL, material);
  222. _material_manager->create_material(mr);
  223. _buffer->submit_with_material(4
  224. , 6
  225. , local_pose*_world
  226. , _view
  227. , depth_u32(depth)
  228. , _material_manager->get(mr)
  229. );
  230. }
  231. void Gui::image_uv(const Vector3 &pos, const Vector2 &size, const Vector2 &uv0, const Vector2 &uv1, StringId64 material, const Color4 &color)
  232. {
  233. image_3d_uv(MATRIX4X4_IDENTITY
  234. , { pos.x, pos.y, 0.0f }
  235. , size
  236. , uv0
  237. , uv1
  238. , material
  239. , color
  240. , pos.z
  241. );
  242. }
  243. void Gui::image_3d(const Matrix4x4 &local_pose, const Vector3 &pos, const Vector2 &size, StringId64 material, const Color4 &color, f32 depth)
  244. {
  245. image_3d_uv(local_pose
  246. , pos
  247. , size
  248. , VECTOR2_ZERO
  249. , VECTOR2_ONE
  250. , material
  251. , color
  252. , depth
  253. );
  254. }
  255. void Gui::image(const Vector3 &pos, const Vector2 &size, StringId64 material, const Color4 &color)
  256. {
  257. image_3d(MATRIX4X4_IDENTITY
  258. , { pos.x, pos.y, 0.0f }
  259. , size
  260. , material
  261. , color
  262. , pos.z
  263. );
  264. }
  265. void Gui::text_3d(const Matrix4x4 &local_pose, const Vector3 &pos, u32 font_size, const char *str, StringId64 font, StringId64 material, const Color4 &color, f32 depth)
  266. {
  267. const MaterialResource *mr = (MaterialResource *)_resource_manager->get(RESOURCE_TYPE_MATERIAL, material);
  268. _material_manager->create_material(mr);
  269. const FontResource *fr = (FontResource *)_resource_manager->get(RESOURCE_TYPE_FONT, font);
  270. const f32 scale = (f32)font_size / (f32)fr->font_size;
  271. VertexData *vd = (VertexData *)_buffer->vertex_buffer_end();
  272. u16 *id = (u16 *)_buffer->index_buffer_end();
  273. u32 num_vertices = 0;
  274. u32 num_indices = 0;
  275. u32 cp;
  276. u32 state = 0;
  277. f32 pen_x = 0.0f;
  278. f32 pen_y = 0.0f;
  279. const GlyphData deffault_glyph = {};
  280. for (const u8 *ch = (u8 *)str; *ch; ++ch) {
  281. if (utf8::decode(&state, &cp, *ch) != UTF8_ACCEPT)
  282. continue;
  283. if (cp == '\n') {
  284. pen_x = 0.0f;
  285. pen_y -= scale*fr->font_size;
  286. continue;
  287. } else if (cp == '\t') {
  288. pen_x += scale*font_size*4;
  289. continue;
  290. }
  291. const GlyphData *glyph = font_resource::glyph(fr, cp, &deffault_glyph);
  292. const f32 baseline = glyph->height - glyph->y_offset;
  293. const f32 x_offset = fsign(pen_x) * scale*glyph->x_offset;
  294. // Glyph position coords.
  295. const f32 x0 = pen_x + pos.x + x_offset;
  296. const f32 y0 = pen_y + pos.y - scale*baseline;
  297. const f32 x1 = x0 + scale*glyph->width;
  298. const f32 y1 = y0 + scale*glyph->height;
  299. pen_x += scale*glyph->x_advance;
  300. // Glyph atlas coords.
  301. const f32 u0 = glyph->x / fr->texture_size;
  302. const f32 v1 = glyph->y / fr->texture_size; // Upper-left char corner
  303. const f32 u1 = glyph->width / fr->texture_size + u0;
  304. const f32 v0 = glyph->height / fr->texture_size + v1; // Bottom-left char corner
  305. const u32 abgr = to_abgr(color);
  306. // Fill vertex buffer.
  307. vd[0].pos.x = x0;
  308. vd[0].pos.y = y0;
  309. vd[0].pos.z = pos.z;
  310. vd[0].uv.x = u0;
  311. vd[0].uv.y = v0;
  312. vd[0].col = abgr;
  313. vd[1].pos.x = x1;
  314. vd[1].pos.y = y0;
  315. vd[1].pos.z = pos.z;
  316. vd[1].uv.x = u1;
  317. vd[1].uv.y = v0;
  318. vd[1].col = abgr;
  319. vd[2].pos.x = x1;
  320. vd[2].pos.y = y1;
  321. vd[2].pos.z = pos.z;
  322. vd[2].uv.x = u1;
  323. vd[2].uv.y = v1;
  324. vd[2].col = abgr;
  325. vd[3].pos.x = x0;
  326. vd[3].pos.y = y1;
  327. vd[3].pos.z = pos.z;
  328. vd[3].uv.x = u0;
  329. vd[3].uv.y = v1;
  330. vd[3].col = abgr;
  331. // Fill index buffer.
  332. id[0] = num_vertices + 0;
  333. id[1] = num_vertices + 1;
  334. id[2] = num_vertices + 2;
  335. id[3] = num_vertices + 0;
  336. id[4] = num_vertices + 2;
  337. id[5] = num_vertices + 3;
  338. vd += 4;
  339. id += 6;
  340. num_vertices += 4;
  341. num_indices += 6;
  342. }
  343. _buffer->submit_with_material(num_vertices
  344. , num_indices
  345. , local_pose*_world
  346. , _view
  347. , depth_u32(depth)
  348. , _material_manager->get(mr)
  349. );
  350. }
  351. void Gui::text(const Vector3 &pos, u32 font_size, const char *str, StringId64 font, StringId64 material, const Color4 &color)
  352. {
  353. const MaterialResource *mr = (MaterialResource *)_resource_manager->get(RESOURCE_TYPE_MATERIAL, material);
  354. _material_manager->create_material(mr);
  355. const FontResource *fr = (FontResource *)_resource_manager->get(RESOURCE_TYPE_FONT, font);
  356. const f32 scale = (f32)font_size / (f32)fr->font_size;
  357. VertexData *vd = (VertexData *)_buffer->vertex_buffer_end();
  358. u16 *id = (u16 *)_buffer->index_buffer_end();
  359. u32 num_vertices = 0;
  360. u32 num_indices = 0;
  361. u32 cp;
  362. u32 state = 0;
  363. f32 pen_x = 0.0f;
  364. f32 pen_y = 0.0f;
  365. const GlyphData deffault_glyph = {};
  366. for (const u8 *ch = (u8 *)str; *ch; ++ch) {
  367. if (utf8::decode(&state, &cp, *ch) != UTF8_ACCEPT)
  368. continue;
  369. if (cp == '\n') {
  370. pen_x = 0.0f;
  371. pen_y -= scale*fr->font_size;
  372. continue;
  373. } else if (cp == '\t') {
  374. pen_x += scale*font_size*4;
  375. continue;
  376. }
  377. const GlyphData *glyph = font_resource::glyph(fr, cp, &deffault_glyph);
  378. const f32 baseline = glyph->height - glyph->y_offset;
  379. const f32 x_offset = fsign(pen_x) * scale*glyph->x_offset;
  380. // Glyph position coords.
  381. const f32 x0 = pen_x + pos.x + x_offset;
  382. const f32 y0 = pen_y + pos.y - scale*baseline;
  383. const f32 x1 = fround(x0 + scale*glyph->width);
  384. const f32 y1 = fround(y0 + scale*glyph->height);
  385. pen_x += scale*glyph->x_advance;
  386. // Glyph atlas coords.
  387. const f32 u0 = glyph->x / fr->texture_size;
  388. const f32 v1 = glyph->y / fr->texture_size; // Upper-left char corner
  389. const f32 u1 = glyph->width / fr->texture_size + u0;
  390. const f32 v0 = glyph->height / fr->texture_size + v1; // Bottom-left char corner
  391. const u32 abgr = to_abgr(color);
  392. // Fill vertex buffer.
  393. vd[0].pos.x = x0;
  394. vd[0].pos.y = y0;
  395. vd[0].pos.z = 0.0f;
  396. vd[0].uv.x = u0;
  397. vd[0].uv.y = v0;
  398. vd[0].col = abgr;
  399. vd[1].pos.x = x1;
  400. vd[1].pos.y = y0;
  401. vd[1].pos.z = 0.0f;
  402. vd[1].uv.x = u1;
  403. vd[1].uv.y = v0;
  404. vd[1].col = abgr;
  405. vd[2].pos.x = x1;
  406. vd[2].pos.y = y1;
  407. vd[2].pos.z = 0.0f;
  408. vd[2].uv.x = u1;
  409. vd[2].uv.y = v1;
  410. vd[2].col = abgr;
  411. vd[3].pos.x = x0;
  412. vd[3].pos.y = y1;
  413. vd[3].pos.z = 0.0f;
  414. vd[3].uv.x = u0;
  415. vd[3].uv.y = v1;
  416. vd[3].col = abgr;
  417. // Fill index buffer.
  418. id[0] = num_vertices + 0;
  419. id[1] = num_vertices + 1;
  420. id[2] = num_vertices + 2;
  421. id[3] = num_vertices + 0;
  422. id[4] = num_vertices + 2;
  423. id[5] = num_vertices + 3;
  424. vd += 4;
  425. id += 6;
  426. num_vertices += 4;
  427. num_indices += 6;
  428. }
  429. _buffer->submit_with_material(num_vertices
  430. , num_indices
  431. , _world
  432. , _view
  433. , depth_u32(pos.z)
  434. , _material_manager->get(mr)
  435. );
  436. }
  437. Vector2 Gui::text_extents(const u32 font_size, const char *str, StringId64 font)
  438. {
  439. const FontResource *fr = (FontResource *)_resource_manager->get(RESOURCE_TYPE_FONT, font);
  440. const f32 scale = (f32)font_size / (f32)fr->font_size;
  441. u32 cp;
  442. u32 state = 0;
  443. const GlyphData deffault_glyph = {};
  444. f32 pen_x = 0.0f;
  445. f32 pen_y = 0.0f;
  446. Vector2 box_min = { FLT_MAX, FLT_MAX };
  447. Vector2 box_max = { -FLT_MAX, -FLT_MAX };
  448. for (const u8 *ch = (u8 *)str; *ch; ++ch) {
  449. if (utf8::decode(&state, &cp, *ch) != UTF8_ACCEPT)
  450. continue;
  451. if (cp == '\n') {
  452. pen_x = 0.0f;
  453. pen_y -= scale*fr->font_size;
  454. continue;
  455. } else if (cp == '\t') {
  456. pen_x += scale*font_size*4;
  457. continue;
  458. }
  459. const GlyphData *glyph = font_resource::glyph(fr, cp, &deffault_glyph);
  460. const f32 baseline = glyph->height - glyph->y_offset;
  461. const f32 x_offset = fsign(pen_x) * scale*glyph->x_offset;
  462. // Glyph position coords.
  463. const f32 x0 = pen_x + x_offset;
  464. const f32 y0 = pen_y - scale*baseline;
  465. const f32 x1 = fround(x0 + scale*glyph->width);
  466. const f32 y1 = fround(y0 + scale*glyph->height);
  467. box_min = min(box_min, { x0, y0 });
  468. box_max = max(box_max, { x1, y1 });
  469. pen_x += scale*glyph->x_advance;
  470. }
  471. // Avoid returning funny extents empty or malformed strings.
  472. box_min = min(box_min, VECTOR2_ZERO);
  473. box_max = max(box_max, VECTOR2_ZERO);
  474. return box_max - box_min;
  475. }
  476. Material *Gui::material(ResourceId material_resource)
  477. {
  478. const MaterialResource *mr = (MaterialResource *)_resource_manager->get(RESOURCE_TYPE_MATERIAL, material_resource);
  479. return _material_manager->get(mr);
  480. }
  481. namespace gui
  482. {
  483. Gui *create_screen_gui(Allocator &allocator
  484. , GuiBuffer &buffer
  485. , ResourceManager &resource_manager
  486. , ShaderManager &shader_manager
  487. , MaterialManager &material_manager
  488. , ShaderData *shader
  489. )
  490. {
  491. Gui *gui = CE_NEW(allocator, Gui)(buffer
  492. , resource_manager
  493. , shader_manager
  494. , material_manager
  495. , shader
  496. , View::SCREEN_GUI
  497. );
  498. return gui;
  499. }
  500. Gui *create_world_gui(Allocator &allocator
  501. , GuiBuffer &buffer
  502. , ResourceManager &resource_manager
  503. , ShaderManager &shader_manager
  504. , MaterialManager &material_manager
  505. , ShaderData *shader
  506. )
  507. {
  508. Gui *gui = CE_NEW(allocator, Gui)(buffer
  509. , resource_manager
  510. , shader_manager
  511. , material_manager
  512. , shader
  513. , View::WORLD_GUI
  514. );
  515. return gui;
  516. }
  517. } // namespace gui
  518. } // namespace crown