catapult_renderer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. #include "catapult_renderer.h"
  2. #include "../../../../game/core/component.h"
  3. #include "../../../../game/visuals/team_colors.h"
  4. #include "../../../geom/math_utils.h"
  5. #include "../../../geom/transforms.h"
  6. #include "../../../gl/primitives.h"
  7. #include "../../../gl/resources.h"
  8. #include "../../../scene_renderer.h"
  9. #include "../../../submitter.h"
  10. #include "../../registry.h"
  11. #include <QMatrix4x4>
  12. #include <QVector3D>
  13. #include <cmath>
  14. namespace Render::GL::Roman {
  15. namespace {
  16. using Render::Geom::clamp01;
  17. using Render::Geom::clamp_vec_01;
  18. using Render::Geom::cylinder_between;
  19. struct RomanCatapultPalette {
  20. QVector3D wood_frame{0.45F, 0.32F, 0.18F};
  21. QVector3D wood_dark{0.32F, 0.22F, 0.12F};
  22. QVector3D wood_light{0.55F, 0.40F, 0.25F};
  23. QVector3D metal_iron{0.38F, 0.36F, 0.34F};
  24. QVector3D metal_bronze{0.72F, 0.52F, 0.30F};
  25. QVector3D rope{0.62F, 0.55F, 0.42F};
  26. QVector3D leather{0.42F, 0.30F, 0.20F};
  27. QVector3D stone{0.55F, 0.52F, 0.48F};
  28. QVector3D team{0.8F, 0.9F, 1.0F};
  29. };
  30. enum class CatapultAnimState { Idle, Loading, Firing, Resetting };
  31. struct CatapultAnimContext {
  32. CatapultAnimState state{CatapultAnimState::Idle};
  33. float loading_progress{0.0F};
  34. float firing_progress{0.0F};
  35. bool show_stone{false};
  36. };
  37. inline auto make_palette(const QVector3D &team) -> RomanCatapultPalette {
  38. RomanCatapultPalette p;
  39. p.team = clamp_vec_01(team);
  40. return p;
  41. }
  42. inline auto
  43. get_anim_context(const Engine::Core::Entity *entity) -> CatapultAnimContext {
  44. CatapultAnimContext ctx;
  45. if (entity == nullptr) {
  46. return ctx;
  47. }
  48. auto *loading =
  49. entity->get_component<Engine::Core::CatapultLoadingComponent>();
  50. if (loading == nullptr) {
  51. return ctx;
  52. }
  53. switch (loading->state) {
  54. case Engine::Core::CatapultLoadingComponent::LoadingState::Idle:
  55. ctx.state = CatapultAnimState::Idle;
  56. ctx.show_stone = false;
  57. break;
  58. case Engine::Core::CatapultLoadingComponent::LoadingState::Loading:
  59. ctx.state = CatapultAnimState::Loading;
  60. ctx.loading_progress = loading->get_loading_progress();
  61. ctx.show_stone = true;
  62. break;
  63. case Engine::Core::CatapultLoadingComponent::LoadingState::ReadyToFire:
  64. ctx.state = CatapultAnimState::Firing;
  65. ctx.loading_progress = 1.0F;
  66. ctx.firing_progress = 0.0F;
  67. ctx.show_stone = true;
  68. break;
  69. case Engine::Core::CatapultLoadingComponent::LoadingState::Firing:
  70. ctx.state = CatapultAnimState::Firing;
  71. ctx.firing_progress = loading->get_firing_progress();
  72. ctx.show_stone = ctx.firing_progress < 0.3F;
  73. break;
  74. }
  75. return ctx;
  76. }
  77. inline void draw_box(ISubmitter &out, Mesh *unit, Texture *white,
  78. const QMatrix4x4 &model, const QVector3D &pos,
  79. const QVector3D &size, const QVector3D &color) {
  80. QMatrix4x4 m = model;
  81. m.translate(pos);
  82. m.scale(size);
  83. out.mesh(unit, m, color, white, 1.0F);
  84. }
  85. inline void draw_cyl(ISubmitter &out, const QMatrix4x4 &model,
  86. const QVector3D &a, const QVector3D &b, float r,
  87. const QVector3D &color, Texture *white) {
  88. out.mesh(get_unit_cylinder(), model * cylinder_between(a, b, r), color, white,
  89. 1.0F);
  90. }
  91. void drawBaseFrame(const DrawContext &p, ISubmitter &out, Mesh *unit,
  92. Texture *white, const RomanCatapultPalette &c) {
  93. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.22F, -0.35F),
  94. QVector3D(0.52F, 0.06F, 0.06F), c.wood_dark);
  95. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.22F, 0.35F),
  96. QVector3D(0.52F, 0.06F, 0.06F), c.wood_dark);
  97. draw_box(out, unit, white, p.model, QVector3D(-0.42F, 0.22F, 0.0F),
  98. QVector3D(0.06F, 0.06F, 0.38F), c.wood_frame);
  99. draw_box(out, unit, white, p.model, QVector3D(0.42F, 0.22F, 0.0F),
  100. QVector3D(0.06F, 0.06F, 0.38F), c.wood_frame);
  101. draw_cyl(out, p.model, QVector3D(-0.38F, 0.20F, -0.30F),
  102. QVector3D(-0.38F, 0.20F, 0.30F), 0.025F, c.wood_dark, white);
  103. draw_cyl(out, p.model, QVector3D(0.38F, 0.20F, -0.30F),
  104. QVector3D(0.38F, 0.20F, 0.30F), 0.025F, c.wood_dark, white);
  105. }
  106. void draw_wheels(const DrawContext &p, ISubmitter &out, Mesh *unit,
  107. Texture *white, const RomanCatapultPalette &c) {
  108. float wheel_radius = 0.18F;
  109. float wheel_thickness = 0.04F;
  110. QVector3D left_front(-0.42F, wheel_radius, -0.25F);
  111. QVector3D left_back(-0.42F, wheel_radius, 0.25F);
  112. QVector3D right_front(0.42F, wheel_radius, -0.25F);
  113. QVector3D right_back(0.42F, wheel_radius, 0.25F);
  114. auto draw_wheel = [&](const QVector3D &pos, float side_offset) {
  115. QVector3D inner = pos + QVector3D(side_offset * wheel_thickness, 0, 0);
  116. QVector3D outer =
  117. pos + QVector3D(side_offset * (wheel_thickness + 0.06F), 0, 0);
  118. draw_cyl(out, p.model, inner, outer, wheel_radius, c.wood_dark, white);
  119. draw_cyl(out, p.model, inner - QVector3D(side_offset * 0.005F, 0, 0),
  120. outer + QVector3D(side_offset * 0.005F, 0, 0),
  121. wheel_radius + 0.015F, c.metal_iron, white);
  122. draw_cyl(out, p.model, inner - QVector3D(side_offset * 0.02F, 0, 0),
  123. outer + QVector3D(side_offset * 0.02F, 0, 0), 0.04F, c.metal_iron,
  124. white);
  125. for (int s = 0; s < 4; ++s) {
  126. float angle = s * 3.14159F / 2.0F;
  127. float spoke_y = std::sin(angle) * wheel_radius * 0.7F;
  128. float spoke_z = std::cos(angle) * wheel_radius * 0.7F;
  129. QVector3D spoke_pos =
  130. pos +
  131. QVector3D(side_offset * (wheel_thickness + 0.03F), spoke_y, spoke_z);
  132. draw_cyl(out, p.model,
  133. pos + QVector3D(side_offset * (wheel_thickness + 0.03F), 0, 0),
  134. spoke_pos, 0.015F, c.wood_frame, white);
  135. }
  136. };
  137. draw_wheel(left_front, -1.0F);
  138. draw_wheel(left_back, -1.0F);
  139. draw_wheel(right_front, 1.0F);
  140. draw_wheel(right_back, 1.0F);
  141. draw_cyl(out, p.model, QVector3D(-0.40F, wheel_radius, -0.25F),
  142. QVector3D(0.40F, wheel_radius, -0.25F), 0.025F, c.metal_iron, white);
  143. draw_cyl(out, p.model, QVector3D(-0.40F, wheel_radius, 0.25F),
  144. QVector3D(0.40F, wheel_radius, 0.25F), 0.025F, c.metal_iron, white);
  145. }
  146. void drawThrowingArm(const DrawContext &p, ISubmitter &out, Mesh *unit,
  147. Texture *white, const RomanCatapultPalette &c,
  148. const CatapultAnimContext &anim_ctx) {
  149. draw_cyl(out, p.model, QVector3D(-0.25F, 0.2F, 0.0F),
  150. QVector3D(-0.25F, 0.65F, 0.0F), 0.05F, c.wood_frame, white);
  151. draw_cyl(out, p.model, QVector3D(0.25F, 0.2F, 0.0F),
  152. QVector3D(0.25F, 0.65F, 0.0F), 0.05F, c.wood_frame, white);
  153. draw_cyl(out, p.model, QVector3D(-0.28F, 0.62F, 0.0F),
  154. QVector3D(0.28F, 0.62F, 0.0F), 0.04F, c.wood_dark, white);
  155. float arm_angle = 0.8F;
  156. switch (anim_ctx.state) {
  157. case CatapultAnimState::Idle:
  158. arm_angle = 0.8F;
  159. break;
  160. case CatapultAnimState::Loading:
  161. arm_angle = 0.8F + anim_ctx.loading_progress * 0.6F;
  162. break;
  163. case CatapultAnimState::Firing:
  164. arm_angle = 1.4F - anim_ctx.firing_progress * 2.0F;
  165. arm_angle = std::max(arm_angle, -0.3F);
  166. break;
  167. case CatapultAnimState::Resetting:
  168. arm_angle = 0.8F;
  169. break;
  170. }
  171. QMatrix4x4 armMatrix = p.model;
  172. armMatrix.translate(0.0F, 0.55F, 0.0F);
  173. armMatrix.rotate(arm_angle * 57.3F, 1.0F, 0.0F, 0.0F);
  174. draw_cyl(out, armMatrix, QVector3D(0.0F, 0.0F, -0.6F),
  175. QVector3D(0.0F, 0.0F, 0.4F), 0.045F, c.wood_frame, white);
  176. draw_box(out, unit, white, armMatrix, QVector3D(0.0F, -0.05F, -0.55F),
  177. QVector3D(0.08F, 0.06F, 0.10F), c.leather);
  178. if (anim_ctx.show_stone) {
  179. QMatrix4x4 stone_matrix = armMatrix;
  180. stone_matrix.translate(0.0F, 0.08F, -0.55F);
  181. float const stone_scale = 0.08F;
  182. stone_matrix.scale(stone_scale, stone_scale, stone_scale);
  183. out.mesh(get_unit_cube(), stone_matrix, c.stone, white, 1.0F);
  184. }
  185. }
  186. void drawTorsionMechanism(const DrawContext &p, ISubmitter &out, Mesh *unit,
  187. Texture *white, const RomanCatapultPalette &c) {
  188. draw_box(out, unit, white, p.model, QVector3D(-0.18F, 0.35F, 0.0F),
  189. QVector3D(0.04F, 0.18F, 0.15F), c.wood_dark);
  190. draw_box(out, unit, white, p.model, QVector3D(0.18F, 0.35F, 0.0F),
  191. QVector3D(0.04F, 0.18F, 0.15F), c.wood_dark);
  192. for (int i = 0; i < 3; ++i) {
  193. float offset = (float(i) - 1.0F) * 0.04F;
  194. draw_cyl(out, p.model, QVector3D(-0.12F, 0.25F + offset, -0.08F),
  195. QVector3D(-0.12F, 0.45F + offset, 0.08F), 0.025F, c.rope, white);
  196. draw_cyl(out, p.model, QVector3D(0.12F, 0.25F + offset, -0.08F),
  197. QVector3D(0.12F, 0.45F + offset, 0.08F), 0.025F, c.rope, white);
  198. }
  199. draw_cyl(out, p.model, QVector3D(-0.20F, 0.30F, 0.0F),
  200. QVector3D(-0.16F, 0.30F, 0.0F), 0.12F, c.metal_iron, white);
  201. draw_cyl(out, p.model, QVector3D(0.16F, 0.30F, 0.0F),
  202. QVector3D(0.20F, 0.30F, 0.0F), 0.12F, c.metal_iron, white);
  203. }
  204. void drawDecorations(const DrawContext &p, ISubmitter &out, Mesh *unit,
  205. Texture *white, const RomanCatapultPalette &c) {
  206. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.72F, -0.12F),
  207. QVector3D(0.04F, 0.06F, 0.02F), c.metal_bronze);
  208. draw_box(out, unit, white, p.model, QVector3D(-0.52F, 0.20F, -0.32F),
  209. QVector3D(0.04F, 0.04F, 0.04F), c.metal_iron);
  210. draw_box(out, unit, white, p.model, QVector3D(0.52F, 0.20F, -0.32F),
  211. QVector3D(0.04F, 0.04F, 0.04F), c.metal_iron);
  212. draw_box(out, unit, white, p.model, QVector3D(-0.52F, 0.20F, 0.32F),
  213. QVector3D(0.04F, 0.04F, 0.04F), c.metal_iron);
  214. draw_box(out, unit, white, p.model, QVector3D(0.52F, 0.20F, 0.32F),
  215. QVector3D(0.04F, 0.04F, 0.04F), c.metal_iron);
  216. }
  217. void drawWindlass(const DrawContext &p, ISubmitter &out, Mesh *unit,
  218. Texture *white, const RomanCatapultPalette &c) {
  219. draw_cyl(out, p.model, QVector3D(-0.20F, 0.22F, 0.30F),
  220. QVector3D(0.20F, 0.22F, 0.30F), 0.05F, c.wood_frame, white);
  221. draw_cyl(out, p.model, QVector3D(-0.25F, 0.22F, 0.30F),
  222. QVector3D(-0.25F, 0.32F, 0.30F), 0.02F, c.wood_dark, white);
  223. draw_cyl(out, p.model, QVector3D(0.25F, 0.22F, 0.30F),
  224. QVector3D(0.25F, 0.32F, 0.30F), 0.02F, c.wood_dark, white);
  225. draw_cyl(out, p.model, QVector3D(-0.15F, 0.22F, 0.30F),
  226. QVector3D(0.15F, 0.22F, 0.30F), 0.06F, c.rope, white);
  227. }
  228. } // namespace
  229. void register_catapult_renderer(EntityRendererRegistry &registry) {
  230. registry.register_renderer("troops/roman/catapult", [](const DrawContext &p,
  231. ISubmitter &out) {
  232. Mesh *unit_cube = get_unit_cube();
  233. Texture *white_tex = nullptr;
  234. if (auto *scene_renderer = dynamic_cast<Renderer *>(&out)) {
  235. unit_cube = scene_renderer->get_mesh_cube();
  236. white_tex = scene_renderer->get_white_texture();
  237. }
  238. if (unit_cube == nullptr || white_tex == nullptr) {
  239. return;
  240. }
  241. QVector3D team_color{0.8F, 0.2F, 0.2F};
  242. if (p.entity != nullptr) {
  243. if (auto *r =
  244. p.entity->get_component<Engine::Core::RenderableComponent>()) {
  245. team_color = QVector3D(r->color[0], r->color[1], r->color[2]);
  246. }
  247. }
  248. auto palette = make_palette(team_color);
  249. auto anim_ctx = get_anim_context(p.entity);
  250. drawBaseFrame(p, out, unit_cube, white_tex, palette);
  251. draw_wheels(p, out, unit_cube, white_tex, palette);
  252. drawTorsionMechanism(p, out, unit_cube, white_tex, palette);
  253. drawThrowingArm(p, out, unit_cube, white_tex, palette, anim_ctx);
  254. drawWindlass(p, out, unit_cube, white_tex, palette);
  255. drawDecorations(p, out, unit_cube, white_tex, palette);
  256. });
  257. }
  258. } // namespace Render::GL::Roman