scene_renderer.cpp 15 KB

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