summaryrefslogtreecommitdiff
path: root/src/sim/sim.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sim/sim.rs')
-rw-r--r--src/sim/sim.rs208
1 files changed, 168 insertions, 40 deletions
diff --git a/src/sim/sim.rs b/src/sim/sim.rs
index 350b823..5cd065e 100644
--- a/src/sim/sim.rs
+++ b/src/sim/sim.rs
@@ -1,26 +1,100 @@
-use crate::{Board, sim::board::Cell};
+use std::marker::PhantomData;
-pub struct UpdateCtx<'a> {
- pub self_x: i32,
- pub self_y: i32,
- pub self_cell: Cell,
- pub delta_time: f32,
+use crate::{
+ config::CHUNK_SIZE,
+ sim::{cell::Cell, chunk::Chunk, materials::MaterialDef, world::World},
+};
+
+struct ChunkAccess<'a> {
+ ptr: *mut Chunk,
+ len: usize,
+ _marker: PhantomData<&'a mut [Chunk]>,
+}
+
+impl<'a> ChunkAccess<'a> {
+ pub fn new(chunks: &'a mut [Chunk]) -> Self {
+ Self {
+ ptr: chunks.as_mut_ptr(),
+ len: chunks.len(),
+ _marker: PhantomData,
+ }
+ }
+ unsafe fn get(&self, i: usize) -> &'a mut Chunk {
+ debug_assert!(i < self.len);
+ unsafe { &mut *self.ptr.add(i) }
+ }
+}
+
+pub struct UpdateCtx<'a, 'b, 'c> {
+ pub chunks: &'a mut [Option<&'b mut Chunk>; 9],
+ pub seqno: u64,
pub seqno_parity: u8,
- pub board: &'a mut Board,
+
+ pub x: i32,
+ pub y: i32,
+ pub cell: &'c mut Cell,
+ pub material: &'c MaterialDef,
+}
+
+fn get_cell(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<Cell> {
+ let dcx = x.div_euclid(CHUNK_SIZE as i32);
+ let dcy = y.div_euclid(CHUNK_SIZE as i32);
+ if dcx != 0 || dcy != 0 {
+ // in a different chunk
+ let nc_x = x.rem_euclid(CHUNK_SIZE as i32) as u8;
+ let nc_y = y.rem_euclid(CHUNK_SIZE as i32) as u8;
+
+ return if let Some(chunk) = &chunks[(dcx + 1 + (dcy + 1) * 3) as usize] {
+ Some(chunk.get_cell_at_local_position(nc_x, nc_y))
+ } else {
+ None
+ };
+ } else {
+ if let Some(target) = &chunks[4] {
+ Some(target.get_cell_at_local_position(x as u8, y as u8))
+ } else {
+ None
+ }
+ }
+}
+
+pub fn set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: Cell) {
+ let dcx = x.div_euclid(CHUNK_SIZE as i32);
+ let dcy = y.div_euclid(CHUNK_SIZE as i32);
+ if dcx != 0 || dcy != 0 {
+ // in a different chunk
+ let nc_x = x.rem_euclid(CHUNK_SIZE as i32) as u8;
+ let nc_y = y.rem_euclid(CHUNK_SIZE as i32) as u8;
+
+ if let Some(chunk) = &mut chunks[(dcx + 1 + (dcy + 1) * 3) as usize] {
+ chunk.set_cell_at_local_position(nc_x, nc_y, cell);
+ }
+ } else {
+ if let Some(target) = &mut chunks[4] {
+ target.set_cell_at_local_position(x as u8, y as u8, cell);
+ }
+ }
}
-impl UpdateCtx<'_> {
+impl UpdateCtx<'_, '_, '_> {
+ pub fn get_cell(&self, dx: i32, dy: i32) -> Option<Cell> {
+ let x = self.x + dx;
+ let y = self.y + dy;
+ get_cell(self.chunks, x, y)
+ }
+
+ fn set_cell(&mut self, dx: i32, dy: i32, cell: Cell) {
+ let x = self.x + dx;
+ let y = self.y + dy;
+ set_cell(self.chunks, x, y, cell);
+ }
+
pub fn candidates_swap(&mut self, candidates: &[(i32, i32)]) -> bool {
- for candidate in candidates {
- let target = self.board.cell_at_position(candidate.0, candidate.1);
- if let Some(target) = target
- && target.material.def().density < self.self_cell.material.def().density
- {
- // swap the cells
- self.board
- .set_cell_at_position(self.self_x, self.self_y, target);
- self.board
- .set_cell_at_position(candidate.0, candidate.1, self.self_cell);
+ for &(dx, dy) in candidates {
+ let candidate_cell = self.get_cell(dx, dy);
+ if candidate_cell.is_some_and(|c| c.material.def().density < self.material.density) {
+ self.set_cell(0, 0, candidate_cell.unwrap());
+ self.set_cell(dx, dy, *self.cell);
return true;
}
}
@@ -28,36 +102,90 @@ impl UpdateCtx<'_> {
}
}
-// TODO: chunks
-pub fn sim_tick(board: &mut Board, seqno: u64, delta_time: f32) {
+pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) {
+ puffin::profile_function!();
// scan bottom to top to enable contiguous falling
let seqno_parity = (seqno as u8) & 0b1;
- let bx = (board.size_x / 2) as i32;
- let by = (board.size_y / 2) as i32;
- for y in (-by..by + 1).rev() {
- // invert scan order on every other frame
- for col in -bx..bx + 1 {
- let x = if seqno_parity == 0 { col } else { -col };
+ if chunks[4].is_some() {
+ for y in (0..CHUNK_SIZE as i32).rev() {
+ for i in 0..CHUNK_SIZE as i32 {
+ let x = if seqno_parity == 0 {
+ i
+ } else {
+ (CHUNK_SIZE as i32) - i - 1
+ };
- let cell = board.cell_at_position(x, y);
- if let Some(mut cur) = cell
- && cur.flags & 0b1 == seqno_parity
- {
- // flip the parity bit
- cur.flags = cur.flags ^ 0b1;
+ let mut cell = get_cell(chunks, x, y).unwrap();
+ if cell.flags & 0b1 == seqno_parity {
+ // flip the parity bit
+ // TODO if the cell doesn't move this doesn't stay
+ cell.flags = cell.flags ^ 0b1;
+ let material = cell.material.def();
- if let Some(update) = cur.material.def().sim_update {
- update(&mut UpdateCtx {
- self_x: x,
- self_y: y,
- self_cell: cur,
- board,
- delta_time,
+ let mut update_ctx = UpdateCtx {
+ chunks,
+ seqno,
seqno_parity,
- });
+
+ x,
+ y,
+ cell: &mut cell,
+ material,
+ };
+
+ if let Some(update) = material.sim_update {
+ update(&mut update_ctx);
+ }
}
}
}
}
}
+
+const NEIGHBORHOOD_OFFSETS: [(i32, i32); 9] = [
+ (-1, -1),
+ (0, -1),
+ (1, -1),
+ (-1, 0),
+ (0, 0),
+ (1, 0),
+ (-1, 1),
+ (0, 1),
+ (1, 1),
+];
+
+pub fn sim_tick(world: &mut World, seqno: u64) {
+ puffin::profile_function!();
+ let mut update_groups: [Vec<(i32, i32)>; 9] = Default::default();
+
+ // assign a color to each chunk s.t. every chunk is surrounded by <= 8 chunks of different colors
+ // --------------------
+ // | 0, 1, 2, 0, 1, 2 |
+ // | 3, 4, 5, 3, 4, 5 |
+ // | 6, 7, 8, 6, 7, 8 |
+ // | 0, 1, 2, 0, 1, 2 |
+ // | 3, 4, 5, 3, 4, 5 |
+ // | 6, 7, 8, 6, 7, 8 |
+ // --------------------
+
+ for (&(cx, cy), _) in &world.chunk_position_to_chunk_idx {
+ let color = (cx.rem_euclid(3) * 3 + cy.rem_euclid(3)) as usize;
+ update_groups[color].push((cx, cy));
+ }
+
+ for group in &update_groups {
+ let access = ChunkAccess::new(&mut world.chunks);
+ // TODO this can be parallelized since they will never share neighbours
+ for &(cx, cy) in group {
+ let mut chunks: [Option<&mut Chunk>; 9] = NEIGHBORHOOD_OFFSETS.map(|(dx, dy)| {
+ world
+ .chunk_position_to_chunk_idx
+ .get(&(cx + dx, cy + dy))
+ .map(|&idx| unsafe { access.get(idx) })
+ });
+
+ sim_tick_chunk(&mut chunks, seqno);
+ }
+ }
+}