projectile_renderer.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #include "projectile_renderer.h"
  2. #include "../game/systems/arrow_projectile.h"
  3. #include "../game/systems/projectile_system.h"
  4. #include "../game/systems/stone_projectile.h"
  5. #include "../gl/resources.h"
  6. #include "../scene_renderer.h"
  7. #include "arrow.h"
  8. #include "stone.h"
  9. #include <algorithm>
  10. #include <cmath>
  11. #include <numbers>
  12. namespace Render::GL {
  13. void render_arrow_projectile(Renderer *renderer, ResourceManager *resources,
  14. const Game::Systems::ArrowProjectile &arrow,
  15. const QVector3D &pos,
  16. const QMatrix4x4 &base_model) {
  17. if (!renderer || !resources) {
  18. return;
  19. }
  20. auto *arrow_shaft_mesh = Geom::Arrow::get_shaft();
  21. auto *arrow_tip_mesh = Geom::Arrow::get_tip();
  22. if (!arrow_shaft_mesh || !arrow_tip_mesh) {
  23. return;
  24. }
  25. const QVector3D delta = arrow.get_end() - arrow.get_start();
  26. const float dist = std::max(0.001F, delta.length());
  27. QMatrix4x4 model = base_model;
  28. constexpr float k_arc_height_multiplier = 8.0F;
  29. constexpr float k_arc_center_offset = 0.5F;
  30. float const vy = (arrow.get_end().y() - arrow.get_start().y()) / dist;
  31. float const pitch_deg =
  32. -std::atan2(vy - (k_arc_height_multiplier * arrow.get_arc_height() *
  33. (arrow.get_progress() - k_arc_center_offset) / dist),
  34. 1.0F) *
  35. (180.0F / std::numbers::pi_v<float>);
  36. model.rotate(pitch_deg, QVector3D(1, 0, 0));
  37. if (arrow.is_ballista_bolt()) {
  38. float const spin_speed = 1440.0F;
  39. float const spin_angle = arrow.get_progress() * spin_speed;
  40. model.rotate(spin_angle, QVector3D(0, 0, 1));
  41. constexpr float bolt_z_scale = 0.85F;
  42. constexpr float bolt_xy_scale = 0.48F;
  43. constexpr float bolt_z_translate_factor = 0.5F;
  44. QMatrix4x4 bolt_model = model;
  45. bolt_model.translate(0.0F, 0.0F, -bolt_z_scale * bolt_z_translate_factor);
  46. bolt_model.scale(bolt_xy_scale, bolt_xy_scale, bolt_z_scale);
  47. QVector3D base_color = arrow.get_color();
  48. QVector3D wood_color =
  49. QVector3D(std::clamp(base_color.x() * 0.6F + 0.35F, 0.0F, 1.0F),
  50. std::clamp(base_color.y() * 0.55F + 0.30F, 0.0F, 1.0F),
  51. std::clamp(base_color.z() * 0.5F + 0.15F, 0.0F, 1.0F));
  52. renderer->mesh(arrow_shaft_mesh, bolt_model, wood_color, nullptr, 1.0F);
  53. QMatrix4x4 tip_model = bolt_model;
  54. tip_model.translate(0.0F, 0.0F, bolt_z_scale * 0.3F);
  55. tip_model.scale(0.85F, 0.85F, 0.2F);
  56. QVector3D iron_color = QVector3D(0.25F, 0.24F, 0.22F);
  57. renderer->mesh(arrow_tip_mesh, tip_model, iron_color, nullptr, 1.0F);
  58. QMatrix4x4 fletch_model = bolt_model;
  59. fletch_model.translate(0.0F, 0.0F, -bolt_z_scale * 0.2F);
  60. fletch_model.scale(0.75F, 0.75F, 0.15F);
  61. QVector3D fletch_color =
  62. QVector3D(std::clamp(wood_color.x() * 1.15F, 0.0F, 1.0F),
  63. std::clamp(wood_color.y() * 1.10F, 0.0F, 1.0F),
  64. std::clamp(wood_color.z() * 0.95F, 0.0F, 1.0F));
  65. renderer->mesh(arrow_shaft_mesh, fletch_model, fletch_color, nullptr, 0.7F);
  66. if (arrow.get_progress() > 0.15F) {
  67. float trail_opacity =
  68. std::clamp((arrow.get_progress() - 0.15F) / 0.85F, 0.0F, 0.3F);
  69. for (int trail_idx = 1; trail_idx <= 2; trail_idx++) {
  70. float trail_t = arrow.get_progress() - (trail_idx * 0.08F);
  71. if (trail_t >= 0.0F) {
  72. QVector3D trail_pos = arrow.get_start() + delta * trail_t;
  73. float trail_h =
  74. arrow.get_arc_height() * 4.0F * trail_t * (1.0F - trail_t);
  75. trail_pos.setY(trail_pos.y() + trail_h);
  76. QMatrix4x4 trail_model;
  77. trail_model.translate(trail_pos.x(), trail_pos.y(), trail_pos.z());
  78. constexpr float k_rad_to_deg = 180.0F / std::numbers::pi_v<float>;
  79. QVector3D const dir = delta.normalized();
  80. float const yaw_deg = std::atan2(dir.x(), dir.z()) * k_rad_to_deg;
  81. trail_model.rotate(yaw_deg, QVector3D(0, 1, 0));
  82. trail_model.rotate(pitch_deg, QVector3D(1, 0, 0));
  83. float trail_spin_angle = trail_t * spin_speed;
  84. trail_model.rotate(trail_spin_angle, QVector3D(0, 0, 1));
  85. float trail_scale_factor = 0.6F - (trail_idx * 0.15F);
  86. trail_model.translate(0.0F, 0.0F,
  87. -bolt_z_scale * bolt_z_translate_factor);
  88. trail_model.scale(bolt_xy_scale * trail_scale_factor,
  89. bolt_xy_scale * trail_scale_factor,
  90. bolt_z_scale * trail_scale_factor);
  91. QVector3D trail_color = wood_color * (1.0F - trail_opacity * 0.4F);
  92. renderer->mesh(arrow_shaft_mesh, trail_model, trail_color, nullptr,
  93. 1.0F - (trail_opacity * 0.7F));
  94. }
  95. }
  96. }
  97. } else {
  98. constexpr float arrow_z_scale = 0.40F;
  99. constexpr float arrow_xy_scale = 0.26F;
  100. constexpr float arrow_z_translate_factor = 0.5F;
  101. model.translate(0.0F, 0.0F, -arrow_z_scale * arrow_z_translate_factor);
  102. model.scale(arrow_xy_scale, arrow_xy_scale, arrow_z_scale);
  103. QVector3D wood_color =
  104. QVector3D(std::clamp(arrow.get_color().x() * 0.6F + 0.35F, 0.0F, 1.0F),
  105. std::clamp(arrow.get_color().y() * 0.55F + 0.30F, 0.0F, 1.0F),
  106. std::clamp(arrow.get_color().z() * 0.5F + 0.15F, 0.0F, 1.0F));
  107. renderer->mesh(arrow_shaft_mesh, model, wood_color, nullptr, 1.0F);
  108. QVector3D tip_color(0.70F, 0.72F, 0.75F);
  109. renderer->mesh(arrow_tip_mesh, model, tip_color, nullptr, 1.0F);
  110. }
  111. }
  112. void render_stone_projectile(Renderer *renderer, ResourceManager *resources,
  113. const Game::Systems::StoneProjectile &stone,
  114. const QVector3D &pos,
  115. const QMatrix4x4 &base_model) {
  116. if (!renderer || !resources) {
  117. return;
  118. }
  119. auto *stone_mesh = Geom::Stone::get();
  120. if (!stone_mesh) {
  121. return;
  122. }
  123. QMatrix4x4 model = base_model;
  124. float const tumble_speed = 720.0F;
  125. float const tumble_angle = stone.get_progress() * tumble_speed;
  126. model.rotate(tumble_angle, QVector3D(1, 0.5F, 0.3F).normalized());
  127. float const stone_scale = stone.get_scale();
  128. model.scale(stone_scale, stone_scale, stone_scale);
  129. QVector3D const stone_color(0.45F, 0.42F, 0.38F);
  130. renderer->mesh(stone_mesh, model, stone_color, nullptr, 1.0F);
  131. }
  132. void render_projectiles(
  133. Renderer *renderer, ResourceManager *resources,
  134. const Game::Systems::ProjectileSystem &projectile_system) {
  135. if (!renderer || !resources) {
  136. return;
  137. }
  138. const auto &projectiles = projectile_system.projectiles();
  139. constexpr float k_rad_to_deg = 180.0F / std::numbers::pi_v<float>;
  140. for (const auto &projectile : projectiles) {
  141. if (!projectile->is_active()) {
  142. continue;
  143. }
  144. const QVector3D delta = projectile->get_end() - projectile->get_start();
  145. const float dist = std::max(0.001F, delta.length());
  146. QVector3D pos =
  147. projectile->get_start() + delta * projectile->get_progress();
  148. float const h = projectile->get_arc_height() * 4.0F *
  149. projectile->get_progress() *
  150. (1.0F - projectile->get_progress());
  151. pos.setY(pos.y() + h);
  152. QMatrix4x4 model;
  153. model.translate(pos.x(), pos.y(), pos.z());
  154. QVector3D const dir = delta.normalized();
  155. float const yaw_deg = std::atan2(dir.x(), dir.z()) * k_rad_to_deg;
  156. model.rotate(yaw_deg, QVector3D(0, 1, 0));
  157. if (auto *arrow = dynamic_cast<const Game::Systems::ArrowProjectile *>(
  158. projectile.get())) {
  159. render_arrow_projectile(renderer, resources, *arrow, pos, model);
  160. } else if (auto *stone =
  161. dynamic_cast<const Game::Systems::StoneProjectile *>(
  162. projectile.get())) {
  163. render_stone_projectile(renderer, resources, *stone, pos, model);
  164. }
  165. }
  166. }
  167. } // namespace Render::GL