55 FireworksWindow(
const std::string &data_root,
int width,
int height,
bool fullscreen,
bool enable_vsync)
60 setFont(data_root +
"/font.ttf", 24);
61 last_update_time = SDL_GetTicks();
65 if (
device != VK_NULL_HANDLE) {
68 point_batch.cleanup();
71 void event(SDL_Event &e)
override {
72 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
74 }
else if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_SPACE) {
75 trigger_random_explosion();
80 if (!has_active_particles()) {
81 printText(
"Explosion - Press Space to Trigger", 25, 25, SDL_Color{255, 64, 64, 255});
86 point_batch.resize(
this);
90 if (!ensure_resources()) {
94 const Uint32 current_time = SDL_GetTicks();
95 float delta_time =
static_cast<float>(current_time - last_update_time) / 1000.0f;
96 last_update_time = current_time;
97 delta_time = std::min(delta_time, 0.1f);
99 update_particles(delta_time);
102 point_batch.upload_vertices(vertices.data(), active_vertices);
103 point_batch.update_mvp(image_index, make_mvp());
104 point_batch.render(cmd, image_index);
108 bool ensure_resources() {
109 if (point_batch.
loaded()) {
118 data_root +
"/star.png",
119 data_root +
"/fireworks.vert.spv",
120 data_root +
"/fireworks.frag.spv",
122 point_batch.set_additive_blending(
true);
123 point_batch.set_depth_test_enabled(
false);
124 point_batch.set_depth_write_enabled(
false);
125 last_update_time = SDL_GetTicks();
129 void trigger_random_explosion() {
133 void trigger_explosion(
const glm::vec3 &origin) {
134 if (explosions.size() >= MAX_EXPLOSIONS) {
139 explosion.particles.resize(PARTICLES_PER_EXPLOSION);
140 for (Particle &particle : explosion.particles) {
141 reset_particle(particle, origin);
143 explosions.push_back(std::move(explosion));
146 void reset_particle(Particle &particle,
const glm::vec3 &origin)
const {
147 particle.position = origin;
148 particle.velocity = glm::sphericalRand(PARTICLE_SPEED);
150 particle.initial_lifetime = particle.
lifetime;
154 void update_particles(
float delta_time) {
155 for (Explosion &explosion : explosions) {
156 for (Particle &particle : explosion.particles) {
161 particle.position += particle.velocity * delta_time;
162 particle.color.a = particle.
lifetime;
166 std::erase_if(explosions, [](
const Explosion &explosion) {
167 return std::ranges::none_of(explosion.particles, [](
const Particle &particle) {
168 return particle.lifetime > 0.0f;
173 [[nodiscard]]
bool has_active_particles()
const {
174 return std::ranges::any_of(explosions, [](
const Explosion &explosion) {
175 return std::ranges::any_of(explosion.particles, [](
const Particle &particle) {
176 return particle.lifetime > 0.0f;
181 void write_vertices() {
183 for (
const Explosion &explosion : explosions) {
184 for (
const Particle &particle : explosion.particles) {
185 if (particle.
lifetime <= 0.0f || active_vertices >= vertices.size()) {
189 mxvk::PointSpriteVertex &vertex = vertices[active_vertices++];
190 vertex.
position[0] = particle.position.
x;
191 vertex.
position[1] = particle.position.
y;
192 vertex.
position[2] = particle.position.z;
194 vertex.
color[0] = particle.color.r;
195 vertex.
color[1] = particle.color.g;
196 vertex.
color[2] = particle.color.b;
197 vertex.
color[3] = particle.color.a;
202 [[nodiscard]] glm::mat4 make_mvp()
const {
204 const float aspect = extent.height > 0U ?
static_cast<float>(extent.width) /
static_cast<float>(extent.height) : 16.0f / 9.0f;
205 glm::mat4 projection = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);
206 projection[1][1] *= -1.0f;
207 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));
208 const glm::mat4 model(1.0f);
209 return projection * view * model;
212 std::string data_root{};
213 std::vector<Explosion> explosions{};
214 std::vector<mxvk::PointSpriteVertex> vertices{};
215 mxvk::VK_PointSpriteBatch point_batch{};
216 std::size_t active_vertices = 0;
217 Uint32 last_update_time = 0;