defense_tower_renderer.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #include "defense_tower_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 "../../../submitter.h"
  9. #include "../../registry.h"
  10. #include <QMatrix4x4>
  11. #include <QVector3D>
  12. #include <algorithm>
  13. namespace Render::GL::Roman {
  14. namespace {
  15. using Render::Geom::clamp_vec_01;
  16. using Render::Geom::cylinder_between;
  17. struct TowerPalette {
  18. QVector3D limestone{0.96F, 0.94F, 0.88F};
  19. QVector3D limestone_shade{0.88F, 0.85F, 0.78F};
  20. QVector3D limestone_dark{0.80F, 0.76F, 0.70F};
  21. QVector3D sandstone_light{0.82F, 0.75F, 0.62F};
  22. QVector3D sandstone_dark{0.70F, 0.62F, 0.50F};
  23. QVector3D sandstone_base{0.75F, 0.68F, 0.56F};
  24. QVector3D marble{0.98F, 0.97F, 0.95F};
  25. QVector3D terracotta{0.80F, 0.55F, 0.38F};
  26. QVector3D terracotta_dark{0.68F, 0.48F, 0.32F};
  27. QVector3D cedar{0.52F, 0.38F, 0.26F};
  28. QVector3D cedar_dark{0.38F, 0.26F, 0.16F};
  29. QVector3D blue_accent{0.28F, 0.48F, 0.68F};
  30. QVector3D blue_light{0.40F, 0.60F, 0.80F};
  31. QVector3D bronze{0.60F, 0.45F, 0.25F};
  32. QVector3D gold{0.85F, 0.72F, 0.35F};
  33. QVector3D team{0.8F, 0.9F, 1.0F};
  34. };
  35. inline auto make_palette(const QVector3D &team) -> TowerPalette {
  36. TowerPalette p;
  37. p.team = clamp_vec_01(team);
  38. return p;
  39. }
  40. inline void draw_box(ISubmitter &out, Mesh *unit, Texture *white,
  41. const QMatrix4x4 &model, const QVector3D &pos,
  42. const QVector3D &size, const QVector3D &color) {
  43. QMatrix4x4 m = model;
  44. m.translate(pos);
  45. m.scale(size);
  46. out.mesh(unit, m, color, white, 1.0F);
  47. }
  48. inline void draw_cyl(ISubmitter &out, const QMatrix4x4 &model,
  49. const QVector3D &a, const QVector3D &b, float r,
  50. const QVector3D &color, Texture *white) {
  51. out.mesh(get_unit_cylinder(), model * cylinder_between(a, b, r), color, white,
  52. 1.0F);
  53. }
  54. void draw_tower_base(const DrawContext &p, ISubmitter &out, Mesh *unit,
  55. Texture *white, const TowerPalette &c) {
  56. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.12F, 0.0F),
  57. QVector3D(1.1F, 0.12F, 1.1F), c.limestone_dark);
  58. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.26F, 0.0F),
  59. QVector3D(1.0F, 0.02F, 1.0F), c.limestone);
  60. for (float x = -0.85F; x <= 0.85F; x += 0.425F) {
  61. for (float z = -0.85F; z <= 0.85F; z += 0.425F) {
  62. if (fabsf(x) > 0.3F || fabsf(z) > 0.3F) {
  63. draw_box(out, unit, white, p.model, QVector3D(x, 0.29F, z),
  64. QVector3D(0.18F, 0.01F, 0.18F), c.terracotta);
  65. }
  66. }
  67. }
  68. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.42F, 0.0F),
  69. QVector3D(0.9F, 0.12F, 0.9F), c.sandstone_light);
  70. }
  71. void draw_tower_body(const DrawContext &p, ISubmitter &out, Mesh *unit,
  72. Texture *white, const TowerPalette &c) {
  73. draw_cyl(out, p.model, QVector3D(0.0F, 0.5F, 0.0F),
  74. QVector3D(0.0F, 2.2F, 0.0F), 0.55F, c.limestone, white);
  75. for (int i = 0; i < 4; ++i) {
  76. float const angle = static_cast<float>(i) * 1.57F + 0.785F;
  77. float const ox = sinf(angle) * 0.48F;
  78. float const oz = cosf(angle) * 0.48F;
  79. draw_cyl(out, p.model, QVector3D(ox, 0.5F, oz), QVector3D(ox, 1.9F, oz),
  80. 0.08F, c.marble, white);
  81. draw_box(out, unit, white, p.model, QVector3D(ox, 0.58F, oz),
  82. QVector3D(0.12F, 0.08F, 0.12F), c.marble);
  83. draw_box(out, unit, white, p.model, QVector3D(ox, 1.95F, oz),
  84. QVector3D(0.13F, 0.08F, 0.13F), c.marble);
  85. draw_box(out, unit, white, p.model, QVector3D(ox, 2.05F, oz),
  86. QVector3D(0.10F, 0.04F, 0.10F), c.gold);
  87. }
  88. for (int i = 0; i < 8; ++i) {
  89. float const angle = static_cast<float>(i) * 0.785F;
  90. float const ox = sinf(angle) * 0.45F;
  91. float const oz = cosf(angle) * 0.45F;
  92. draw_box(out, unit, white, p.model, QVector3D(ox, 1.2F, oz),
  93. QVector3D(0.06F, 0.25F, 0.06F), c.sandstone_dark);
  94. }
  95. }
  96. void draw_tower_platform(const DrawContext &p, ISubmitter &out, Mesh *unit,
  97. Texture *white, const TowerPalette &c) {
  98. draw_box(out, unit, white, p.model, QVector3D(0.0F, 2.28F, 0.0F),
  99. QVector3D(0.8F, 0.05F, 0.8F), c.cedar);
  100. for (int i = 0; i < 8; ++i) {
  101. float const angle = static_cast<float>(i) * 0.785F;
  102. float const ox = sinf(angle) * 0.7F;
  103. float const oz = cosf(angle) * 0.7F;
  104. draw_box(out, unit, white, p.model, QVector3D(ox, 2.45F, oz),
  105. QVector3D(0.14F, 0.17F, 0.14F), c.terracotta);
  106. }
  107. draw_box(out, unit, white, p.model, QVector3D(0.0F, 2.58F, 0.0F),
  108. QVector3D(0.85F, 0.04F, 0.85F), c.limestone);
  109. for (float x : {-0.75F, 0.75F}) {
  110. for (float z : {-0.75F, 0.75F}) {
  111. draw_box(out, unit, white, p.model, QVector3D(x, 2.64F, z),
  112. QVector3D(0.06F, 0.06F, 0.06F), c.blue_accent);
  113. }
  114. }
  115. }
  116. void draw_tower_top(const DrawContext &p, ISubmitter &out, Mesh *unit,
  117. Texture *white, const TowerPalette &c) {
  118. draw_cyl(out, p.model, QVector3D(0.0F, 2.25F, 0.0F),
  119. QVector3D(0.0F, 3.1F, 0.0F), 0.07F, c.cedar_dark, white);
  120. draw_box(out, unit, white, p.model, QVector3D(0.12F, 2.75F, 0.0F),
  121. QVector3D(0.22F, 0.15F, 0.025F), c.team);
  122. for (int i = 0; i < 2; ++i) {
  123. float ring_y = 2.65F + static_cast<float>(i) * 0.30F;
  124. out.mesh(get_unit_cylinder(),
  125. p.model * Render::Geom::cylinder_between(
  126. QVector3D(0.0F, ring_y, 0.0F),
  127. QVector3D(0.0F, ring_y + 0.025F, 0.0F), 0.11F),
  128. c.gold, white, 1.0F);
  129. }
  130. draw_box(out, unit, white, p.model, QVector3D(0.0F, 3.15F, 0.0F),
  131. QVector3D(0.08F, 0.06F, 0.08F), c.bronze);
  132. }
  133. void draw_health_bar(const DrawContext &p, ISubmitter &out, Mesh *unit,
  134. Texture *white) {
  135. if (p.entity == nullptr) {
  136. return;
  137. }
  138. auto *u = p.entity->get_component<Engine::Core::UnitComponent>();
  139. if (u == nullptr) {
  140. return;
  141. }
  142. float const ratio =
  143. std::clamp(u->health / float(std::max(1, u->max_health)), 0.0F, 1.0F);
  144. if (ratio <= 0.0F) {
  145. return;
  146. }
  147. QVector3D const bg(0.06F, 0.06F, 0.06F);
  148. draw_box(out, unit, white, p.model, QVector3D(0.0F, 3.2F, 0.0F),
  149. QVector3D(0.6F, 0.03F, 0.05F), bg);
  150. QVector3D const fg = QVector3D(0.22F, 0.78F, 0.22F) * ratio +
  151. QVector3D(0.85F, 0.15F, 0.15F) * (1.0F - ratio);
  152. draw_box(out, unit, white, p.model,
  153. QVector3D(-0.3F * (1.0F - ratio), 3.21F, 0.0F),
  154. QVector3D(0.3F * ratio, 0.025F, 0.045F), fg);
  155. }
  156. void draw_selection(const DrawContext &p, ISubmitter &out) {
  157. QMatrix4x4 m;
  158. QVector3D const pos = p.model.column(3).toVector3D();
  159. m.translate(pos.x(), 0.0F, pos.z());
  160. m.scale(1.6F, 1.0F, 1.6F);
  161. if (p.selected) {
  162. out.selection_smoke(m, QVector3D(0.2F, 0.85F, 0.2F), 0.35F);
  163. } else if (p.hovered) {
  164. out.selection_smoke(m, QVector3D(0.95F, 0.92F, 0.25F), 0.22F);
  165. }
  166. }
  167. void draw_defense_tower(const DrawContext &p, ISubmitter &out) {
  168. if (!p.resources || !p.entity) {
  169. return;
  170. }
  171. auto *t = p.entity->get_component<Engine::Core::TransformComponent>();
  172. auto *r = p.entity->get_component<Engine::Core::RenderableComponent>();
  173. if (!t || !r) {
  174. return;
  175. }
  176. Mesh *unit = p.resources->unit();
  177. Texture *white = p.resources->white();
  178. QVector3D const team(r->color[0], r->color[1], r->color[2]);
  179. TowerPalette const c = make_palette(team);
  180. draw_tower_base(p, out, unit, white, c);
  181. draw_tower_body(p, out, unit, white, c);
  182. draw_tower_platform(p, out, unit, white, c);
  183. draw_tower_top(p, out, unit, white, c);
  184. draw_health_bar(p, out, unit, white);
  185. draw_selection(p, out);
  186. }
  187. } // namespace
  188. void register_defense_tower_renderer(
  189. Render::GL::EntityRendererRegistry &registry) {
  190. registry.register_renderer("troops/roman/defense_tower", draw_defense_tower);
  191. }
  192. } // namespace Render::GL::Roman