1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
use glam::{IVec2, Vec2};
use crate::{
config::PIXELS_TO_METRES,
sim::{
cell::{
cell::Cell,
materials::{MaterialForm, 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)
&& [MaterialForm::Solid, MaterialForm::Powder]
.contains(&cell.material.def().form)
{
// 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)
&& [MaterialForm::Solid, MaterialForm::Powder]
.contains(&cell.material.def().form)
{
// 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(),
}
}
}
|