scene_renderer.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #include "scene_renderer.h"
  2. #include "../game/map/terrain_service.h"
  3. #include "../game/map/visibility_service.h"
  4. #include "../game/units/troop_config.h"
  5. #include "entity/registry.h"
  6. #include "game/core/component.h"
  7. #include "game/core/world.h"
  8. #include "gl/backend.h"
  9. #include "gl/camera.h"
  10. #include "gl/primitives.h"
  11. #include "gl/resources.h"
  12. #include <QDebug>
  13. #include <algorithm>
  14. #include <cmath>
  15. namespace Render::GL {
  16. namespace {
  17. const QVector3D kAxisX(1.0f, 0.0f, 0.0f);
  18. const QVector3D kAxisY(0.0f, 1.0f, 0.0f);
  19. const QVector3D kAxisZ(0.0f, 0.0f, 1.0f);
  20. } // namespace
  21. Renderer::Renderer() { m_activeQueue = &m_queues[m_fillQueueIndex]; }
  22. Renderer::~Renderer() { shutdown(); }
  23. bool Renderer::initialize() {
  24. if (!m_backend)
  25. m_backend = std::make_shared<Backend>();
  26. m_backend->initialize();
  27. m_entityRegistry = std::make_unique<EntityRendererRegistry>();
  28. registerBuiltInEntityRenderers(*m_entityRegistry);
  29. return true;
  30. }
  31. void Renderer::shutdown() { m_backend.reset(); }
  32. void Renderer::beginFrame() {
  33. m_activeQueue = &m_queues[m_fillQueueIndex];
  34. m_activeQueue->clear();
  35. if (m_camera) {
  36. m_viewProj = m_camera->getProjectionMatrix() * m_camera->getViewMatrix();
  37. }
  38. if (m_backend)
  39. m_backend->beginFrame();
  40. }
  41. void Renderer::endFrame() {
  42. if (m_paused.load())
  43. return;
  44. if (m_backend && m_camera) {
  45. std::swap(m_fillQueueIndex, m_renderQueueIndex);
  46. DrawQueue &renderQueue = m_queues[m_renderQueueIndex];
  47. renderQueue.sortForBatching();
  48. m_backend->setAnimationTime(m_accumulatedTime);
  49. m_backend->execute(renderQueue, *m_camera);
  50. }
  51. }
  52. void Renderer::setCamera(Camera *camera) { m_camera = camera; }
  53. void Renderer::setClearColor(float r, float g, float b, float a) {
  54. if (m_backend)
  55. m_backend->setClearColor(r, g, b, a);
  56. }
  57. void Renderer::setViewport(int width, int height) {
  58. m_viewportWidth = width;
  59. m_viewportHeight = height;
  60. if (m_backend)
  61. m_backend->setViewport(width, height);
  62. if (m_camera && height > 0) {
  63. float aspect = float(width) / float(height);
  64. m_camera->setPerspective(m_camera->getFOV(), aspect, m_camera->getNear(),
  65. m_camera->getFar());
  66. }
  67. }
  68. void Renderer::mesh(Mesh *mesh, const QMatrix4x4 &model, const QVector3D &color,
  69. Texture *texture, float alpha) {
  70. if (!mesh)
  71. return;
  72. if (mesh == getUnitCylinder() && (!texture) && (!m_currentShader)) {
  73. QVector3D start, end;
  74. float radius = 0.0f;
  75. if (detail::decomposeUnitCylinder(model, start, end, radius)) {
  76. cylinder(start, end, radius, color, alpha);
  77. return;
  78. }
  79. }
  80. MeshCmd cmd;
  81. cmd.mesh = mesh;
  82. cmd.texture = texture;
  83. cmd.model = model;
  84. cmd.mvp = m_viewProj * model;
  85. cmd.color = color;
  86. cmd.alpha = alpha;
  87. cmd.shader = m_currentShader;
  88. if (m_activeQueue)
  89. m_activeQueue->submit(cmd);
  90. }
  91. void Renderer::cylinder(const QVector3D &start, const QVector3D &end,
  92. float radius, const QVector3D &color, float alpha) {
  93. CylinderCmd cmd;
  94. cmd.start = start;
  95. cmd.end = end;
  96. cmd.radius = radius;
  97. cmd.color = color;
  98. cmd.alpha = alpha;
  99. if (m_activeQueue)
  100. m_activeQueue->submit(cmd);
  101. }
  102. void Renderer::fogBatch(const FogInstanceData *instances, std::size_t count) {
  103. if (!instances || count == 0 || !m_activeQueue)
  104. return;
  105. FogBatchCmd cmd;
  106. cmd.instances = instances;
  107. cmd.count = count;
  108. m_activeQueue->submit(cmd);
  109. }
  110. void Renderer::grassBatch(Buffer *instanceBuffer, std::size_t instanceCount,
  111. const GrassBatchParams &params) {
  112. if (!instanceBuffer || instanceCount == 0 || !m_activeQueue)
  113. return;
  114. GrassBatchCmd cmd;
  115. cmd.instanceBuffer = instanceBuffer;
  116. cmd.instanceCount = instanceCount;
  117. cmd.params = params;
  118. cmd.params.time = m_accumulatedTime;
  119. m_activeQueue->submit(cmd);
  120. }
  121. void Renderer::stoneBatch(Buffer *instanceBuffer, std::size_t instanceCount,
  122. const StoneBatchParams &params) {
  123. if (!instanceBuffer || instanceCount == 0 || !m_activeQueue)
  124. return;
  125. StoneBatchCmd cmd;
  126. cmd.instanceBuffer = instanceBuffer;
  127. cmd.instanceCount = instanceCount;
  128. cmd.params = params;
  129. m_activeQueue->submit(cmd);
  130. }
  131. void Renderer::plantBatch(Buffer *instanceBuffer, std::size_t instanceCount,
  132. const PlantBatchParams &params) {
  133. if (!instanceBuffer || instanceCount == 0 || !m_activeQueue)
  134. return;
  135. PlantBatchCmd cmd;
  136. cmd.instanceBuffer = instanceBuffer;
  137. cmd.instanceCount = instanceCount;
  138. cmd.params = params;
  139. cmd.params.time = m_accumulatedTime;
  140. m_activeQueue->submit(cmd);
  141. }
  142. void Renderer::pineBatch(Buffer *instanceBuffer, std::size_t instanceCount,
  143. const PineBatchParams &params) {
  144. if (!instanceBuffer || instanceCount == 0 || !m_activeQueue)
  145. return;
  146. PineBatchCmd cmd;
  147. cmd.instanceBuffer = instanceBuffer;
  148. cmd.instanceCount = instanceCount;
  149. cmd.params = params;
  150. cmd.params.time = m_accumulatedTime;
  151. m_activeQueue->submit(cmd);
  152. }
  153. void Renderer::firecampBatch(Buffer *instanceBuffer, std::size_t instanceCount,
  154. const FireCampBatchParams &params) {
  155. if (!instanceBuffer || instanceCount == 0 || !m_activeQueue)
  156. return;
  157. FireCampBatchCmd cmd;
  158. cmd.instanceBuffer = instanceBuffer;
  159. cmd.instanceCount = instanceCount;
  160. cmd.params = params;
  161. cmd.params.time = m_accumulatedTime;
  162. m_activeQueue->submit(cmd);
  163. }
  164. void Renderer::terrainChunk(Mesh *mesh, const QMatrix4x4 &model,
  165. const TerrainChunkParams &params,
  166. std::uint16_t sortKey, bool depthWrite,
  167. float depthBias) {
  168. if (!mesh || !m_activeQueue)
  169. return;
  170. TerrainChunkCmd cmd;
  171. cmd.mesh = mesh;
  172. cmd.model = model;
  173. cmd.params = params;
  174. cmd.sortKey = sortKey;
  175. cmd.depthWrite = depthWrite;
  176. cmd.depthBias = depthBias;
  177. m_activeQueue->submit(cmd);
  178. }
  179. void Renderer::selectionRing(const QMatrix4x4 &model, float alphaInner,
  180. float alphaOuter, const QVector3D &color) {
  181. SelectionRingCmd cmd;
  182. cmd.model = model;
  183. cmd.mvp = m_viewProj * model;
  184. cmd.alphaInner = alphaInner;
  185. cmd.alphaOuter = alphaOuter;
  186. cmd.color = color;
  187. if (m_activeQueue)
  188. m_activeQueue->submit(cmd);
  189. }
  190. void Renderer::grid(const QMatrix4x4 &model, const QVector3D &color,
  191. float cellSize, float thickness, float extent) {
  192. GridCmd cmd;
  193. cmd.model = model;
  194. cmd.mvp = m_viewProj * model;
  195. cmd.color = color;
  196. cmd.cellSize = cellSize;
  197. cmd.thickness = thickness;
  198. cmd.extent = extent;
  199. if (m_activeQueue)
  200. m_activeQueue->submit(cmd);
  201. }
  202. void Renderer::selectionSmoke(const QMatrix4x4 &model, const QVector3D &color,
  203. float baseAlpha) {
  204. SelectionSmokeCmd cmd;
  205. cmd.model = model;
  206. cmd.mvp = m_viewProj * model;
  207. cmd.color = color;
  208. cmd.baseAlpha = baseAlpha;
  209. if (m_activeQueue)
  210. m_activeQueue->submit(cmd);
  211. }
  212. void Renderer::enqueueSelectionRing(Engine::Core::Entity *,
  213. Engine::Core::TransformComponent *transform,
  214. Engine::Core::UnitComponent *unitComp,
  215. bool selected, bool hovered) {
  216. if ((!selected && !hovered) || !transform)
  217. return;
  218. float ringSize = 0.5f;
  219. float ringOffset = 0.05f;
  220. float groundOffset = 0.0f;
  221. if (unitComp && !unitComp->unitType.empty()) {
  222. auto &config = Game::Units::TroopConfig::instance();
  223. ringSize = config.getSelectionRingSize(unitComp->unitType);
  224. ringOffset += config.getSelectionRingYOffset(unitComp->unitType);
  225. groundOffset = config.getSelectionRingGroundOffset(unitComp->unitType);
  226. }
  227. QVector3D pos(transform->position.x, transform->position.y,
  228. transform->position.z);
  229. auto &terrainService = Game::Map::TerrainService::instance();
  230. float terrainY = transform->position.y;
  231. if (terrainService.isInitialized()) {
  232. terrainY = terrainService.getTerrainHeight(pos.x(), pos.z());
  233. } else {
  234. terrainY -= groundOffset * transform->scale.y;
  235. }
  236. pos.setY(terrainY);
  237. QMatrix4x4 ringModel;
  238. ringModel.translate(pos.x(), pos.y() + ringOffset, pos.z());
  239. ringModel.scale(ringSize, 1.0f, ringSize);
  240. if (selected) {
  241. selectionRing(ringModel, 0.6f, 0.25f, QVector3D(0.2f, 0.4f, 1.0f));
  242. } else if (hovered) {
  243. selectionRing(ringModel, 0.35f, 0.15f, QVector3D(0.90f, 0.90f, 0.25f));
  244. }
  245. }
  246. void Renderer::renderWorld(Engine::Core::World *world) {
  247. if (m_paused.load())
  248. return;
  249. if (!world)
  250. return;
  251. std::lock_guard<std::recursive_mutex> guard(world->getEntityMutex());
  252. auto &vis = Game::Map::VisibilityService::instance();
  253. const bool visibilityEnabled = vis.isInitialized();
  254. auto renderableEntities =
  255. world->getEntitiesWith<Engine::Core::RenderableComponent>();
  256. for (auto entity : renderableEntities) {
  257. if (entity->hasComponent<Engine::Core::PendingRemovalComponent>()) {
  258. continue;
  259. }
  260. auto renderable = entity->getComponent<Engine::Core::RenderableComponent>();
  261. auto transform = entity->getComponent<Engine::Core::TransformComponent>();
  262. if (!renderable->visible || !transform) {
  263. continue;
  264. }
  265. auto *unitComp = entity->getComponent<Engine::Core::UnitComponent>();
  266. if (unitComp && unitComp->health <= 0) {
  267. continue;
  268. }
  269. if (m_camera && unitComp) {
  270. float cullRadius = 3.0f;
  271. if (!unitComp->unitType.empty()) {
  272. if (unitComp->unitType.find("mounted") != std::string::npos) {
  273. cullRadius = 4.0f;
  274. } else if (unitComp->unitType == "spearman" ||
  275. unitComp->unitType == "archer" ||
  276. unitComp->unitType == "knight") {
  277. cullRadius = 2.5f;
  278. }
  279. }
  280. QVector3D unitPos(transform->position.x, transform->position.y,
  281. transform->position.z);
  282. if (!m_camera->isInFrustum(unitPos, cullRadius)) {
  283. continue;
  284. }
  285. }
  286. if (unitComp && unitComp->ownerId != m_localOwnerId) {
  287. if (visibilityEnabled) {
  288. if (!vis.isVisibleWorld(transform->position.x, transform->position.z)) {
  289. continue;
  290. }
  291. }
  292. }
  293. bool isSelected =
  294. (m_selectedIds.find(entity->getId()) != m_selectedIds.end());
  295. bool isHovered = (entity->getId() == m_hoveredEntityId);
  296. QMatrix4x4 modelMatrix;
  297. modelMatrix.translate(transform->position.x, transform->position.y,
  298. transform->position.z);
  299. modelMatrix.rotate(transform->rotation.x, kAxisX);
  300. modelMatrix.rotate(transform->rotation.y, kAxisY);
  301. modelMatrix.rotate(transform->rotation.z, kAxisZ);
  302. modelMatrix.scale(transform->scale.x, transform->scale.y,
  303. transform->scale.z);
  304. bool drawnByRegistry = false;
  305. if (unitComp && !unitComp->unitType.empty() && m_entityRegistry) {
  306. auto fn = m_entityRegistry->get(unitComp->unitType);
  307. if (fn) {
  308. DrawContext ctx{resources(), entity, world, modelMatrix};
  309. ctx.selected = isSelected;
  310. ctx.hovered = isHovered;
  311. ctx.animationTime = m_accumulatedTime;
  312. ctx.backend = m_backend.get();
  313. fn(ctx, *this);
  314. enqueueSelectionRing(entity, transform, unitComp, isSelected,
  315. isHovered);
  316. drawnByRegistry = true;
  317. }
  318. }
  319. if (drawnByRegistry)
  320. continue;
  321. Mesh *meshToDraw = nullptr;
  322. ResourceManager *res = resources();
  323. switch (renderable->mesh) {
  324. case Engine::Core::RenderableComponent::MeshKind::Quad:
  325. meshToDraw = res ? res->quad() : nullptr;
  326. break;
  327. case Engine::Core::RenderableComponent::MeshKind::Plane:
  328. meshToDraw = res ? res->ground() : nullptr;
  329. break;
  330. case Engine::Core::RenderableComponent::MeshKind::Cube:
  331. meshToDraw = res ? res->unit() : nullptr;
  332. break;
  333. case Engine::Core::RenderableComponent::MeshKind::Capsule:
  334. meshToDraw = nullptr;
  335. break;
  336. case Engine::Core::RenderableComponent::MeshKind::Ring:
  337. meshToDraw = nullptr;
  338. break;
  339. case Engine::Core::RenderableComponent::MeshKind::None:
  340. default:
  341. break;
  342. }
  343. if (!meshToDraw && res)
  344. meshToDraw = res->unit();
  345. if (!meshToDraw && res)
  346. meshToDraw = res->quad();
  347. QVector3D color = QVector3D(renderable->color[0], renderable->color[1],
  348. renderable->color[2]);
  349. if (res) {
  350. Mesh *contactQuad = res->quad();
  351. Texture *white = res->white();
  352. if (contactQuad && white) {
  353. QMatrix4x4 contactBase;
  354. contactBase.translate(transform->position.x,
  355. transform->position.y + 0.03f,
  356. transform->position.z);
  357. contactBase.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
  358. float footprint =
  359. std::max({transform->scale.x, transform->scale.z, 0.6f});
  360. float sizeRatio = 1.0f;
  361. if (auto *unit = entity->getComponent<Engine::Core::UnitComponent>()) {
  362. int mh = std::max(1, unit->maxHealth);
  363. sizeRatio = std::clamp(unit->health / float(mh), 0.0f, 1.0f);
  364. }
  365. float eased = 0.25f + 0.75f * sizeRatio;
  366. float baseScaleX = footprint * 0.55f * eased;
  367. float baseScaleY = footprint * 0.35f * eased;
  368. QVector3D col(0.03f, 0.03f, 0.03f);
  369. float centerAlpha = 0.32f * eased;
  370. float midAlpha = 0.16f * eased;
  371. float outerAlpha = 0.07f * eased;
  372. QMatrix4x4 c0 = contactBase;
  373. c0.scale(baseScaleX * 0.60f, baseScaleY * 0.60f, 1.0f);
  374. mesh(contactQuad, c0, col, white, centerAlpha);
  375. QMatrix4x4 c1 = contactBase;
  376. c1.scale(baseScaleX * 0.95f, baseScaleY * 0.95f, 1.0f);
  377. mesh(contactQuad, c1, col, white, midAlpha);
  378. QMatrix4x4 c2 = contactBase;
  379. c2.scale(baseScaleX * 1.35f, baseScaleY * 1.35f, 1.0f);
  380. mesh(contactQuad, c2, col, white, outerAlpha);
  381. }
  382. }
  383. enqueueSelectionRing(entity, transform, unitComp, isSelected, isHovered);
  384. mesh(meshToDraw, modelMatrix, color, res ? res->white() : nullptr, 1.0f);
  385. }
  386. }
  387. } // namespace Render::GL