healing_waves_renderer.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #include "healing_waves_renderer.h"
  2. #include "../../game/core/component.h"
  3. #include "../../game/systems/healing_beam.h"
  4. #include "../../game/systems/healing_beam_system.h"
  5. #include "../../game/systems/healing_colors.h"
  6. #include "../../game/systems/nation_id.h"
  7. #include "../scene_renderer.h"
  8. #include <QVector3D>
  9. #include <cmath>
  10. #include <numbers>
  11. namespace Render::GL {
  12. namespace {
  13. constexpr int k_num_waves = 3;
  14. constexpr float k_wave_spacing = 0.3F;
  15. constexpr float k_wave_speed = 2.5F;
  16. constexpr float k_wave_width = 0.12F;
  17. constexpr int k_wave_ribbons = 6;
  18. constexpr float k_ribbon_radius = 0.08F;
  19. constexpr int k_segments_per_wave = 10;
  20. constexpr float k_spiral_twist_rate = 8.0F;
  21. constexpr float k_edge_fade_factor = 1.5F;
  22. } // namespace
  23. void render_healing_waves(Renderer *renderer, ResourceManager *,
  24. const Game::Systems::HealingBeamSystem &beam_system) {
  25. if (renderer == nullptr || beam_system.get_beam_count() == 0) {
  26. return;
  27. }
  28. float animation_time = renderer->get_animation_time();
  29. for (const auto &beam : beam_system.get_beams()) {
  30. if (!beam || !beam->is_active()) {
  31. continue;
  32. }
  33. float intensity = beam->get_intensity();
  34. if (intensity < 0.01F) {
  35. continue;
  36. }
  37. QVector3D start = beam->get_start();
  38. QVector3D end = beam->get_end();
  39. QVector3D color = beam->get_color();
  40. if (!Game::Systems::is_roman_healing_color(color)) {
  41. continue;
  42. }
  43. QVector3D direction = end - start;
  44. float distance = direction.length();
  45. if (distance < 0.01F) {
  46. continue;
  47. }
  48. direction.normalize();
  49. QVector3D perpendicular1;
  50. if (std::abs(direction.y()) < 0.9F) {
  51. perpendicular1 = QVector3D::crossProduct(direction, QVector3D(0, 1, 0));
  52. } else {
  53. perpendicular1 = QVector3D::crossProduct(direction, QVector3D(1, 0, 0));
  54. }
  55. perpendicular1.normalize();
  56. QVector3D perpendicular2 =
  57. QVector3D::crossProduct(direction, perpendicular1);
  58. perpendicular2.normalize();
  59. constexpr float pi = std::numbers::pi_v<float>;
  60. for (int wave_idx = 0; wave_idx < k_num_waves; ++wave_idx) {
  61. float wave_cycle_time =
  62. animation_time * k_wave_speed + wave_idx * k_wave_spacing;
  63. float wave_offset =
  64. std::fmod(wave_cycle_time, distance + k_wave_spacing * k_num_waves);
  65. if (wave_offset > distance) {
  66. continue;
  67. }
  68. float wave_progress = wave_offset / distance;
  69. QVector3D wave_center = start + direction * wave_offset;
  70. float wave_intensity = intensity;
  71. if (wave_progress < 0.15F) {
  72. wave_intensity *= wave_progress / 0.15F;
  73. } else if (wave_progress > 0.85F) {
  74. wave_intensity *= (1.0F - wave_progress) / 0.15F;
  75. }
  76. for (int ribbon = 0; ribbon < k_wave_ribbons; ++ribbon) {
  77. float ribbon_angle_offset =
  78. (static_cast<float>(ribbon) / k_wave_ribbons) * 2.0F * pi;
  79. for (int seg = 0; seg < k_segments_per_wave; ++seg) {
  80. float seg_t = static_cast<float>(seg) / k_segments_per_wave;
  81. float next_seg_t = static_cast<float>(seg + 1) / k_segments_per_wave;
  82. float seg_dist = (seg_t - 0.5F) * k_wave_width;
  83. float next_seg_dist = (next_seg_t - 0.5F) * k_wave_width;
  84. float spiral_phase = animation_time * 3.0F + wave_idx * pi;
  85. float seg_angle = ribbon_angle_offset +
  86. seg_dist * k_spiral_twist_rate + spiral_phase;
  87. float next_seg_angle = ribbon_angle_offset +
  88. next_seg_dist * k_spiral_twist_rate +
  89. spiral_phase;
  90. float seg_radius =
  91. k_ribbon_radius * (1.0F - std::abs(seg_t - 0.5F) * 2.0F);
  92. float next_seg_radius =
  93. k_ribbon_radius * (1.0F - std::abs(next_seg_t - 0.5F) * 2.0F);
  94. QVector3D seg_offset =
  95. perpendicular1 * std::cos(seg_angle) * seg_radius +
  96. perpendicular2 * std::sin(seg_angle) * seg_radius;
  97. QVector3D next_seg_offset =
  98. perpendicular1 * std::cos(next_seg_angle) * next_seg_radius +
  99. perpendicular2 * std::sin(next_seg_angle) * next_seg_radius;
  100. QVector3D seg_pos = wave_center + direction * seg_dist + seg_offset;
  101. QVector3D next_seg_pos =
  102. wave_center + direction * next_seg_dist + next_seg_offset;
  103. float seg_intensity =
  104. wave_intensity *
  105. (1.0F - std::abs(seg_t - 0.5F) * k_edge_fade_factor);
  106. seg_intensity = std::max(0.0F, seg_intensity);
  107. if (seg_intensity > 0.01F) {
  108. renderer->healing_beam(seg_pos, next_seg_pos, color, 1.0F, 0.04F,
  109. seg_intensity, animation_time);
  110. }
  111. }
  112. }
  113. }
  114. }
  115. }
  116. } // namespace Render::GL