arrow.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #include "arrow.h"
  2. #include "../../game/systems/arrow_system.h"
  3. #include "../entity/registry.h"
  4. #include "../gl/mesh.h"
  5. #include "../gl/resources.h"
  6. #include "../scene_renderer.h"
  7. #include <QMatrix4x4>
  8. #include <QVector3D>
  9. #include <algorithm>
  10. #include <cmath>
  11. #include <numbers>
  12. #include <qmatrix4x4.h>
  13. #include <qvectornd.h>
  14. #include <vector>
  15. namespace Render {
  16. namespace Geom {
  17. static auto createArrowMesh() -> GL::Mesh * {
  18. using GL::Vertex;
  19. std::vector<GL::Vertex> verts;
  20. std::vector<unsigned int> idx;
  21. constexpr int k_arrow_radial_segments = 12;
  22. const float shaft_radius = 0.05F;
  23. const float shaft_len = 0.85F;
  24. const float tip_len = 0.15F;
  25. const float tip_start_z = shaft_len;
  26. const float tip_end_z = shaft_len + tip_len;
  27. int const base_index = 0;
  28. for (int ring = 0; ring < 2; ++ring) {
  29. float z = (ring == 0) ? 0.0F : shaft_len;
  30. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  31. float const a = (float(i) / k_arrow_radial_segments) * 6.2831853F;
  32. float x = std::cos(a) * shaft_radius;
  33. float y = std::sin(a) * shaft_radius;
  34. QVector3D n(x, y, 0.0F);
  35. n.normalize();
  36. verts.push_back({{x, y, z},
  37. {n.x(), n.y(), n.z()},
  38. {float(i) / k_arrow_radial_segments, z}});
  39. }
  40. }
  41. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  42. int const next = (i + 1) % k_arrow_radial_segments;
  43. int a = base_index + i;
  44. int b = base_index + next;
  45. int c = base_index + k_arrow_radial_segments + next;
  46. int d = base_index + k_arrow_radial_segments + i;
  47. idx.push_back(a);
  48. idx.push_back(b);
  49. idx.push_back(c);
  50. idx.push_back(c);
  51. idx.push_back(d);
  52. idx.push_back(a);
  53. }
  54. int const ring_start = verts.size();
  55. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  56. float const a = (float(i) / k_arrow_radial_segments) * 6.2831853F;
  57. float x = std::cos(a) * shaft_radius * 1.4F;
  58. float y = std::sin(a) * shaft_radius * 1.4F;
  59. QVector3D n(x, y, 0.2F);
  60. n.normalize();
  61. verts.push_back({{x, y, tip_start_z},
  62. {n.x(), n.y(), n.z()},
  63. {float(i) / k_arrow_radial_segments, 0.0F}});
  64. }
  65. int apex_index = verts.size();
  66. verts.push_back({{0.0F, 0.0F, tip_end_z}, {0.0F, 0.0F, 1.0F}, {0.5F, 1.0F}});
  67. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  68. int const next = (i + 1) % k_arrow_radial_segments;
  69. idx.push_back(ring_start + i);
  70. idx.push_back(apex_index);
  71. idx.push_back(ring_start + next);
  72. }
  73. return new GL::Mesh(verts, idx);
  74. }
  75. auto Arrow::get() -> GL::Mesh * {
  76. static GL::Mesh *mesh = createArrowMesh();
  77. return mesh;
  78. }
  79. } // namespace Geom
  80. namespace GL {
  81. void renderArrows(Renderer *renderer, ResourceManager *resources,
  82. const Game::Systems::ArrowSystem &arrow_system) {
  83. if ((renderer == nullptr) || (resources == nullptr)) {
  84. return;
  85. }
  86. auto *arrow_mesh = Render::GL::ResourceManager::arrow();
  87. if (arrow_mesh == nullptr) {
  88. return;
  89. }
  90. const auto &arrows = arrow_system.arrows();
  91. for (const auto &arrow : arrows) {
  92. if (!arrow.active) {
  93. continue;
  94. }
  95. const QVector3D delta = arrow.end - arrow.start;
  96. const float dist = std::max(0.001F, delta.length());
  97. QVector3D pos = arrow.start + delta * arrow.t;
  98. float const h = arrow.arcHeight * 4.0F * arrow.t * (1.0F - arrow.t);
  99. pos.setY(pos.y() + h);
  100. QMatrix4x4 model;
  101. model.translate(pos.x(), pos.y(), pos.z());
  102. constexpr float k_rad_to_deg = 180.0F / std::numbers::pi_v<float>;
  103. QVector3D const dir = delta.normalized();
  104. float const yaw_deg = std::atan2(dir.x(), dir.z()) * k_rad_to_deg;
  105. model.rotate(yaw_deg, QVector3D(0, 1, 0));
  106. constexpr float k_arc_height_multiplier = 8.0F;
  107. constexpr float k_arc_center_offset = 0.5F;
  108. float const vy = (arrow.end.y() - arrow.start.y()) / dist;
  109. float const pitch_deg =
  110. -std::atan2(vy - (k_arc_height_multiplier * arrow.arcHeight *
  111. (arrow.t - k_arc_center_offset) / dist),
  112. 1.0F) *
  113. k_rad_to_deg;
  114. model.rotate(pitch_deg, QVector3D(1, 0, 0));
  115. constexpr float arrow_z_scale = 0.40F;
  116. constexpr float arrow_xy_scale = 0.26F;
  117. constexpr float arrow_z_translate_factor = 0.5F;
  118. model.translate(0.0F, 0.0F, -arrow_z_scale * arrow_z_translate_factor);
  119. model.scale(arrow_xy_scale, arrow_xy_scale, arrow_z_scale);
  120. renderer->mesh(arrow_mesh, model, arrow.color, nullptr, 1.0F);
  121. }
  122. }
  123. } // namespace GL
  124. } // namespace Render