diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-22 15:00:35 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-22 15:00:35 -0700 |
| commit | 1a515237afb7ad09353a65f5fbc6e98a7c29ce8e (patch) | |
| tree | 48cd4b4bcf8f8e357811f905f22607838d7c1f96 /src/sim | |
| parent | 6350e4ffca8ce1e46465284ef3d7559e0f40229b (diff) | |
sim manager refactor
Diffstat (limited to 'src/sim')
| -rw-r--r-- | src/sim/cell/materials/fire.rs | 2 | ||||
| -rw-r--r-- | src/sim/cell/materials/gas.rs | 2 | ||||
| -rw-r--r-- | src/sim/cell/materials/liquid.rs | 2 | ||||
| -rw-r--r-- | src/sim/cell/materials/mod.rs | 2 | ||||
| -rw-r--r-- | src/sim/cell/materials/powder.rs | 2 | ||||
| -rw-r--r-- | src/sim/cell_manager/chunk.rs (renamed from src/sim/cell_sim/chunk.rs) | 0 | ||||
| -rw-r--r-- | src/sim/cell_manager/manager.rs (renamed from src/sim/cell_sim/world.rs) | 29 | ||||
| -rw-r--r-- | src/sim/cell_manager/mod.rs (renamed from src/sim/cell_sim/mod.rs) | 2 | ||||
| -rw-r--r-- | src/sim/cell_manager/sim.rs (renamed from src/sim/cell_sim/sim.rs) | 6 | ||||
| -rw-r--r-- | src/sim/entity/mod.rs | 28 | ||||
| -rw-r--r-- | src/sim/lib/force.rs | 24 | ||||
| -rw-r--r-- | src/sim/mod.rs | 69 | ||||
| -rw-r--r-- | src/sim/particle_manager/mod.rs (renamed from src/sim/particle_sim/mod.rs) | 6 | ||||
| -rw-r--r-- | src/sim/particle_manager/particle.rs (renamed from src/sim/particle_sim/particle.rs) | 0 | ||||
| -rw-r--r-- | src/sim/rb_manager/debug_ops.rs (renamed from src/sim/rb_sim/debug_ops.rs) | 4 | ||||
| -rw-r--r-- | src/sim/rb_manager/debug_render.rs (renamed from src/sim/rb_sim/debug_render.rs) | 0 | ||||
| -rw-r--r-- | src/sim/rb_manager/mod.rs (renamed from src/sim/rb_sim/mod.rs) | 12 | ||||
| -rw-r--r-- | src/sim/rb_manager/rb_entity.rs (renamed from src/sim/rb_sim/rb_entity.rs) | 0 | ||||
| -rw-r--r-- | src/sim/sim_manager/mod.rs | 152 | ||||
| -rw-r--r-- | src/sim/sim_manager/utils.rs | 62 |
20 files changed, 296 insertions, 108 deletions
diff --git a/src/sim/cell/materials/fire.rs b/src/sim/cell/materials/fire.rs index 8667cea..8cabe30 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::{PostUpdateAction, UpdateCtx}, + cell_manager::sim::{PostUpdateAction, UpdateCtx}, }; pub trait FireCellView { diff --git a/src/sim/cell/materials/gas.rs b/src/sim/cell/materials/gas.rs index af8f4fd..229d0e9 100644 --- a/src/sim/cell/materials/gas.rs +++ b/src/sim/cell/materials/gas.rs @@ -1,6 +1,6 @@ use rand::RngExt; -use crate::sim::cell_sim::sim::{PostUpdateAction, UpdateCtx}; +use crate::sim::cell_manager::sim::{PostUpdateAction, UpdateCtx}; #[inline] pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction { diff --git a/src/sim/cell/materials/liquid.rs b/src/sim/cell/materials/liquid.rs index bff0ac0..6c3bdcd 100644 --- a/src/sim/cell/materials/liquid.rs +++ b/src/sim/cell/materials/liquid.rs @@ -1,4 +1,4 @@ -use crate::sim::cell_sim::sim::{PostUpdateAction, UpdateCtx}; +use crate::sim::cell_manager::sim::{PostUpdateAction, UpdateCtx}; #[inline] pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction { diff --git a/src/sim/cell/materials/mod.rs b/src/sim/cell/materials/mod.rs index ee2b4d0..5439cca 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::{PostUpdateAction, UpdateCtx}; +use crate::sim::cell_manager::sim::{PostUpdateAction, UpdateCtx}; pub mod fire; pub mod gas; diff --git a/src/sim/cell/materials/powder.rs b/src/sim/cell/materials/powder.rs index e08ea1f..8c916e4 100644 --- a/src/sim/cell/materials/powder.rs +++ b/src/sim/cell/materials/powder.rs @@ -1,4 +1,4 @@ -use crate::sim::cell_sim::sim::{PostUpdateAction, UpdateCtx}; +use crate::sim::cell_manager::sim::{PostUpdateAction, UpdateCtx}; #[inline] pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction { diff --git a/src/sim/cell_sim/chunk.rs b/src/sim/cell_manager/chunk.rs index 3fe5056..3fe5056 100644 --- a/src/sim/cell_sim/chunk.rs +++ b/src/sim/cell_manager/chunk.rs diff --git a/src/sim/cell_sim/world.rs b/src/sim/cell_manager/manager.rs index 924639a..094f345 100644 --- a/src/sim/cell_sim/world.rs +++ b/src/sim/cell_manager/manager.rs @@ -2,24 +2,24 @@ use fxhash::FxHashMap; use crate::{ config::CHUNK_SIZE, - sim::{cell::cell::Cell, cell_sim::chunk::Chunk}, + sim::{ + cell::cell::Cell, + cell_manager::{chunk::Chunk, sim::sim_tick}, + }, }; -pub struct World { +pub struct CellManager { + pub seqno: u64, pub chunks: Vec<Chunk>, // TODO FxFxHashMap? pub chunk_position_to_chunk_idx: FxHashMap<(i32, i32), usize>, } -impl World { +impl CellManager { #[inline] pub fn split_game_position(x: i32, y: i32) -> ((i32, i32), (u8, u8)) { ( - ( - // TODO is this cast expensive? - x.div_euclid(CHUNK_SIZE), - y.div_euclid(CHUNK_SIZE), - ), + (x.div_euclid(CHUNK_SIZE), y.div_euclid(CHUNK_SIZE)), ( x.rem_euclid(CHUNK_SIZE) as u8, y.rem_euclid(CHUNK_SIZE) as u8, @@ -29,7 +29,7 @@ impl World { // VERY EXPENSIVE pub fn get_cell_from_game_position(&self, x: i32, y: i32) -> Option<Cell> { - let ((cx, cy), (dx, dy)) = World::split_game_position(x, y); + let ((cx, cy), (dx, dy)) = CellManager::split_game_position(x, y); self.chunk_position_to_chunk_idx .get(&(cx, cy)) .map(|&idx| self.chunks[idx].get_cell_at_local_position(dx, dy)) @@ -37,7 +37,7 @@ impl World { // VERY EXPENSIVE pub fn set_cell_from_game_position(&mut self, x: i32, y: i32, cell: Cell, sleeping: bool) { - let ((cx, cy), (dx, dy)) = World::split_game_position(x, y); + let ((cx, cy), (dx, dy)) = CellManager::split_game_position(x, y); if let Some(&idx) = self.chunk_position_to_chunk_idx.get(&(cx, cy)) { self.chunks[idx].set_cell_at_local_position(dx, dy, cell); // this is a temporary hack @@ -52,8 +52,15 @@ impl World { self.chunks.push(chunk); } + pub fn tick(&mut self, use_threading: bool) { + let seqno = self.seqno; + sim_tick(self, seqno, use_threading); + self.seqno += 1; + } + pub fn from_default_size() -> Self { - let mut world = World { + let mut world = CellManager { + seqno: 0, chunks: Vec::new(), chunk_position_to_chunk_idx: FxHashMap::default(), }; diff --git a/src/sim/cell_sim/mod.rs b/src/sim/cell_manager/mod.rs index b7d0597..67d7614 100644 --- a/src/sim/cell_sim/mod.rs +++ b/src/sim/cell_manager/mod.rs @@ -1,3 +1,3 @@ pub mod chunk; +pub mod manager; pub mod sim; -pub mod world; diff --git a/src/sim/cell_sim/sim.rs b/src/sim/cell_manager/sim.rs index fbaf2f0..b79608e 100644 --- a/src/sim/cell_sim/sim.rs +++ b/src/sim/cell_manager/sim.rs @@ -8,7 +8,7 @@ use crate::{ config::{CHUNK_SIZE, SETTLED_THRESOHLD}, sim::{ cell::{cell::Cell, materials::MaterialDef}, - cell_sim::{chunk::Chunk, world::World}, + cell_manager::{chunk::Chunk, manager::CellManager}, }, }; @@ -220,7 +220,7 @@ impl UpdateCtx<'_, '_, '_> { } } -pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) { +fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) { puffin::profile_function!(); let seqno_parity = (seqno as u8) & 0b1; let mut rng = SmallRng::seed_from_u64(seqno); @@ -280,7 +280,7 @@ pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) { } } -pub fn sim_tick(world: &mut World, seqno: u64, use_threading: bool) { +pub fn sim_tick(world: &mut CellManager, seqno: u64, use_threading: bool) { puffin::profile_function!(); let mut columns: FxHashMap<i32, Vec<i32>> = FxHashMap::default(); diff --git a/src/sim/entity/mod.rs b/src/sim/entity/mod.rs new file mode 100644 index 0000000..ab10ea8 --- /dev/null +++ b/src/sim/entity/mod.rs @@ -0,0 +1,28 @@ +// use glam::{IVec2, Vec2}; +// use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; + +// use crate::sim::cell::cell::Cell; + +// pub struct EntityCells { +// pub size: IVec2, +// pub cells: Vec<Cell>, +// } +// pub trait EntityBehaviour { +// fn update (&mut self) -> (); +// } + +// pub struct Entity { +// pub rb_h: Option<RigidBodyHandle>, +// pub collider_h: Option<ColliderHandle>, +// pub cells: EntityCells, +// pub behaviour: +// } + +// impl Entity { +// pub fn position(&self, rbsm:) -> Vec2 { + +// } +// pub fn destroy(&mut self) -> { + +// } +// } diff --git a/src/sim/lib/force.rs b/src/sim/lib/force.rs index 50b5c70..e8f7e90 100644 --- a/src/sim/lib/force.rs +++ b/src/sim/lib/force.rs @@ -6,20 +6,16 @@ use crate::sim::{ cell::Cell, materials::{MaterialId, fire::FireCellView}, }, - cell_sim::world::World, - particle_sim::{ParticleManager, particle::Particle}, - rb_sim::RbSimManager, + particle_manager::particle::Particle, + sim_manager::SimManager, }; pub fn apply_explosion( + sim: &mut SimManager, centre: Vec2, radius: i32, force_offset: Vec2, force_multiplier: f32, - next_seqno: u64, - particle_manager: &mut ParticleManager, - world: &mut World, - rbsm: &mut RbSimManager, ) { let get_vel = |pos: Vec2| { let d = pos.distance(centre); @@ -38,17 +34,18 @@ pub fn apply_explosion( // if there's a cell, convert it to a particle let pos = Vec2::new(centre.x + x as f32, centre.y + y as f32); let ipos = pos.round().as_ivec2(); - if let Some(cell) = world.get_cell_from_game_position(ipos.x, ipos.y) { + if let Some(cell) = sim.cell_manager.get_cell_from_game_position(ipos.x, ipos.y) { let mut fire = Cell::from_material(MaterialId::Fire); fire.set_ticks_lived(300); - fire.match_parity(next_seqno); + fire.match_parity(sim.cell_manager.seqno); if cell.material == MaterialId::Void && random_range(0.0..1.0) > 0.5 { if random_range(0.0..1.0) > 0.1 { - world.set_cell_from_game_position(ipos.x, ipos.y, fire, false); + sim.cell_manager + .set_cell_from_game_position(ipos.x, ipos.y, fire, false); } else { let vel = get_vel(pos); - particle_manager.particles.push(Particle::new( + sim.particle_manager.particles.push(Particle::new( pos, vel, MaterialId::Fire, @@ -56,10 +53,11 @@ pub fn apply_explosion( )); } } else { - world.set_cell_from_game_position(ipos.x, ipos.y, fire, false); + sim.cell_manager + .set_cell_from_game_position(ipos.x, ipos.y, fire, false); if random_range(0.0..1.0) > 0.6 { let vel = get_vel(pos); - particle_manager.particles.push(Particle::new( + sim.particle_manager.particles.push(Particle::new( pos, vel, cell.material, diff --git a/src/sim/mod.rs b/src/sim/mod.rs index d9e358b..95fb2db 100644 --- a/src/sim/mod.rs +++ b/src/sim/mod.rs @@ -1,66 +1,7 @@ -use crate::sim::{cell::materials::MaterialId, cell_sim::world::World, rb_sim::RbSimManager}; - pub mod cell; -pub mod cell_sim; +pub mod cell_manager; +pub mod entity; pub mod lib; -pub mod particle_sim; -pub mod rb_sim; - -pub fn write_rb_entity_to_world( - world: &mut World, - rb_sim_manager: &RbSimManager, - rb_entity_id: u32, - // make sure these cells will be simulated - seqno: u64, - // (entity_x, entity_y, cell_x, cell_y) -) -> Vec<(u8, u8, i32, i32)> { - let mut cells_written: Vec<(u8, u8, i32, i32)> = Vec::new(); - if let Some(rb_entity) = rb_sim_manager.rb_entities.get(&rb_entity_id) - && let Some((rb_x, rb_y, cos, sin)) = rb_sim_manager.get_rb_entity_transform(rb_entity_id) - { - let (half_size_x, half_size_y) = - (rb_entity.width as f32 / 2.0, rb_entity.height as f32 / 2.0); - // half-extent of the rotated grid's axis-aligned bounding box, plus a cell of margin - let (radius_x, radius_y) = ( - half_size_x * (cos.abs() + sin.abs()) + 1.0, - half_size_y * (cos.abs() + sin.abs()) + 1.0, - ); - - let world_xl = (rb_x - radius_x).floor() as i32; - let world_xu = (rb_x + radius_x).ceil() as i32; - let world_yl = (rb_y - radius_y).floor() as i32; - let world_yu = (rb_y + radius_y).ceil() as i32; - - for world_x in world_xl..=world_xu { - for world_y in world_yl..=world_yu { - if let Some(cur_world_cell) = world.get_cell_from_game_position(world_x, world_y) - && cur_world_cell.material == MaterialId::Void - { - // same as shader - let d = (world_x as f32 + 0.5 - rb_x, world_y as f32 + 0.5 - rb_y); - let q = (d.0.floor() + 0.5, d.1.floor() + 0.5); - let (lx, ly) = ( - (q.0 * cos + q.1 * sin + half_size_x).floor() as i32, - (-q.0 * sin + q.1 * cos + half_size_y).floor() as i32, - ); - - if lx < 0 || ly < 0 || lx >= rb_entity.width || ly >= rb_entity.height { - continue; - } - - let mut cell = rb_entity.get_cell_at_local_position(lx as u8, ly as u8); - if cell.material == MaterialId::Void { - continue; - } - - cell.match_parity(seqno); - - // TODO: OPTIMIZE!! - world.set_cell_from_game_position(world_x, world_y, cell, false); - cells_written.push((lx as u8, ly as u8, world_x, world_y)); - } - } - } - } - cells_written -} +pub mod particle_manager; +pub mod rb_manager; +pub mod sim_manager; diff --git a/src/sim/particle_sim/mod.rs b/src/sim/particle_manager/mod.rs index 84a728a..e5e47eb 100644 --- a/src/sim/particle_sim/mod.rs +++ b/src/sim/particle_manager/mod.rs @@ -2,9 +2,9 @@ use crate::{ config::PIXELS_TO_METRES, sim::{ cell::{cell::Cell, materials::MaterialForm}, - cell_sim::world::World, + cell_manager::manager::CellManager, lib::ray::AwDda, - particle_sim::particle::Particle, + particle_manager::particle::Particle, }, }; @@ -17,7 +17,7 @@ pub struct ParticleManager { const PARTICLE_GRAVITY: f32 = 9.81 * PIXELS_TO_METRES; impl ParticleManager { - pub fn particle_tick(&mut self, world: &mut World, delta_time: f32) { + pub fn tick(&mut self, world: &mut CellManager, delta_time: f32) { puffin::profile_function!(); let mut i = 0; 'outer: while i < self.particles.len() { diff --git a/src/sim/particle_sim/particle.rs b/src/sim/particle_manager/particle.rs index cc7dd28..cc7dd28 100644 --- a/src/sim/particle_sim/particle.rs +++ b/src/sim/particle_manager/particle.rs diff --git a/src/sim/rb_sim/debug_ops.rs b/src/sim/rb_manager/debug_ops.rs index d6852da..cb56256 100644 --- a/src/sim/rb_sim/debug_ops.rs +++ b/src/sim/rb_manager/debug_ops.rs @@ -2,7 +2,7 @@ use glam::{ivec2, vec2}; use crate::sim::{ cell::{cell::Cell, materials::MaterialId}, - rb_sim::RbSimManager, + rb_manager::RbManager, }; pub trait DebugOperator { @@ -10,7 +10,7 @@ pub trait DebugOperator { fn test_spawn_ball(&mut self, x: f32, y: f32, material: MaterialId) -> (); } -impl DebugOperator for RbSimManager { +impl DebugOperator for RbManager { fn test_spawn_box(&mut self, x: f32, y: f32, material: MaterialId) { let w = 10; let h = 10; diff --git a/src/sim/rb_sim/debug_render.rs b/src/sim/rb_manager/debug_render.rs index 342fc5b..342fc5b 100644 --- a/src/sim/rb_sim/debug_render.rs +++ b/src/sim/rb_manager/debug_render.rs diff --git a/src/sim/rb_sim/mod.rs b/src/sim/rb_manager/mod.rs index f81d5f1..2c64f12 100644 --- a/src/sim/rb_sim/mod.rs +++ b/src/sim/rb_manager/mod.rs @@ -10,9 +10,9 @@ use crate::{ config::{CHUNK_SIZE, PHYSICS_DELTA_TIME, PIXELS_TO_METRES}, sim::{ cell::cell::Cell, - cell_sim::chunk::Chunk, + cell_manager::chunk::Chunk, lib::marching_squares::{Marchable, marching_squares_vertex_trace}, - rb_sim::{ + rb_manager::{ debug_render::{DebugLineBuffer, DebugVertex}, rb_entity::RbEntity, }, @@ -65,7 +65,7 @@ impl PhysicsManager { } } -pub struct RbSimManager { +pub struct RbManager { chunk_colliders: FxHashMap<(i32, i32), geometry::ColliderHandle>, pub rb_entities: FxHashMap<u32, RbEntity>, @@ -74,8 +74,8 @@ pub struct RbSimManager { debug_line_buffer: DebugLineBuffer, } -impl RbSimManager { - pub fn rb_tick(&mut self, delta_time: f32) { +impl RbManager { + pub fn tick(&mut self, _delta_time: f32) { let gravity = vec2(0.0, 9.81); self.physics_manager.physics_pipeline.step( @@ -277,7 +277,7 @@ impl RbSimManager { } pub fn new() -> Self { - RbSimManager { + RbManager { rb_entities: FxHashMap::default(), chunk_colliders: FxHashMap::default(), diff --git a/src/sim/rb_sim/rb_entity.rs b/src/sim/rb_manager/rb_entity.rs index 18a6693..18a6693 100644 --- a/src/sim/rb_sim/rb_entity.rs +++ b/src/sim/rb_manager/rb_entity.rs diff --git a/src/sim/sim_manager/mod.rs b/src/sim/sim_manager/mod.rs new file mode 100644 index 0000000..a98f3b2 --- /dev/null +++ b/src/sim/sim_manager/mod.rs @@ -0,0 +1,152 @@ +use std::time::Instant; + +use crate::{ + Config, + config::{PHYSICS_DELTA_TIME, PHYSICS_FPS, SIM_FPS}, + sim::{ + cell::{cell::Cell, materials::MaterialId}, + cell_manager::manager::CellManager, + particle_manager::ParticleManager, + rb_manager::RbManager, + sim_manager::utils::write_rb_entity_to_world, + }, +}; + +mod utils; + +pub struct SimManager { + // timing + pub paused: bool, + pub ignore_pause_next_tick: bool, + pub last_cell_update: Instant, + pub cell_updates_due: f32, + pub last_physics_update: Instant, + pub physics_updates_due: f32, + + // systems + pub cell_manager: CellManager, + pub rb_manager: RbManager, + pub particle_manager: ParticleManager, +} + +impl SimManager { + pub fn new() -> Self { + SimManager { + paused: false, + ignore_pause_next_tick: false, + last_cell_update: Instant::now(), + cell_updates_due: 0.0, + last_physics_update: Instant::now(), + physics_updates_due: 0.0, + cell_manager: CellManager::from_default_size(), + rb_manager: RbManager::new(), + particle_manager: ParticleManager::new(), + } + } + + fn cell_update(&mut self, config: &Config) { + // before we tick, write all the rb entities into the sim world + // TODO optimize + let entity_ids: Vec<u32> = self.rb_manager.rb_entities.keys().copied().collect(); + let mut cells_written_by_entity: Vec<(u32, Vec<(u8, u8, i32, i32)>)> = Vec::new(); + + for entity_id in entity_ids { + let cells_written = write_rb_entity_to_world(self, entity_id, self.cell_manager.seqno); + cells_written_by_entity.push((entity_id, cells_written)); + } + + self.cell_manager.tick(config.use_threading); + + // after we tick, remove the written rb cells and update the entities + // TODO optimize + for (entity_id, cells_written) in cells_written_by_entity { + let rb_entity = self.rb_manager.rb_entities.get_mut(&entity_id).unwrap(); + for (lx, ly, x, y) in cells_written { + // update the entity + // TODO we should skip cells that weren't changed? + // TODO optimize + let new_local_cell = self.cell_manager.get_cell_from_game_position(x, y).unwrap(); + if new_local_cell.material != MaterialId::Void && !new_local_cell.rb() { + panic!( + "Someone swapped into this rb's cell! ({x}, {y}, {m:#?}, {f})", + m = new_local_cell.material, + f = new_local_cell.flags + ); + } + rb_entity.set_cell_at_local_position(lx, ly, new_local_cell); + // update the world + self.cell_manager + .set_cell_from_game_position(x, y, Cell::void(), false); + } + } + } + + fn physics_update(&mut self, delta_time: f32) { + // before we move the rigidbodies, upsert the current terrain state + // TODO use the chunk sleeping, and make this range dynamic + for cx in -5..5 { + for cy in -5..5 { + if let Some(chunk) = self + .cell_manager + .chunk_position_to_chunk_idx + .get(&(cx, cy)) + .map(|&idx| &self.cell_manager.chunks[idx]) + && !chunk.sleeping + { + self.rb_manager.upsert_chunk_collider(cx, cy, chunk); + } + } + } + + // move the rbs + self.rb_manager.tick(delta_time); + + // move the particles + self.particle_manager + .tick(&mut self.cell_manager, delta_time); + } + + pub fn update(&mut self, config: &Config, _delta_time: f32) -> () { + let now = Instant::now(); + let secs_since_last_cell_update = (now - self.last_cell_update).as_secs_f32(); + let expected_secs_since_last_cell_update = 1.0 / SIM_FPS as f32; + + self.last_cell_update = now; + if self.paused && self.ignore_pause_next_tick { + self.cell_update(config); + } else if !self.paused { + self.cell_updates_due += + secs_since_last_cell_update / expected_secs_since_last_cell_update; + let mut cell_updates_done = 0; + // don't ever update more than 3 times per frame, or else we can get a pseudo deadlock + while self.cell_updates_due >= 1.0 && cell_updates_done < 3 { + self.cell_update(config); + self.cell_updates_due -= 1.0; + cell_updates_done += 1; + } + self.cell_updates_due = self.cell_updates_due.min(3.0); + } + + // PHYSICS UPDATE + let secs_since_last_physics_update = (now - self.last_physics_update).as_secs_f32(); + let expected_secs_since_last_physics_update = 1.0 / PHYSICS_FPS as f32; + + self.last_physics_update = now; + if self.paused && self.ignore_pause_next_tick { + self.physics_update(PHYSICS_DELTA_TIME); + } else if !self.paused { + self.physics_updates_due += + secs_since_last_physics_update / expected_secs_since_last_physics_update; + let mut updates_done = 0; + // don't ever update more than 3 times per frame, or else we can get a pseudo deadlock + while self.physics_updates_due >= 1.0 && updates_done < 3 { + self.physics_update(PHYSICS_DELTA_TIME); + self.physics_updates_due -= 1.0; + updates_done += 1; + } + self.physics_updates_due = self.physics_updates_due.min(3.0); + } + + self.ignore_pause_next_tick = false; + } +} diff --git a/src/sim/sim_manager/utils.rs b/src/sim/sim_manager/utils.rs new file mode 100644 index 0000000..b636186 --- /dev/null +++ b/src/sim/sim_manager/utils.rs @@ -0,0 +1,62 @@ +use crate::sim::{cell::materials::MaterialId, sim_manager::SimManager}; + +pub fn write_rb_entity_to_world( + sim: &mut SimManager, + rb_entity_id: u32, + // make sure these cells will be simulated + seqno: u64, + // (entity_x, entity_y, cell_x, cell_y) +) -> Vec<(u8, u8, i32, i32)> { + let mut cells_written: Vec<(u8, u8, i32, i32)> = Vec::new(); + if let Some(rb_entity) = sim.rb_manager.rb_entities.get(&rb_entity_id) + && let Some((rb_x, rb_y, cos, sin)) = sim.rb_manager.get_rb_entity_transform(rb_entity_id) + { + let (half_size_x, half_size_y) = + (rb_entity.width as f32 / 2.0, rb_entity.height as f32 / 2.0); + // half-extent of the rotated grid's axis-aligned bounding box, plus a cell of margin + let (radius_x, radius_y) = ( + half_size_x * (cos.abs() + sin.abs()) + 1.0, + half_size_y * (cos.abs() + sin.abs()) + 1.0, + ); + + let world_xl = (rb_x - radius_x).floor() as i32; + let world_xu = (rb_x + radius_x).ceil() as i32; + let world_yl = (rb_y - radius_y).floor() as i32; + let world_yu = (rb_y + radius_y).ceil() as i32; + + for world_x in world_xl..=world_xu { + for world_y in world_yl..=world_yu { + if let Some(cur_world_cell) = sim + .cell_manager + .get_cell_from_game_position(world_x, world_y) + && cur_world_cell.material == MaterialId::Void + { + // same as shader + let d = (world_x as f32 + 0.5 - rb_x, world_y as f32 + 0.5 - rb_y); + let q = (d.0.floor() + 0.5, d.1.floor() + 0.5); + let (lx, ly) = ( + (q.0 * cos + q.1 * sin + half_size_x).floor() as i32, + (-q.0 * sin + q.1 * cos + half_size_y).floor() as i32, + ); + + if lx < 0 || ly < 0 || lx >= rb_entity.width || ly >= rb_entity.height { + continue; + } + + let mut cell = rb_entity.get_cell_at_local_position(lx as u8, ly as u8); + if cell.material == MaterialId::Void { + continue; + } + + cell.match_parity(seqno); + + // TODO: OPTIMIZE!! + sim.cell_manager + .set_cell_from_game_position(world_x, world_y, cell, false); + cells_written.push((lx as u8, ly as u8, world_x, world_y)); + } + } + } + } + cells_written +} |
