57 if (m_paused || deltaTime <= 0.0F)
62 for (
auto &p : m_particles)
64 p.velocity += p.acceleration * deltaTime;
65 p.position += p.velocity * deltaTime;
70 std::remove_if(m_particles.begin(), m_particles.end(), [](
const Particle &p) { return !p.IsAlive(); }),
77 if (!m_shader || !m_quad || m_particles.empty())
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);
88 GLboolean prevDepthMask = GL_TRUE;
89 GLboolean prevBlend = GL_FALSE;
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);
97 glDepthMask(GL_FALSE);
99 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
102 m_shader->SetUniform(
"uView", cameraData.
viewMatrix);
108 for (
const auto &p : m_particles)
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);
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);
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());
125 graphicsAPI.UnbindMesh(m_quad.get());
128 glBlendFunc(
static_cast<GLenum>(prevSrcRGB),
static_cast<GLenum>(prevDstRGB));
129 if (prevBlend == GL_FALSE)
133 glDepthMask(prevDepthMask);
Pool-allocated CPU particles drawn as additive billboards.
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.