summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2026-08-17 18:18:25 -0700
committerKai Stevenson <kai@kaistevenson.com>2026-08-17 18:18:25 -0700
commit54ae26e7c41f6c0a465a55c3cc5ee16ff81a1d70 (patch)
tree40aa246f01cd9f66ba581528592ea0704e90164a /src
parent3d476186f53d695c340c51dbd3aa14ff4621c520 (diff)
refactor/UX
Diffstat (limited to 'src')
-rw-r--r--src/main.rs47
-rw-r--r--src/renderer/ui.rs2
-rw-r--r--src/sim/cell/cell.rs21
-rw-r--r--src/sim/cell_sim/sim.rs46
-rw-r--r--src/sim/mod.rs56
-rw-r--r--src/sim/rb_sim/mod.rs1
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<u32> = 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<u32> = 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);
}
}