healer_aura_pipeline.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. #include "healer_aura_pipeline.h"
  2. #include "../../../game/core/component.h"
  3. #include "../../../game/core/world.h"
  4. #include "../../../game/systems/nation_id.h"
  5. #include "../backend.h"
  6. #include "../camera.h"
  7. #include "../render_constants.h"
  8. #include "../shader_cache.h"
  9. #include <QDebug>
  10. #include <QMatrix4x4>
  11. #include <QOpenGLContext>
  12. #include <cmath>
  13. #include <numbers>
  14. namespace Render::GL::BackendPipelines {
  15. using namespace Render::GL::VertexAttrib;
  16. using namespace Render::GL::ComponentCount;
  17. namespace {
  18. void clear_gl_errors() {
  19. #ifndef NDEBUG
  20. while (glGetError() != GL_NO_ERROR) {
  21. }
  22. #endif
  23. }
  24. auto check_gl_error(const char *operation) -> bool {
  25. #ifndef NDEBUG
  26. GLenum err = glGetError();
  27. if (err != GL_NO_ERROR) {
  28. qWarning() << "HealerAuraPipeline GL error in" << operation << ":" << err;
  29. return false;
  30. }
  31. #else
  32. Q_UNUSED(operation);
  33. #endif
  34. return true;
  35. }
  36. } // namespace
  37. auto HealerAuraPipeline::initialize() -> bool {
  38. if (m_shaderCache == nullptr) {
  39. qWarning() << "HealerAuraPipeline::initialize: null ShaderCache";
  40. return false;
  41. }
  42. clear_gl_errors();
  43. m_auraShader = m_shaderCache->get("healing_aura");
  44. if (m_auraShader == nullptr) {
  45. qWarning() << "HealerAuraPipeline: Failed to get healing_aura shader";
  46. return false;
  47. }
  48. cache_uniforms();
  49. if (!create_dome_geometry()) {
  50. qWarning() << "HealerAuraPipeline: Failed to create dome geometry";
  51. return false;
  52. }
  53. qInfo() << "HealerAuraPipeline initialized successfully";
  54. return is_initialized();
  55. }
  56. void HealerAuraPipeline::shutdown() {
  57. shutdown_geometry();
  58. m_auraShader = nullptr;
  59. }
  60. void HealerAuraPipeline::shutdown_geometry() {
  61. if (QOpenGLContext::currentContext() == nullptr) {
  62. m_vao = 0;
  63. m_vertexBuffer = 0;
  64. m_indexBuffer = 0;
  65. m_indexCount = 0;
  66. return;
  67. }
  68. clear_gl_errors();
  69. if (m_vao != 0) {
  70. glDeleteVertexArrays(1, &m_vao);
  71. m_vao = 0;
  72. }
  73. if (m_vertexBuffer != 0) {
  74. glDeleteBuffers(1, &m_vertexBuffer);
  75. m_vertexBuffer = 0;
  76. }
  77. if (m_indexBuffer != 0) {
  78. glDeleteBuffers(1, &m_indexBuffer);
  79. m_indexBuffer = 0;
  80. }
  81. m_indexCount = 0;
  82. }
  83. void HealerAuraPipeline::cache_uniforms() {
  84. if (m_auraShader == nullptr) {
  85. return;
  86. }
  87. m_uniforms.mvp = m_auraShader->uniform_handle("u_mvp");
  88. m_uniforms.model = m_auraShader->uniform_handle("u_model");
  89. m_uniforms.time = m_auraShader->uniform_handle("u_time");
  90. m_uniforms.auraRadius = m_auraShader->uniform_handle("u_auraRadius");
  91. m_uniforms.intensity = m_auraShader->uniform_handle("u_intensity");
  92. m_uniforms.auraColor = m_auraShader->uniform_handle("u_auraColor");
  93. }
  94. auto HealerAuraPipeline::is_initialized() const -> bool {
  95. return m_auraShader != nullptr && m_vao != 0 && m_indexCount > 0;
  96. }
  97. struct AuraVertex {
  98. float position[3];
  99. float normal[3];
  100. float tex_coord[2];
  101. };
  102. auto HealerAuraPipeline::create_dome_geometry() -> bool {
  103. initializeOpenGLFunctions();
  104. shutdown_geometry();
  105. clear_gl_errors();
  106. std::vector<AuraVertex> vertices;
  107. std::vector<unsigned int> indices;
  108. constexpr int stacks = 8;
  109. constexpr int slices = 16;
  110. constexpr float pi = std::numbers::pi_v<float>;
  111. vertices.reserve(static_cast<size_t>((stacks + 1) * (slices + 1)));
  112. for (int i = 0; i <= stacks; ++i) {
  113. float phi =
  114. (static_cast<float>(i) / static_cast<float>(stacks)) * pi * 0.5F;
  115. float y = std::sin(phi);
  116. float r = std::cos(phi);
  117. for (int j = 0; j <= slices; ++j) {
  118. float theta =
  119. (static_cast<float>(j) / static_cast<float>(slices)) * pi * 2.0F;
  120. float x = r * std::cos(theta);
  121. float z = r * std::sin(theta);
  122. AuraVertex v;
  123. v.position[0] = x;
  124. v.position[1] = y;
  125. v.position[2] = z;
  126. v.normal[0] = x;
  127. v.normal[1] = y;
  128. v.normal[2] = z;
  129. v.tex_coord[0] = static_cast<float>(j) / static_cast<float>(slices);
  130. v.tex_coord[1] = static_cast<float>(i) / static_cast<float>(stacks);
  131. vertices.push_back(v);
  132. }
  133. }
  134. indices.reserve(static_cast<size_t>(stacks * slices * 6));
  135. for (int i = 0; i < stacks; ++i) {
  136. for (int j = 0; j < slices; ++j) {
  137. unsigned int curr = static_cast<unsigned int>(i * (slices + 1) + j);
  138. unsigned int next = curr + static_cast<unsigned int>(slices + 1);
  139. indices.push_back(curr);
  140. indices.push_back(next);
  141. indices.push_back(curr + 1);
  142. indices.push_back(curr + 1);
  143. indices.push_back(next);
  144. indices.push_back(next + 1);
  145. }
  146. }
  147. glGenVertexArrays(1, &m_vao);
  148. if (!check_gl_error("glGenVertexArrays") || m_vao == 0) {
  149. return false;
  150. }
  151. glBindVertexArray(m_vao);
  152. if (!check_gl_error("glBindVertexArray")) {
  153. glDeleteVertexArrays(1, &m_vao);
  154. m_vao = 0;
  155. return false;
  156. }
  157. glGenBuffers(1, &m_vertexBuffer);
  158. glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
  159. glBufferData(GL_ARRAY_BUFFER,
  160. static_cast<GLsizeiptr>(vertices.size() * sizeof(AuraVertex)),
  161. vertices.data(), GL_STATIC_DRAW);
  162. if (!check_gl_error("vertex buffer")) {
  163. shutdown_geometry();
  164. return false;
  165. }
  166. glGenBuffers(1, &m_indexBuffer);
  167. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
  168. glBufferData(GL_ELEMENT_ARRAY_BUFFER,
  169. static_cast<GLsizeiptr>(indices.size() * sizeof(unsigned int)),
  170. indices.data(), GL_STATIC_DRAW);
  171. if (!check_gl_error("index buffer")) {
  172. shutdown_geometry();
  173. return false;
  174. }
  175. m_indexCount = static_cast<GLsizei>(indices.size());
  176. glEnableVertexAttribArray(VertexAttrib::Position);
  177. glVertexAttribPointer(
  178. VertexAttrib::Position, ComponentCount::Vec3, GL_FLOAT, GL_FALSE,
  179. sizeof(AuraVertex),
  180. reinterpret_cast<void *>(offsetof(AuraVertex, position)));
  181. glEnableVertexAttribArray(VertexAttrib::Normal);
  182. glVertexAttribPointer(VertexAttrib::Normal, ComponentCount::Vec3, GL_FLOAT,
  183. GL_FALSE, sizeof(AuraVertex),
  184. reinterpret_cast<void *>(offsetof(AuraVertex, normal)));
  185. glEnableVertexAttribArray(VertexAttrib::TexCoord);
  186. glVertexAttribPointer(
  187. VertexAttrib::TexCoord, ComponentCount::Vec2, GL_FLOAT, GL_FALSE,
  188. sizeof(AuraVertex),
  189. reinterpret_cast<void *>(offsetof(AuraVertex, tex_coord)));
  190. glBindVertexArray(0);
  191. if (!check_gl_error("vertex attributes")) {
  192. shutdown_geometry();
  193. return false;
  194. }
  195. return true;
  196. }
  197. void HealerAuraPipeline::collect_healers(Engine::Core::World *world) {
  198. m_healerData.clear();
  199. if (world == nullptr) {
  200. return;
  201. }
  202. auto healers = world->get_entities_with<Engine::Core::HealerComponent>();
  203. for (auto *healer : healers) {
  204. if (healer->has_component<Engine::Core::PendingRemovalComponent>()) {
  205. continue;
  206. }
  207. auto *transform = healer->get_component<Engine::Core::TransformComponent>();
  208. auto *healer_comp = healer->get_component<Engine::Core::HealerComponent>();
  209. auto *unit_comp = healer->get_component<Engine::Core::UnitComponent>();
  210. if (transform == nullptr || healer_comp == nullptr) {
  211. continue;
  212. }
  213. if (unit_comp != nullptr && unit_comp->health <= 0) {
  214. continue;
  215. }
  216. if (unit_comp != nullptr &&
  217. unit_comp->nation_id == Game::Systems::NationID::RomanRepublic) {
  218. continue;
  219. }
  220. HealerAuraData data;
  221. data.position = QVector3D(transform->position.x, transform->position.y,
  222. transform->position.z);
  223. data.radius = healer_comp->healing_range;
  224. data.is_active = healer_comp->is_healing_active;
  225. data.intensity = data.is_active ? 1.0F : 0.5F;
  226. data.color = QVector3D(0.4F, 1.0F, 0.5F);
  227. m_healerData.push_back(data);
  228. }
  229. }
  230. void HealerAuraPipeline::render(const Camera &cam, float animation_time) {
  231. if (!is_initialized() || m_healerData.empty()) {
  232. return;
  233. }
  234. clear_gl_errors();
  235. GLboolean cullEnabled = glIsEnabled(GL_CULL_FACE);
  236. GLboolean depthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
  237. GLboolean blendEnabled = glIsEnabled(GL_BLEND);
  238. GLboolean depthMaskEnabled = GL_TRUE;
  239. glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMaskEnabled);
  240. glDisable(GL_CULL_FACE);
  241. glEnable(GL_DEPTH_TEST);
  242. glDepthMask(GL_FALSE);
  243. glEnable(GL_BLEND);
  244. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  245. m_auraShader->use();
  246. glBindVertexArray(m_vao);
  247. for (const auto &data : m_healerData) {
  248. render_aura(data, cam, animation_time);
  249. }
  250. glBindVertexArray(0);
  251. glDepthMask(depthMaskEnabled);
  252. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  253. if (!blendEnabled) {
  254. glDisable(GL_BLEND);
  255. }
  256. if (depthTestEnabled) {
  257. glEnable(GL_DEPTH_TEST);
  258. } else {
  259. glDisable(GL_DEPTH_TEST);
  260. }
  261. if (cullEnabled) {
  262. glEnable(GL_CULL_FACE);
  263. }
  264. }
  265. void HealerAuraPipeline::render_aura(const HealerAuraData &data,
  266. const Camera &cam, float animation_time) {
  267. QMatrix4x4 model;
  268. model.setToIdentity();
  269. model.translate(data.position);
  270. model.scale(data.radius);
  271. QMatrix4x4 vp = cam.get_projection_matrix() * cam.get_view_matrix();
  272. QMatrix4x4 mvp = vp * model;
  273. m_auraShader->set_uniform(m_uniforms.mvp, mvp);
  274. m_auraShader->set_uniform(m_uniforms.model, model);
  275. m_auraShader->set_uniform(m_uniforms.time, animation_time);
  276. m_auraShader->set_uniform(m_uniforms.auraRadius, 1.0F);
  277. m_auraShader->set_uniform(m_uniforms.intensity, data.intensity);
  278. m_auraShader->set_uniform(m_uniforms.auraColor, data.color);
  279. glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr);
  280. }
  281. void HealerAuraPipeline::render_single_aura(const QVector3D &position,
  282. const QVector3D &color,
  283. float radius, float intensity,
  284. float time,
  285. const QMatrix4x4 &view_proj) {
  286. if (!is_initialized()) {
  287. return;
  288. }
  289. if (intensity < 0.01F) {
  290. return;
  291. }
  292. GLboolean cullEnabled = glIsEnabled(GL_CULL_FACE);
  293. GLboolean depthMaskEnabled = GL_TRUE;
  294. glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMaskEnabled);
  295. glDisable(GL_CULL_FACE);
  296. glEnable(GL_DEPTH_TEST);
  297. glDepthMask(GL_FALSE);
  298. glEnable(GL_BLEND);
  299. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  300. m_auraShader->use();
  301. glBindVertexArray(m_vao);
  302. QMatrix4x4 model;
  303. model.setToIdentity();
  304. model.translate(position);
  305. model.scale(radius);
  306. QMatrix4x4 mvp = view_proj * model;
  307. m_auraShader->set_uniform(m_uniforms.mvp, mvp);
  308. m_auraShader->set_uniform(m_uniforms.model, model);
  309. m_auraShader->set_uniform(m_uniforms.time, time);
  310. m_auraShader->set_uniform(m_uniforms.auraRadius, 1.0F);
  311. m_auraShader->set_uniform(m_uniforms.intensity, intensity);
  312. m_auraShader->set_uniform(m_uniforms.auraColor, color);
  313. glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr);
  314. glBindVertexArray(0);
  315. glDepthMask(depthMaskEnabled);
  316. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  317. if (cullEnabled) {
  318. glEnable(GL_CULL_FACE);
  319. }
  320. }
  321. } // namespace Render::GL::BackendPipelines