bow_renderer.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #include "bow_renderer.h"
  2. #include "../../entity/registry.h"
  3. #include "../../entity/renderer_constants.h"
  4. #include "../../geom/math_utils.h"
  5. #include "../../geom/transforms.h"
  6. #include "../../gl/primitives.h"
  7. #include "../../humanoid/humanoid_math.h"
  8. #include "../../humanoid/humanoid_specs.h"
  9. #include "../../humanoid/rig.h"
  10. #include "../../submitter.h"
  11. #include <QMatrix4x4>
  12. #include <QVector3D>
  13. #include <algorithm>
  14. #include <cmath>
  15. namespace Render::GL {
  16. using Render::Geom::clamp_f;
  17. using Render::Geom::cone_from_to;
  18. using Render::Geom::cylinder_between;
  19. namespace {
  20. constexpr QVector3D k_dark_bow_color(0.05F, 0.035F, 0.02F);
  21. }
  22. BowRenderer::BowRenderer(BowRenderConfig config)
  23. : m_config(std::move(config)) {}
  24. void BowRenderer::render(const DrawContext &ctx, const BodyFrames &frames,
  25. const HumanoidPalette &palette,
  26. const HumanoidAnimationContext &anim,
  27. ISubmitter &submitter) {
  28. const QVector3D up(0.0F, 1.0F, 0.0F);
  29. const QVector3D forward(0.0F, 0.0F, 1.0F);
  30. QVector3D const grip = frames.hand_r.origin;
  31. float const bow_half_height = (m_config.bow_top_y - m_config.bow_bot_y) *
  32. 0.5F * m_config.bow_height_scale;
  33. float const bow_mid_y = grip.y();
  34. float const bow_top_y = bow_mid_y + bow_half_height;
  35. float const bow_bot_y = bow_mid_y - bow_half_height;
  36. QVector3D outward = frames.hand_r.right;
  37. if (outward.lengthSquared() < 1e-6F) {
  38. outward = QVector3D(-1.0F, 0.0F, 0.0F);
  39. }
  40. outward.setY(0.0F);
  41. if (outward.lengthSquared() < 1e-6F) {
  42. outward = QVector3D(-1.0F, 0.0F, 0.0F);
  43. } else {
  44. outward.normalize();
  45. }
  46. QVector3D const side = outward * 0.02F;
  47. float const bow_plane_x = grip.x() + m_config.bow_x + side.x();
  48. float const bow_plane_z = grip.z() + side.z();
  49. QVector3D const top_end(bow_plane_x, bow_top_y, bow_plane_z);
  50. QVector3D const bot_end(bow_plane_x, bow_bot_y, bow_plane_z);
  51. QVector3D const string_hand = frames.hand_l.origin;
  52. QVector3D const nock(
  53. bow_plane_x,
  54. clamp_f(string_hand.y(), bow_bot_y + 0.05F, bow_top_y - 0.05F),
  55. clamp_f(string_hand.z(), bow_plane_z - 0.30F, bow_plane_z + 0.30F));
  56. constexpr int k_bowstring_segments = 22;
  57. auto q_bezier = [](const QVector3D &a, const QVector3D &c, const QVector3D &b,
  58. float t) {
  59. float const u = 1.0F - t;
  60. return u * u * a + 2.0F * u * t * c + t * t * b;
  61. };
  62. float const ctrl_y = bow_mid_y + (0.45F * m_config.bow_curve_factor);
  63. QVector3D const ctrl(bow_plane_x, ctrl_y,
  64. bow_plane_z + m_config.bow_depth * 0.6F *
  65. m_config.bow_curve_factor);
  66. QVector3D prev = bot_end;
  67. for (int i = 1; i <= k_bowstring_segments; ++i) {
  68. float const t = float(i) / float(k_bowstring_segments);
  69. QVector3D const cur = q_bezier(bot_end, ctrl, top_end, t);
  70. submitter.mesh(
  71. get_unit_cylinder(),
  72. cylinder_between(ctx.model, prev, cur, m_config.bow_rod_radius),
  73. k_dark_bow_color, nullptr, 1.0F, m_config.material_id);
  74. prev = cur;
  75. }
  76. submitter.mesh(get_unit_cylinder(),
  77. cylinder_between(ctx.model, grip - up * 0.05F,
  78. grip + up * 0.05F,
  79. m_config.bow_rod_radius * 1.45F),
  80. k_dark_bow_color, nullptr, 1.0F, m_config.material_id);
  81. submitter.mesh(
  82. get_unit_cylinder(),
  83. cylinder_between(ctx.model, top_end, nock, m_config.string_radius),
  84. m_config.string_color, nullptr, 1.0F, m_config.material_id);
  85. submitter.mesh(
  86. get_unit_cylinder(),
  87. cylinder_between(ctx.model, nock, bot_end, m_config.string_radius),
  88. m_config.string_color, nullptr, 1.0F, m_config.material_id);
  89. bool const is_bow_attacking =
  90. anim.inputs.is_attacking && !anim.inputs.is_melee;
  91. if (is_bow_attacking) {
  92. submitter.mesh(
  93. get_unit_cylinder(),
  94. cylinder_between(ctx.model, frames.hand_l.origin, nock, 0.0045F),
  95. m_config.string_color * 0.9F, nullptr, 1.0F, m_config.material_id);
  96. }
  97. float attack_phase = 0.0F;
  98. if (is_bow_attacking) {
  99. attack_phase =
  100. std::fmod(anim.inputs.time * ARCHER_INV_ATTACK_CYCLE_TIME, 1.0F);
  101. }
  102. constexpr float k_attack_arrow_window_end = 0.52F;
  103. bool const attack_window_active =
  104. is_bow_attacking &&
  105. (attack_phase >= 0.0F && attack_phase < k_attack_arrow_window_end);
  106. auto arrow_visible = [this, is_bow_attacking,
  107. attack_window_active]() -> bool {
  108. switch (m_config.arrow_visibility) {
  109. case ArrowVisibility::Hidden:
  110. return false;
  111. case ArrowVisibility::AttackCycleOnly:
  112. return attack_window_active;
  113. case ArrowVisibility::IdleAndAttackCycle:
  114. if (!is_bow_attacking) {
  115. return true;
  116. }
  117. return attack_window_active;
  118. }
  119. return attack_window_active;
  120. };
  121. bool const show_arrow = arrow_visible();
  122. if (show_arrow) {
  123. QVector3D const tail = nock - forward * 0.06F;
  124. QVector3D const tip = tail + forward * 0.90F;
  125. submitter.mesh(get_unit_cylinder(),
  126. cylinder_between(ctx.model, tail, tip, 0.018F), palette.wood,
  127. nullptr, 1.0F, m_config.material_id);
  128. QVector3D const head_base = tip - forward * 0.10F;
  129. submitter.mesh(get_unit_cone(),
  130. cone_from_to(ctx.model, head_base, tip, 0.05F),
  131. m_config.metal_color, nullptr, 1.0F, m_config.material_id);
  132. QVector3D const f1b = tail - forward * 0.02F;
  133. QVector3D const f1a = f1b - forward * 0.06F;
  134. QVector3D const f2b = tail + forward * 0.02F;
  135. QVector3D const f2a = f2b + forward * 0.06F;
  136. submitter.mesh(get_unit_cone(), cone_from_to(ctx.model, f1b, f1a, 0.04F),
  137. m_config.fletching_color, nullptr, 1.0F,
  138. m_config.material_id);
  139. submitter.mesh(get_unit_cone(), cone_from_to(ctx.model, f2a, f2b, 0.04F),
  140. m_config.fletching_color, nullptr, 1.0F,
  141. m_config.material_id);
  142. }
  143. }
  144. } // namespace Render::GL