defense_tower_renderer.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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::Carthage {
  14. namespace {
  15. using Render::Geom::clamp_vec_01;
  16. using Render::Geom::cylinder_between;
  17. struct TowerPalette {
  18. QVector3D stone_light{0.62F, 0.60F, 0.58F};
  19. QVector3D stone_dark{0.50F, 0.48F, 0.46F};
  20. QVector3D stone_base{0.55F, 0.53F, 0.51F};
  21. QVector3D brick{0.75F, 0.52F, 0.42F};
  22. QVector3D brick_dark{0.62F, 0.42F, 0.32F};
  23. QVector3D tile_red{0.72F, 0.40F, 0.30F};
  24. QVector3D wood{0.42F, 0.28F, 0.16F};
  25. QVector3D wood_dark{0.32F, 0.20F, 0.10F};
  26. QVector3D iron{0.35F, 0.35F, 0.38F};
  27. QVector3D team{0.8F, 0.9F, 1.0F};
  28. };
  29. inline auto make_palette(const QVector3D &team) -> TowerPalette {
  30. TowerPalette p;
  31. p.team = clamp_vec_01(team);
  32. return p;
  33. }
  34. inline void draw_box(ISubmitter &out, Mesh *unit, Texture *white,
  35. const QMatrix4x4 &model, const QVector3D &pos,
  36. const QVector3D &size, const QVector3D &color) {
  37. QMatrix4x4 m = model;
  38. m.translate(pos);
  39. m.scale(size);
  40. out.mesh(unit, m, color, white, 1.0F);
  41. }
  42. inline void draw_cyl(ISubmitter &out, const QMatrix4x4 &model,
  43. const QVector3D &a, const QVector3D &b, float r,
  44. const QVector3D &color, Texture *white) {
  45. out.mesh(get_unit_cylinder(), model * cylinder_between(a, b, r), color, white,
  46. 1.0F);
  47. }
  48. void draw_tower_base(const DrawContext &p, ISubmitter &out, Mesh *unit,
  49. Texture *white, const TowerPalette &c) {
  50. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.15F, 0.0F),
  51. QVector3D(1.0F, 0.15F, 1.0F), c.stone_base);
  52. for (float x = -0.9F; x <= 0.9F; x += 0.45F) {
  53. draw_box(out, unit, white, p.model, QVector3D(x, 0.35F, -0.85F),
  54. QVector3D(0.12F, 0.08F, 0.08F), c.brick_dark);
  55. draw_box(out, unit, white, p.model, QVector3D(x, 0.35F, 0.85F),
  56. QVector3D(0.12F, 0.08F, 0.08F), c.brick_dark);
  57. }
  58. for (float z = -0.8F; z <= 0.8F; z += 0.4F) {
  59. draw_box(out, unit, white, p.model, QVector3D(-0.85F, 0.35F, z),
  60. QVector3D(0.08F, 0.08F, 0.12F), c.brick_dark);
  61. draw_box(out, unit, white, p.model, QVector3D(0.85F, 0.35F, z),
  62. QVector3D(0.08F, 0.08F, 0.12F), c.brick_dark);
  63. }
  64. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.5F, 0.0F),
  65. QVector3D(0.9F, 0.1F, 0.9F), c.stone_light);
  66. }
  67. void draw_tower_body(const DrawContext &p, ISubmitter &out, Mesh *unit,
  68. Texture *white, const TowerPalette &c) {
  69. draw_box(out, unit, white, p.model, QVector3D(0.0F, 1.2F, 0.0F),
  70. QVector3D(0.75F, 0.7F, 0.75F), c.stone_light);
  71. for (int i = 0; i < 4; ++i) {
  72. float const angle = static_cast<float>(i) * 1.57F;
  73. float const ox = sinf(angle) * 0.65F;
  74. float const oz = cosf(angle) * 0.65F;
  75. draw_cyl(out, p.model, QVector3D(ox, 0.5F, oz), QVector3D(ox, 1.9F, oz),
  76. 0.14F, c.stone_dark, white);
  77. }
  78. for (int i = 0; i < 4; ++i) {
  79. float const angle = static_cast<float>(i) * 1.57F + 0.785F;
  80. float const ox = sinf(angle) * 0.62F;
  81. float const oz = cosf(angle) * 0.62F;
  82. draw_box(out, unit, white, p.model, QVector3D(ox, 0.9F, oz),
  83. QVector3D(0.12F, 0.4F, 0.12F), c.brick);
  84. }
  85. draw_box(out, unit, white, p.model, QVector3D(0.0F, 1.65F, 0.0F),
  86. QVector3D(0.82F, 0.08F, 0.82F), c.brick_dark);
  87. }
  88. void draw_tower_platform(const DrawContext &p, ISubmitter &out, Mesh *unit,
  89. Texture *white, const TowerPalette &c) {
  90. draw_box(out, unit, white, p.model, QVector3D(0.0F, 1.95F, 0.0F),
  91. QVector3D(0.95F, 0.05F, 0.95F), c.wood);
  92. for (int i = 0; i < 8; ++i) {
  93. float const angle = static_cast<float>(i) * 0.785F;
  94. float const ox = sinf(angle) * 0.82F;
  95. float const oz = cosf(angle) * 0.82F;
  96. draw_box(out, unit, white, p.model, QVector3D(ox, 2.12F, oz),
  97. QVector3D(0.12F, 0.17F, 0.12F), c.brick);
  98. }
  99. draw_box(out, unit, white, p.model, QVector3D(0.0F, 2.32F, 0.0F),
  100. QVector3D(1.0F, 0.03F, 1.0F), c.tile_red);
  101. }
  102. void draw_tower_top(const DrawContext &p, ISubmitter &out, Mesh *unit,
  103. Texture *white, const TowerPalette &c) {
  104. draw_cyl(out, p.model, QVector3D(0.0F, 2.05F, 0.0F),
  105. QVector3D(0.0F, 2.9F, 0.0F), 0.08F, c.wood_dark, white);
  106. draw_box(out, unit, white, p.model, QVector3D(0.15F, 2.6F, 0.0F),
  107. QVector3D(0.25F, 0.18F, 0.025F), c.team);
  108. for (int i = 0; i < 3; ++i) {
  109. float ring_y = 2.3F + static_cast<float>(i) * 0.25F;
  110. out.mesh(get_unit_cylinder(),
  111. p.model * Render::Geom::cylinder_between(
  112. QVector3D(0.0F, ring_y, 0.0F),
  113. QVector3D(0.0F, ring_y + 0.03F, 0.0F), 0.12F),
  114. c.iron, white, 1.0F);
  115. }
  116. draw_box(out, unit, white, p.model, QVector3D(0.0F, 2.95F, 0.0F),
  117. QVector3D(0.1F, 0.08F, 0.1F), c.iron);
  118. }
  119. void draw_health_bar(const DrawContext &p, ISubmitter &out, Mesh *unit,
  120. Texture *white) {
  121. if (p.entity == nullptr) {
  122. return;
  123. }
  124. auto *u = p.entity->get_component<Engine::Core::UnitComponent>();
  125. if (u == nullptr) {
  126. return;
  127. }
  128. float const ratio =
  129. std::clamp(u->health / float(std::max(1, u->max_health)), 0.0F, 1.0F);
  130. if (ratio <= 0.0F) {
  131. return;
  132. }
  133. QVector3D const bg(0.06F, 0.06F, 0.06F);
  134. draw_box(out, unit, white, p.model, QVector3D(0.0F, 3.2F, 0.0F),
  135. QVector3D(0.6F, 0.03F, 0.05F), bg);
  136. QVector3D const fg = QVector3D(0.22F, 0.78F, 0.22F) * ratio +
  137. QVector3D(0.85F, 0.15F, 0.15F) * (1.0F - ratio);
  138. draw_box(out, unit, white, p.model,
  139. QVector3D(-0.3F * (1.0F - ratio), 3.21F, 0.0F),
  140. QVector3D(0.3F * ratio, 0.025F, 0.045F), fg);
  141. }
  142. void draw_selection(const DrawContext &p, ISubmitter &out) {
  143. QMatrix4x4 m;
  144. QVector3D const pos = p.model.column(3).toVector3D();
  145. m.translate(pos.x(), 0.0F, pos.z());
  146. m.scale(1.6F, 1.0F, 1.6F);
  147. if (p.selected) {
  148. out.selection_smoke(m, QVector3D(0.2F, 0.85F, 0.2F), 0.35F);
  149. } else if (p.hovered) {
  150. out.selection_smoke(m, QVector3D(0.95F, 0.92F, 0.25F), 0.22F);
  151. }
  152. }
  153. void draw_defense_tower(const DrawContext &p, ISubmitter &out) {
  154. if (!p.resources || !p.entity) {
  155. return;
  156. }
  157. auto *t = p.entity->get_component<Engine::Core::TransformComponent>();
  158. auto *r = p.entity->get_component<Engine::Core::RenderableComponent>();
  159. if (!t || !r) {
  160. return;
  161. }
  162. Mesh *unit = p.resources->unit();
  163. Texture *white = p.resources->white();
  164. QVector3D const team(r->color[0], r->color[1], r->color[2]);
  165. TowerPalette const c = make_palette(team);
  166. draw_tower_base(p, out, unit, white, c);
  167. draw_tower_body(p, out, unit, white, c);
  168. draw_tower_platform(p, out, unit, white, c);
  169. draw_tower_top(p, out, unit, white, c);
  170. draw_health_bar(p, out, unit, white);
  171. draw_selection(p, out);
  172. }
  173. } // namespace
  174. void register_defense_tower_renderer(
  175. Render::GL::EntityRendererRegistry &registry) {
  176. registry.register_renderer("troops/carthage/defense_tower",
  177. draw_defense_tower);
  178. }
  179. } // namespace Render::GL::Carthage