barracks_renderer.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  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/primitives.h"
  8. #include "../gl/resources.h"
  9. #include "registry.h"
  10. #include <QMatrix4x4>
  11. #include <QVector3D>
  12. namespace Render::GL {
  13. namespace {
  14. using Render::Geom::clamp01;
  15. using Render::Geom::clampVec01;
  16. using Render::Geom::cylinderBetween;
  17. using Render::Geom::lerp;
  18. using Render::Geom::sphereAt;
  19. struct BuildingProportions {
  20. static constexpr float baseWidth = 2.4f;
  21. static constexpr float baseDepth = 2.0f;
  22. static constexpr float baseHeight = 1.8f;
  23. static constexpr float foundationHeight = 0.2f;
  24. static constexpr float wallThickness = 0.08f;
  25. static constexpr float beamThickness = 0.12f;
  26. static constexpr float cornerPostRadius = 0.08f;
  27. static constexpr float roofPitch = 0.8f;
  28. static constexpr float roofOverhang = 0.15f;
  29. static constexpr float thatchLayerHeight = 0.12f;
  30. static constexpr float annexWidth = 1.0f;
  31. static constexpr float annexDepth = 1.0f;
  32. static constexpr float annexHeight = 1.2f;
  33. static constexpr float annexRoofHeight = 0.5f;
  34. static constexpr float doorWidth = 0.5f;
  35. static constexpr float doorHeight = 0.8f;
  36. static constexpr float windowWidth = 0.4f;
  37. static constexpr float windowHeight = 0.5f;
  38. static constexpr float chimneyWidth = 0.25f;
  39. static constexpr float chimneyHeight = 1.0f;
  40. static constexpr float chimneyCapSize = 0.35f;
  41. static constexpr float bannerPoleHeight = 2.0f;
  42. static constexpr float bannerPoleRadius = 0.05f;
  43. static constexpr float bannerWidth = 0.5f;
  44. static constexpr float bannerHeight = 0.6f;
  45. };
  46. struct BarracksPalette {
  47. QVector3D plaster{0.92f, 0.88f, 0.78f};
  48. QVector3D plasterShade{0.78f, 0.74f, 0.64f};
  49. QVector3D timber{0.35f, 0.25f, 0.15f};
  50. QVector3D timberLight{0.50f, 0.38f, 0.22f};
  51. QVector3D woodDark{0.30f, 0.20f, 0.12f};
  52. QVector3D thatch{0.82f, 0.70f, 0.28f};
  53. QVector3D thatchDark{0.68f, 0.58f, 0.22f};
  54. QVector3D stone{0.55f, 0.54f, 0.52f};
  55. QVector3D stoneDark{0.42f, 0.41f, 0.39f};
  56. QVector3D door{0.28f, 0.20f, 0.12f};
  57. QVector3D window{0.35f, 0.42f, 0.48f};
  58. QVector3D path{0.62f, 0.60f, 0.54f};
  59. QVector3D crate{0.48f, 0.34f, 0.18f};
  60. QVector3D team{0.8f, 0.9f, 1.0f};
  61. QVector3D teamTrim{0.48f, 0.54f, 0.60f};
  62. };
  63. static inline BarracksPalette makePalette(const QVector3D &team) {
  64. BarracksPalette p;
  65. p.team = clampVec01(team);
  66. p.teamTrim =
  67. clampVec01(QVector3D(team.x() * 0.6f, team.y() * 0.6f, team.z() * 0.6f));
  68. return p;
  69. }
  70. static inline void drawCylinder(ISubmitter &out, const QMatrix4x4 &model,
  71. const QVector3D &a, const QVector3D &b,
  72. float radius, const QVector3D &color,
  73. Texture *white) {
  74. out.mesh(getUnitCylinder(), model * cylinderBetween(a, b, radius), color,
  75. white, 1.0f);
  76. }
  77. static inline void unitBox(ISubmitter &out, Mesh *unitMesh, Texture *white,
  78. const QMatrix4x4 &model, const QVector3D &t,
  79. const QVector3D &s, const QVector3D &color) {
  80. QMatrix4x4 M = model;
  81. M.translate(t);
  82. M.scale(s);
  83. out.mesh(unitMesh, M, color, white, 1.0f);
  84. }
  85. static inline void unitBox(ISubmitter &out, Mesh *unitMesh, Texture *white,
  86. const QMatrix4x4 &model, const QVector3D &s,
  87. const QVector3D &color) {
  88. QMatrix4x4 M = model;
  89. M.scale(s);
  90. out.mesh(unitMesh, M, color, white, 1.0f);
  91. }
  92. static inline void drawFoundation(const DrawContext &p, ISubmitter &out,
  93. Mesh *unit, Texture *white,
  94. const BarracksPalette &C) {
  95. constexpr float baseWidth = BuildingProportions::baseWidth;
  96. constexpr float baseDepth = BuildingProportions::baseDepth;
  97. constexpr float foundationHeight = BuildingProportions::foundationHeight;
  98. unitBox(out, unit, white, p.model,
  99. QVector3D(0.0f, -foundationHeight / 2, 0.0f),
  100. QVector3D(baseWidth / 2 + 0.1f, foundationHeight / 2,
  101. baseDepth / 2 + 0.1f),
  102. C.stoneDark);
  103. const float stepH = 0.015f;
  104. const float stepW = 0.16f;
  105. const float stepD = 0.10f;
  106. const float frontZ = baseDepth * 0.5f + 0.12f;
  107. for (int i = 0; i < 5; ++i) {
  108. float t = i / 4.0f;
  109. float x = (i % 2 == 0) ? -0.18f : 0.18f;
  110. QVector3D c = lerp(C.path, C.stone, 0.25f * (i % 2));
  111. unitBox(out, unit, white, p.model,
  112. QVector3D(x, -foundationHeight + stepH, frontZ + t * 0.55f),
  113. QVector3D(stepW * (0.95f + 0.1f * (i % 2)), stepH, stepD), c);
  114. }
  115. QVector3D skirtColor = lerp(C.stoneDark, QVector3D(0.0f, 0.0f, 0.0f), 0.25f);
  116. unitBox(out, unit, white, p.model, QVector3D(0.0f, 0.02f, 0.0f),
  117. QVector3D(baseWidth * 0.50f, 0.01f, baseDepth * 0.50f), skirtColor);
  118. }
  119. static inline void drawWalls(const DrawContext &p, ISubmitter &out, Mesh *,
  120. Texture *white, const BarracksPalette &C) {
  121. constexpr float W = BuildingProportions::baseWidth;
  122. constexpr float D = BuildingProportions::baseDepth;
  123. constexpr float H = BuildingProportions::baseHeight;
  124. const float r = 0.09f;
  125. const float notch = 0.07f;
  126. const float leftX = -W * 0.5f;
  127. const float rightX = W * 0.5f;
  128. const float backZ = -D * 0.5f;
  129. const float frontZ = D * 0.5f;
  130. const int courses = std::max(4, int(H / (2.0f * r)));
  131. const float y0 = r;
  132. auto logX = [&](float y, float z, float x0, float x1, const QVector3D &col) {
  133. drawCylinder(out, p.model, QVector3D(x0 - notch, y, z),
  134. QVector3D(x1 + notch, y, z), r, col, white);
  135. };
  136. auto logZ = [&](float y, float x, float z0, float z1, const QVector3D &col) {
  137. drawCylinder(out, p.model, QVector3D(x, y, z0 - notch),
  138. QVector3D(x, y, z1 + notch), r, col, white);
  139. };
  140. const float doorW = BuildingProportions::doorWidth;
  141. const float doorH = BuildingProportions::doorHeight;
  142. const float gapHalf = doorW * 0.5f;
  143. for (int i = 0; i < courses; ++i) {
  144. float y = y0 + i * (2.0f * r);
  145. QVector3D logCol = lerp(C.timber, C.timberLight, (i % 2) * 0.25f);
  146. if (y <= (doorH - 0.5f * r)) {
  147. logX(y, frontZ, leftX, -gapHalf, logCol);
  148. logX(y, frontZ, +gapHalf, rightX, logCol);
  149. } else {
  150. logX(y, frontZ, leftX, rightX, logCol);
  151. }
  152. logX(y, backZ, leftX, rightX, logCol);
  153. logZ(y, leftX, backZ, frontZ, logCol);
  154. logZ(y, rightX, backZ, frontZ, logCol);
  155. }
  156. QVector3D postCol = C.woodDark;
  157. drawCylinder(out, p.model, QVector3D(-gapHalf, y0, frontZ),
  158. QVector3D(-gapHalf, y0 + doorH, frontZ), r * 0.95f, postCol,
  159. white);
  160. drawCylinder(out, p.model, QVector3D(+gapHalf, y0, frontZ),
  161. QVector3D(+gapHalf, y0 + doorH, frontZ), r * 0.95f, postCol,
  162. white);
  163. drawCylinder(out, p.model, QVector3D(-gapHalf, y0 + doorH, frontZ),
  164. QVector3D(+gapHalf, y0 + doorH, frontZ), r, C.timberLight,
  165. white);
  166. float braceY0 = H * 0.35f;
  167. float braceY1 = H * 0.95f;
  168. drawCylinder(out, p.model, QVector3D(leftX + 0.08f, braceY0, backZ + 0.10f),
  169. QVector3D(leftX + 0.38f, braceY1, backZ + 0.10f), r * 0.6f,
  170. C.woodDark, white);
  171. drawCylinder(out, p.model, QVector3D(rightX - 0.08f, braceY0, backZ + 0.10f),
  172. QVector3D(rightX - 0.38f, braceY1, backZ + 0.10f), r * 0.6f,
  173. C.woodDark, white);
  174. drawCylinder(out, p.model, QVector3D(leftX + 0.08f, braceY0, frontZ - 0.10f),
  175. QVector3D(leftX + 0.38f, braceY1, frontZ - 0.10f), r * 0.6f,
  176. C.woodDark, white);
  177. drawCylinder(out, p.model, QVector3D(rightX - 0.08f, braceY0, frontZ - 0.10f),
  178. QVector3D(rightX - 0.38f, braceY1, frontZ - 0.10f), r * 0.6f,
  179. C.woodDark, white);
  180. }
  181. struct ChimneyInfo {
  182. float x;
  183. float z;
  184. float baseY;
  185. float topY;
  186. float gapRadius;
  187. };
  188. static inline ChimneyInfo drawChimney(const DrawContext &p, ISubmitter &out,
  189. Mesh *unit, Texture *white,
  190. const BarracksPalette &C) {
  191. constexpr float W = BuildingProportions::baseWidth;
  192. constexpr float D = BuildingProportions::baseDepth;
  193. constexpr float H = BuildingProportions::baseHeight;
  194. constexpr float rise = BuildingProportions::roofPitch;
  195. float x = -W * 0.32f;
  196. float z = -D * 0.5f - 0.06f;
  197. float baseY = 0.18f;
  198. float ridgeY = H + rise;
  199. float topY = ridgeY + 0.35f;
  200. QVector3D baseSz(BuildingProportions::chimneyWidth * 0.65f, 0.16f,
  201. BuildingProportions::chimneyWidth * 0.55f);
  202. unitBox(out, unit, white, p.model, QVector3D(x, baseY + baseSz.y(), z),
  203. baseSz, C.stoneDark);
  204. int segments = 4;
  205. float segH = (topY - (baseY + baseSz.y() * 2.0f)) / float(segments);
  206. float w0 = BuildingProportions::chimneyWidth * 0.55f;
  207. float w1 = BuildingProportions::chimneyWidth * 0.34f;
  208. for (int i = 0; i < segments; ++i) {
  209. float t = float(i) / float(segments - 1);
  210. float wy = w0 * (1.0f - t) + w1 * t;
  211. float hz = wy * 0.85f;
  212. QVector3D col = (i % 2 == 0) ? C.stone : lerp(C.stone, C.stoneDark, 0.35f);
  213. float yMid = baseY + baseSz.y() * 2.0f + segH * (i + 0.5f);
  214. unitBox(out, unit, white, p.model, QVector3D(x, yMid, z),
  215. QVector3D(wy, segH * 0.5f, hz), col);
  216. }
  217. float corbelY = topY - 0.14f;
  218. unitBox(out, unit, white, p.model, QVector3D(x, corbelY, z),
  219. QVector3D(w1 * 1.22f, 0.025f, w1 * 1.22f), C.stoneDark);
  220. unitBox(out, unit, white, p.model, QVector3D(x, corbelY + 0.05f, z),
  221. QVector3D(w1 * 1.05f, 0.02f, w1 * 1.05f),
  222. lerp(C.stone, C.stoneDark, 0.2f));
  223. float potH = 0.10f;
  224. unitBox(out, unit, white, p.model, QVector3D(x, topY + potH * 0.5f, z),
  225. QVector3D(w1 * 0.45f, potH * 0.5f, w1 * 0.45f),
  226. lerp(C.stoneDark, QVector3D(0.08f, 0.08f, 0.08f), 0.35f));
  227. unitBox(out, unit, white, p.model, QVector3D(x, H + rise * 0.55f, z + 0.06f),
  228. QVector3D(w1 * 1.35f, 0.01f, 0.04f),
  229. lerp(C.stoneDark, QVector3D(0.05f, 0.05f, 0.05f), 0.3f));
  230. return ChimneyInfo{x, z, baseY, topY + potH, 0.28f};
  231. }
  232. static inline void drawRoofs(const DrawContext &p, ISubmitter &out, Mesh *,
  233. Texture *white, const BarracksPalette &C,
  234. const ChimneyInfo &ch) {
  235. constexpr float W = BuildingProportions::baseWidth;
  236. constexpr float D = BuildingProportions::baseDepth;
  237. constexpr float H = BuildingProportions::baseHeight;
  238. constexpr float rise = BuildingProportions::roofPitch;
  239. constexpr float over = BuildingProportions::roofOverhang;
  240. const float r = 0.085f;
  241. const float leftX = -W * 0.5f;
  242. const float rightX = W * 0.5f;
  243. const float backZ = -D * 0.5f;
  244. const float frontZ = D * 0.5f;
  245. const float plateY = H;
  246. const float ridgeY = H + rise;
  247. drawCylinder(out, p.model, QVector3D(leftX - over, plateY, frontZ + over),
  248. QVector3D(rightX + over, plateY, frontZ + over), r, C.woodDark,
  249. white);
  250. drawCylinder(out, p.model, QVector3D(leftX - over, plateY, backZ - over),
  251. QVector3D(rightX + over, plateY, backZ - over), r, C.woodDark,
  252. white);
  253. drawCylinder(out, p.model, QVector3D(leftX - over * 0.5f, ridgeY, 0.0f),
  254. QVector3D(rightX + over * 0.5f, ridgeY, 0.0f), r, C.timberLight,
  255. white);
  256. const int pairs = 7;
  257. for (int i = 0; i < pairs; ++i) {
  258. float t = (pairs == 1) ? 0.0f : (float(i) / float(pairs - 1));
  259. float x = (leftX - over * 0.5f) * (1.0f - t) + (rightX + over * 0.5f) * t;
  260. drawCylinder(out, p.model, QVector3D(x, plateY, backZ - over),
  261. QVector3D(x, ridgeY, 0.0f), r * 0.85f, C.woodDark, white);
  262. drawCylinder(out, p.model, QVector3D(x, plateY, frontZ + over),
  263. QVector3D(x, ridgeY, 0.0f), r * 0.85f, C.woodDark, white);
  264. }
  265. auto purlin = [&](float tz, bool front) {
  266. float z = front ? (frontZ + over - tz * (frontZ + over))
  267. : (backZ - over - tz * (backZ - over));
  268. float y = plateY + tz * (ridgeY - plateY);
  269. drawCylinder(out, p.model, QVector3D(leftX - over * 0.4f, y, z),
  270. QVector3D(rightX + over * 0.4f, y, z), r * 0.6f, C.timber,
  271. white);
  272. };
  273. purlin(0.35f, true);
  274. purlin(0.70f, true);
  275. purlin(0.35f, false);
  276. purlin(0.70f, false);
  277. auto splitThatch = [&](float y, float z, float rad, const QVector3D &col) {
  278. float gapL = ch.x - ch.gapRadius;
  279. float gapR = ch.x + ch.gapRadius;
  280. drawCylinder(out, p.model, QVector3D(leftX - over * 0.35f, y, z),
  281. QVector3D(gapL, y, z), rad, col, white);
  282. drawCylinder(out, p.model, QVector3D(gapR, y, z),
  283. QVector3D(rightX + over * 0.35f, y, z), rad, col, white);
  284. };
  285. auto thatchRow = [&](float tz, bool front, float radScale, float tint) {
  286. float z = front ? (frontZ + over - tz * (frontZ + over))
  287. : (backZ - over - tz * (backZ - over));
  288. float y = plateY + tz * (ridgeY - plateY);
  289. QVector3D col = lerp(C.thatchDark, C.thatch, clamp01(tint));
  290. splitThatch(y, z, r * radScale, col);
  291. };
  292. const int rows = 9;
  293. for (int i = 0; i < rows; ++i) {
  294. float tz = float(i) / float(rows - 1);
  295. float s = 1.30f - 0.6f * tz;
  296. float tint = 0.2f + 0.6f * (1.0f - tz);
  297. thatchRow(tz, true, s, tint);
  298. thatchRow(tz * 0.98f, false, s, tint * 0.95f);
  299. }
  300. float eaveY = plateY + 0.06f;
  301. splitThatch(eaveY, frontZ + over * 1.02f, r * 0.55f, C.thatchDark);
  302. splitThatch(eaveY, backZ - over * 1.02f, r * 0.55f, C.thatchDark);
  303. float flashY = plateY + (ridgeY - plateY) * 0.55f;
  304. float flashZBack = backZ - over * 0.20f;
  305. float ring = ch.gapRadius + 0.04f;
  306. unitBox(out, nullptr, white, p.model, QVector3D(ch.x, flashY, flashZBack),
  307. QVector3D(ring, 0.008f, 0.02f), C.stoneDark);
  308. }
  309. static inline void drawDoor(const DrawContext &p, ISubmitter &out, Mesh *unit,
  310. Texture *white, const BarracksPalette &C) {
  311. constexpr float D = BuildingProportions::baseDepth;
  312. constexpr float dW = BuildingProportions::doorWidth;
  313. constexpr float dH = BuildingProportions::doorHeight;
  314. const float y0 = 0.09f;
  315. const float zf = D * 0.5f;
  316. QVector3D frameCol = C.woodDark;
  317. unitBox(out, unit, white, p.model,
  318. QVector3D(0.0f, y0 + dH * 0.5f, zf + 0.015f),
  319. QVector3D(dW * 0.5f, dH * 0.5f, 0.02f), C.door);
  320. float plankW = dW / 6.0f;
  321. for (int i = 0; i < 6; ++i) {
  322. float cx = -dW * 0.5f + plankW * (i + 0.5f);
  323. QVector3D plankCol = lerp(C.door, C.woodDark, 0.15f * (i % 2));
  324. unitBox(out, unit, white, p.model,
  325. QVector3D(cx, y0 + dH * 0.5f, zf + 0.022f),
  326. QVector3D(plankW * 0.48f, dH * 0.48f, 0.006f), plankCol);
  327. }
  328. drawCylinder(out, p.model,
  329. QVector3D(-dW * 0.45f, y0 + dH * 0.35f, zf + 0.03f),
  330. QVector3D(+dW * 0.45f, y0 + dH * 0.35f, zf + 0.03f), 0.02f,
  331. frameCol, white);
  332. drawCylinder(out, p.model, QVector3D(dW * 0.32f, y0 + dH * 0.45f, zf + 0.04f),
  333. QVector3D(dW * 0.42f, y0 + dH * 0.45f, zf + 0.04f), 0.012f,
  334. C.timberLight, white);
  335. unitBox(out, unit, white, p.model,
  336. QVector3D(0.0f, y0 + dH + 0.10f, zf + 0.02f),
  337. QVector3D(0.22f, 0.06f, 0.01f), C.woodDark);
  338. unitBox(out, unit, white, p.model,
  339. QVector3D(0.0f, y0 + dH + 0.10f, zf + 0.025f),
  340. QVector3D(0.18f, 0.05f, 0.008f), C.team);
  341. unitBox(out, unit, white, p.model,
  342. QVector3D(0.0f, y0 + dH + 0.10f, zf + 0.03f),
  343. QVector3D(0.08f, 0.02f, 0.007f), C.teamTrim);
  344. }
  345. static inline void drawWindows(const DrawContext &p, ISubmitter &out,
  346. Mesh *unit, Texture *white,
  347. const BarracksPalette &C) {
  348. constexpr float W = BuildingProportions::baseWidth;
  349. constexpr float D = BuildingProportions::baseDepth;
  350. constexpr float H = BuildingProportions::baseHeight;
  351. const float leftX = -W * 0.5f;
  352. const float rightX = W * 0.5f;
  353. const float backZ = -D * 0.5f;
  354. const float frontZ = D * 0.5f;
  355. float w = BuildingProportions::windowWidth * 0.55f;
  356. float h = BuildingProportions::windowHeight * 0.55f;
  357. float frameT = 0.03f;
  358. auto framedWindow = [&](QVector3D center, bool shutters) {
  359. unitBox(out, unit, white, p.model, center + QVector3D(0, 0, 0.012f),
  360. QVector3D(w * 0.5f, h * 0.5f, 0.008f), C.window);
  361. unitBox(out, unit, white, p.model, center + QVector3D(0, 0, 0.016f),
  362. QVector3D(w * 0.5f, frameT, 0.006f), C.timber);
  363. unitBox(out, unit, white, p.model, center + QVector3D(0, 0, 0.016f),
  364. QVector3D(frameT, h * 0.5f, 0.006f), C.timber);
  365. unitBox(out, unit, white, p.model, center + QVector3D(0, 0, 0.016f),
  366. QVector3D(w * 0.5f, frameT, 0.006f), C.timber);
  367. unitBox(out, unit, white, p.model, center + QVector3D(0, 0, 0.016f),
  368. QVector3D(frameT, h * 0.5f, 0.006f), C.timber);
  369. unitBox(out, unit, white, p.model, center + QVector3D(0, 0, 0.02f),
  370. QVector3D(w * 0.02f, h * 0.48f, 0.004f), C.timberLight);
  371. unitBox(out, unit, white, p.model, center + QVector3D(0, 0, 0.02f),
  372. QVector3D(w * 0.48f, h * 0.02f, 0.004f), C.timberLight);
  373. if (shutters) {
  374. unitBox(out, unit, white, p.model,
  375. center + QVector3D(-w * 0.65f, 0, 0.018f),
  376. QVector3D(w * 0.30f, h * 0.55f, 0.004f), C.woodDark);
  377. unitBox(out, unit, white, p.model,
  378. center + QVector3D(+w * 0.65f, 0, 0.018f),
  379. QVector3D(w * 0.30f, h * 0.55f, 0.004f), C.woodDark);
  380. }
  381. };
  382. framedWindow(QVector3D(-0.65f, 0.95f, frontZ + 0.01f), true);
  383. framedWindow(QVector3D(+0.65f, 0.95f, frontZ + 0.01f), true);
  384. framedWindow(QVector3D(leftX + 0.06f, 0.85f, 0.0f), false);
  385. framedWindow(QVector3D(rightX - 0.06f, 0.85f, 0.0f), false);
  386. framedWindow(QVector3D(0.0f, 1.00f, backZ - 0.01f), true);
  387. }
  388. static inline void drawAnnex(const DrawContext &p, ISubmitter &out, Mesh *unit,
  389. Texture *white, const BarracksPalette &C) {
  390. constexpr float W = BuildingProportions::baseWidth;
  391. constexpr float D = BuildingProportions::baseDepth;
  392. constexpr float h = BuildingProportions::annexHeight;
  393. constexpr float w = BuildingProportions::annexWidth;
  394. constexpr float d = BuildingProportions::annexDepth;
  395. float x = W * 0.5f + w * 0.5f - 0.05f;
  396. float z = 0.05f;
  397. unitBox(out, unit, white, p.model, QVector3D(x, h * 0.5f, z),
  398. QVector3D(w * 0.5f, h * 0.5f, d * 0.5f), C.plasterShade);
  399. unitBox(out, unit, white, p.model, QVector3D(x, h + 0.02f, z),
  400. QVector3D(w * 0.55f, 0.02f, d * 0.55f), C.woodDark);
  401. float plateY = h;
  402. float frontZ = z + d * 0.5f;
  403. float backZ = z - d * 0.5f;
  404. drawCylinder(out, p.model, QVector3D(x - w * 0.52f, plateY, backZ - 0.12f),
  405. QVector3D(x + w * 0.52f, plateY, backZ - 0.12f), 0.05f,
  406. C.woodDark, white);
  407. float ridgeY = h + BuildingProportions::annexRoofHeight;
  408. drawCylinder(out, p.model, QVector3D(x - w * 0.50f, ridgeY, backZ - 0.02f),
  409. QVector3D(x + w * 0.50f, ridgeY, backZ - 0.02f), 0.05f,
  410. C.timberLight, white);
  411. int rows = 6;
  412. for (int i = 0; i < rows; ++i) {
  413. float t = float(i) / float(rows - 1);
  414. float y = plateY + t * (ridgeY - plateY);
  415. float zrow = backZ - 0.02f - 0.10f * (1.0f - t);
  416. QVector3D col = lerp(C.thatchDark, C.thatch, 0.5f + 0.4f * (1.0f - t));
  417. drawCylinder(out, p.model, QVector3D(x - w * 0.55f, y, zrow),
  418. QVector3D(x + w * 0.55f, y, zrow), 0.06f * (1.15f - 0.6f * t),
  419. col, white);
  420. }
  421. unitBox(out, unit, white, p.model,
  422. QVector3D(x + w * 0.01f, 0.55f, frontZ + 0.01f),
  423. QVector3D(0.20f, 0.18f, 0.01f), C.door);
  424. }
  425. static inline void drawProps(const DrawContext &p, ISubmitter &out, Mesh *unit,
  426. Texture *white, const BarracksPalette &C) {
  427. unitBox(out, unit, white, p.model, QVector3D(0.85f, 0.10f, 0.90f),
  428. QVector3D(0.16f, 0.10f, 0.16f), C.crate);
  429. unitBox(out, unit, white, p.model, QVector3D(0.85f, 0.22f, 0.90f),
  430. QVector3D(0.12f, 0.02f, 0.12f), C.woodDark);
  431. unitBox(out, unit, white, p.model, QVector3D(-0.9f, 0.12f, -0.80f),
  432. QVector3D(0.12f, 0.10f, 0.12f), C.crate);
  433. unitBox(out, unit, white, p.model, QVector3D(-0.9f, 0.20f, -0.80f),
  434. QVector3D(0.13f, 0.02f, 0.13f), C.woodDark);
  435. }
  436. static inline void drawBannerAndPole(const DrawContext &p, ISubmitter &out,
  437. Mesh *unit, Texture *white,
  438. const BarracksPalette &C) {
  439. constexpr float baseWidth = BuildingProportions::baseWidth;
  440. constexpr float baseDepth = BuildingProportions::baseDepth;
  441. constexpr float bannerPoleHeight = BuildingProportions::bannerPoleHeight;
  442. constexpr float bannerPoleRadius = BuildingProportions::bannerPoleRadius;
  443. constexpr float bannerWidth = BuildingProportions::bannerWidth;
  444. constexpr float bannerHeight = BuildingProportions::bannerHeight;
  445. float poleX = -baseWidth / 2 - 0.65f;
  446. float poleZ = baseDepth / 2 - 0.2f;
  447. float poleHeight = bannerPoleHeight * 1.9f;
  448. float poleRadius = bannerPoleRadius * 1.3f;
  449. float bw = bannerWidth * 1.8f;
  450. float bh = bannerHeight * 1.8f;
  451. QVector3D poleCenter(poleX, poleHeight / 2.0f, poleZ);
  452. QVector3D poleSize(poleRadius * 1.6f, poleHeight / 2.0f, poleRadius * 1.6f);
  453. unitBox(out, unit, white, p.model, poleCenter, poleSize, C.woodDark);
  454. float targetWidth = bw * 1.25f;
  455. float targetHeight = bh * 0.75f;
  456. float panelDepth = 0.02f;
  457. float beamLength = targetWidth * 0.45f;
  458. float beamY = poleHeight - targetHeight * 0.25f;
  459. QVector3D teamColor = C.team;
  460. QVector3D teamTrimColor = C.teamTrim;
  461. float flagY = poleHeight - targetHeight / 2.0f;
  462. if (p.entity) {
  463. auto *capture = p.entity->getComponent<Engine::Core::CaptureComponent>();
  464. if (capture && capture->isBeingCaptured) {
  465. float progress = std::clamp(
  466. capture->captureProgress / capture->requiredTime, 0.0f, 1.0f);
  467. QVector3D newTeamColor =
  468. Game::Visuals::teamColorForOwner(capture->capturingPlayerId);
  469. teamColor = lerp(C.team, clampVec01(newTeamColor), progress);
  470. teamTrimColor = lerp(
  471. C.teamTrim,
  472. clampVec01(QVector3D(newTeamColor.x() * 0.6f, newTeamColor.y() * 0.6f,
  473. newTeamColor.z() * 0.6f)),
  474. progress);
  475. float loweredAmount = progress * poleHeight * 0.85f;
  476. flagY -= loweredAmount;
  477. beamY -= loweredAmount;
  478. }
  479. }
  480. QVector3D beamStart(poleX + 0.02f, beamY, poleZ);
  481. QVector3D beamEnd(poleX + beamLength + 0.02f, beamY, poleZ);
  482. drawCylinder(out, p.model, beamStart, beamEnd, poleRadius * 0.35f, C.timber,
  483. white);
  484. QVector3D connectorTop(beamEnd.x(), beamEnd.y() - targetHeight * 0.35f,
  485. beamEnd.z());
  486. drawCylinder(out, p.model, beamEnd, connectorTop, poleRadius * 0.18f,
  487. C.timberLight, white);
  488. float panelX = beamEnd.x() + (targetWidth * 0.5f - beamLength);
  489. unitBox(out, unit, white, p.model, QVector3D(panelX, flagY, poleZ + 0.01f),
  490. QVector3D(targetWidth / 2.0f, targetHeight / 2.0f, panelDepth),
  491. teamColor);
  492. unitBox(out, unit, white, p.model,
  493. QVector3D(panelX, flagY - targetHeight / 2.0f + 0.04f, poleZ + 0.01f),
  494. QVector3D(targetWidth / 2.0f + 0.02f, 0.04f, 0.015f), teamTrimColor);
  495. unitBox(out, unit, white, p.model,
  496. QVector3D(panelX, flagY + targetHeight / 2.0f - 0.04f, poleZ + 0.01f),
  497. QVector3D(targetWidth / 2.0f + 0.02f, 0.04f, 0.015f), teamTrimColor);
  498. }
  499. static inline void drawRallyFlagIfAny(const DrawContext &p, ISubmitter &out,
  500. Texture *white,
  501. const BarracksPalette &C) {
  502. if (auto *prod =
  503. p.entity->getComponent<Engine::Core::ProductionComponent>()) {
  504. if (prod->rallySet && p.resources) {
  505. auto flag = Render::Geom::Flag::create(prod->rallyX, prod->rallyZ,
  506. QVector3D(1.0f, 0.9f, 0.2f),
  507. C.woodDark, 1.0f);
  508. Mesh *unit = p.resources->unit();
  509. out.mesh(unit, flag.pole, flag.poleColor, white, 1.0f);
  510. out.mesh(unit, flag.pennant, flag.pennantColor, white, 1.0f);
  511. out.mesh(unit, flag.finial, flag.pennantColor, white, 1.0f);
  512. }
  513. }
  514. }
  515. static inline void drawHealthBar(const DrawContext &p, ISubmitter &out,
  516. Mesh *unit, Texture *white) {
  517. if (!p.entity) {
  518. return;
  519. }
  520. auto *u = p.entity->getComponent<Engine::Core::UnitComponent>();
  521. if (!u) {
  522. return;
  523. }
  524. int mh = std::max(1, u->maxHealth);
  525. float ratio = std::clamp(u->health / float(mh), 0.0f, 1.0f);
  526. if (ratio <= 0.0f) {
  527. return;
  528. }
  529. constexpr float baseHeight = BuildingProportions::baseHeight;
  530. constexpr float roofPitch = BuildingProportions::roofPitch;
  531. float roofPeak = baseHeight + roofPitch;
  532. float barY = roofPeak + 0.12f;
  533. constexpr float barWidth = BuildingProportions::baseWidth * 0.9f;
  534. constexpr float barHeight = 0.08f;
  535. constexpr float barDepth = 0.12f;
  536. QVector3D bgColor(0.06f, 0.06f, 0.06f);
  537. unitBox(out, unit, white, p.model, QVector3D(0.0f, barY, 0.0f),
  538. QVector3D(barWidth / 2.0f, barHeight / 2.0f, barDepth / 2.0f),
  539. bgColor);
  540. float fillWidth = barWidth * ratio;
  541. float fillX = -(barWidth - fillWidth) * 0.5f;
  542. QVector3D red(0.85f, 0.15f, 0.15f);
  543. QVector3D green(0.22f, 0.78f, 0.22f);
  544. QVector3D fgColor = green * ratio + red * (1.0f - ratio);
  545. unitBox(out, unit, white, p.model, QVector3D(fillX, barY + 0.005f, 0.0f),
  546. QVector3D(fillWidth / 2.0f, (barHeight / 2.0f) * 0.9f,
  547. (barDepth / 2.0f) * 0.95f),
  548. fgColor);
  549. }
  550. static inline void drawSelectionFX(const DrawContext &p, ISubmitter &out) {
  551. QMatrix4x4 M;
  552. QVector3D pos = p.model.column(3).toVector3D();
  553. M.translate(pos.x(), 0.0f, pos.z());
  554. M.scale(2.2f, 1.0f, 2.0f);
  555. if (p.selected) {
  556. out.selectionSmoke(M, QVector3D(0.2f, 0.85f, 0.2f), 0.35f);
  557. } else if (p.hovered) {
  558. out.selectionSmoke(M, QVector3D(0.95f, 0.92f, 0.25f), 0.22f);
  559. }
  560. }
  561. static void drawBarracks(const DrawContext &p, ISubmitter &out) {
  562. if (!p.resources || !p.entity) {
  563. return;
  564. }
  565. auto *t = p.entity->getComponent<Engine::Core::TransformComponent>();
  566. auto *r = p.entity->getComponent<Engine::Core::RenderableComponent>();
  567. if (!t || !r) {
  568. return;
  569. }
  570. Mesh *unit = p.resources->unit();
  571. Texture *white = p.resources->white();
  572. QVector3D team(r->color[0], r->color[1], r->color[2]);
  573. BarracksPalette C = makePalette(team);
  574. drawFoundation(p, out, unit, white, C);
  575. drawAnnex(p, out, unit, white, C);
  576. drawWalls(p, out, unit, white, C);
  577. ChimneyInfo ch = drawChimney(p, out, unit, white, C);
  578. drawRoofs(p, out, unit, white, C, ch);
  579. drawDoor(p, out, unit, white, C);
  580. drawWindows(p, out, unit, white, C);
  581. drawBannerAndPole(p, out, unit, white, C);
  582. drawProps(p, out, unit, white, C);
  583. drawRallyFlagIfAny(p, out, white, C);
  584. drawHealthBar(p, out, unit, white);
  585. drawSelectionFX(p, out);
  586. }
  587. } // namespace
  588. void registerBarracksRenderer(Render::GL::EntityRendererRegistry &registry) {
  589. registry.registerRenderer("barracks", drawBarracks);
  590. }
  591. } // namespace Render::GL