barracks_renderer.cpp 27 KB

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