diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-20 23:20:21 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-20 23:20:21 -0700 |
| commit | 533da47f2cc03a8b60a7c6c0bb5a938a7e523a03 (patch) | |
| tree | e4709c9d56954c0a9b2bd3e21f64c036490ed070 /src/sim/particle_sim/mod.rs | |
| parent | 29575e8592f5d6d566abb0ce9beaf794fb98a4e4 (diff) | |
particles
Diffstat (limited to 'src/sim/particle_sim/mod.rs')
| -rw-r--r-- | src/sim/particle_sim/mod.rs | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/src/sim/particle_sim/mod.rs b/src/sim/particle_sim/mod.rs new file mode 100644 index 0000000..8bad61d --- /dev/null +++ b/src/sim/particle_sim/mod.rs @@ -0,0 +1,101 @@ +use glam::{IVec2, Vec2}; + +use crate::{ + config::PIXELS_TO_METRES, + sim::{ + cell::{cell::Cell, materials::MaterialId}, + cell_sim::world::World, + particle_sim::particle::Particle, + }, +}; + +pub mod particle; + +pub struct ParticleManager { + pub particles: Vec<Particle>, +} + +const PARTICLE_GRAVITY: f32 = 9.81 * PIXELS_TO_METRES; + +impl ParticleManager { + pub fn particle_tick(&mut self, world: &mut World, delta_time: f32) { + let mut i = 0; + 'outer: while i < self.particles.len() { + let p = &mut self.particles[i]; + + p.life -= delta_time; + if p.life <= 0.0 { + self.particles.swap_remove(i); + continue 'outer; + } + + p.velocity.y += PARTICLE_GRAVITY * delta_time; + let dt_velocity = p.velocity * delta_time; + + // Amanatides and Woo's fast Voxel Traversal + { + let mut cur = IVec2::new(p.position.x.round() as i32, p.position.y.round() as i32); + // direction we step for each component on each iteration + let step_sign = + IVec2::new(dt_velocity.x.signum() as i32, dt_velocity.y.signum() as i32); + let delta = Vec2::new(1.0 / dt_velocity.x.abs(), 1.0 / dt_velocity.y.abs()); + + let frac = Vec2::new( + if step_sign.x > 0 { + (cur.x as f32 + 0.5) - p.position.x + } else { + p.position.x - (cur.x as f32 - 0.5) + }, + if step_sign.y > 0 { + (cur.y as f32 + 0.5) - p.position.y + } else { + p.position.y - (cur.y as f32 - 0.5) + }, + ); + let mut t_max = frac * delta; + + let mut prev = cur; + if let Some(cell) = world.get_cell_from_game_position(prev.x, prev.y) + && cell.material != MaterialId::Void + { + // already occupied, die + self.particles.swap_remove(i); + continue 'outer; + } + while t_max.min_element() <= 1.0 { + if t_max.x < t_max.y { + cur.x += step_sign.x; + t_max.x += delta.x; + } else { + cur.y += step_sign.y; + t_max.y += delta.y; + } + if let Some(cell) = world.get_cell_from_game_position(cur.x, cur.y) + && cell.material != MaterialId::Void + { + // write ourselves to the board + world.set_cell_from_game_position( + prev.x, + prev.y, + Cell::from_material(p.material), + false, + ); + self.particles.swap_remove(i); + continue 'outer; + } + prev = cur; + } + } + + // no collision, just move + p.position = p.position + dt_velocity; + i += 1; + } + } + + pub fn new() -> Self { + ParticleManager { + particles: Vec::new(), + } + } +} |
