healing_beam_pipeline.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #include "healing_beam_pipeline.h"
  2. #include "../../../game/systems/healing_beam.h"
  3. #include "../../../game/systems/healing_beam_system.h"
  4. #include "../backend.h"
  5. #include "../camera.h"
  6. #include "../mesh.h"
  7. #include "../render_constants.h"
  8. #include "../shader_cache.h"
  9. #include <QDebug>
  10. #include <QMatrix4x4>
  11. #include <cmath>
  12. #include <numbers>
  13. namespace Render::GL::BackendPipelines {
  14. using namespace Render::GL::VertexAttrib;
  15. using namespace Render::GL::ComponentCount;
  16. namespace {
  17. void clear_gl_errors() {
  18. #ifndef NDEBUG
  19. while (glGetError() != GL_NO_ERROR) {
  20. }
  21. #endif
  22. }
  23. auto check_gl_error(const char *operation) -> bool {
  24. #ifndef NDEBUG
  25. GLenum err = glGetError();
  26. if (err != GL_NO_ERROR) {
  27. qWarning() << "HealingBeamPipeline GL error in" << operation << ":" << err;
  28. return false;
  29. }
  30. #else
  31. Q_UNUSED(operation);
  32. #endif
  33. return true;
  34. }
  35. } // namespace
  36. auto HealingBeamPipeline::initialize() -> bool {
  37. if (m_shaderCache == nullptr) {
  38. qWarning() << "HealingBeamPipeline::initialize: null ShaderCache";
  39. return false;
  40. }
  41. clear_gl_errors();
  42. m_beamShader = m_shaderCache->get("healing_beam");
  43. if (m_beamShader == nullptr) {
  44. qWarning() << "HealingBeamPipeline: Failed to get healing_beam shader";
  45. return false;
  46. }
  47. cache_uniforms();
  48. if (!create_beam_geometry()) {
  49. qWarning() << "HealingBeamPipeline: Failed to create beam geometry";
  50. return false;
  51. }
  52. qInfo() << "HealingBeamPipeline initialized successfully";
  53. return is_initialized();
  54. }
  55. void HealingBeamPipeline::shutdown() {
  56. shutdown_geometry();
  57. m_beamShader = nullptr;
  58. }
  59. void HealingBeamPipeline::shutdown_geometry() {
  60. if (QOpenGLContext::currentContext() == nullptr) {
  61. m_vao = 0;
  62. m_vertexBuffer = 0;
  63. m_indexBuffer = 0;
  64. m_indexCount = 0;
  65. return;
  66. }
  67. clear_gl_errors();
  68. if (m_vao != 0) {
  69. glDeleteVertexArrays(1, &m_vao);
  70. m_vao = 0;
  71. }
  72. if (m_vertexBuffer != 0) {
  73. glDeleteBuffers(1, &m_vertexBuffer);
  74. m_vertexBuffer = 0;
  75. }
  76. if (m_indexBuffer != 0) {
  77. glDeleteBuffers(1, &m_indexBuffer);
  78. m_indexBuffer = 0;
  79. }
  80. m_indexCount = 0;
  81. }
  82. void HealingBeamPipeline::cache_uniforms() {
  83. if (m_beamShader == nullptr) {
  84. return;
  85. }
  86. m_uniforms.mvp = m_beamShader->uniform_handle("u_mvp");
  87. m_uniforms.time = m_beamShader->uniform_handle("u_time");
  88. m_uniforms.progress = m_beamShader->uniform_handle("u_progress");
  89. m_uniforms.startPos = m_beamShader->uniform_handle("u_startPos");
  90. m_uniforms.endPos = m_beamShader->uniform_handle("u_endPos");
  91. m_uniforms.beamWidth = m_beamShader->uniform_handle("u_beamWidth");
  92. m_uniforms.healColor = m_beamShader->uniform_handle("u_healColor");
  93. m_uniforms.alpha = m_beamShader->uniform_handle("u_alpha");
  94. }
  95. auto HealingBeamPipeline::is_initialized() const -> bool {
  96. return m_beamShader != nullptr && m_vao != 0 && m_indexCount > 0;
  97. }
  98. auto HealingBeamPipeline::create_beam_geometry() -> bool {
  99. initializeOpenGLFunctions();
  100. shutdown_geometry();
  101. clear_gl_errors();
  102. std::vector<Vertex> vertices;
  103. std::vector<unsigned int> indices;
  104. constexpr int segments_along = 24;
  105. constexpr int segments_around = 8;
  106. constexpr float pi = std::numbers::pi_v<float>;
  107. vertices.reserve(
  108. static_cast<size_t>((segments_along + 1) * (segments_around + 1)));
  109. indices.reserve(static_cast<size_t>(segments_along * segments_around * 6));
  110. for (int i = 0; i <= segments_along; ++i) {
  111. float t = static_cast<float>(i) / static_cast<float>(segments_along);
  112. for (int j = 0; j <= segments_around; ++j) {
  113. float angle = static_cast<float>(j) /
  114. static_cast<float>(segments_around) * 2.0F * pi;
  115. float x = std::cos(angle);
  116. float y = std::sin(angle);
  117. Vertex v;
  118. v.position = {x, y, t};
  119. v.normal = {x, y, 0.0F};
  120. v.tex_coord = {
  121. static_cast<float>(j) / static_cast<float>(segments_around), t};
  122. vertices.push_back(v);
  123. }
  124. }
  125. for (int i = 0; i < segments_along; ++i) {
  126. for (int j = 0; j < segments_around; ++j) {
  127. unsigned int curr =
  128. static_cast<unsigned int>(i * (segments_around + 1) + j);
  129. unsigned int next = curr + static_cast<unsigned int>(segments_around + 1);
  130. indices.push_back(curr);
  131. indices.push_back(next);
  132. indices.push_back(curr + 1);
  133. indices.push_back(curr + 1);
  134. indices.push_back(next);
  135. indices.push_back(next + 1);
  136. }
  137. }
  138. glGenVertexArrays(1, &m_vao);
  139. if (!check_gl_error("glGenVertexArrays") || m_vao == 0) {
  140. return false;
  141. }
  142. glBindVertexArray(m_vao);
  143. if (!check_gl_error("glBindVertexArray")) {
  144. glDeleteVertexArrays(1, &m_vao);
  145. m_vao = 0;
  146. return false;
  147. }
  148. glGenBuffers(1, &m_vertexBuffer);
  149. glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
  150. glBufferData(GL_ARRAY_BUFFER,
  151. static_cast<GLsizeiptr>(vertices.size() * sizeof(Vertex)),
  152. vertices.data(), GL_STATIC_DRAW);
  153. if (!check_gl_error("vertex buffer")) {
  154. shutdown_geometry();
  155. return false;
  156. }
  157. glGenBuffers(1, &m_indexBuffer);
  158. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
  159. glBufferData(GL_ELEMENT_ARRAY_BUFFER,
  160. static_cast<GLsizeiptr>(indices.size() * sizeof(unsigned int)),
  161. indices.data(), GL_STATIC_DRAW);
  162. if (!check_gl_error("index buffer")) {
  163. shutdown_geometry();
  164. return false;
  165. }
  166. m_indexCount = static_cast<GLsizei>(indices.size());
  167. glEnableVertexAttribArray(VertexAttrib::Position);
  168. glVertexAttribPointer(VertexAttrib::Position, ComponentCount::Vec3, GL_FLOAT,
  169. GL_FALSE, sizeof(Vertex),
  170. reinterpret_cast<void *>(offsetof(Vertex, position)));
  171. glEnableVertexAttribArray(VertexAttrib::Normal);
  172. glVertexAttribPointer(VertexAttrib::Normal, ComponentCount::Vec3, GL_FLOAT,
  173. GL_FALSE, sizeof(Vertex),
  174. reinterpret_cast<void *>(offsetof(Vertex, normal)));
  175. glEnableVertexAttribArray(VertexAttrib::TexCoord);
  176. glVertexAttribPointer(VertexAttrib::TexCoord, ComponentCount::Vec2, GL_FLOAT,
  177. GL_FALSE, sizeof(Vertex),
  178. reinterpret_cast<void *>(offsetof(Vertex, tex_coord)));
  179. glBindVertexArray(0);
  180. if (!check_gl_error("vertex attributes")) {
  181. shutdown_geometry();
  182. return false;
  183. }
  184. return true;
  185. }
  186. void HealingBeamPipeline::render(
  187. const Game::Systems::HealingBeamSystem *beam_system, const Camera &cam,
  188. float animation_time) {
  189. if (!is_initialized()) {
  190. return;
  191. }
  192. if (beam_system == nullptr || beam_system->get_beam_count() == 0) {
  193. return;
  194. }
  195. clear_gl_errors();
  196. GLboolean cullEnabled = glIsEnabled(GL_CULL_FACE);
  197. GLboolean depthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
  198. GLboolean blendEnabled = glIsEnabled(GL_BLEND);
  199. GLboolean depthMaskEnabled = GL_TRUE;
  200. glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMaskEnabled);
  201. GLint prevBlendSrc = 0;
  202. GLint prevBlendDst = 0;
  203. glGetIntegerv(GL_BLEND_SRC_ALPHA, &prevBlendSrc);
  204. glGetIntegerv(GL_BLEND_DST_ALPHA, &prevBlendDst);
  205. glDisable(GL_CULL_FACE);
  206. glEnable(GL_DEPTH_TEST);
  207. glDepthMask(GL_FALSE);
  208. glEnable(GL_BLEND);
  209. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  210. m_beamShader->use();
  211. glBindVertexArray(m_vao);
  212. for (const auto &beam : beam_system->get_beams()) {
  213. if (beam && beam->is_active()) {
  214. render_beam(*beam, cam, animation_time);
  215. }
  216. }
  217. glBindVertexArray(0);
  218. glDepthMask(depthMaskEnabled);
  219. glBlendFunc(static_cast<GLenum>(prevBlendSrc),
  220. static_cast<GLenum>(prevBlendDst));
  221. if (!blendEnabled) {
  222. glDisable(GL_BLEND);
  223. }
  224. if (depthTestEnabled) {
  225. glEnable(GL_DEPTH_TEST);
  226. } else {
  227. glDisable(GL_DEPTH_TEST);
  228. }
  229. if (cullEnabled) {
  230. glEnable(GL_CULL_FACE);
  231. }
  232. }
  233. void HealingBeamPipeline::render_beam(const Game::Systems::HealingBeam &beam,
  234. const Camera &cam, float animation_time) {
  235. QMatrix4x4 mvp = cam.get_projection_matrix() * cam.get_view_matrix();
  236. float progress = std::clamp(beam.get_progress(), 0.0F, 1.0F);
  237. float alpha = std::clamp(beam.get_intensity(), 0.0F, 1.0F);
  238. if (alpha < 0.01F) {
  239. return;
  240. }
  241. m_beamShader->set_uniform(m_uniforms.mvp, mvp);
  242. m_beamShader->set_uniform(m_uniforms.time, animation_time);
  243. m_beamShader->set_uniform(m_uniforms.progress, progress);
  244. m_beamShader->set_uniform(m_uniforms.startPos, beam.get_start());
  245. m_beamShader->set_uniform(m_uniforms.endPos, beam.get_end());
  246. m_beamShader->set_uniform(m_uniforms.beamWidth, beam.get_beam_width());
  247. m_beamShader->set_uniform(m_uniforms.healColor, beam.get_color());
  248. m_beamShader->set_uniform(m_uniforms.alpha, alpha);
  249. glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr);
  250. }
  251. void HealingBeamPipeline::render_single_beam(const QVector3D &start,
  252. const QVector3D &end,
  253. const QVector3D &color,
  254. float progress, float beam_width,
  255. float intensity, float time,
  256. const QMatrix4x4 &view_proj) {
  257. if (!is_initialized()) {
  258. return;
  259. }
  260. if (intensity < 0.01F) {
  261. return;
  262. }
  263. GLboolean cullEnabled = glIsEnabled(GL_CULL_FACE);
  264. GLboolean depthMaskEnabled = GL_TRUE;
  265. glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMaskEnabled);
  266. glDisable(GL_CULL_FACE);
  267. glEnable(GL_DEPTH_TEST);
  268. glDepthMask(GL_FALSE);
  269. glEnable(GL_BLEND);
  270. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  271. m_beamShader->use();
  272. glBindVertexArray(m_vao);
  273. m_beamShader->set_uniform(m_uniforms.mvp, view_proj);
  274. m_beamShader->set_uniform(m_uniforms.time, time);
  275. m_beamShader->set_uniform(m_uniforms.progress,
  276. std::clamp(progress, 0.0F, 1.0F));
  277. m_beamShader->set_uniform(m_uniforms.startPos, start);
  278. m_beamShader->set_uniform(m_uniforms.endPos, end);
  279. m_beamShader->set_uniform(m_uniforms.beamWidth, beam_width);
  280. m_beamShader->set_uniform(m_uniforms.healColor, color);
  281. m_beamShader->set_uniform(m_uniforms.alpha,
  282. std::clamp(intensity, 0.0F, 1.0F));
  283. glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr);
  284. glBindVertexArray(0);
  285. glDepthMask(depthMaskEnabled);
  286. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  287. if (cullEnabled) {
  288. glEnable(GL_CULL_FACE);
  289. }
  290. }
  291. } // namespace Render::GL::BackendPipelines