barracks_renderer.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. #include "barracks_renderer.h"
  2. #include "../../../../game/core/component.h"
  3. #include "../../../../game/visuals/team_colors.h"
  4. #include "../../../geom/flag.h"
  5. #include "../../../geom/math_utils.h"
  6. #include "../../../geom/transforms.h"
  7. #include "../../../gl/backend.h"
  8. #include "../../../gl/primitives.h"
  9. #include "../../../gl/resources.h"
  10. #include "../../../submitter.h"
  11. #include "../../barracks_flag_renderer.h"
  12. #include "../../building_state.h"
  13. #include "../../registry.h"
  14. #include <QMatrix4x4>
  15. #include <QVector3D>
  16. #include <algorithm>
  17. #include <cmath>
  18. namespace Render::GL::Carthage {
  19. namespace {
  20. using Render::Geom::clamp01;
  21. using Render::Geom::clamp_vec_01;
  22. using Render::Geom::cylinder_between;
  23. struct CarthagePalette {
  24. QVector3D stone_light{0.62F, 0.60F, 0.58F};
  25. QVector3D stone_dark{0.50F, 0.48F, 0.46F};
  26. QVector3D stone_base{0.55F, 0.53F, 0.51F};
  27. QVector3D brick{0.75F, 0.52F, 0.42F};
  28. QVector3D brick_dark{0.62F, 0.42F, 0.32F};
  29. QVector3D tile_red{0.72F, 0.40F, 0.30F};
  30. QVector3D tile_dark{0.58F, 0.30F, 0.22F};
  31. QVector3D wood{0.42F, 0.28F, 0.16F};
  32. QVector3D wood_dark{0.32F, 0.20F, 0.10F};
  33. QVector3D iron{0.35F, 0.35F, 0.38F};
  34. QVector3D team{0.8F, 0.9F, 1.0F};
  35. QVector3D team_trim{0.48F, 0.54F, 0.60F};
  36. };
  37. inline auto make_palette(const QVector3D &team) -> CarthagePalette {
  38. CarthagePalette p;
  39. p.team = clamp_vec_01(team);
  40. p.team_trim = clamp_vec_01(
  41. QVector3D(team.x() * 0.6F, team.y() * 0.6F, team.z() * 0.6F));
  42. return p;
  43. }
  44. inline void draw_box(ISubmitter &out, Mesh *unit, Texture *white,
  45. const QMatrix4x4 &model, const QVector3D &pos,
  46. const QVector3D &size, const QVector3D &color) {
  47. QMatrix4x4 m = model;
  48. m.translate(pos);
  49. m.scale(size);
  50. out.mesh(unit, m, color, white, 1.0F);
  51. }
  52. inline void draw_cyl(ISubmitter &out, const QMatrix4x4 &model,
  53. const QVector3D &a, const QVector3D &b, float r,
  54. const QVector3D &color, Texture *white) {
  55. out.mesh(get_unit_cylinder(), model * cylinder_between(a, b, r), color, white,
  56. 1.0F);
  57. }
  58. void drawFortressBase(const DrawContext &p, ISubmitter &out, Mesh *unit,
  59. Texture *white, const CarthagePalette &c) {
  60. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.15F, 0.0F),
  61. QVector3D(1.8F, 0.15F, 1.5F), c.stone_base);
  62. for (float x = -1.6F; x <= 1.6F; x += 0.4F) {
  63. draw_box(out, unit, white, p.model, QVector3D(x, 0.35F, -1.4F),
  64. QVector3D(0.18F, 0.08F, 0.08F), c.stone_dark);
  65. draw_box(out, unit, white, p.model, QVector3D(x, 0.35F, 1.4F),
  66. QVector3D(0.18F, 0.08F, 0.08F), c.stone_dark);
  67. }
  68. for (float z = -1.3F; z <= 1.3F; z += 0.4F) {
  69. draw_box(out, unit, white, p.model, QVector3D(-1.7F, 0.35F, z),
  70. QVector3D(0.08F, 0.08F, 0.18F), c.stone_dark);
  71. draw_box(out, unit, white, p.model, QVector3D(1.7F, 0.35F, z),
  72. QVector3D(0.08F, 0.08F, 0.18F), c.stone_dark);
  73. }
  74. }
  75. void drawFortressWalls(const DrawContext &p, ISubmitter &out, Mesh *unit,
  76. Texture *white, const CarthagePalette &c,
  77. BuildingState state) {
  78. float const wall_height = 1.2F;
  79. float height_multiplier = 1.0F;
  80. if (state == BuildingState::Damaged) {
  81. height_multiplier = 0.7F;
  82. } else if (state == BuildingState::Destroyed) {
  83. height_multiplier = 0.4F;
  84. }
  85. draw_box(
  86. out, unit, white, p.model,
  87. QVector3D(0.0F, wall_height * 0.5F * height_multiplier + 0.3F, -1.3F),
  88. QVector3D(1.5F, wall_height * 0.5F * height_multiplier, 0.12F),
  89. c.stone_light);
  90. draw_box(out, unit, white, p.model,
  91. QVector3D(0.0F, wall_height * 0.5F * height_multiplier + 0.3F, 1.3F),
  92. QVector3D(1.5F, wall_height * 0.5F * height_multiplier, 0.12F),
  93. c.stone_light);
  94. draw_box(
  95. out, unit, white, p.model,
  96. QVector3D(-1.6F, wall_height * 0.5F * height_multiplier + 0.3F, 0.0F),
  97. QVector3D(0.12F, wall_height * 0.5F * height_multiplier, 1.2F),
  98. c.stone_light);
  99. draw_box(out, unit, white, p.model,
  100. QVector3D(1.6F, wall_height * 0.5F * height_multiplier + 0.3F, 0.0F),
  101. QVector3D(0.12F, wall_height * 0.5F * height_multiplier, 1.2F),
  102. c.stone_light);
  103. if (state != BuildingState::Destroyed) {
  104. for (int i = 0; i < 6; ++i) {
  105. float const x = -1.2F + float(i) * 0.5F;
  106. draw_box(out, unit, white, p.model,
  107. QVector3D(x, wall_height * height_multiplier + 0.35F, -1.25F),
  108. QVector3D(0.2F, 0.05F, 0.05F), c.brick);
  109. }
  110. }
  111. }
  112. void drawCornerTowers(const DrawContext &p, ISubmitter &out, Mesh *unit,
  113. Texture *white, const CarthagePalette &c,
  114. BuildingState state) {
  115. QVector3D corners[4] = {
  116. QVector3D(-1.5F, 0.0F, -1.2F), QVector3D(1.5F, 0.0F, -1.2F),
  117. QVector3D(-1.5F, 0.0F, 1.2F), QVector3D(1.5F, 0.0F, 1.2F)};
  118. float height_multiplier = 1.0F;
  119. if (state == BuildingState::Damaged) {
  120. height_multiplier = 0.7F;
  121. } else if (state == BuildingState::Destroyed) {
  122. height_multiplier = 0.3F;
  123. }
  124. for (int i = 0; i < 4; ++i) {
  125. draw_box(
  126. out, unit, white, p.model,
  127. QVector3D(corners[i].x(), 0.65F * height_multiplier, corners[i].z()),
  128. QVector3D(0.25F, 0.65F * height_multiplier, 0.25F), c.stone_dark);
  129. if (state != BuildingState::Destroyed) {
  130. draw_box(
  131. out, unit, white, p.model,
  132. QVector3D(corners[i].x(), 1.45F * height_multiplier, corners[i].z()),
  133. QVector3D(0.28F, 0.15F, 0.28F), c.brick_dark);
  134. for (int j = 0; j < 4; ++j) {
  135. float angle = float(j) * 1.57F;
  136. float ox = sinf(angle) * 0.18F;
  137. float oz = cosf(angle) * 0.18F;
  138. draw_box(out, unit, white, p.model,
  139. QVector3D(corners[i].x() + ox, 1.68F * height_multiplier,
  140. corners[i].z() + oz),
  141. QVector3D(0.06F, 0.08F, 0.06F), c.stone_light);
  142. }
  143. }
  144. }
  145. }
  146. void drawCourtyard(const DrawContext &p, ISubmitter &out, Mesh *unit,
  147. Texture *white, const CarthagePalette &c) {
  148. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.32F, 0.0F),
  149. QVector3D(1.2F, 0.02F, 0.9F), c.stone_base);
  150. draw_cyl(out, p.model, QVector3D(0.0F, 0.3F, 0.0F),
  151. QVector3D(0.0F, 0.95F, 0.0F), 0.08F, c.stone_light, white);
  152. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.65F, -0.85F),
  153. QVector3D(0.35F, 0.35F, 0.08F), c.brick);
  154. }
  155. void drawCarthageRoof(const DrawContext &p, ISubmitter &out, Mesh *unit,
  156. Texture *white, const CarthagePalette &c,
  157. BuildingState state) {
  158. if (state == BuildingState::Destroyed) {
  159. return;
  160. }
  161. draw_box(out, unit, white, p.model, QVector3D(0.0F, 1.58F, 0.0F),
  162. QVector3D(1.55F, 0.05F, 1.25F), c.tile_red);
  163. for (float z = -1.0F; z <= 1.0F; z += 0.3F) {
  164. draw_box(out, unit, white, p.model, QVector3D(0.0F, 1.62F, z),
  165. QVector3D(1.5F, 0.02F, 0.08F), c.tile_dark);
  166. }
  167. }
  168. void drawGate(const DrawContext &p, ISubmitter &out, Mesh *unit, Texture *white,
  169. const CarthagePalette &c) {
  170. draw_box(out, unit, white, p.model, QVector3D(0.0F, 0.6F, 1.35F),
  171. QVector3D(0.5F, 0.6F, 0.08F), c.wood_dark);
  172. for (int i = 0; i < 3; ++i) {
  173. float y = 0.3F + float(i) * 0.3F;
  174. draw_box(out, unit, white, p.model, QVector3D(0.0F, y, 1.37F),
  175. QVector3D(0.45F, 0.03F, 0.02F), c.iron);
  176. }
  177. }
  178. void drawStandards(const DrawContext &p, ISubmitter &out, Mesh *unit,
  179. Texture *white, const CarthagePalette &c,
  180. const BarracksFlagRenderer::ClothBannerResources *cloth) {
  181. float const pole_x = 2.0F;
  182. float const pole_z = -1.5F;
  183. float const pole_height = 2.6F;
  184. float const pole_radius = 0.045F;
  185. float const banner_width = 0.8F;
  186. float const banner_height = 0.5F;
  187. QVector3D const pole_center(pole_x, pole_height / 2.0F, pole_z);
  188. QVector3D const pole_size(pole_radius * 1.8F, pole_height / 2.0F,
  189. pole_radius * 1.8F);
  190. QMatrix4x4 pole_transform = p.model;
  191. pole_transform.translate(pole_center);
  192. pole_transform.scale(pole_size);
  193. out.mesh(unit, pole_transform, c.wood, white, 1.0F);
  194. float const beam_length = banner_width * 0.5F;
  195. float const max_lowering = pole_height * 0.85F;
  196. auto captureColors = BarracksFlagRenderer::get_capture_colors(
  197. p, c.team, c.team_trim, max_lowering);
  198. float beam_y =
  199. pole_height - banner_height * 0.2F - captureColors.loweringOffset;
  200. float flag_y =
  201. pole_height - banner_height / 2.0F - captureColors.loweringOffset;
  202. QVector3D const beam_start(pole_x + 0.02F, beam_y, pole_z);
  203. QVector3D const beam_end(pole_x + beam_length + 0.02F, beam_y, pole_z);
  204. out.mesh(get_unit_cylinder(),
  205. p.model * Render::Geom::cylinder_between(beam_start, beam_end,
  206. pole_radius * 0.35F),
  207. c.wood, white, 1.0F);
  208. QVector3D const connector_top(
  209. beam_end.x(), beam_end.y() - banner_height * 0.35F, beam_end.z());
  210. out.mesh(get_unit_cylinder(),
  211. p.model * Render::Geom::cylinder_between(beam_end, connector_top,
  212. pole_radius * 0.18F),
  213. c.stone_light, white, 1.0F);
  214. float const panel_x = beam_end.x() + (banner_width * 0.5F - beam_length);
  215. QVector3D banner_center(panel_x, flag_y, pole_z + 0.02F);
  216. BarracksFlagRenderer::drawBannerWithTassels(
  217. p, out, unit, white, banner_center, banner_width * 0.5F,
  218. banner_height * 0.5F, 0.02F, captureColors.teamColor,
  219. captureColors.teamTrimColor, cloth);
  220. draw_box(out, unit, white, p.model,
  221. QVector3D(pole_x, pole_height + 0.2F, pole_z),
  222. QVector3D(0.12F, 0.10F, 0.12F), c.iron);
  223. for (int i = 0; i < 3; ++i) {
  224. float ring_y = 0.5F + static_cast<float>(i) * 0.6F;
  225. out.mesh(get_unit_cylinder(),
  226. p.model * Render::Geom::cylinder_between(
  227. QVector3D(pole_x, ring_y, pole_z),
  228. QVector3D(pole_x, ring_y + 0.03F, pole_z),
  229. pole_radius * 2.2F),
  230. c.iron, white, 1.0F);
  231. }
  232. }
  233. void draw_rally_flag(const DrawContext &p, ISubmitter &out, Texture *white,
  234. const CarthagePalette &c) {
  235. BarracksFlagRenderer::FlagColors colors{.team = c.team,
  236. .teamTrim = c.team_trim,
  237. .timber = c.wood,
  238. .timberLight = c.stone_light,
  239. .woodDark = c.wood_dark};
  240. BarracksFlagRenderer::draw_rally_flag_if_any(p, out, white, colors);
  241. }
  242. void draw_health_bar(const DrawContext &p, ISubmitter &out, Mesh *unit,
  243. Texture *white) {
  244. if (p.entity == nullptr) {
  245. return;
  246. }
  247. auto *u = p.entity->get_component<Engine::Core::UnitComponent>();
  248. if (u == nullptr) {
  249. return;
  250. }
  251. float const ratio =
  252. std::clamp(u->health / float(std::max(1, u->max_health)), 0.0F, 1.0F);
  253. if (ratio <= 0.0F) {
  254. return;
  255. }
  256. auto *capture = p.entity->get_component<Engine::Core::CaptureComponent>();
  257. bool under_attack = (capture != nullptr && capture->is_being_captured);
  258. if (!under_attack && u->health >= u->max_health) {
  259. return;
  260. }
  261. float const bar_width = 1.4F;
  262. float const bar_height = 0.10F;
  263. float const bar_y = 2.45F;
  264. float const border_thickness = 0.012F;
  265. if (under_attack) {
  266. float pulse = HEALTHBAR_PULSE_MIN +
  267. HEALTHBAR_PULSE_AMPLITUDE *
  268. sinf(p.animation_time * HEALTHBAR_PULSE_SPEED);
  269. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y, 0.0F),
  270. QVector3D(bar_width * 0.5F + border_thickness * 3.0F,
  271. bar_height * 0.5F + border_thickness * 3.0F, 0.095F),
  272. HealthBarColors::GLOW_ATTACK * pulse * 0.6F);
  273. }
  274. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y, 0.0F),
  275. QVector3D(bar_width * 0.5F + border_thickness,
  276. bar_height * 0.5F + border_thickness, 0.09F),
  277. HealthBarColors::BORDER);
  278. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y, 0.0F),
  279. QVector3D(bar_width * 0.5F + border_thickness * 0.5F,
  280. bar_height * 0.5F + border_thickness * 0.5F, 0.088F),
  281. HealthBarColors::INNER_BORDER);
  282. draw_box(out, unit, white, p.model, QVector3D(0.0F, bar_y + 0.003F, 0.0F),
  283. QVector3D(bar_width * 0.5F, bar_height * 0.5F, 0.085F),
  284. HealthBarColors::BACKGROUND);
  285. QVector3D fg_color;
  286. QVector3D fg_dark;
  287. if (ratio >= HEALTH_THRESHOLD_NORMAL) {
  288. fg_color = HealthBarColors::NORMAL_BRIGHT;
  289. fg_dark = HealthBarColors::NORMAL_DARK;
  290. } else if (ratio >= HEALTH_THRESHOLD_DAMAGED) {
  291. float t = (ratio - HEALTH_THRESHOLD_DAMAGED) /
  292. (HEALTH_THRESHOLD_NORMAL - HEALTH_THRESHOLD_DAMAGED);
  293. fg_color = HealthBarColors::NORMAL_BRIGHT * t +
  294. HealthBarColors::DAMAGED_BRIGHT * (1.0F - t);
  295. fg_dark = HealthBarColors::NORMAL_DARK * t +
  296. HealthBarColors::DAMAGED_DARK * (1.0F - t);
  297. } else {
  298. float t = ratio / HEALTH_THRESHOLD_DAMAGED;
  299. fg_color = HealthBarColors::DAMAGED_BRIGHT * t +
  300. HealthBarColors::CRITICAL_BRIGHT * (1.0F - t);
  301. fg_dark = HealthBarColors::DAMAGED_DARK * t +
  302. HealthBarColors::CRITICAL_DARK * (1.0F - t);
  303. }
  304. draw_box(
  305. out, unit, white, p.model,
  306. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F, bar_y + 0.005F, 0.0F),
  307. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.48F, 0.08F), fg_dark);
  308. draw_box(
  309. out, unit, white, p.model,
  310. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F, bar_y + 0.008F, 0.0F),
  311. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.40F, 0.078F),
  312. fg_color);
  313. QVector3D const highlight = fg_color * 1.6F;
  314. draw_box(out, unit, white, p.model,
  315. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F,
  316. bar_y + bar_height * 0.35F, 0.0F),
  317. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.20F, 0.075F),
  318. clamp_vec_01(highlight));
  319. draw_box(out, unit, white, p.model,
  320. QVector3D(-(bar_width * (1.0F - ratio)) * 0.5F,
  321. bar_y + bar_height * 0.48F, 0.0F),
  322. QVector3D(bar_width * ratio * 0.5F, bar_height * 0.08F, 0.073F),
  323. HealthBarColors::SHINE * 0.8F);
  324. float marker_70_x = bar_width * 0.5F * (HEALTH_THRESHOLD_NORMAL - 0.5F);
  325. draw_box(out, unit, white, p.model, QVector3D(marker_70_x, bar_y, 0.0F),
  326. QVector3D(0.015F, bar_height * 0.55F, 0.09F),
  327. HealthBarColors::SEGMENT);
  328. draw_box(out, unit, white, p.model,
  329. QVector3D(marker_70_x - 0.003F, bar_y + bar_height * 0.40F, 0.0F),
  330. QVector3D(0.008F, bar_height * 0.15F, 0.091F),
  331. HealthBarColors::SEGMENT_HIGHLIGHT);
  332. float marker_30_x = bar_width * 0.5F * (HEALTH_THRESHOLD_DAMAGED - 0.5F);
  333. draw_box(out, unit, white, p.model, QVector3D(marker_30_x, bar_y, 0.0F),
  334. QVector3D(0.015F, bar_height * 0.55F, 0.09F),
  335. HealthBarColors::SEGMENT);
  336. draw_box(out, unit, white, p.model,
  337. QVector3D(marker_30_x - 0.003F, bar_y + bar_height * 0.40F, 0.0F),
  338. QVector3D(0.008F, bar_height * 0.15F, 0.091F),
  339. HealthBarColors::SEGMENT_HIGHLIGHT);
  340. }
  341. void draw_selection(const DrawContext &p, ISubmitter &out) {
  342. QMatrix4x4 m;
  343. QVector3D const pos = p.model.column(3).toVector3D();
  344. m.translate(pos.x(), 0.0F, pos.z());
  345. m.scale(2.4F, 1.0F, 2.0F);
  346. if (p.selected) {
  347. out.selection_smoke(m, QVector3D(0.2F, 0.85F, 0.2F), 0.35F);
  348. } else if (p.hovered) {
  349. out.selection_smoke(m, QVector3D(0.95F, 0.92F, 0.25F), 0.22F);
  350. }
  351. }
  352. void draw_barracks(const DrawContext &p, ISubmitter &out) {
  353. if (!p.resources || !p.entity) {
  354. return;
  355. }
  356. auto *t = p.entity->get_component<Engine::Core::TransformComponent>();
  357. auto *r = p.entity->get_component<Engine::Core::RenderableComponent>();
  358. auto *u = p.entity->get_component<Engine::Core::UnitComponent>();
  359. if (!t || !r) {
  360. return;
  361. }
  362. BuildingState state = BuildingState::Normal;
  363. if (u != nullptr) {
  364. float const health_ratio =
  365. std::clamp(u->health / float(std::max(1, u->max_health)), 0.0F, 1.0F);
  366. state = get_building_state(health_ratio);
  367. }
  368. Mesh *unit = p.resources->unit();
  369. Texture *white = p.resources->white();
  370. QVector3D const team(r->color[0], r->color[1], r->color[2]);
  371. CarthagePalette const c = make_palette(team);
  372. BarracksFlagRenderer::ClothBannerResources cloth;
  373. if (p.backend != nullptr) {
  374. cloth.clothMesh = p.backend->banner_mesh();
  375. cloth.bannerShader = p.backend->banner_shader();
  376. }
  377. drawFortressBase(p, out, unit, white, c);
  378. drawFortressWalls(p, out, unit, white, c, state);
  379. drawCornerTowers(p, out, unit, white, c, state);
  380. drawCourtyard(p, out, unit, white, c);
  381. drawCarthageRoof(p, out, unit, white, c, state);
  382. drawGate(p, out, unit, white, c);
  383. drawStandards(p, out, unit, white, c, &cloth);
  384. draw_rally_flag(p, out, white, c);
  385. draw_health_bar(p, out, unit, white);
  386. draw_selection(p, out);
  387. }
  388. } // namespace
  389. void register_barracks_renderer(Render::GL::EntityRendererRegistry &registry) {
  390. registry.register_renderer("barracks_carthage", draw_barracks);
  391. }
  392. } // namespace Render::GL::Carthage