arrow.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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 createArrowShaftMesh() -> std::unique_ptr<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. int const base_index = 0;
  25. for (int ring = 0; ring < 2; ++ring) {
  26. float z = (ring == 0) ? 0.0F : shaft_len;
  27. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  28. float const a = (float(i) / k_arrow_radial_segments) * 6.2831853F;
  29. float x = std::cos(a) * shaft_radius;
  30. float y = std::sin(a) * shaft_radius;
  31. QVector3D n(x, y, 0.0F);
  32. n.normalize();
  33. verts.push_back({{x, y, z},
  34. {n.x(), n.y(), n.z()},
  35. {float(i) / k_arrow_radial_segments, z}});
  36. }
  37. }
  38. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  39. int const next = (i + 1) % k_arrow_radial_segments;
  40. int a = base_index + i;
  41. int b = base_index + next;
  42. int c = base_index + k_arrow_radial_segments + next;
  43. int d = base_index + k_arrow_radial_segments + i;
  44. idx.push_back(a);
  45. idx.push_back(b);
  46. idx.push_back(c);
  47. idx.push_back(c);
  48. idx.push_back(d);
  49. idx.push_back(a);
  50. }
  51. return std::make_unique<GL::Mesh>(verts, idx);
  52. }
  53. static auto createArrowTipMesh() -> std::unique_ptr<GL::Mesh> {
  54. using GL::Vertex;
  55. std::vector<GL::Vertex> verts;
  56. std::vector<unsigned int> idx;
  57. constexpr int k_arrow_radial_segments = 12;
  58. const float shaft_radius = 0.05F;
  59. const float shaft_len = 0.85F;
  60. const float tip_len = 0.15F;
  61. const float tip_start_z = shaft_len;
  62. const float tip_end_z = shaft_len + tip_len;
  63. int const ring_start = 0;
  64. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  65. float const a = (float(i) / k_arrow_radial_segments) * 6.2831853F;
  66. float x = std::cos(a) * shaft_radius * 1.4F;
  67. float y = std::sin(a) * shaft_radius * 1.4F;
  68. QVector3D n(x, y, 0.2F);
  69. n.normalize();
  70. verts.push_back({{x, y, tip_start_z},
  71. {n.x(), n.y(), n.z()},
  72. {float(i) / k_arrow_radial_segments, 0.0F}});
  73. }
  74. int apex_index = verts.size();
  75. verts.push_back({{0.0F, 0.0F, tip_end_z}, {0.0F, 0.0F, 1.0F}, {0.5F, 1.0F}});
  76. for (int i = 0; i < k_arrow_radial_segments; ++i) {
  77. int const next = (i + 1) % k_arrow_radial_segments;
  78. idx.push_back(ring_start + i);
  79. idx.push_back(apex_index);
  80. idx.push_back(ring_start + next);
  81. }
  82. return std::make_unique<GL::Mesh>(verts, idx);
  83. }
  84. auto Arrow::get_shaft() -> GL::Mesh * {
  85. static std::unique_ptr<GL::Mesh> const mesh = createArrowShaftMesh();
  86. return mesh.get();
  87. }
  88. auto Arrow::get_tip() -> GL::Mesh * {
  89. static std::unique_ptr<GL::Mesh> const mesh = createArrowTipMesh();
  90. return mesh.get();
  91. }
  92. } // namespace Geom
  93. namespace GL {
  94. void render_arrows(Renderer *renderer, ResourceManager *resources,
  95. const Game::Systems::ArrowSystem &arrow_system) {
  96. if ((renderer == nullptr) || (resources == nullptr)) {
  97. return;
  98. }
  99. auto *arrow_shaft_mesh = Render::Geom::Arrow::get_shaft();
  100. auto *arrow_tip_mesh = Render::Geom::Arrow::get_tip();
  101. if ((arrow_shaft_mesh == nullptr) || (arrow_tip_mesh == nullptr)) {
  102. return;
  103. }
  104. const auto &arrows = arrow_system.arrows();
  105. for (const auto &arrow : arrows) {
  106. if (!arrow.active) {
  107. continue;
  108. }
  109. const QVector3D delta = arrow.end - arrow.start;
  110. const float dist = std::max(0.001F, delta.length());
  111. QVector3D pos = arrow.start + delta * arrow.t;
  112. float const h = arrow.arc_height * 4.0F * arrow.t * (1.0F - arrow.t);
  113. pos.setY(pos.y() + h);
  114. QMatrix4x4 model;
  115. model.translate(pos.x(), pos.y(), pos.z());
  116. constexpr float k_rad_to_deg = 180.0F / std::numbers::pi_v<float>;
  117. QVector3D const dir = delta.normalized();
  118. float const yaw_deg = std::atan2(dir.x(), dir.z()) * k_rad_to_deg;
  119. model.rotate(yaw_deg, QVector3D(0, 1, 0));
  120. constexpr float k_arc_height_multiplier = 8.0F;
  121. constexpr float k_arc_center_offset = 0.5F;
  122. float const vy = (arrow.end.y() - arrow.start.y()) / dist;
  123. float const pitch_deg =
  124. -std::atan2(vy - (k_arc_height_multiplier * arrow.arc_height *
  125. (arrow.t - k_arc_center_offset) / dist),
  126. 1.0F) *
  127. k_rad_to_deg;
  128. model.rotate(pitch_deg, QVector3D(1, 0, 0));
  129. constexpr float arrow_z_scale = 0.40F;
  130. constexpr float arrow_xy_scale = 0.26F;
  131. constexpr float arrow_z_translate_factor = 0.5F;
  132. model.translate(0.0F, 0.0F, -arrow_z_scale * arrow_z_translate_factor);
  133. model.scale(arrow_xy_scale, arrow_xy_scale, arrow_z_scale);
  134. QVector3D wood_color(0.35F, 0.25F, 0.15F);
  135. renderer->mesh(arrow_shaft_mesh, model, wood_color, nullptr, 1.0F);
  136. QVector3D metal_color(0.70F, 0.72F, 0.75F);
  137. renderer->mesh(arrow_tip_mesh, model, metal_color, nullptr, 1.0F);
  138. }
  139. }
  140. } // namespace GL
  141. } // namespace Render