mode_indicator.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. #include "mode_indicator.h"
  2. #include "../gl/mesh.h"
  3. #include <QVector3D>
  4. #include <cmath>
  5. #include <cstddef>
  6. #include <memory>
  7. #include <vector>
  8. namespace {
  9. constexpr float k_pi = 3.14159265358979323846F;
  10. }
  11. namespace Render::Geom {
  12. std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_attack_mesh;
  13. std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_guard_mesh;
  14. std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_hold_mesh;
  15. std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_patrol_mesh;
  16. auto ModeIndicator::create_attack_mode_mesh()
  17. -> std::unique_ptr<Render::GL::Mesh> {
  18. using namespace Render::GL;
  19. std::vector<Vertex> verts;
  20. std::vector<unsigned int> idx;
  21. constexpr float sword_length = 0.9F;
  22. constexpr float sword_width = 0.1F;
  23. constexpr float blade_length = 0.65F;
  24. constexpr float handle_length = 0.2F;
  25. constexpr float cross_guard_width = 0.25F;
  26. constexpr float cross_guard_height = 0.06F;
  27. constexpr float blade_tip_width = 0.03F;
  28. constexpr float handle_offset = -0.18F;
  29. constexpr float guard_position_ratio = 0.15F;
  30. constexpr float handle_width_ratio = 0.3F;
  31. const float angles[] = {0.785398F, -0.785398F};
  32. const float x_offsets[] = {-handle_offset, handle_offset};
  33. for (int sword_idx = 0; sword_idx < 2; ++sword_idx) {
  34. float const angle = angles[sword_idx];
  35. float const cos_a = std::cos(angle);
  36. float const sin_a = std::sin(angle);
  37. float const x_offset = x_offsets[sword_idx];
  38. size_t const base = verts.size();
  39. float const blade_half_width = sword_width * 0.5F;
  40. QVector3D const n(0, 0, 1);
  41. float blade_verts[4][2] = {{-blade_half_width, 0.0F},
  42. {blade_half_width, 0.0F},
  43. {blade_tip_width, blade_length},
  44. {-blade_tip_width, blade_length}};
  45. for (int i = 0; i < 4; ++i) {
  46. float const local_x = blade_verts[i][0];
  47. float const local_y = blade_verts[i][1];
  48. float const world_x = local_x * cos_a - local_y * sin_a + x_offset;
  49. float const world_y = local_x * sin_a + local_y * cos_a;
  50. verts.push_back({{world_x, world_y, 0.0F},
  51. {n.x(), n.y(), n.z()},
  52. {(i % 2) == 0 ? 0.0F : 1.0F, i < 2 ? 0.0F : 1.0F}});
  53. }
  54. idx.push_back(base + 0);
  55. idx.push_back(base + 1);
  56. idx.push_back(base + 2);
  57. idx.push_back(base + 2);
  58. idx.push_back(base + 3);
  59. idx.push_back(base + 0);
  60. size_t const guard_base = verts.size();
  61. float const guard_half_width = cross_guard_width * 0.5F;
  62. float const guard_half_height = cross_guard_height * 0.5F;
  63. float const guard_y = blade_length * guard_position_ratio;
  64. float guard_verts[4][2] = {
  65. {-guard_half_width, guard_y - guard_half_height},
  66. {guard_half_width, guard_y - guard_half_height},
  67. {guard_half_width, guard_y + guard_half_height},
  68. {-guard_half_width, guard_y + guard_half_height}};
  69. for (int i = 0; i < 4; ++i) {
  70. float const local_x = guard_verts[i][0];
  71. float const local_y = guard_verts[i][1];
  72. float const world_x = local_x * cos_a - local_y * sin_a + x_offset;
  73. float const world_y = local_x * sin_a + local_y * cos_a;
  74. verts.push_back({{world_x, world_y, 0.0F},
  75. {n.x(), n.y(), n.z()},
  76. {(i % 2) == 0 ? 0.0F : 1.0F, i < 2 ? 0.0F : 1.0F}});
  77. }
  78. idx.push_back(guard_base + 0);
  79. idx.push_back(guard_base + 1);
  80. idx.push_back(guard_base + 2);
  81. idx.push_back(guard_base + 2);
  82. idx.push_back(guard_base + 3);
  83. idx.push_back(guard_base + 0);
  84. size_t const handle_base = verts.size();
  85. float const handle_half_width = sword_width * handle_width_ratio;
  86. float const handle_start = -handle_length;
  87. float handle_verts[4][2] = {{-handle_half_width, handle_start},
  88. {handle_half_width, handle_start},
  89. {handle_half_width, 0.0F},
  90. {-handle_half_width, 0.0F}};
  91. for (int i = 0; i < 4; ++i) {
  92. float const local_x = handle_verts[i][0];
  93. float const local_y = handle_verts[i][1];
  94. float const world_x = local_x * cos_a - local_y * sin_a + x_offset;
  95. float const world_y = local_x * sin_a + local_y * cos_a;
  96. verts.push_back({{world_x, world_y, 0.0F},
  97. {n.x(), n.y(), n.z()},
  98. {(i % 2) == 0 ? 0.0F : 1.0F, i < 2 ? 0.0F : 1.0F}});
  99. }
  100. idx.push_back(handle_base + 0);
  101. idx.push_back(handle_base + 1);
  102. idx.push_back(handle_base + 2);
  103. idx.push_back(handle_base + 2);
  104. idx.push_back(handle_base + 3);
  105. idx.push_back(handle_base + 0);
  106. }
  107. return std::make_unique<Mesh>(verts, idx);
  108. }
  109. auto ModeIndicator::create_guard_mode_mesh()
  110. -> std::unique_ptr<Render::GL::Mesh> {
  111. using namespace Render::GL;
  112. std::vector<Vertex> verts;
  113. std::vector<unsigned int> idx;
  114. QVector3D const n(0, 0, 1);
  115. constexpr float shield_width = 0.42F;
  116. constexpr float shield_height = 0.62F;
  117. constexpr float inner_scale = 0.82F;
  118. constexpr float face_curve_z = 0.025F;
  119. constexpr float boss_radius = 0.095F;
  120. constexpr float boss_height = 0.04F;
  121. auto add_vert = [&](float x, float y, float u, float v, float z = 0.0F) {
  122. verts.push_back({{x, y, z}, {n.x(), n.y(), n.z()}, {u, v}});
  123. return static_cast<unsigned int>(verts.size() - 1);
  124. };
  125. auto uv_for = [&](float x, float y) {
  126. float u = (x / shield_width) * 0.5F + 0.5F;
  127. float v = (y / shield_height) * 0.5F + 0.5F;
  128. return QVector2D(u, v);
  129. };
  130. const std::vector<QVector2D> outline = {
  131. {-0.55F * shield_width, 0.55F * shield_height},
  132. {-0.20F * shield_width, 0.60F * shield_height},
  133. {0.20F * shield_width, 0.60F * shield_height},
  134. {0.55F * shield_width, 0.55F * shield_height},
  135. {0.65F * shield_width, 0.15F * shield_height},
  136. {0.55F * shield_width, -0.25F * shield_height},
  137. {0.25F * shield_width, -0.60F * shield_height},
  138. {0.00F, -0.85F * shield_height},
  139. {-0.25F * shield_width, -0.60F * shield_height},
  140. {-0.55F * shield_width, -0.25F * shield_height},
  141. {-0.65F * shield_width, 0.15F * shield_height},
  142. };
  143. std::vector<unsigned int> outer_idx;
  144. std::vector<unsigned int> inner_idx;
  145. for (auto const &p : outline) {
  146. auto uv = uv_for(p.x(), p.y());
  147. outer_idx.push_back(add_vert(p.x(), p.y(), uv.x(), uv.y(), 0.0F));
  148. QVector2D ip = p * inner_scale;
  149. float z = face_curve_z * (1.0F - std::abs(ip.y()) / shield_height);
  150. auto iuv = uv_for(ip.x(), ip.y());
  151. inner_idx.push_back(add_vert(ip.x(), ip.y(), iuv.x(), iuv.y(), z));
  152. }
  153. for (size_t i = 0; i < outer_idx.size(); ++i) {
  154. size_t next = (i + 1) % outer_idx.size();
  155. idx.insert(idx.end(), {outer_idx[i], outer_idx[next], inner_idx[i],
  156. inner_idx[i], outer_idx[next], inner_idx[next]});
  157. }
  158. unsigned int face_center =
  159. add_vert(0.0F, -shield_height * 0.05F, 0.5F, 0.45F, face_curve_z);
  160. for (size_t i = 0; i < inner_idx.size(); ++i) {
  161. size_t next = (i + 1) % inner_idx.size();
  162. idx.insert(idx.end(), {face_center, inner_idx[i], inner_idx[next]});
  163. }
  164. constexpr int boss_segments = 18;
  165. unsigned int boss_center =
  166. add_vert(0.0F, shield_height * 0.08F, 0.5F, 0.58F, boss_height);
  167. std::vector<unsigned int> boss_ring;
  168. boss_ring.reserve(boss_segments + 1);
  169. for (int i = 0; i <= boss_segments; ++i) {
  170. float a = (i / float(boss_segments)) * 2.0F * k_pi;
  171. float x = boss_radius * std::cos(a);
  172. float y = shield_height * 0.08F + boss_radius * std::sin(a);
  173. auto uv = uv_for(x, y);
  174. boss_ring.push_back(add_vert(x, y, uv.x(), uv.y(), boss_height));
  175. }
  176. for (int i = 0; i < boss_segments; ++i) {
  177. idx.insert(idx.end(), {boss_center, boss_ring[i], boss_ring[i + 1]});
  178. }
  179. return std::make_unique<Mesh>(verts, idx);
  180. }
  181. auto ModeIndicator::create_hold_mode_mesh()
  182. -> std::unique_ptr<Render::GL::Mesh> {
  183. using namespace Render::GL;
  184. std::vector<Vertex> verts;
  185. std::vector<unsigned int> idx;
  186. constexpr float anchor_height = 0.9F;
  187. constexpr float ring_outer = 0.11F;
  188. constexpr float ring_inner = 0.065F;
  189. constexpr float shank_width = 0.12F;
  190. constexpr float cross_width = 0.32F;
  191. constexpr float cross_height = 0.11F;
  192. constexpr float fluke_span = 0.48F;
  193. constexpr float fluke_drop = 0.28F;
  194. QVector3D const n(0, 0, 1);
  195. auto add = [&](float x, float y, float u, float v) {
  196. verts.push_back({{x, y, 0.0F}, {n.x(), n.y(), n.z()}, {u, v}});
  197. return static_cast<unsigned int>(verts.size() - 1);
  198. };
  199. constexpr int ring_segments = 18;
  200. float const ring_y = anchor_height * 0.32F;
  201. for (int i = 0; i < ring_segments; ++i) {
  202. float a0 = (i / float(ring_segments)) * 2.0F * k_pi;
  203. float a1 = ((i + 1) / float(ring_segments)) * 2.0F * k_pi;
  204. float x0o = ring_outer * std::cos(a0);
  205. float y0o = ring_y + ring_outer * std::sin(a0);
  206. float x1o = ring_outer * std::cos(a1);
  207. float y1o = ring_y + ring_outer * std::sin(a1);
  208. float x0i = ring_inner * std::cos(a0);
  209. float y0i = ring_y + ring_inner * std::sin(a0);
  210. float x1i = ring_inner * std::cos(a1);
  211. float y1i = ring_y + ring_inner * std::sin(a1);
  212. unsigned int b = verts.size();
  213. verts.push_back({{x0o, y0o, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 1.0F}});
  214. verts.push_back({{x1o, y1o, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 1.0F}});
  215. verts.push_back({{x1i, y1i, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 0.0F}});
  216. verts.push_back({{x0i, y0i, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 0.0F}});
  217. idx.insert(idx.end(), {b + 0, b + 1, b + 2, b + 2, b + 3, b + 0});
  218. }
  219. float const shank_half = shank_width * 0.5F;
  220. float const shank_top = ring_y - ring_inner * 0.9F;
  221. float const shank_bottom = -anchor_height * 0.18F;
  222. unsigned int shank_base = verts.size();
  223. verts.push_back(
  224. {{-shank_half, shank_top, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 1.0F}});
  225. verts.push_back(
  226. {{shank_half, shank_top, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 1.0F}});
  227. verts.push_back(
  228. {{shank_half, shank_bottom, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 0.0F}});
  229. verts.push_back(
  230. {{-shank_half, shank_bottom, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 0.0F}});
  231. idx.insert(idx.end(), {shank_base + 0, shank_base + 1, shank_base + 2,
  232. shank_base + 2, shank_base + 3, shank_base + 0});
  233. float const cross_y = shank_bottom;
  234. float const cross_half_w = cross_width * 0.5F;
  235. float const cross_half_h = cross_height * 0.5F;
  236. unsigned int cross_base = verts.size();
  237. verts.push_back({{-cross_half_w, cross_y - cross_half_h, 0.0F},
  238. {n.x(), n.y(), n.z()},
  239. {0.0F, 0.5F}});
  240. verts.push_back({{cross_half_w, cross_y - cross_half_h, 0.0F},
  241. {n.x(), n.y(), n.z()},
  242. {1.0F, 0.5F}});
  243. verts.push_back({{cross_half_w, cross_y + cross_half_h, 0.0F},
  244. {n.x(), n.y(), n.z()},
  245. {1.0F, 0.5F}});
  246. verts.push_back({{-cross_half_w, cross_y + cross_half_h, 0.0F},
  247. {n.x(), n.y(), n.z()},
  248. {0.0F, 0.5F}});
  249. idx.insert(idx.end(), {cross_base + 0, cross_base + 1, cross_base + 2,
  250. cross_base + 2, cross_base + 3, cross_base + 0});
  251. float const fluke_y = cross_y - fluke_drop;
  252. float const fluke_tip_y = -anchor_height * 0.58F;
  253. auto add_tri = [&](QVector2D a, QVector2D b, QVector2D c) {
  254. unsigned int ia = add(a.x(), a.y(), 0.0F, 0.0F);
  255. unsigned int ib = add(b.x(), b.y(), 1.0F, 0.5F);
  256. unsigned int ic = add(c.x(), c.y(), 0.0F, 1.0F);
  257. idx.insert(idx.end(), {ia, ib, ic});
  258. };
  259. add_tri({-cross_half_w * 0.9F, cross_y}, {-fluke_span * 0.6F, fluke_y},
  260. {-fluke_span, fluke_tip_y});
  261. add_tri({cross_half_w * 0.9F, cross_y}, {fluke_span * 0.6F, fluke_y},
  262. {fluke_span, fluke_tip_y});
  263. add_tri({-shank_half * 0.8F, cross_y}, {shank_half * 0.8F, cross_y},
  264. {0.0F, fluke_tip_y});
  265. return std::make_unique<Mesh>(verts, idx);
  266. }
  267. auto ModeIndicator::create_patrol_mode_mesh()
  268. -> std::unique_ptr<Render::GL::Mesh> {
  269. using namespace Render::GL;
  270. std::vector<Vertex> verts;
  271. std::vector<unsigned int> idx;
  272. constexpr float circle_radius = 0.3F;
  273. constexpr float arrow_width = 0.08F;
  274. constexpr float arrow_head_length = 0.15F;
  275. constexpr float arrow_head_width = 0.15F;
  276. constexpr int circle_segments = 24;
  277. constexpr float arrow_end_ratio = 0.85F;
  278. QVector3D const n(0, 0, 1);
  279. for (int arrow = 0; arrow < 2; ++arrow) {
  280. float const start_angle = arrow == 0 ? 0.0F : k_pi;
  281. float const end_angle =
  282. arrow == 0 ? k_pi * arrow_end_ratio : k_pi * (1.0F + arrow_end_ratio);
  283. int const segments = circle_segments / 2;
  284. for (int i = 0; i < segments; ++i) {
  285. float const t1 = i / float(segments);
  286. float const t2 = (i + 1) / float(segments);
  287. float const angle1 = start_angle + (end_angle - start_angle) * t1;
  288. float const angle2 = start_angle + (end_angle - start_angle) * t2;
  289. float const x1_inner =
  290. (circle_radius - arrow_width * 0.5F) * std::cos(angle1);
  291. float const y1_inner =
  292. (circle_radius - arrow_width * 0.5F) * std::sin(angle1);
  293. float const x1_outer =
  294. (circle_radius + arrow_width * 0.5F) * std::cos(angle1);
  295. float const y1_outer =
  296. (circle_radius + arrow_width * 0.5F) * std::sin(angle1);
  297. float const x2_inner =
  298. (circle_radius - arrow_width * 0.5F) * std::cos(angle2);
  299. float const y2_inner =
  300. (circle_radius - arrow_width * 0.5F) * std::sin(angle2);
  301. float const x2_outer =
  302. (circle_radius + arrow_width * 0.5F) * std::cos(angle2);
  303. float const y2_outer =
  304. (circle_radius + arrow_width * 0.5F) * std::sin(angle2);
  305. size_t const base = verts.size();
  306. verts.push_back(
  307. {{x1_inner, y1_inner, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 0.0F}});
  308. verts.push_back(
  309. {{x1_outer, y1_outer, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 0.0F}});
  310. verts.push_back(
  311. {{x2_outer, y2_outer, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 1.0F}});
  312. verts.push_back(
  313. {{x2_inner, y2_inner, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 1.0F}});
  314. idx.push_back(base + 0);
  315. idx.push_back(base + 1);
  316. idx.push_back(base + 2);
  317. idx.push_back(base + 2);
  318. idx.push_back(base + 3);
  319. idx.push_back(base + 0);
  320. }
  321. float const arrow_angle = end_angle;
  322. float const arrow_x = circle_radius * std::cos(arrow_angle);
  323. float const arrow_y = circle_radius * std::sin(arrow_angle);
  324. float const tangent_x = -std::sin(arrow_angle);
  325. float const tangent_y = std::cos(arrow_angle);
  326. float const normal_x = std::cos(arrow_angle);
  327. float const normal_y = std::sin(arrow_angle);
  328. float const tip_x = arrow_x + tangent_x * arrow_head_length;
  329. float const tip_y = arrow_y + tangent_y * arrow_head_length;
  330. float const base1_x = arrow_x + normal_x * arrow_head_width * 0.5F;
  331. float const base1_y = arrow_y + normal_y * arrow_head_width * 0.5F;
  332. float const base2_x = arrow_x - normal_x * arrow_head_width * 0.5F;
  333. float const base2_y = arrow_y - normal_y * arrow_head_width * 0.5F;
  334. size_t const arrow_base = verts.size();
  335. verts.push_back(
  336. {{tip_x, tip_y, 0.0F}, {n.x(), n.y(), n.z()}, {0.5F, 1.0F}});
  337. verts.push_back(
  338. {{base1_x, base1_y, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 0.0F}});
  339. verts.push_back(
  340. {{base2_x, base2_y, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 0.0F}});
  341. idx.push_back(arrow_base + 0);
  342. idx.push_back(arrow_base + 1);
  343. idx.push_back(arrow_base + 2);
  344. }
  345. return std::make_unique<Mesh>(verts, idx);
  346. }
  347. auto ModeIndicator::get_attack_mode_mesh() -> Render::GL::Mesh * {
  348. if (!s_attack_mesh) {
  349. s_attack_mesh = create_attack_mode_mesh();
  350. }
  351. return s_attack_mesh.get();
  352. }
  353. auto ModeIndicator::get_guard_mode_mesh() -> Render::GL::Mesh * {
  354. if (!s_guard_mesh) {
  355. s_guard_mesh = create_guard_mode_mesh();
  356. }
  357. return s_guard_mesh.get();
  358. }
  359. auto ModeIndicator::get_hold_mode_mesh() -> Render::GL::Mesh * {
  360. if (!s_hold_mesh) {
  361. s_hold_mesh = create_hold_mode_mesh();
  362. }
  363. return s_hold_mesh.get();
  364. }
  365. auto ModeIndicator::get_patrol_mode_mesh() -> Render::GL::Mesh * {
  366. if (!s_patrol_mesh) {
  367. s_patrol_mesh = create_patrol_mode_mesh();
  368. }
  369. return s_patrol_mesh.get();
  370. }
  371. } // namespace Render::Geom