From 54ae26e7c41f6c0a465a55c3cc5ee16ff81a1d70 Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Mon, 17 Aug 2026 18:18:25 -0700 Subject: refactor/UX --- src/main.rs | 47 ++++++++++++++++++++++++++++++++++------- src/renderer/ui.rs | 2 +- src/sim/cell/cell.rs | 21 +++++++++++++++++-- src/sim/cell_sim/sim.rs | 46 ---------------------------------------- src/sim/mod.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sim/rb_sim/mod.rs | 1 + 6 files changed, 116 insertions(+), 57 deletions(-) diff --git a/src/main.rs b/src/main.rs index ccc5ec5..043d125 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,11 +24,9 @@ use crate::{ renderer::RendererState, sim::{ cell::{cell::Cell, materials::MaterialId}, - cell_sim::{ - sim::{sim_tick, write_rb_entity_to_world}, - world::World, - }, + cell_sim::{sim::sim_tick, world::World}, rb_sim::RbSimManager, + write_rb_entity_to_world, }, }; @@ -146,9 +144,39 @@ impl App { // will be called before the render and before the physics update(s) // may be called multiple times if the sim time is behind fn sim_update(&mut self) { - if let Some(world) = &mut self.world { + if let Some(world) = &mut self.world + && let Some(rbsm) = &mut self.rb_sim_manager + { + // before we tick, write all the rb entities into the sim world + // TODO optimize + let entity_ids: Vec = rbsm.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(world, rbsm, entity_id, self.sim_seqno); + cells_written_by_entity.push((entity_id, cells_written)); + } + sim_tick(world, self.sim_seqno, self.config.use_threading); self.sim_seqno += 1; + + // 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 = rbsm.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? + let new_local_cell = world.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!"); + // } + rb_entity.set_cell_at_local_position(lx, ly, new_local_cell); + // update the world + world.set_cell_from_game_position(x, y, Cell::void(), false); + } + } } } // called PHYSICS_FPS times per second @@ -270,9 +298,12 @@ impl ApplicationHandler for App { KeyCode::KeyD => self.input.is_right_pressed = pressed, KeyCode::KeyC => self.world = Some(World::from_default_size()), KeyCode::KeyV => { - self.rb_sim_manager - .as_mut() - .map(|rbsm| rbsm.rb_entities = FxHashMap::default()); + if let Some(rbsm) = self.rb_sim_manager.as_mut() { + let entity_ids: Vec = rbsm.rb_entities.keys().copied().collect(); + for entity_id in entity_ids { + rbsm.destroy_rb_entity(entity_id); + } + } } KeyCode::KeyP if pressed && !repeat => { if let Some(rbsm) = self.rb_sim_manager.as_mut() { diff --git a/src/renderer/ui.rs b/src/renderer/ui.rs index 590c720..bf51ab4 100644 --- a/src/renderer/ui.rs +++ b/src/renderer/ui.rs @@ -43,7 +43,7 @@ pub fn draw_egui<'a>( } }); - let dropper_material = config.brush_material.def(); + let dropper_material = config.dropper_material.def(); egui::ComboBox::from_label("Select a dropper material") .selected_text(format!( diff --git a/src/sim/cell/cell.rs b/src/sim/cell/cell.rs index 2664721..c4b0a8f 100644 --- a/src/sim/cell/cell.rs +++ b/src/sim/cell/cell.rs @@ -9,6 +9,7 @@ pub struct Cell { impl Cell { const FLAG_PARITY: u8 = 0b0000_0001; + const FLAG_RB: u8 = 0b0000_0010; #[inline] pub fn parity(self) -> u8 { @@ -20,8 +21,24 @@ impl Cell { } #[inline] pub fn match_parity(&mut self, seqno: u64) { - // TODO this will break with more flags - self.flags = (seqno as u8) & 0b1; + let parity = seqno % 2 != 0; + if parity { + self.flags |= Self::FLAG_PARITY; + } else { + self.flags = self.flags & !Self::FLAG_PARITY + } + } + + #[inline] + pub fn rb(self) -> bool { + self.flags & Self::FLAG_RB == 1 + } + pub fn set_rb(&mut self, rb: bool) { + if rb { + self.flags |= Self::FLAG_RB + } else { + self.flags = self.flags & !Self::FLAG_RB + } } } diff --git a/src/sim/cell_sim/sim.rs b/src/sim/cell_sim/sim.rs index 0f02a23..8831bb4 100644 --- a/src/sim/cell_sim/sim.rs +++ b/src/sim/cell_sim/sim.rs @@ -181,52 +181,6 @@ const NEIGHBORHOOD_OFFSETS: [(i32, i32); 9] = [ (1, 1), ]; -pub fn write_rb_entity_to_world( - world: &mut World, - rb_sim_manager: &RbSimManager, - rb_entity_id: u32, - next_seqno: u64, -) { - 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 = CHUNK_SIZE as f32 / 2.0; - // half-extent of the rotated grid's axis-aligned bounding box, plus a cell of margin - let radius = half_size * (cos.abs() + sin.abs()) + 1.0; - - let world_xl = (rb_x - radius).floor() as i32; - let world_xu = (rb_x + radius).ceil() as i32; - let world_yl = (rb_y - radius).floor() as i32; - let world_yu = (rb_y + radius).ceil() as i32; - - for world_x in world_xl..=world_xu { - for world_y in world_yl..=world_yu { - // 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).floor() as i32, - (-q.0 * sin + q.1 * cos + half_size).floor() as i32, - ); - - if lx < 0 || ly < 0 || lx >= CHUNK_SIZE || ly >= CHUNK_SIZE { - 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(next_seqno); - - // TODO: OPTIMIZE!! - world.set_cell_from_game_position(world_x, world_y, cell, false); - } - } - } -} - pub fn sim_tick(world: &mut World, seqno: u64, use_threading: bool) { puffin::profile_function!(); diff --git a/src/sim/mod.rs b/src/sim/mod.rs index 63a9c56..dfc99c3 100644 --- a/src/sim/mod.rs +++ b/src/sim/mod.rs @@ -1,3 +1,59 @@ +use crate::{ + config::CHUNK_SIZE, + sim::{cell::materials::MaterialId, cell_sim::world::World, rb_sim::RbSimManager}, +}; + pub mod cell; pub mod cell_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 = CHUNK_SIZE as f32 / 2.0; + // half-extent of the rotated grid's axis-aligned bounding box, plus a cell of margin + let radius = half_size * (cos.abs() + sin.abs()) + 1.0; + + let world_xl = (rb_x - radius).floor() as i32; + let world_xu = (rb_x + radius).ceil() as i32; + let world_yl = (rb_y - radius).floor() as i32; + let world_yu = (rb_y + radius).ceil() as i32; + + for world_x in world_xl..=world_xu { + for world_y in world_yl..=world_yu { + // 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).floor() as i32, + (-q.0 * sin + q.1 * cos + half_size).floor() as i32, + ); + + if lx < 0 || ly < 0 || lx >= CHUNK_SIZE || ly >= CHUNK_SIZE { + 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 +} diff --git a/src/sim/rb_sim/mod.rs b/src/sim/rb_sim/mod.rs index 10172a1..8101afd 100644 --- a/src/sim/rb_sim/mod.rs +++ b/src/sim/rb_sim/mod.rs @@ -163,6 +163,7 @@ impl RbSimManager { for y in CHUNK_SIZE / 2 - 5..CHUNK_SIZE / 2 + 5 { let cell_idx = x + y * CHUNK_SIZE; test_cells[cell_idx as usize] = Cell::from_material(material); + test_cells[cell_idx as usize].set_rb(true); } } -- cgit v1.3.1