home_renderer.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. #include "home_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 "../../building_state.h"
  10. #include "../../registry.h"
  11. #include <QMatrix4x4>
  12. #include <QVector3D>
  13. #include <algorithm>
  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 RomanPalette {
  20. QVector3D limestone{0.96F, 0.94F, 0.88F};
  21. QVector3D limestone_shade{0.88F, 0.85F, 0.78F};
  22. QVector3D limestone_dark{0.80F, 0.76F, 0.70F};
  23. QVector3D marble{0.98F, 0.97F, 0.95F};
  24. QVector3D cedar{0.52F, 0.38F, 0.26F};
  25. QVector3D cedar_dark{0.38F, 0.26F, 0.16F};
  26. QVector3D terracotta{0.82F, 0.62F, 0.45F};
  27. QVector3D terracotta_dark{0.68F, 0.48F, 0.32F};
  28. QVector3D blue_accent{0.28F, 0.48F, 0.68F};
  29. QVector3D blue_light{0.40F, 0.60F, 0.80F};
  30. QVector3D team{0.8F, 0.9F, 1.0F};
  31. QVector3D team_trim{0.48F, 0.54F, 0.60F};
  32. };
  33. inline auto make_palette(const QVector3D &team) -> RomanPalette {
  34. RomanPalette p;
  35. p.team = clamp_vec_01(team);
  36. p.team_trim = clamp_vec_01(
  37. QVector3D(team.x() * 0.6F, team.y() * 0.6F, team.z() * 0.6F));
  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_home_platform(const DrawContext &p, ISubmitter &out, Mesh *unit,
  55. Texture *white, const RomanPalette &c) {
  56. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.06F, 0.0F),
  57. QVector3D(1.1F, 0.06F, 1.1F), c.limestone_dark);
  58. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.14F, 0.0F),
  59. QVector3D(1.0F, 0.02F, 1.0F), c.limestone);
  60. }
  61. void draw_home_walls(const DrawContext &p, ISubmitter &out, Mesh *unit,
  62. Texture *white, const RomanPalette &c,
  63. BuildingState state) {
  64. float const wall_height = 0.9F;
  65. float height_multiplier = 1.0F;
  66. if (state == BuildingState::Damaged) {
  67. height_multiplier = 0.7F;
  68. } else if (state == BuildingState::Destroyed) {
  69. height_multiplier = 0.4F;
  70. }
  71. draw_box(
  72. out, unit, white, p.model,
  73. QVector3D(0.0F, wall_height * 0.5F * height_multiplier + 0.16F, -0.85F),
  74. QVector3D(0.8F, wall_height * 0.5F * height_multiplier, 0.08F),
  75. c.limestone);
  76. draw_box(
  77. out, unit, white, p.model,
  78. QVector3D(0.0F, wall_height * 0.5F * height_multiplier + 0.16F, 0.85F),
  79. QVector3D(0.8F, wall_height * 0.5F * height_multiplier, 0.08F),
  80. c.limestone);
  81. draw_box(
  82. out, unit, white, p.model,
  83. QVector3D(-0.85F, wall_height * 0.5F * height_multiplier + 0.16F, 0.0F),
  84. QVector3D(0.08F, wall_height * 0.5F * height_multiplier, 0.75F),
  85. c.limestone);
  86. draw_box(
  87. out, unit, white, p.model,
  88. QVector3D(0.85F, wall_height * 0.5F * height_multiplier + 0.16F, 0.0F),
  89. QVector3D(0.08F, wall_height * 0.5F * height_multiplier, 0.75F),
  90. c.limestone);
  91. }
  92. void draw_home_columns(const DrawContext &p, ISubmitter &out, Mesh *unit,
  93. Texture *white, const RomanPalette &c,
  94. BuildingState state) {
  95. float const col_height = 0.8F;
  96. float const col_radius = 0.06F;
  97. float height_multiplier = 1.0F;
  98. if (state == BuildingState::Damaged) {
  99. height_multiplier = 0.7F;
  100. } else if (state == BuildingState::Destroyed) {
  101. height_multiplier = 0.4F;
  102. }
  103. QVector3D columns[4] = {
  104. QVector3D(-0.7F, 0.0F, 0.88F), QVector3D(0.7F, 0.0F, 0.88F),
  105. QVector3D(-0.7F, 0.0F, -0.88F), QVector3D(0.7F, 0.0F, -0.88F)};
  106. for (int i = 0; i < 4; ++i) {
  107. draw_box(out, unit, white, p.model,
  108. QVector3D(columns[i].x(), 0.18F, columns[i].z()),
  109. QVector3D(col_radius * 1.2F, 0.04F, col_radius * 1.2F), c.marble);
  110. draw_cyl(out, p.model, QVector3D(columns[i].x(), 0.16F, columns[i].z()),
  111. QVector3D(columns[i].x(), 0.16F + col_height * height_multiplier,
  112. columns[i].z()),
  113. col_radius, c.limestone_shade, white);
  114. if (state != BuildingState::Destroyed) {
  115. draw_box(out, unit, white, p.model,
  116. QVector3D(columns[i].x(),
  117. 0.16F + col_height * height_multiplier + 0.04F,
  118. columns[i].z()),
  119. QVector3D(col_radius * 1.4F, 0.06F, col_radius * 1.4F),
  120. c.marble);
  121. }
  122. }
  123. }
  124. void draw_home_roof(const DrawContext &p, ISubmitter &out, Mesh *unit,
  125. Texture *white, const RomanPalette &c,
  126. BuildingState state) {
  127. if (state == BuildingState::Destroyed) {
  128. return;
  129. }
  130. draw_box(out, unit, white, p.model, QVector3D(0.0F, 1.25F, 0.0F),
  131. QVector3D(1.05F, 0.06F, 1.05F), c.terracotta);
  132. draw_box(out, unit, white, p.model, QVector3D(0.0F, 1.3F, 0.0F),
  133. QVector3D(1.0F, 0.04F, 1.0F), c.terracotta_dark);
  134. }
  135. void draw_home_door(const DrawContext &p, ISubmitter &out, Mesh *unit,
  136. Texture *white, const RomanPalette &c) {
  137. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.45F, 0.9F),
  138. QVector3D(0.3F, 0.4F, 0.05F), c.cedar_dark);
  139. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.62F, 0.92F),
  140. QVector3D(0.32F, 0.04F, 0.02F), c.blue_accent);
  141. }
  142. void draw_health_bar(const DrawContext &p, ISubmitter &out, Mesh *unit,
  143. Texture *white) {
  144. if (p.entity == nullptr) {
  145. return;
  146. }
  147. auto *u = p.entity->get_component<Engine::Core::UnitComponent>();
  148. if (u == nullptr) {
  149. return;
  150. }
  151. float const ratio =
  152. std::clamp(u->health / float(std::max(1, u->max_health)), 0.0F, 1.0F);
  153. if (ratio <= 0.0F) {
  154. return;
  155. }
  156. auto *capture = p.entity->get_component<Engine::Core::CaptureComponent>();
  157. bool under_attack = (capture != nullptr && capture->is_being_captured);
  158. if (!under_attack && u->health >= u->max_health) {
  159. return;
  160. }
  161. float const bar_width = 1.0F;
  162. float const bar_height = 0.08F;
  163. float const bar_y = 1.6F;
  164. float const border_thickness = 0.012F;
  165. if (under_attack) {
  166. float pulse = HEALTHBAR_PULSE_MIN +
  167. HEALTHBAR_PULSE_AMPLITUDE *
  168. sinf(p.animation_time * HEALTHBAR_PULSE_SPEED);
  169. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y, 0.0F),
  170. QVector3D(bar_width * 0.5F + border_thickness * 3.0F,
  171. bar_height * 0.5F + border_thickness * 3.0F, 0.095F),
  172. HealthBarColors::GLOW_ATTACK * pulse * 0.6F);
  173. }
  174. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y, 0.0F),
  175. QVector3D(bar_width * 0.5F + border_thickness,
  176. bar_height * 0.5F + border_thickness, 0.09F),
  177. HealthBarColors::BORDER);
  178. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y, 0.0F),
  179. QVector3D(bar_width * 0.5F + border_thickness * 0.5F,
  180. bar_height * 0.5F + border_thickness * 0.5F, 0.088F),
  181. HealthBarColors::INNER_BORDER);
  182. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y + 0.003F, 0.0F),
  183. QVector3D(bar_width * 0.5F, bar_height * 0.5F, 0.085F),
  184. HealthBarColors::BACKGROUND);
  185. QVector3D fg_color;
  186. QVector3D fg_dark;
  187. if (ratio >= HEALTH_THRESHOLD_NORMAL) {
  188. fg_color = HealthBarColors::NORMAL_BRIGHT;
  189. fg_dark = HealthBarColors::NORMAL_DARK;
  190. } else if (ratio >= HEALTH_THRESHOLD_DAMAGED) {
  191. float t = (ratio - HEALTH_THRESHOLD_DAMAGED) /
  192. (HEALTH_THRESHOLD_NORMAL - HEALTH_THRESHOLD_DAMAGED);
  193. fg_color = HealthBarColors::NORMAL_BRIGHT * t +
  194. HealthBarColors::DAMAGED_BRIGHT * (1.0F - t);
  195. fg_dark = HealthBarColors::NORMAL_DARK * t +
  196. HealthBarColors::DAMAGED_DARK * (1.0F - t);
  197. } else {
  198. float t = ratio / HEALTH_THRESHOLD_DAMAGED;
  199. fg_color = HealthBarColors::DAMAGED_BRIGHT * t +
  200. HealthBarColors::CRITICAL_BRIGHT * (1.0F - t);
  201. fg_dark = HealthBarColors::DAMAGED_DARK * t +
  202. HealthBarColors::CRITICAL_DARK * (1.0F - t);
  203. }
  204. draw_box(
  205. out, unit, white, p.model,
  206. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F, bar_y + 0.005F, 0.0F),
  207. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.48F, 0.08F), fg_dark);
  208. draw_box(
  209. out, unit, white, p.model,
  210. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F, bar_y + 0.008F, 0.0F),
  211. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.40F, 0.078F),
  212. fg_color);
  213. QVector3D const highlight = fg_color * 1.6F;
  214. draw_box(out, unit, white, p.model,
  215. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F,
  216. bar_y + bar_height * 0.35F, 0.0F),
  217. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.20F, 0.075F),
  218. clamp_vec_01(highlight));
  219. draw_box(out, unit, white, p.model,
  220. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F,
  221. bar_y + bar_height * 0.48F, 0.0F),
  222. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.08F, 0.073F),
  223. HealthBarColors::SHINE * 0.8F);
  224. float marker_70_x = bar_width * 0.5F * (HEALTH_THRESHOLD_NORMAL - 0.5F);
  225. draw_box(out, unit, white, p.model, QVector3D(marker_70_x, bar_y, 0.0F),
  226. QVector3D(0.015F, bar_height * 0.55F, 0.09F),
  227. HealthBarColors::SEGMENT);
  228. float marker_30_x = bar_width * 0.5F * (HEALTH_THRESHOLD_DAMAGED - 0.5F);
  229. draw_box(out, unit, white, p.model, QVector3D(marker_30_x, bar_y, 0.0F),
  230. QVector3D(0.015F, bar_height * 0.55F, 0.09F),
  231. HealthBarColors::SEGMENT);
  232. }
  233. void draw_selection(const DrawContext &p, ISubmitter &out) {
  234. QMatrix4x4 m;
  235. QVector3D const pos = p.model.column(3).toVector3D();
  236. m.translate(pos.x(), 0.0F, pos.z());
  237. m.scale(1.5F, 1.0F, 1.5F);
  238. if (p.selected) {
  239. out.selection_smoke(m, QVector3D(0.2F, 0.85F, 0.2F), 0.35F);
  240. } else if (p.hovered) {
  241. out.selection_smoke(m, QVector3D(0.95F, 0.92F, 0.25F), 0.22F);
  242. }
  243. }
  244. void draw_home(const DrawContext &p, ISubmitter &out) {
  245. if (!p.resources || !p.entity) {
  246. return;
  247. }
  248. auto *t = p.entity->get_component<Engine::Core::TransformComponent>();
  249. auto *r = p.entity->get_component<Engine::Core::RenderableComponent>();
  250. auto *u = p.entity->get_component<Engine::Core::UnitComponent>();
  251. if (!t || !r) {
  252. return;
  253. }
  254. BuildingState state = BuildingState::Normal;
  255. if (u != nullptr) {
  256. float const health_ratio =
  257. std::clamp(u->health / float(std::max(1, u->max_health)), 0.0F, 1.0F);
  258. state = get_building_state(health_ratio);
  259. }
  260. Mesh *unit = p.resources->unit();
  261. Texture *white = p.resources->white();
  262. QVector3D const team(r->color[0], r->color[1], r->color[2]);
  263. RomanPalette const c = make_palette(team);
  264. draw_home_platform(p, out, unit, white, c);
  265. draw_home_walls(p, out, unit, white, c, state);
  266. draw_home_columns(p, out, unit, white, c, state);
  267. draw_home_roof(p, out, unit, white, c, state);
  268. draw_home_door(p, out, unit, white, c);
  269. draw_health_bar(p, out, unit, white);
  270. draw_selection(p, out);
  271. }
  272. } // namespace
  273. void register_home_renderer(Render::GL::EntityRendererRegistry &registry) {
  274. registry.register_renderer("troops/roman/home", draw_home);
  275. }
  276. } // namespace Render::GL::Roman