Monad Engine da02fba2
> Undercity Codex_
Loading...
Searching...
No Matches
ParticleSystem.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4
5#include <GL/glew.h>
6#include <glm/gtc/matrix_transform.hpp>
7
8#include "Common.h"
9#include "Engine.h"
10#include "Log.h"
11#include "graphics/GraphicsAPI.h"
12#include "graphics/ShaderProgram.h"
13#include "render/Builder.h"
14#include "render/Mesh.h"
15
16namespace mnd
17{
18
19namespace
20{
21constexpr const char *kParticleVertPath = "shaders/particle.vert";
22constexpr const char *kParticleFragPath = "shaders/particle.frag";
23} // namespace
24
26{
27 auto data = Builder::CreateRectangle(1.0F, 1.0F);
28 m_quad = data.buildMesh();
29
31 auto vert = fs.LoadAssetFileText(kParticleVertPath);
32 auto frag = fs.LoadAssetFileText(kParticleFragPath);
33 if (vert.empty() || frag.empty())
34 {
35 LOG_ERROR("ParticleSystem::Init failed to load shader sources");
36 return;
37 }
38
40 if (!m_shader)
41 {
42 LOG_ERROR("ParticleSystem::Init failed to compile particle shader");
43 }
44
45 m_particles.reserve(m_capacity);
46}
47
49{
50 m_particles.clear();
51 m_shader.reset();
52 m_quad.reset();
53}
54
56{
57 if (m_paused || deltaTime <= 0.0F)
58 {
59 return;
60 }
61
62 for (auto &p : m_particles)
63 {
64 p.velocity += p.acceleration * deltaTime;
65 p.position += p.velocity * deltaTime;
66 p.age += deltaTime;
67 }
68
69 m_particles.erase(
70 std::remove_if(m_particles.begin(), m_particles.end(), [](const Particle &p) { return !p.IsAlive(); }),
71 m_particles.end()
72 );
73}
74
75void ParticleSystem::Render(const CameraData &cameraData)
76{
77 if (!m_shader || !m_quad || m_particles.empty())
78 {
79 return;
80 }
81
82 // Camera-aligned billboard basis: extract right and up from the view matrix.
83 const glm::mat4 &view = cameraData.viewMatrix;
84 const vec3 right = glm::vec3(view[0][0], view[1][0], view[2][0]);
85 const vec3 up = glm::vec3(view[0][1], view[1][1], view[2][1]);
86 const vec3 fwd = glm::cross(right, up);
87
88 GLboolean prevDepthMask = GL_TRUE;
89 GLboolean prevBlend = GL_FALSE;
90 GLint prevSrcRGB = 0;
91 GLint prevDstRGB = 0;
92 glGetBooleanv(GL_DEPTH_WRITEMASK, &prevDepthMask);
93 prevBlend = glIsEnabled(GL_BLEND);
94 glGetIntegerv(GL_BLEND_SRC_RGB, &prevSrcRGB);
95 glGetIntegerv(GL_BLEND_DST_RGB, &prevDstRGB);
96
97 glDepthMask(GL_FALSE);
98 glEnable(GL_BLEND);
99 glBlendFunc(GL_SRC_ALPHA, GL_ONE); // additive
100
101 m_shader->Bind();
102 m_shader->SetUniform("uView", cameraData.viewMatrix);
103 m_shader->SetUniform("uProjection", cameraData.projectionMatrix);
104
105 auto &graphicsAPI = Engine::GetInstance().GetGraphicsAPI();
106 graphicsAPI.BindMesh(m_quad.get());
107
108 for (const auto &p : m_particles)
109 {
110 const float t = std::clamp(p.age / std::max(p.lifetime, 1e-4F), 0.0F, 1.0F);
111 const float size = glm::mix(p.sizeStart, p.sizeEnd, t);
112 const vec4 color = glm::mix(p.colorStart, p.colorEnd, t);
113
114 glm::mat4 model(0.0F);
115 model[0] = glm::vec4(right * size, 0.0F);
116 model[1] = glm::vec4(up * size, 0.0F);
117 model[2] = glm::vec4(fwd * size, 0.0F);
118 model[3] = glm::vec4(p.position, 1.0F);
119
120 m_shader->SetUniform("uModel", model);
121 m_shader->SetUniform("uColor", color.r, color.g, color.b, color.a);
122 graphicsAPI.DrawMesh(m_quad.get());
123 }
124
125 graphicsAPI.UnbindMesh(m_quad.get());
126
127 // Restore prior GL state.
128 glBlendFunc(static_cast<GLenum>(prevSrcRGB), static_cast<GLenum>(prevDstRGB));
129 if (prevBlend == GL_FALSE)
130 {
131 glDisable(GL_BLEND);
132 }
133 glDepthMask(prevDepthMask);
134}
135
136void ParticleSystem::Spawn(const Particle &particle)
137{
138 if (m_particles.size() >= m_capacity)
139 {
140 // Drop oldest to keep recent emissions visible. O(n) but capacity is small.
141 m_particles.erase(m_particles.begin());
142 }
143 m_particles.push_back(particle);
144}
145
146} // namespace mnd
Static factory methods for creating common primitive mesh shapes.
int GLint
Definition GLForward.h:13
unsigned int GLenum
Definition GLForward.h:12
Console logging macros for all engine and game code.
#define LOG_ERROR(fmt,...)
Definition Log.h:81
Pool-allocated CPU particles drawn as additive billboards.
static MeshData CreateRectangle(f32 width, f32 height)
Create a flat quad with the given dimensions.
Definition Builder.cpp:97
GraphicsAPI & GetGraphicsAPI()
Returns the low-level OpenGL wrapper used for all draw calls.
Definition Engine.cpp:418
FileSystem & GetFileSystem()
Returns the file system helper for asset-relative path resolution.
Definition Engine.cpp:453
static Engine & GetInstance()
Returns the single Engine instance (created on first call).
Definition Engine.cpp:59
std::string LoadAssetFileText(const std::string &relativePath)
Load a text file from the assets directory into a string.
std::shared_ptr< ShaderProgram > CreateShaderProgram(const std::string &vertexSource, const std::string &fragmentSource)
Compile vertex + fragment GLSL source and link into a program.
void BindMesh(Mesh *mesh)
Bind the mesh's VAO so the next draw call uses its geometry.
void Update(f32 deltaTime)
Step every live particle, swap-erase dead ones.
void Spawn(const Particle &particle)
Push a fully-configured particle into the pool. Drops oldest if full.
void Render(const CameraData &cameraData)
Camera-aligned billboard pass. Call after the lit pass, before UI.
glm::vec4 vec4
4-component float vector (e.g. RGBA colour, homogeneous coords).
Definition Types.h:52
glm::vec3 vec3
3-component float vector (e.g. world position, RGB colour, normals).
Definition Types.h:51
float f32
32-bit IEEE float — the standard GL scalar type.
Definition Types.h:40
Per-frame camera matrices and world-space position.
Definition Common.h:35
mat4 projectionMatrix
Camera → Clip space transform (perspective or ortho).
Definition Common.h:37
mat4 viewMatrix
World → Camera space transform (glm::lookAt result).
Definition Common.h:36