shield_carthage.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #include "shield_carthage.h"
  2. #include "../../geom/transforms.h"
  3. #include "../../gl/mesh.h"
  4. #include "../../gl/primitives.h"
  5. #include "../../humanoid/rig.h"
  6. #include "../../submitter.h"
  7. #include <QMatrix4x4>
  8. #include <QVector3D>
  9. #include <cmath>
  10. #include <memory>
  11. #include <numbers>
  12. #include <vector>
  13. namespace Render::GL {
  14. using Render::Geom::cone_from_to;
  15. using Render::Geom::cylinder_between;
  16. using Render::Geom::sphere_at;
  17. namespace {
  18. auto create_unit_hemisphere_mesh(int lat_segments = 12, int lon_segments = 32)
  19. -> std::unique_ptr<Mesh> {
  20. std::vector<Vertex> vertices;
  21. std::vector<unsigned int> indices;
  22. vertices.reserve((lat_segments + 1) * (lon_segments + 1));
  23. for (int lat = 0; lat <= lat_segments; ++lat) {
  24. float const v = static_cast<float>(lat) / static_cast<float>(lat_segments);
  25. float const phi = v * (std::numbers::pi_v<float> * 0.5F);
  26. float const z = std::cos(phi);
  27. float const ring_r = std::sin(phi);
  28. for (int lon = 0; lon <= lon_segments; ++lon) {
  29. float const u =
  30. static_cast<float>(lon) / static_cast<float>(lon_segments);
  31. float const theta = u * 2.0F * std::numbers::pi_v<float>;
  32. float const x = ring_r * std::cos(theta);
  33. float const y = ring_r * std::sin(theta);
  34. QVector3D normal(x, y, z);
  35. normal.normalize();
  36. vertices.push_back(
  37. {{x, y, z}, {normal.x(), normal.y(), normal.z()}, {u, v}});
  38. }
  39. }
  40. int const row = lon_segments + 1;
  41. for (int lat = 0; lat < lat_segments; ++lat) {
  42. for (int lon = 0; lon < lon_segments; ++lon) {
  43. int const a = lat * row + lon;
  44. int const b = a + 1;
  45. int const c = (lat + 1) * row + lon + 1;
  46. int const d = (lat + 1) * row + lon;
  47. indices.push_back(a);
  48. indices.push_back(b);
  49. indices.push_back(c);
  50. indices.push_back(c);
  51. indices.push_back(d);
  52. indices.push_back(a);
  53. }
  54. }
  55. return std::make_unique<Mesh>(vertices, indices);
  56. }
  57. auto get_unit_hemisphere_mesh() -> Mesh * {
  58. static std::unique_ptr<Mesh> const mesh = create_unit_hemisphere_mesh();
  59. return mesh.get();
  60. }
  61. } // namespace
  62. CarthageShieldRenderer::CarthageShieldRenderer(float scale_multiplier)
  63. : m_scale_multiplier(scale_multiplier) {
  64. ShieldRenderConfig config;
  65. config.shield_color = {0.20F, 0.46F, 0.62F};
  66. config.trim_color = {0.76F, 0.68F, 0.42F};
  67. config.metal_color = {0.70F, 0.68F, 0.52F};
  68. config.shield_radius = 0.18F * 0.9F;
  69. config.shield_aspect = 1.0F;
  70. config.has_cross_decal = false;
  71. set_config(config);
  72. }
  73. void CarthageShieldRenderer::render(const DrawContext &ctx,
  74. const BodyFrames &frames,
  75. const HumanoidPalette &palette,
  76. const HumanoidAnimationContext &,
  77. ISubmitter &submitter) {
  78. constexpr float k_shield_yaw_degrees = -70.0F;
  79. constexpr float k_scale_factor = 2.5F;
  80. QMatrix4x4 rot;
  81. rot.rotate(k_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
  82. const QVector3D n = rot.map(QVector3D(0.0F, 0.0F, 1.0F));
  83. const QVector3D axis_x = rot.map(QVector3D(1.0F, 0.0F, 0.0F));
  84. const QVector3D axis_y = rot.map(QVector3D(0.0F, 1.0F, 0.0F));
  85. float const shield_radius =
  86. 0.18F * 0.9F * k_scale_factor * m_scale_multiplier;
  87. QVector3D shield_center = frames.hand_l.origin +
  88. axis_x * (-shield_radius * 0.35F) +
  89. axis_y * (-0.05F) + n * (0.06F);
  90. QVector3D const shield_color{0.20F, 0.46F, 0.62F};
  91. QVector3D const trim_color{0.76F, 0.68F, 0.42F};
  92. QVector3D const metal_color{0.70F, 0.68F, 0.52F};
  93. float const dome_depth = shield_radius * 0.55F;
  94. {
  95. QMatrix4x4 m = ctx.model;
  96. m.translate(shield_center);
  97. m.rotate(k_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
  98. m.scale(shield_radius, shield_radius, dome_depth);
  99. submitter.mesh(get_unit_hemisphere_mesh(), m, shield_color, nullptr, 1.0F,
  100. 4);
  101. }
  102. constexpr int rim_segments = 24;
  103. for (int i = 0; i < rim_segments; ++i) {
  104. float const a0 =
  105. (static_cast<float>(i) / static_cast<float>(rim_segments)) * 2.0F *
  106. std::numbers::pi_v<float>;
  107. float const a1 =
  108. (static_cast<float>(i + 1) / static_cast<float>(rim_segments)) * 2.0F *
  109. std::numbers::pi_v<float>;
  110. QVector3D const p0 = shield_center +
  111. axis_x * (shield_radius * std::cos(a0)) +
  112. axis_y * (shield_radius * std::sin(a0));
  113. QVector3D const p1 = shield_center +
  114. axis_x * (shield_radius * std::cos(a1)) +
  115. axis_y * (shield_radius * std::sin(a1));
  116. submitter.mesh(get_unit_cylinder(),
  117. cylinder_between(ctx.model, p0, p1, 0.012F), trim_color,
  118. nullptr, 1.0F, 4);
  119. }
  120. QVector3D const emblem_plane = shield_center + n * (dome_depth * 0.92F);
  121. {
  122. QMatrix4x4 medallion = ctx.model;
  123. medallion.translate(emblem_plane);
  124. medallion.rotate(k_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
  125. medallion.scale(shield_radius * 0.34F, shield_radius * 0.34F,
  126. shield_radius * 0.08F);
  127. submitter.mesh(get_unit_cylinder(), medallion, trim_color * 0.95F, nullptr,
  128. 1.0F, 4);
  129. }
  130. QVector3D const emblem_body_top =
  131. emblem_plane + axis_y * (shield_radius * 0.14F);
  132. QVector3D const emblem_body_bot =
  133. emblem_plane - axis_y * (shield_radius * 0.08F);
  134. float const emblem_radius = shield_radius * 0.028F;
  135. submitter.mesh(get_unit_cylinder(),
  136. cylinder_between(ctx.model, emblem_body_bot, emblem_body_top,
  137. emblem_radius),
  138. metal_color, nullptr, 1.0F, 4);
  139. QVector3D const emblem_arm_height =
  140. emblem_plane + axis_y * (shield_radius * 0.02F);
  141. QVector3D const emblem_arm_left =
  142. emblem_arm_height - axis_x * (shield_radius * 0.22F);
  143. QVector3D const emblem_arm_right =
  144. emblem_arm_height + axis_x * (shield_radius * 0.22F);
  145. submitter.mesh(get_unit_cylinder(),
  146. cylinder_between(ctx.model, emblem_arm_left, emblem_arm_right,
  147. emblem_radius * 0.75F),
  148. metal_color, nullptr, 1.0F, 4);
  149. submitter.mesh(get_unit_sphere(),
  150. sphere_at(ctx.model,
  151. emblem_body_top + axis_y * (shield_radius * 0.05F),
  152. emblem_radius * 1.4F),
  153. metal_color, nullptr, 1.0F, 4);
  154. submitter.mesh(
  155. get_unit_cone(),
  156. cone_from_to(ctx.model,
  157. emblem_body_bot - axis_y * (shield_radius * 0.04F),
  158. emblem_plane - axis_y * (shield_radius * 0.22F),
  159. emblem_radius * 1.6F),
  160. metal_color, nullptr, 1.0F, 4);
  161. QVector3D const grip_a = shield_center - axis_x * 0.035F - n * 0.030F;
  162. QVector3D const grip_b = shield_center + axis_x * 0.035F - n * 0.030F;
  163. submitter.mesh(get_unit_cylinder(),
  164. cylinder_between(ctx.model, grip_a, grip_b, 0.010F),
  165. palette.leather, nullptr, 1.0F, 4);
  166. }
  167. } // namespace Render::GL