vegetation_pipeline.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. #include "vegetation_pipeline.h"
  2. #include "../render_constants.h"
  3. #include "gl/shader_cache.h"
  4. #include <GL/gl.h>
  5. #include <QDebug>
  6. #include <QOpenGLContext>
  7. #include <cmath>
  8. #include <cstddef>
  9. #include <cstdint>
  10. #include <qglobal.h>
  11. #include <qopenglcontext.h>
  12. #include <qopenglext.h>
  13. #include <qstringliteral.h>
  14. #include <qvectornd.h>
  15. #include <vector>
  16. namespace Render::GL::BackendPipelines {
  17. using namespace Render::GL::VertexAttrib;
  18. using namespace Render::GL::ComponentCount;
  19. using namespace Render::GL::Geometry;
  20. VegetationPipeline::VegetationPipeline(ShaderCache *shader_cache)
  21. : m_shaderCache(shader_cache) {}
  22. VegetationPipeline::~VegetationPipeline() { shutdown(); }
  23. auto VegetationPipeline::initialize() -> bool {
  24. initializeOpenGLFunctions();
  25. if (m_shaderCache == nullptr) {
  26. return false;
  27. }
  28. m_stoneShader = m_shaderCache->get(QStringLiteral("stone_instanced"));
  29. m_plantShader = m_shaderCache->get(QStringLiteral("plant_instanced"));
  30. m_pineShader = m_shaderCache->get(QStringLiteral("pine_instanced"));
  31. m_oliveShader = m_shaderCache->get(QStringLiteral("olive_instanced"));
  32. m_firecampShader = m_shaderCache->get(QStringLiteral("firecamp"));
  33. if (m_stoneShader == nullptr) {
  34. qWarning() << "VegetationPipeline: stone shader missing";
  35. }
  36. if (m_plantShader == nullptr) {
  37. qWarning() << "VegetationPipeline: plant shader missing";
  38. }
  39. if (m_pineShader == nullptr) {
  40. qWarning() << "VegetationPipeline: pine shader missing";
  41. }
  42. if (m_oliveShader == nullptr) {
  43. qWarning() << "VegetationPipeline: olive shader missing";
  44. }
  45. if (m_firecampShader == nullptr) {
  46. qWarning() << "VegetationPipeline: firecamp shader missing";
  47. }
  48. initialize_stone_pipeline();
  49. initialize_plant_pipeline();
  50. initialize_pine_pipeline();
  51. initialize_olive_pipeline();
  52. initialize_fire_camp_pipeline();
  53. cache_uniforms();
  54. m_initialized = true;
  55. return true;
  56. }
  57. void VegetationPipeline::shutdown() {
  58. shutdown_stone_pipeline();
  59. shutdown_plant_pipeline();
  60. shutdown_pine_pipeline();
  61. shutdown_olive_pipeline();
  62. shutdown_fire_camp_pipeline();
  63. m_initialized = false;
  64. }
  65. void VegetationPipeline::cache_uniforms() {
  66. if (m_stoneShader != nullptr) {
  67. m_stoneUniforms.view_proj = m_stoneShader->uniform_handle("uViewProj");
  68. m_stoneUniforms.light_direction =
  69. m_stoneShader->uniform_handle("uLightDirection");
  70. }
  71. if (m_plantShader != nullptr) {
  72. m_plantUniforms.view_proj = m_plantShader->uniform_handle("uViewProj");
  73. m_plantUniforms.time = m_plantShader->uniform_handle("uTime");
  74. m_plantUniforms.wind_strength =
  75. m_plantShader->uniform_handle("uWindStrength");
  76. m_plantUniforms.wind_speed = m_plantShader->uniform_handle("uWindSpeed");
  77. m_plantUniforms.light_direction =
  78. m_plantShader->uniform_handle("uLightDirection");
  79. }
  80. if (m_pineShader != nullptr) {
  81. m_pineUniforms.view_proj = m_pineShader->uniform_handle("uViewProj");
  82. m_pineUniforms.time = m_pineShader->uniform_handle("uTime");
  83. m_pineUniforms.wind_strength =
  84. m_pineShader->uniform_handle("uWindStrength");
  85. m_pineUniforms.wind_speed = m_pineShader->uniform_handle("uWindSpeed");
  86. m_pineUniforms.light_direction =
  87. m_pineShader->uniform_handle("uLightDirection");
  88. }
  89. if (m_oliveShader != nullptr) {
  90. m_oliveUniforms.view_proj = m_oliveShader->uniform_handle("uViewProj");
  91. m_oliveUniforms.time = m_oliveShader->uniform_handle("uTime");
  92. m_oliveUniforms.wind_strength =
  93. m_oliveShader->uniform_handle("uWindStrength");
  94. m_oliveUniforms.wind_speed = m_oliveShader->uniform_handle("uWindSpeed");
  95. m_oliveUniforms.light_direction =
  96. m_oliveShader->uniform_handle("uLightDirection");
  97. }
  98. if (m_firecampShader != nullptr) {
  99. m_firecampUniforms.view_proj =
  100. m_firecampShader->uniform_handle("u_viewProj");
  101. m_firecampUniforms.time = m_firecampShader->uniform_handle("u_time");
  102. m_firecampUniforms.flickerSpeed =
  103. m_firecampShader->uniform_handle("u_flickerSpeed");
  104. m_firecampUniforms.flickerAmount =
  105. m_firecampShader->uniform_handle("u_flickerAmount");
  106. m_firecampUniforms.glowStrength =
  107. m_firecampShader->uniform_handle("u_glowStrength");
  108. m_firecampUniforms.fireTexture =
  109. m_firecampShader->uniform_handle("fireTexture");
  110. m_firecampUniforms.camera_right =
  111. m_firecampShader->uniform_handle("u_cameraRight");
  112. m_firecampUniforms.camera_forward =
  113. m_firecampShader->uniform_handle("u_cameraForward");
  114. }
  115. }
  116. void VegetationPipeline::initialize_stone_pipeline() {
  117. initializeOpenGLFunctions();
  118. shutdown_stone_pipeline();
  119. struct StoneVertex {
  120. QVector3D position;
  121. QVector3D normal;
  122. };
  123. const StoneVertex stone_vertices[] = {
  124. {{-0.5F, -0.5F, 0.5F}, {0.0F, 0.0F, 1.0F}},
  125. {{0.5F, -0.5F, 0.5F}, {0.0F, 0.0F, 1.0F}},
  126. {{0.5F, 0.5F, 0.5F}, {0.0F, 0.0F, 1.0F}},
  127. {{-0.5F, 0.5F, 0.5F}, {0.0F, 0.0F, 1.0F}},
  128. {{-0.5F, -0.5F, -0.5F}, {0.0F, 0.0F, -1.0F}},
  129. {{-0.5F, 0.5F, -0.5F}, {0.0F, 0.0F, -1.0F}},
  130. {{0.5F, 0.5F, -0.5F}, {0.0F, 0.0F, -1.0F}},
  131. {{0.5F, -0.5F, -0.5F}, {0.0F, 0.0F, -1.0F}},
  132. {{-0.5F, 0.5F, -0.5F}, {0.0F, 1.0F, 0.0F}},
  133. {{-0.5F, 0.5F, 0.5F}, {0.0F, 1.0F, 0.0F}},
  134. {{0.5F, 0.5F, 0.5F}, {0.0F, 1.0F, 0.0F}},
  135. {{0.5F, 0.5F, -0.5F}, {0.0F, 1.0F, 0.0F}},
  136. {{-0.5F, -0.5F, -0.5F}, {0.0F, -1.0F, 0.0F}},
  137. {{0.5F, -0.5F, -0.5F}, {0.0F, -1.0F, 0.0F}},
  138. {{0.5F, -0.5F, 0.5F}, {0.0F, -1.0F, 0.0F}},
  139. {{-0.5F, -0.5F, 0.5F}, {0.0F, -1.0F, 0.0F}},
  140. {{0.5F, -0.5F, -0.5F}, {1.0F, 0.0F, 0.0F}},
  141. {{0.5F, 0.5F, -0.5F}, {1.0F, 0.0F, 0.0F}},
  142. {{0.5F, 0.5F, 0.5F}, {1.0F, 0.0F, 0.0F}},
  143. {{0.5F, -0.5F, 0.5F}, {1.0F, 0.0F, 0.0F}},
  144. {{-0.5F, -0.5F, -0.5F}, {-1.0F, 0.0F, 0.0F}},
  145. {{-0.5F, -0.5F, 0.5F}, {-1.0F, 0.0F, 0.0F}},
  146. {{-0.5F, 0.5F, 0.5F}, {-1.0F, 0.0F, 0.0F}},
  147. {{-0.5F, 0.5F, -0.5F}, {-1.0F, 0.0F, 0.0F}},
  148. };
  149. const uint16_t stone_indices[] = {
  150. 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 9, 10, 10, 11, 8,
  151. 12, 13, 14, 14, 15, 12, 16, 17, 18, 18, 19, 16, 20, 21, 22, 22, 23, 20};
  152. glGenVertexArrays(1, &m_stoneVao);
  153. glBindVertexArray(m_stoneVao);
  154. glGenBuffers(1, &m_stoneVertexBuffer);
  155. glBindBuffer(GL_ARRAY_BUFFER, m_stoneVertexBuffer);
  156. glBufferData(GL_ARRAY_BUFFER, sizeof(stone_vertices), stone_vertices,
  157. GL_STATIC_DRAW);
  158. m_stoneVertexCount = CubeVertexCount;
  159. glGenBuffers(1, &m_stoneIndexBuffer);
  160. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_stoneIndexBuffer);
  161. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(stone_indices), stone_indices,
  162. GL_STATIC_DRAW);
  163. constexpr int k_stone_index_count = 36;
  164. m_stoneIndexCount = k_stone_index_count;
  165. glEnableVertexAttribArray(Position);
  166. glVertexAttribPointer(
  167. Position, Vec3, GL_FLOAT, GL_FALSE, sizeof(StoneVertex),
  168. reinterpret_cast<void *>(offsetof(StoneVertex, position)));
  169. glEnableVertexAttribArray(Normal);
  170. glVertexAttribPointer(
  171. Normal, Vec3, GL_FLOAT, GL_FALSE, sizeof(StoneVertex),
  172. reinterpret_cast<void *>(offsetof(StoneVertex, normal)));
  173. glEnableVertexAttribArray(TexCoord);
  174. glVertexAttribDivisor(TexCoord, 1);
  175. glEnableVertexAttribArray(InstancePosition);
  176. glVertexAttribDivisor(InstancePosition, 1);
  177. glBindVertexArray(0);
  178. glBindBuffer(GL_ARRAY_BUFFER, 0);
  179. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  180. }
  181. void VegetationPipeline::shutdown_stone_pipeline() {
  182. if (QOpenGLContext::currentContext() == nullptr) {
  183. m_stoneVao = 0;
  184. m_stoneVertexBuffer = 0;
  185. m_stoneIndexBuffer = 0;
  186. m_stoneVertexCount = 0;
  187. m_stoneIndexCount = 0;
  188. return;
  189. }
  190. initializeOpenGLFunctions();
  191. if (m_stoneIndexBuffer != 0U) {
  192. glDeleteBuffers(1, &m_stoneIndexBuffer);
  193. m_stoneIndexBuffer = 0;
  194. }
  195. if (m_stoneVertexBuffer != 0U) {
  196. glDeleteBuffers(1, &m_stoneVertexBuffer);
  197. m_stoneVertexBuffer = 0;
  198. }
  199. if (m_stoneVao != 0U) {
  200. glDeleteVertexArrays(1, &m_stoneVao);
  201. m_stoneVao = 0;
  202. }
  203. m_stoneVertexCount = 0;
  204. m_stoneIndexCount = 0;
  205. }
  206. void VegetationPipeline::initialize_plant_pipeline() {
  207. initializeOpenGLFunctions();
  208. shutdown_plant_pipeline();
  209. struct PlantVertex {
  210. QVector3D position;
  211. QVector2D tex_coord;
  212. QVector3D normal;
  213. };
  214. const PlantVertex plant_vertices[] = {
  215. {{-0.5F, 0.0F, 0.0F}, {0.0F, 0.0F}, {0.0F, 0.0F, 1.0F}},
  216. {{0.5F, 0.0F, 0.0F}, {1.0F, 0.0F}, {0.0F, 0.0F, 1.0F}},
  217. {{0.5F, 1.0F, 0.0F}, {1.0F, 1.0F}, {0.0F, 0.0F, 1.0F}},
  218. {{-0.5F, 1.0F, 0.0F}, {0.0F, 1.0F}, {0.0F, 0.0F, 1.0F}},
  219. {{0.5F, 0.0F, 0.0F}, {0.0F, 0.0F}, {0.0F, 0.0F, -1.0F}},
  220. {{-0.5F, 0.0F, 0.0F}, {1.0F, 0.0F}, {0.0F, 0.0F, -1.0F}},
  221. {{-0.5F, 1.0F, 0.0F}, {1.0F, 1.0F}, {0.0F, 0.0F, -1.0F}},
  222. {{0.5F, 1.0F, 0.0F}, {0.0F, 1.0F}, {0.0F, 0.0F, -1.0F}},
  223. {{0.0F, 0.0F, -0.5F}, {0.0F, 0.0F}, {1.0F, 0.0F, 0.0F}},
  224. {{0.0F, 0.0F, 0.5F}, {1.0F, 0.0F}, {1.0F, 0.0F, 0.0F}},
  225. {{0.0F, 1.0F, 0.5F}, {1.0F, 1.0F}, {1.0F, 0.0F, 0.0F}},
  226. {{0.0F, 1.0F, -0.5F}, {0.0F, 1.0F}, {1.0F, 0.0F, 0.0F}},
  227. {{0.0F, 0.0F, 0.5F}, {0.0F, 0.0F}, {-1.0F, 0.0F, 0.0F}},
  228. {{0.0F, 0.0F, -0.5F}, {1.0F, 0.0F}, {-1.0F, 0.0F, 0.0F}},
  229. {{0.0F, 1.0F, -0.5F}, {1.0F, 1.0F}, {-1.0F, 0.0F, 0.0F}},
  230. {{0.0F, 1.0F, 0.5F}, {0.0F, 1.0F}, {-1.0F, 0.0F, 0.0F}},
  231. };
  232. const unsigned short plant_indices[] = {
  233. 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7,
  234. 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15,
  235. };
  236. glGenVertexArrays(1, &m_plantVao);
  237. glBindVertexArray(m_plantVao);
  238. glGenBuffers(1, &m_plantVertexBuffer);
  239. glBindBuffer(GL_ARRAY_BUFFER, m_plantVertexBuffer);
  240. glBufferData(GL_ARRAY_BUFFER, sizeof(plant_vertices), plant_vertices,
  241. GL_STATIC_DRAW);
  242. m_plantVertexCount = PlantCrossQuadVertexCount;
  243. glEnableVertexAttribArray(Position);
  244. glVertexAttribPointer(
  245. Position, Vec3, GL_FLOAT, GL_FALSE, sizeof(PlantVertex),
  246. reinterpret_cast<void *>(offsetof(PlantVertex, position)));
  247. glEnableVertexAttribArray(Normal);
  248. glVertexAttribPointer(
  249. Normal, Vec2, GL_FLOAT, GL_FALSE, sizeof(PlantVertex),
  250. reinterpret_cast<void *>(offsetof(PlantVertex, tex_coord)));
  251. glEnableVertexAttribArray(TexCoord);
  252. glVertexAttribPointer(
  253. TexCoord, Vec3, GL_FLOAT, GL_FALSE, sizeof(PlantVertex),
  254. reinterpret_cast<void *>(offsetof(PlantVertex, normal)));
  255. glGenBuffers(1, &m_plantIndexBuffer);
  256. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_plantIndexBuffer);
  257. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(plant_indices), plant_indices,
  258. GL_STATIC_DRAW);
  259. m_plantIndexCount = PlantCrossQuadIndexCount;
  260. glEnableVertexAttribArray(InstancePosition);
  261. glVertexAttribDivisor(InstancePosition, 1);
  262. glEnableVertexAttribArray(InstanceScale);
  263. glVertexAttribDivisor(InstanceScale, 1);
  264. glEnableVertexAttribArray(InstanceColor);
  265. glVertexAttribDivisor(InstanceColor, 1);
  266. glBindVertexArray(0);
  267. glBindBuffer(GL_ARRAY_BUFFER, 0);
  268. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  269. }
  270. void VegetationPipeline::shutdown_plant_pipeline() {
  271. if (QOpenGLContext::currentContext() == nullptr) {
  272. m_plantVao = 0;
  273. m_plantVertexBuffer = 0;
  274. m_plantIndexBuffer = 0;
  275. m_plantVertexCount = 0;
  276. m_plantIndexCount = 0;
  277. return;
  278. }
  279. initializeOpenGLFunctions();
  280. if (m_plantIndexBuffer != 0U) {
  281. glDeleteBuffers(1, &m_plantIndexBuffer);
  282. m_plantIndexBuffer = 0;
  283. }
  284. if (m_plantVertexBuffer != 0U) {
  285. glDeleteBuffers(1, &m_plantVertexBuffer);
  286. m_plantVertexBuffer = 0;
  287. }
  288. if (m_plantVao != 0U) {
  289. glDeleteVertexArrays(1, &m_plantVao);
  290. m_plantVao = 0;
  291. }
  292. m_plantVertexCount = 0;
  293. m_plantIndexCount = 0;
  294. }
  295. void VegetationPipeline::initialize_pine_pipeline() {
  296. initializeOpenGLFunctions();
  297. shutdown_pine_pipeline();
  298. struct PineVertex {
  299. QVector3D position;
  300. QVector2D tex_coord;
  301. QVector3D normal;
  302. };
  303. constexpr int k_segments = PineTreeSegments;
  304. constexpr float k_two_pi = 6.28318530718F;
  305. std::vector<PineVertex> vertices;
  306. vertices.reserve(k_segments * 5 + 1);
  307. std::vector<unsigned short> indices;
  308. indices.reserve(k_segments * 6 * 4 + k_segments * 3);
  309. auto add_ring = [&](float radius, float y, float normalUp, float v_coord,
  310. const QVector2D &center_offset =
  311. QVector2D(0.0F, 0.0F)) -> int {
  312. const int start = static_cast<int>(vertices.size());
  313. for (int i = 0; i < k_segments; ++i) {
  314. const float t = static_cast<float>(i) / static_cast<float>(k_segments);
  315. const float angle = t * k_two_pi;
  316. const float nx = std::cos(angle);
  317. const float nz = std::sin(angle);
  318. QVector3D normal(nx, normalUp, nz);
  319. normal.normalize();
  320. QVector3D const position(radius * nx + center_offset.x(), y,
  321. radius * nz + center_offset.y());
  322. QVector2D const tex_coord(t, v_coord);
  323. vertices.push_back({position, tex_coord, normal});
  324. }
  325. return start;
  326. };
  327. auto connect_rings = [&](int lowerStart, int upperStart) {
  328. for (int i = 0; i < k_segments; ++i) {
  329. const int next = (i + 1) % k_segments;
  330. const auto lower0 = static_cast<unsigned short>(lowerStart + i);
  331. const auto lower1 = static_cast<unsigned short>(lowerStart + next);
  332. const auto upper0 = static_cast<unsigned short>(upperStart + i);
  333. const auto upper1 = static_cast<unsigned short>(upperStart + next);
  334. indices.push_back(lower0);
  335. indices.push_back(lower1);
  336. indices.push_back(upper1);
  337. indices.push_back(lower0);
  338. indices.push_back(upper1);
  339. indices.push_back(upper0);
  340. }
  341. };
  342. const int trunk_bottom = add_ring(0.12F, 0.0F, 0.0F, 0.0F);
  343. const int trunk_mid = add_ring(0.11F, 0.35F, 0.0F, 0.12F);
  344. const int trunk_top = add_ring(0.10F, 0.58F, 0.05F, 0.30F);
  345. const int branch_base = add_ring(0.60F, 0.64F, 0.35F, 0.46F);
  346. const int branch_mid = add_ring(0.42F, 0.82F, 0.6F, 0.68F);
  347. const int branch_upper = add_ring(0.24F, 1.00F, 0.7F, 0.88F);
  348. const int branch_tip = add_ring(0.12F, 1.10F, 0.85F, 0.96F);
  349. connect_rings(trunk_bottom, trunk_mid);
  350. connect_rings(trunk_mid, trunk_top);
  351. connect_rings(trunk_top, branch_base);
  352. connect_rings(branch_base, branch_mid);
  353. connect_rings(branch_mid, branch_upper);
  354. connect_rings(branch_upper, branch_tip);
  355. const auto trunk_cap_index = static_cast<unsigned short>(vertices.size());
  356. vertices.push_back({QVector3D(0.0F, 0.0F, 0.0F), QVector2D(0.5F, 0.0F),
  357. QVector3D(0.0F, -1.0F, 0.0F)});
  358. for (int i = 0; i < k_segments; ++i) {
  359. const int next = (i + 1) % k_segments;
  360. indices.push_back(static_cast<unsigned short>(trunk_bottom + next));
  361. indices.push_back(static_cast<unsigned short>(trunk_bottom + i));
  362. indices.push_back(trunk_cap_index);
  363. }
  364. const auto apex_index = static_cast<unsigned short>(vertices.size());
  365. vertices.push_back({QVector3D(0.0F, 1.18F, 0.0F), QVector2D(0.5F, 1.0F),
  366. QVector3D(0.0F, 1.0F, 0.0F)});
  367. for (int i = 0; i < k_segments; ++i) {
  368. const int next = (i + 1) % k_segments;
  369. indices.push_back(static_cast<unsigned short>(branch_tip + i));
  370. indices.push_back(static_cast<unsigned short>(branch_tip + next));
  371. indices.push_back(apex_index);
  372. }
  373. glGenVertexArrays(1, &m_pineVao);
  374. glBindVertexArray(m_pineVao);
  375. glGenBuffers(1, &m_pineVertexBuffer);
  376. glBindBuffer(GL_ARRAY_BUFFER, m_pineVertexBuffer);
  377. glBufferData(GL_ARRAY_BUFFER,
  378. static_cast<GLsizeiptr>(vertices.size() * sizeof(PineVertex)),
  379. vertices.data(), GL_STATIC_DRAW);
  380. m_pineVertexCount = static_cast<GLsizei>(vertices.size());
  381. glEnableVertexAttribArray(Position);
  382. glVertexAttribPointer(
  383. Position, Vec3, GL_FLOAT, GL_FALSE, sizeof(PineVertex),
  384. reinterpret_cast<void *>(offsetof(PineVertex, position)));
  385. glEnableVertexAttribArray(Normal);
  386. glVertexAttribPointer(
  387. Normal, Vec2, GL_FLOAT, GL_FALSE, sizeof(PineVertex),
  388. reinterpret_cast<void *>(offsetof(PineVertex, tex_coord)));
  389. glEnableVertexAttribArray(TexCoord);
  390. glVertexAttribPointer(TexCoord, Vec3, GL_FLOAT, GL_FALSE, sizeof(PineVertex),
  391. reinterpret_cast<void *>(offsetof(PineVertex, normal)));
  392. glGenBuffers(1, &m_pineIndexBuffer);
  393. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pineIndexBuffer);
  394. glBufferData(GL_ELEMENT_ARRAY_BUFFER,
  395. static_cast<GLsizeiptr>(indices.size() * sizeof(unsigned short)),
  396. indices.data(), GL_STATIC_DRAW);
  397. m_pineIndexCount = static_cast<GLsizei>(indices.size());
  398. glEnableVertexAttribArray(InstancePosition);
  399. glVertexAttribDivisor(InstancePosition, 1);
  400. glEnableVertexAttribArray(InstanceScale);
  401. glVertexAttribDivisor(InstanceScale, 1);
  402. glEnableVertexAttribArray(InstanceColor);
  403. glVertexAttribDivisor(InstanceColor, 1);
  404. glBindVertexArray(0);
  405. glBindBuffer(GL_ARRAY_BUFFER, 0);
  406. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  407. }
  408. void VegetationPipeline::shutdown_pine_pipeline() {
  409. if (QOpenGLContext::currentContext() == nullptr) {
  410. m_pineVao = 0;
  411. m_pineVertexBuffer = 0;
  412. m_pineIndexBuffer = 0;
  413. m_pineVertexCount = 0;
  414. m_pineIndexCount = 0;
  415. return;
  416. }
  417. initializeOpenGLFunctions();
  418. if (m_pineIndexBuffer != 0U) {
  419. glDeleteBuffers(1, &m_pineIndexBuffer);
  420. m_pineIndexBuffer = 0;
  421. }
  422. if (m_pineVertexBuffer != 0U) {
  423. glDeleteBuffers(1, &m_pineVertexBuffer);
  424. m_pineVertexBuffer = 0;
  425. }
  426. if (m_pineVao != 0U) {
  427. glDeleteVertexArrays(1, &m_pineVao);
  428. m_pineVao = 0;
  429. }
  430. m_pineVertexCount = 0;
  431. m_pineIndexCount = 0;
  432. }
  433. void VegetationPipeline::initialize_olive_pipeline() {
  434. initializeOpenGLFunctions();
  435. shutdown_olive_pipeline();
  436. struct OliveVertex {
  437. QVector3D position;
  438. QVector2D tex_coord;
  439. QVector3D normal;
  440. };
  441. constexpr int k_segments = OliveTreeSegments;
  442. constexpr float k_two_pi = 6.28318530718F;
  443. std::vector<OliveVertex> vertices;
  444. vertices.reserve(k_segments * 40);
  445. std::vector<unsigned short> indices;
  446. indices.reserve(k_segments * 6 * 40);
  447. auto add_ring = [&](float radius, float y, float normalUp, float v_coord,
  448. const QVector2D &offset = QVector2D(0.0F, 0.0F)) -> int {
  449. const int start = static_cast<int>(vertices.size());
  450. for (int i = 0; i < k_segments; ++i) {
  451. const float t = static_cast<float>(i) / static_cast<float>(k_segments);
  452. const float angle = t * k_two_pi;
  453. const float nx = std::cos(angle);
  454. const float nz = std::sin(angle);
  455. QVector3D normal(nx, normalUp, nz);
  456. normal.normalize();
  457. QVector3D const position(radius * nx + offset.x(), y,
  458. radius * nz + offset.y());
  459. vertices.push_back({position, QVector2D(t, v_coord), normal});
  460. }
  461. return start;
  462. };
  463. auto connect_rings = [&](int lower, int upper) {
  464. for (int i = 0; i < k_segments; ++i) {
  465. const int next = (i + 1) % k_segments;
  466. indices.push_back(static_cast<unsigned short>(lower + i));
  467. indices.push_back(static_cast<unsigned short>(lower + next));
  468. indices.push_back(static_cast<unsigned short>(upper + next));
  469. indices.push_back(static_cast<unsigned short>(lower + i));
  470. indices.push_back(static_cast<unsigned short>(upper + next));
  471. indices.push_back(static_cast<unsigned short>(upper + i));
  472. }
  473. };
  474. auto add_cap = [&](int ring, float capY, const QVector2D &offset, float v) {
  475. const int topIdx = static_cast<int>(vertices.size());
  476. vertices.push_back({QVector3D(offset.x(), capY, offset.y()),
  477. QVector2D(0.5F, v), QVector3D(0.0F, 1.0F, 0.0F)});
  478. for (int i = 0; i < k_segments; ++i) {
  479. const int next = (i + 1) % k_segments;
  480. indices.push_back(static_cast<unsigned short>(ring + i));
  481. indices.push_back(static_cast<unsigned short>(ring + next));
  482. indices.push_back(static_cast<unsigned short>(topIdx));
  483. }
  484. };
  485. int t0 = add_ring(0.14F, 0.00F, -0.2F, 0.00F);
  486. int t1 = add_ring(0.12F, 0.08F, 0.0F, 0.06F);
  487. int t2 = add_ring(0.09F, 0.15F, 0.1F, 0.12F);
  488. connect_rings(t0, t1);
  489. connect_rings(t1, t2);
  490. auto add_branch = [&](float dir_x, float dir_z, float base_y, float length,
  491. float branch_r, float leaf_r, float v_start) {
  492. float len = std::sqrt(dir_x * dir_x + dir_z * dir_z);
  493. dir_x /= len;
  494. dir_z /= len;
  495. float rise = 0.5F;
  496. float dx = dir_x * std::cos(0.5F);
  497. float dz = dir_z * std::cos(0.5F);
  498. float dy = std::sin(0.4F);
  499. int b0 = add_ring(branch_r, base_y, 0.0F, v_start);
  500. float mid_dist = length * 0.5F;
  501. QVector2D mid_offset(dx * mid_dist, dz * mid_dist);
  502. int b1 = add_ring(branch_r * 0.6F, base_y + dy * mid_dist, 0.3F,
  503. v_start + 0.1F, mid_offset);
  504. float tip_dist = length;
  505. QVector2D tip_offset(dx * tip_dist, dz * tip_dist);
  506. float tip_y = base_y + dy * tip_dist;
  507. int b2 = add_ring(branch_r * 0.3F, tip_y, 0.5F, v_start + 0.2F, tip_offset);
  508. connect_rings(b0, b1);
  509. connect_rings(b1, b2);
  510. float ly = tip_y - leaf_r * 0.2F;
  511. int l0 = add_ring(leaf_r * 0.6F, ly, -0.4F, 0.50F, tip_offset);
  512. int l1 =
  513. add_ring(leaf_r * 0.9F, ly + leaf_r * 0.35F, 0.0F, 0.65F, tip_offset);
  514. int l2 =
  515. add_ring(leaf_r * 0.85F, ly + leaf_r * 0.65F, 0.2F, 0.80F, tip_offset);
  516. int l3 =
  517. add_ring(leaf_r * 0.5F, ly + leaf_r * 0.90F, 0.6F, 0.92F, tip_offset);
  518. connect_rings(b2, l0);
  519. connect_rings(l0, l1);
  520. connect_rings(l1, l2);
  521. connect_rings(l2, l3);
  522. add_cap(l3, ly + leaf_r * 1.0F, tip_offset, 1.0F);
  523. };
  524. add_branch(0.8F, 0.3F, 0.14F, 0.30F, 0.025F, 0.18F, 0.18F);
  525. add_branch(-0.7F, 0.5F, 0.15F, 0.32F, 0.022F, 0.20F, 0.20F);
  526. add_branch(0.4F, -0.9F, 0.16F, 0.28F, 0.020F, 0.16F, 0.22F);
  527. add_branch(-0.5F, -0.7F, 0.14F, 0.34F, 0.024F, 0.19F, 0.19F);
  528. m_oliveVertexCount = static_cast<GLsizei>(vertices.size());
  529. m_oliveIndexCount = static_cast<GLsizei>(indices.size());
  530. glGenVertexArrays(1, &m_oliveVao);
  531. glBindVertexArray(m_oliveVao);
  532. glGenBuffers(1, &m_oliveVertexBuffer);
  533. glBindBuffer(GL_ARRAY_BUFFER, m_oliveVertexBuffer);
  534. glBufferData(GL_ARRAY_BUFFER,
  535. static_cast<GLsizeiptr>(vertices.size() * sizeof(OliveVertex)),
  536. vertices.data(), GL_STATIC_DRAW);
  537. glGenBuffers(1, &m_oliveIndexBuffer);
  538. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_oliveIndexBuffer);
  539. glBufferData(GL_ELEMENT_ARRAY_BUFFER,
  540. static_cast<GLsizeiptr>(indices.size() * sizeof(unsigned short)),
  541. indices.data(), GL_STATIC_DRAW);
  542. glEnableVertexAttribArray(0);
  543. glVertexAttribPointer(
  544. 0, 3, GL_FLOAT, GL_FALSE, sizeof(OliveVertex),
  545. reinterpret_cast<void *>(offsetof(OliveVertex, position)));
  546. glEnableVertexAttribArray(1);
  547. glVertexAttribPointer(
  548. 1, 2, GL_FLOAT, GL_FALSE, sizeof(OliveVertex),
  549. reinterpret_cast<void *>(offsetof(OliveVertex, tex_coord)));
  550. glEnableVertexAttribArray(2);
  551. glVertexAttribPointer(
  552. 2, 3, GL_FLOAT, GL_FALSE, sizeof(OliveVertex),
  553. reinterpret_cast<void *>(offsetof(OliveVertex, normal)));
  554. glEnableVertexAttribArray(InstancePosition);
  555. glVertexAttribDivisor(InstancePosition, 1);
  556. glEnableVertexAttribArray(InstanceScale);
  557. glVertexAttribDivisor(InstanceScale, 1);
  558. glEnableVertexAttribArray(InstanceColor);
  559. glVertexAttribDivisor(InstanceColor, 1);
  560. glBindVertexArray(0);
  561. glBindBuffer(GL_ARRAY_BUFFER, 0);
  562. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  563. }
  564. void VegetationPipeline::shutdown_olive_pipeline() {
  565. if (QOpenGLContext::currentContext() == nullptr) {
  566. m_oliveVao = 0;
  567. m_oliveVertexBuffer = 0;
  568. m_oliveIndexBuffer = 0;
  569. m_oliveVertexCount = 0;
  570. m_oliveIndexCount = 0;
  571. return;
  572. }
  573. initializeOpenGLFunctions();
  574. if (m_oliveIndexBuffer != 0U) {
  575. glDeleteBuffers(1, &m_oliveIndexBuffer);
  576. m_oliveIndexBuffer = 0;
  577. }
  578. if (m_oliveVertexBuffer != 0U) {
  579. glDeleteBuffers(1, &m_oliveVertexBuffer);
  580. m_oliveVertexBuffer = 0;
  581. }
  582. if (m_oliveVao != 0U) {
  583. glDeleteVertexArrays(1, &m_oliveVao);
  584. m_oliveVao = 0;
  585. }
  586. m_oliveVertexCount = 0;
  587. m_oliveIndexCount = 0;
  588. }
  589. void VegetationPipeline::initialize_fire_camp_pipeline() {
  590. initializeOpenGLFunctions();
  591. shutdown_fire_camp_pipeline();
  592. struct FireCampVertex {
  593. QVector3D position;
  594. QVector2D tex_coord;
  595. };
  596. constexpr std::size_t k_firecamp_vertex_reserve = 12;
  597. constexpr std::size_t k_firecamp_index_reserve = 18;
  598. std::vector<FireCampVertex> vertices;
  599. vertices.reserve(k_firecamp_vertex_reserve);
  600. std::vector<unsigned short> indices;
  601. indices.reserve(k_firecamp_index_reserve);
  602. auto append_plane = [&](float planeIndex) {
  603. auto const base = static_cast<unsigned short>(vertices.size());
  604. vertices.push_back(
  605. {QVector3D(-1.0F, 0.0F, planeIndex), QVector2D(0.0F, 0.0F)});
  606. vertices.push_back(
  607. {QVector3D(1.0F, 0.0F, planeIndex), QVector2D(1.0F, 0.0F)});
  608. vertices.push_back(
  609. {QVector3D(1.0F, 2.0F, planeIndex), QVector2D(1.0F, 1.0F)});
  610. vertices.push_back(
  611. {QVector3D(-1.0F, 2.0F, planeIndex), QVector2D(0.0F, 1.0F)});
  612. indices.push_back(base + 0);
  613. indices.push_back(base + 1);
  614. indices.push_back(base + 2);
  615. indices.push_back(base + 0);
  616. indices.push_back(base + 2);
  617. indices.push_back(base + 3);
  618. };
  619. append_plane(0.0F);
  620. append_plane(1.0F);
  621. append_plane(2.0F);
  622. glGenVertexArrays(1, &m_firecampVao);
  623. glBindVertexArray(m_firecampVao);
  624. glGenBuffers(1, &m_firecampVertexBuffer);
  625. glBindBuffer(GL_ARRAY_BUFFER, m_firecampVertexBuffer);
  626. glBufferData(
  627. GL_ARRAY_BUFFER,
  628. static_cast<GLsizeiptr>(vertices.size() * sizeof(FireCampVertex)),
  629. vertices.data(), GL_STATIC_DRAW);
  630. m_firecampVertexCount = static_cast<GLsizei>(vertices.size());
  631. glEnableVertexAttribArray(Position);
  632. glVertexAttribPointer(Position, Vec3, GL_FLOAT, GL_FALSE,
  633. sizeof(FireCampVertex), reinterpret_cast<void *>(0));
  634. glEnableVertexAttribArray(Normal);
  635. glVertexAttribPointer(
  636. Normal, Vec2, GL_FLOAT, GL_FALSE, sizeof(FireCampVertex),
  637. reinterpret_cast<void *>(offsetof(FireCampVertex, tex_coord)));
  638. glGenBuffers(1, &m_firecampIndexBuffer);
  639. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_firecampIndexBuffer);
  640. glBufferData(GL_ELEMENT_ARRAY_BUFFER,
  641. static_cast<GLsizeiptr>(indices.size() * sizeof(unsigned short)),
  642. indices.data(), GL_STATIC_DRAW);
  643. m_firecampIndexCount = static_cast<GLsizei>(indices.size());
  644. glEnableVertexAttribArray(InstancePosition);
  645. glVertexAttribDivisor(InstancePosition, 1);
  646. glEnableVertexAttribArray(InstanceScale);
  647. glVertexAttribDivisor(InstanceScale, 1);
  648. glBindVertexArray(0);
  649. glBindBuffer(GL_ARRAY_BUFFER, 0);
  650. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  651. }
  652. void VegetationPipeline::shutdown_fire_camp_pipeline() {
  653. if (QOpenGLContext::currentContext() == nullptr) {
  654. m_firecampVao = 0;
  655. m_firecampVertexBuffer = 0;
  656. m_firecampIndexBuffer = 0;
  657. m_firecampVertexCount = 0;
  658. m_firecampIndexCount = 0;
  659. return;
  660. }
  661. initializeOpenGLFunctions();
  662. if (m_firecampIndexBuffer != 0U) {
  663. glDeleteBuffers(1, &m_firecampIndexBuffer);
  664. m_firecampIndexBuffer = 0;
  665. }
  666. if (m_firecampVertexBuffer != 0U) {
  667. glDeleteBuffers(1, &m_firecampVertexBuffer);
  668. m_firecampVertexBuffer = 0;
  669. }
  670. if (m_firecampVao != 0U) {
  671. glDeleteVertexArrays(1, &m_firecampVao);
  672. m_firecampVao = 0;
  673. }
  674. m_firecampVertexCount = 0;
  675. m_firecampIndexCount = 0;
  676. }
  677. } // namespace Render::GL::BackendPipelines