summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/config.rs2
-rw-r--r--src/main.rs7
-rw-r--r--src/renderer/ui.rs9
-rw-r--r--src/sim/cell/cell.rs4
-rw-r--r--src/sim/cell/materials/fire.rs17
-rw-r--r--src/sim/cell/materials/gas.rs22
-rw-r--r--src/sim/cell/materials/liquid.rs25
-rw-r--r--src/sim/cell/materials/mod.rs4
-rw-r--r--src/sim/cell/materials/powder.rs8
-rw-r--r--src/sim/cell_sim/sim.rs48
-rw-r--r--src/sim/lib/ray.rs10
-rw-r--r--src/sim/particle_sim/mod.rs35
12 files changed, 114 insertions, 77 deletions
diff --git a/src/config.rs b/src/config.rs
index 7506c44..25986ae 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -11,3 +11,5 @@ pub const PHYSICS_FPS: u32 = 60;
pub const PHYSICS_DELTA_TIME: f32 = 1.0 / PHYSICS_FPS as f32;
pub const PIXELS_TO_METRES: f32 = 20.0;
+
+pub const SETTLED_THRESOHLD: u8 = 7;
diff --git a/src/main.rs b/src/main.rs
index e066e56..caedc23 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -502,7 +502,6 @@ impl ApplicationHandler for App {
self.last_sim_update = now;
if self.sim_paused && self.ignore_pause_next_tick {
self.sim_update();
- self.ignore_pause_next_tick = false;
} else if !self.sim_paused {
self.sim_updates_due +=
secs_since_last_sim_update / expected_secs_since_last_sim_update;
@@ -521,7 +520,9 @@ impl ApplicationHandler for App {
let expected_secs_since_last_physics_update = 1.0 / PHYSICS_FPS as f32;
self.last_physics_update = now;
- if !self.sim_paused {
+ if self.sim_paused && self.ignore_pause_next_tick {
+ self.physics_update(PHYSICS_DELTA_TIME);
+ } else if !self.sim_paused {
self.physics_updates_due +=
secs_since_last_physics_update / expected_secs_since_last_physics_update;
let mut updates_done = 0;
@@ -534,6 +535,8 @@ impl ApplicationHandler for App {
self.physics_updates_due = self.physics_updates_due.min(3.0);
}
+ self.ignore_pause_next_tick = false;
+
if let Some(renderer_state) = &mut self.renderer_state
&& let Some(world) = &mut self.world
&& let Some(rb_sim_manager) = &mut self.rb_sim_manager
diff --git a/src/renderer/ui.rs b/src/renderer/ui.rs
index 27b3be4..c4f7585 100644
--- a/src/renderer/ui.rs
+++ b/src/renderer/ui.rs
@@ -107,9 +107,12 @@ pub fn draw_egui<'a>(
.last_mouse_world_pos
.map(|p| ui.label(format!("Mouse (world): x,y=({x}, {y})", x = p.0, y = p.1,)));
- input
- .last_mouse_chunk_pos
- .map(|p| ui.label(format!("Mouse (chunk): x,y=({x}, {y})", x = p.0, y = p.1,)));
+ if let Some(p) = input.last_mouse_chunk_pos {
+ ui.label(format!("Mouse (chunk): x,y=({x}, {y})", x = p.0, y = p.1,));
+ if let Some(&chunk) = world.chunk_position_to_chunk_idx.get(&p) {
+ ui.label(format!("Sleeping={}", world.chunks[chunk].sleeping));
+ }
+ }
input
.last_mouse_local_pos
diff --git a/src/sim/cell/cell.rs b/src/sim/cell/cell.rs
index edb2402..054ac62 100644
--- a/src/sim/cell/cell.rs
+++ b/src/sim/cell/cell.rs
@@ -1,4 +1,4 @@
-use crate::sim::cell::materials::MaterialId;
+use crate::{config::SETTLED_THRESOHLD, sim::cell::materials::MaterialId};
#[derive(Clone, Copy)]
pub struct Cell {
@@ -60,7 +60,7 @@ impl Cell {
#[inline]
pub fn increment_settled(&mut self) {
let s = self.settled();
- if s < 7 {
+ if s < SETTLED_THRESOHLD {
self.set_settled(s + 1);
}
}
diff --git a/src/sim/cell/materials/fire.rs b/src/sim/cell/materials/fire.rs
index 2a1225e..8667cea 100644
--- a/src/sim/cell/materials/fire.rs
+++ b/src/sim/cell/materials/fire.rs
@@ -2,7 +2,7 @@ use rand::RngExt;
use crate::sim::{
cell::{cell::Cell, materials::MaterialId},
- cell_sim::sim::UpdateCtx,
+ cell_sim::sim::{PostUpdateAction, UpdateCtx},
};
pub trait FireCellView {
@@ -27,21 +27,16 @@ impl FireCellView for Cell {
// TODO optimize number of rng calls?
// TODO wtf is fire
#[inline]
-pub fn sim_update(ctx: &mut UpdateCtx) {
+pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction {
let ticks_lived = ctx.cell.get_ticks_lived();
// 2 seconds
if ticks_lived > 240 {
// we have a chance to live longer--roughly ?% chance of living an extra second
if ctx.rng.random_range(0.0..1.0) > 0.9 {
// kill ourselves, with a chance to turn into ash
- if ctx.rng.random_range(0.0..1.0) > 0.8 {
- // TODO ash material
- // ctx.set_cell(0, 0, Cell::from_material(MaterialId::Sand));
- } else {
- ctx.do_rewrite = false;
- ctx.set_cell(0, 0, Cell::void());
- }
- return;
+ ctx.set_cell(0, 0, Cell::void());
+ // the updater should take no action since we already killed ourselves
+ return PostUpdateAction::None;
}
}
@@ -77,4 +72,6 @@ pub fn sim_update(ctx: &mut UpdateCtx) {
}
ctx.cell.set_ticks_lived(ticks_lived + 1);
+ // apply our new lifespan
+ PostUpdateAction::Apply
}
diff --git a/src/sim/cell/materials/gas.rs b/src/sim/cell/materials/gas.rs
index 2ee7c00..af8f4fd 100644
--- a/src/sim/cell/materials/gas.rs
+++ b/src/sim/cell/materials/gas.rs
@@ -1,20 +1,24 @@
use rand::RngExt;
-use crate::sim::cell_sim::sim::UpdateCtx;
+use crate::sim::cell_sim::sim::{PostUpdateAction, UpdateCtx};
#[inline]
-pub fn sim_update(ctx: &mut UpdateCtx) {
+pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction {
// only allow upward movement some of the time to limit movement speed
- if ctx.rng.random_range(0.0..1.0) > 0.8 && ctx.candidates_swap(&[(0, -1)]) {
- return;
+ if ctx.rng.random_range(0.0..1.0) > 0.8
+ && ctx.swap_or_settle(&[(0, -1)]) == PostUpdateAction::None
+ {
+ return PostUpdateAction::None;
}
// same for each horizontal direction
if ctx.rng.random_range(0.0..1.0) > 0.8
- && ctx.candidates_swap(&[(1 - ctx.seqno_parity as i32 * 2, 0)])
+ && ctx.swap_or_settle(&[(1 - ctx.seqno_parity as i32 * 2, 0)]) == PostUpdateAction::None
{
- return;
+ return PostUpdateAction::None;
}
- if ctx.rng.random_range(0.0..1.0) > 0.8
- && ctx.candidates_swap(&[(-1 + ctx.seqno_parity as i32 * 2, 0)])
- {}
+ if ctx.rng.random_range(0.0..1.0) > 0.8 {
+ return ctx.swap_or_settle(&[(-1 + ctx.seqno_parity as i32 * 2, 0)]);
+ }
+
+ PostUpdateAction::Apply
}
diff --git a/src/sim/cell/materials/liquid.rs b/src/sim/cell/materials/liquid.rs
index 4b848fb..bff0ac0 100644
--- a/src/sim/cell/materials/liquid.rs
+++ b/src/sim/cell/materials/liquid.rs
@@ -1,14 +1,15 @@
-use crate::sim::cell_sim::sim::UpdateCtx;
+use crate::sim::cell_sim::sim::{PostUpdateAction, UpdateCtx};
#[inline]
-pub fn sim_update(ctx: &mut UpdateCtx) {
+pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction {
// if the water can fall, do so
- if ctx.candidates_swap(&[
+ if ctx.swap_or_settle(&[
(0, 1),
(-1 + 2 * ctx.seqno_parity as i32, 1),
(1 - 2 * ctx.seqno_parity as i32, 1),
- ]) {
- return;
+ ]) == PostUpdateAction::None
+ {
+ return PostUpdateAction::None;
}
// if the water can't fall, check if we can move left or right
@@ -22,18 +23,16 @@ pub fn sim_update(ctx: &mut UpdateCtx) {
// we can't move down or to other side, so we're stuck
if !can_move_left && !can_move_right {
- return;
+ return PostUpdateAction::Settle;
}
// if we can't move left, just move right
if !can_move_left {
- ctx.candidates_swap(&[(1, 0)]);
- return;
+ return ctx.swap_or_settle(&[(1, 0)]);
}
// and vice versa
if !can_move_right {
- ctx.candidates_swap(&[(-1, 0)]);
- return;
+ return ctx.swap_or_settle(&[(-1, 0)]);
}
// find the closest hole within 20 pixels (TODO optimize)
@@ -54,12 +53,12 @@ pub fn sim_update(ctx: &mut UpdateCtx) {
{
// we identified a hole and we know that the space on this side is open
// move toward the hole
- // new_target.flags = new_target.flags ^ 0b1;
- ctx.candidates_swap(&[(side, 0)]);
- return;
+ return ctx.swap_or_settle(&[(side, 0)]);
}
}
+ return PostUpdateAction::Settle;
+
// we didn't find a hole, so just move "randomly" on the same surface
// TODO when to settle?
// let target_x = if !can_move_left {
diff --git a/src/sim/cell/materials/mod.rs b/src/sim/cell/materials/mod.rs
index e2b5fc6..ee2b4d0 100644
--- a/src/sim/cell/materials/mod.rs
+++ b/src/sim/cell/materials/mod.rs
@@ -1,4 +1,4 @@
-use crate::sim::cell_sim::sim::UpdateCtx;
+use crate::sim::cell_sim::sim::{PostUpdateAction, UpdateCtx};
pub mod fire;
pub mod gas;
@@ -31,7 +31,7 @@ pub struct MaterialDef {
pub color: (u8, u8, u8, u8),
pub density: u8,
pub form: MaterialForm,
- pub sim_update: Option<fn(ctx: &mut UpdateCtx) -> ()>,
+ pub sim_update: Option<fn(ctx: &mut UpdateCtx) -> PostUpdateAction>,
}
static MATERIALS: [MaterialDef; 6] = [
diff --git a/src/sim/cell/materials/powder.rs b/src/sim/cell/materials/powder.rs
index d2d1265..e08ea1f 100644
--- a/src/sim/cell/materials/powder.rs
+++ b/src/sim/cell/materials/powder.rs
@@ -1,10 +1,10 @@
-use crate::sim::cell_sim::sim::UpdateCtx;
+use crate::sim::cell_sim::sim::{PostUpdateAction, UpdateCtx};
#[inline]
-pub fn sim_update(ctx: &mut UpdateCtx) {
- ctx.candidates_swap(&[
+pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction {
+ ctx.swap_or_settle(&[
(0, 1),
(-1 + 2 * ctx.seqno_parity as i32, 1),
(1 - 2 * ctx.seqno_parity as i32, 1),
- ]);
+ ])
}
diff --git a/src/sim/cell_sim/sim.rs b/src/sim/cell_sim/sim.rs
index 92a263e..fbaf2f0 100644
--- a/src/sim/cell_sim/sim.rs
+++ b/src/sim/cell_sim/sim.rs
@@ -5,7 +5,7 @@ use rand::{Rng, SeedableRng, rngs::SmallRng};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use crate::{
- config::CHUNK_SIZE,
+ config::{CHUNK_SIZE, SETTLED_THRESOHLD},
sim::{
cell::{cell::Cell, materials::MaterialDef},
cell_sim::{chunk::Chunk, world::World},
@@ -127,7 +127,7 @@ fn adjacent_chunks(x: u8, y: u8) -> Vec<usize> {
}
}
-pub fn set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: Cell) {
+fn internal_set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: Cell) {
let cx = x.div_euclid(CHUNK_SIZE);
let cy = y.div_euclid(CHUNK_SIZE);
let lx = x.rem_euclid(CHUNK_SIZE) as u8;
@@ -160,6 +160,18 @@ pub fn set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: Cell
}
}
+#[derive(PartialEq, Eq, Clone, Copy)]
+// the action that the cell's update fn took
+pub enum PostUpdateAction {
+ // the cell managed its own lifecycle, the updater will take no action
+ None,
+ // the updater should rewrite this cell, settling it, and let the chunk sleep if it's fully settled
+ // this cell's parity should be flipped if it is not yet settled
+ Settle,
+ // the cell changed itself, the updater should unconditionally rewrite it and not settle the cell or sleep
+ Apply,
+}
+
pub struct UpdateCtx<'a, 'b, 'c> {
pub chunks: &'a mut [Option<&'b mut Chunk>; 9],
pub seqno: u64,
@@ -171,7 +183,6 @@ pub struct UpdateCtx<'a, 'b, 'c> {
pub material: &'c MaterialDef,
pub rng: &'c mut dyn Rng,
- pub do_rewrite: bool,
}
impl UpdateCtx<'_, '_, '_> {
@@ -188,10 +199,10 @@ impl UpdateCtx<'_, '_, '_> {
debug_assert!(dy > -CHUNK_SIZE + 1 && dy < CHUNK_SIZE - 1);
let x = self.x + dx;
let y = self.y + dy;
- set_cell(self.chunks, x, y, cell);
+ internal_set_cell(self.chunks, x, y, cell);
}
- pub fn candidates_swap(&mut self, candidates: &[(i32, i32)]) -> bool {
+ pub fn swap_or_settle(&mut self, candidates: &[(i32, i32)]) -> PostUpdateAction {
for &(dx, dy) in candidates {
if let Some(mut candidate_cell) = self.get_cell(dx, dy)
&& candidate_cell.material.def().density < self.material.density
@@ -202,11 +213,10 @@ impl UpdateCtx<'_, '_, '_> {
self.set_cell(0, 0, candidate_cell);
self.set_cell(dx, dy, *self.cell);
- self.do_rewrite = false;
- return true;
+ return PostUpdateAction::None;
}
}
- false
+ PostUpdateAction::Settle
}
}
@@ -245,16 +255,24 @@ pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) {
// TODO this is platform-dependent, will break for multiplayer
rng: &mut rng,
- do_rewrite: true,
};
- update(&mut update_ctx);
+ let action = update(&mut update_ctx);
- if update_ctx.do_rewrite {
- // the cell didn't move, so it's more settled, and we also need to update its state for parity
- cell.match_parity(seqno + 1);
- cell.increment_settled();
- set_cell(chunks, x, y, cell);
+ match action {
+ PostUpdateAction::None => {}
+ PostUpdateAction::Settle => {
+ // the cell didn't move, so it's more settled, and we also need to update its state for parity
+ if cell.settled() < SETTLED_THRESOHLD {
+ cell.match_parity(seqno + 1);
+ cell.increment_settled();
+ internal_set_cell(chunks, x, y, cell);
+ }
+ }
+ PostUpdateAction::Apply => {
+ cell.match_parity(seqno + 1);
+ internal_set_cell(chunks, x, y, cell);
+ }
}
}
}
diff --git a/src/sim/lib/ray.rs b/src/sim/lib/ray.rs
index 52d5ba9..ec90d00 100644
--- a/src/sim/lib/ray.rs
+++ b/src/sim/lib/ray.rs
@@ -1,11 +1,12 @@
use glam::{IVec2, Vec2};
-// Amanatides and Woo's fast Voxel Traversal
+// Amanatide's and Woo's fast Voxel Traversal
pub struct AwDda {
next: IVec2,
step_sign: IVec2,
step_delta: Vec2,
t_max: Vec2,
+ done: bool,
}
impl AwDda {
@@ -37,6 +38,7 @@ impl AwDda {
step_sign,
step_delta,
t_max,
+ done: false,
}
}
}
@@ -44,9 +46,13 @@ impl AwDda {
impl Iterator for AwDda {
type Item = IVec2;
fn next(&mut self) -> Option<Self::Item> {
- if self.t_max.min_element() >= 1.0 {
+ if self.done {
return None;
}
+ if self.t_max.min_element() >= 1.0 {
+ self.done = true;
+ return Some(self.next);
+ }
let cur = self.next;
diff --git a/src/sim/particle_sim/mod.rs b/src/sim/particle_sim/mod.rs
index 4378d25..84a728a 100644
--- a/src/sim/particle_sim/mod.rs
+++ b/src/sim/particle_sim/mod.rs
@@ -1,12 +1,7 @@
-use glam::{IVec2, Vec2};
-
use crate::{
config::PIXELS_TO_METRES,
sim::{
- cell::{
- cell::Cell,
- materials::{MaterialForm, MaterialId},
- },
+ cell::{cell::Cell, materials::MaterialForm},
cell_sim::world::World,
lib::ray::AwDda,
particle_sim::particle::Particle,
@@ -23,6 +18,7 @@ const PARTICLE_GRAVITY: f32 = 9.81 * PIXELS_TO_METRES;
impl ParticleManager {
pub fn particle_tick(&mut self, world: &mut World, delta_time: f32) {
+ puffin::profile_function!();
let mut i = 0;
'outer: while i < self.particles.len() {
let p = &mut self.particles[i];
@@ -36,22 +32,30 @@ impl ParticleManager {
p.velocity.y += PARTICLE_GRAVITY * delta_time;
let dt_velocity = p.velocity * delta_time;
- let mut dda = AwDda::new(p.position, p.position + p.velocity * delta_time);
+ let mut dda = AwDda::new(p.position, p.position + dt_velocity);
- if let Some(prev) = dda.next() {
+ if let Some(mut prev) = dda.next() {
if let Some(cell) = world.get_cell_from_game_position(prev.x, prev.y)
- && [MaterialForm::Solid, MaterialForm::Powder]
- .contains(&cell.material.def().form)
+ && [
+ MaterialForm::Solid,
+ MaterialForm::Powder,
+ MaterialForm::Liquid,
+ ]
+ .contains(&cell.material.def().form)
{
// the particle is already inside a collider, we should just kill it
self.particles.swap_remove(i);
continue 'outer;
}
- for cell in dda.map(|p| world.get_cell_from_game_position(p.x, p.y)) {
- if let Some(cell) = cell
- && [MaterialForm::Solid, MaterialForm::Powder]
- .contains(&cell.material.def().form)
+ for next in dda {
+ if let Some(cell) = world.get_cell_from_game_position(next.x, next.y)
+ && [
+ MaterialForm::Solid,
+ MaterialForm::Powder,
+ MaterialForm::Liquid,
+ ]
+ .contains(&cell.material.def().form)
{
// write ourselves to the board
world.set_cell_from_game_position(
@@ -63,11 +67,12 @@ impl ParticleManager {
self.particles.swap_remove(i);
continue 'outer;
}
+ prev = next;
}
}
// no collision, just move
- p.position = p.position + dt_velocity;
+ p.position += dt_velocity;
i += 1;
}
}