57 FireworksWindow(
const std::string &data_root,
int width,
int height,
bool fullscreen,
bool enable_vsync) :
mxvk::
VK_Window(
"Fireworks - [3D Particle Effect]", width, height, fullscreen,
MXVK_VALIDATION, enable_vsync), data_root(data_root), vertices(
MAX_PARTICLES) {
59 setFont(data_root +
"/font.ttf", 24);
60 last_update_time = SDL_GetTicks();
64 if (
device != VK_NULL_HANDLE) {
67 point_batch.cleanup();
70 void event(SDL_Event &e)
override {
71 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
73 }
else if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_SPACE) {
74 trigger_random_explosion();
79 if (!has_active_particles()) {
80 printText(
"Explosion - Press Space to Trigger", 25, 25, SDL_Color{255, 64, 64, 255});
87 if (!ensure_resources()) {
91 const Uint32 current_time = SDL_GetTicks();
92 float delta_time =
static_cast<float>(current_time - last_update_time) / 1000.0f;
93 last_update_time = current_time;
94 delta_time = std::min(delta_time, 0.1f);
96 update_particles(delta_time);
99 point_batch.upload_vertices(vertices.data(), active_vertices);
100 point_batch.update_mvp(image_index, make_mvp());
101 point_batch.render(cmd, image_index);
105 bool ensure_resources() {
106 if (point_batch.
loaded()) {
113 point_batch.load(
this, data_root +
"/star.png", data_root +
"/fireworks.vert.spv", data_root +
"/fireworks.frag.spv", vertices.size());
114 point_batch.set_additive_blending(
true);
115 point_batch.set_depth_test_enabled(
false);
116 point_batch.set_depth_write_enabled(
false);
117 last_update_time = SDL_GetTicks();
121 void trigger_random_explosion() { trigger_explosion(glm::vec3(
random_float(-3.0f, 3.0f),
random_float(-3.0f, 3.0f), 0.0f)); }
123 void trigger_explosion(
const glm::vec3 &origin) {
124 if (explosions.size() >= MAX_EXPLOSIONS) {
129 explosion.particles.resize(PARTICLES_PER_EXPLOSION);
130 for (Particle &particle : explosion.particles) {
131 reset_particle(particle, origin);
133 explosions.push_back(std::move(explosion));
136 void reset_particle(Particle &particle,
const glm::vec3 &origin)
const {
137 particle.position = origin;
138 particle.velocity = glm::sphericalRand(PARTICLE_SPEED);
140 particle.initial_lifetime = particle.
lifetime;
144 void update_particles(
float delta_time) {
145 for (Explosion &explosion : explosions) {
146 for (Particle &particle : explosion.particles) {
151 particle.position += particle.velocity * delta_time;
152 particle.color.a = particle.
lifetime;
156 std::erase_if(explosions, [](
const Explosion &explosion) {
return std::ranges::none_of(explosion.particles, [](
const Particle &particle) { return particle.lifetime > 0.0f; }); });
159 [[nodiscard]]
bool has_active_particles()
const {
160 return std::ranges::any_of(explosions, [](
const Explosion &explosion) {
return std::ranges::any_of(explosion.particles, [](
const Particle &particle) { return particle.lifetime > 0.0f; }); });
163 void write_vertices() {
165 for (
const Explosion &explosion : explosions) {
166 for (
const Particle &particle : explosion.particles) {
167 if (particle.
lifetime <= 0.0f || active_vertices >= vertices.size()) {
171 mxvk::PointSpriteVertex &vertex = vertices[active_vertices++];
172 vertex.
position[0] = particle.position.
x;
173 vertex.
position[1] = particle.position.
y;
174 vertex.
position[2] = particle.position.z;
176 vertex.
color[0] = particle.color.r;
177 vertex.
color[1] = particle.color.g;
178 vertex.
color[2] = particle.color.b;
179 vertex.
color[3] = particle.color.a;
184 [[nodiscard]] glm::mat4 make_mvp()
const {
186 const float aspect = extent.height > 0U ?
static_cast<float>(extent.width) /
static_cast<float>(extent.height) :
SCENE_REFERENCE_ASPECT;
188 float vertical_field_of_view = glm::radians(VERTICAL_FIELD_OF_VIEW_DEGREES);
189 if (aspect < SCENE_REFERENCE_ASPECT) {
191 vertical_field_of_view = 2.0f * std::atan(reference_half_width / aspect);
194 glm::mat4 projection = glm::perspective(vertical_field_of_view, aspect, 0.1f, 100.0f);
195 projection[1][1] *= -1.0f;
196 const glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
197 const glm::mat4 model(1.0f);
198 return projection * view * model;
201 std::string data_root{};
202 std::vector<Explosion> explosions{};
203 std::vector<mxvk::PointSpriteVertex> vertices{};
204 mxvk::VK_PointSpriteBatch point_batch{};
205 std::size_t active_vertices = 0;
206 Uint32 last_update_time = 0;