diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-23 12:14:19 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-23 12:14:19 -0700 |
| commit | e6742f59102f2beb305b813d7f0ee1d544bbc3e0 (patch) | |
| tree | a4787655e441b4fb7acebf51373a8f3f7afbd9a4 /src | |
| parent | 5f137b41ebeadfca3907221713f85f2c9fe431bf (diff) | |
separate data from cells
Diffstat (limited to 'src')
| -rw-r--r-- | src/content/materials/fire.rs | 26 | ||||
| -rw-r--r-- | src/renderer/ui.rs | 1 | ||||
| -rw-r--r-- | src/sim/cell.rs | 9 | ||||
| -rw-r--r-- | src/sim/cell_manager/chunk.rs | 31 | ||||
| -rw-r--r-- | src/sim/cell_manager/manager.rs | 35 | ||||
| -rw-r--r-- | src/sim/cell_manager/sim.rs | 101 | ||||
| -rw-r--r-- | src/sim/lib/force.rs | 5 |
7 files changed, 160 insertions, 48 deletions
diff --git a/src/content/materials/fire.rs b/src/content/materials/fire.rs index 2e5ef2f..5e5b29a 100644 --- a/src/content/materials/fire.rs +++ b/src/content/materials/fire.rs @@ -8,19 +8,25 @@ use crate::{ }, }; +pub trait FireCtxView { + fn get_ticks_lived(&self, x: i32, y: i32) -> u16; + fn set_ticks_lived(&mut self, x: i32, y: i32, ticks: u16) -> (); +} + +impl FireCtxView for UpdateCtx<'_, '_, '_> { + fn get_ticks_lived(&self, x: i32, y: i32) -> u16 { + self.get_cell_data(x, y).unwrap_or(0) + } + fn set_ticks_lived(&mut self, x: i32, y: i32, ticks: u16) { + self.set_cell_data(x, y, ticks); + } +} + pub trait FireCellView { - fn get_ticks_lived(self) -> u16; - fn set_ticks_lived(&mut self, ticks: u16) -> (); fn is_flammable(self) -> bool; } impl FireCellView for Cell { - fn get_ticks_lived(self) -> u16 { - self.data - } - fn set_ticks_lived(&mut self, ticks: u16) { - self.data = ticks; - } // this could be a property of the material def, I think it's better here for now fn is_flammable(self) -> bool { [MaterialId::Wood].contains(&self.material) @@ -31,7 +37,7 @@ impl FireCellView for Cell { // TODO wtf is fire #[inline] pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction { - let ticks_lived = ctx.cell.get_ticks_lived(); + let ticks_lived = ctx.get_ticks_lived(0, 0); // 2 seconds if ticks_lived > 240 { // we have a chance to live longer--roughly ?% chance of living an extra second @@ -74,7 +80,7 @@ pub fn sim_update(ctx: &mut UpdateCtx) -> PostUpdateAction { } } - ctx.cell.set_ticks_lived(ticks_lived + 1); + ctx.set_ticks_lived(0, 0, ticks_lived + 1); // apply our new lifespan PostUpdateAction::Apply } diff --git a/src/renderer/ui.rs b/src/renderer/ui.rs index a7395a0..c0deb16 100644 --- a/src/renderer/ui.rs +++ b/src/renderer/ui.rs @@ -141,7 +141,6 @@ pub fn draw_egui<'a>( ui.label( egui::RichText::new(format!("Flags: {:b}", cell.flags)).color(Color32::YELLOW), ); - ui.label(egui::RichText::new(format!("Data: {:b}", cell.data)).color(Color32::RED)); } } } diff --git a/src/sim/cell.rs b/src/sim/cell.rs index ca0b969..d12255e 100644 --- a/src/sim/cell.rs +++ b/src/sim/cell.rs @@ -4,8 +4,6 @@ use crate::{config::SETTLED_THRESOHLD, content::materials::MaterialId}; pub struct Cell { pub material: MaterialId, pub flags: u8, - // TODO move this to a per-chunk hashmap - pub data: u16, } impl Cell { @@ -75,14 +73,9 @@ impl Cell { Cell { material: MaterialId::Void, flags: 0, - data: 0, } } pub fn from_material(material: MaterialId) -> Cell { - Cell { - material, - flags: 0, - data: 0, - } + Cell { material, flags: 0 } } } diff --git a/src/sim/cell_manager/chunk.rs b/src/sim/cell_manager/chunk.rs index 463242d..3bf1e9f 100644 --- a/src/sim/cell_manager/chunk.rs +++ b/src/sim/cell_manager/chunk.rs @@ -1,13 +1,16 @@ +use fxhash::FxHashMap; use glam::IVec2; use crate::{ config::{CELLS_IN_CHUNK, CHUNK_SIZE}, content::materials::MaterialForm, - sim::{cell::Cell, lib::marching_squares::Marchable}, + sim::{cell::Cell, entity::EntityId, lib::marching_squares::Marchable}, }; pub struct Chunk { pub cells: Box<[Cell; CELLS_IN_CHUNK]>, + pub cell_data: FxHashMap<usize, u16>, + pub cell_entity: FxHashMap<usize, EntityId>, pub sleeping: bool, pub needs_texture_update: bool, } @@ -22,9 +25,35 @@ impl Chunk { self.cells[x as usize + y as usize * CHUNK_SIZE as usize] = cell; } + #[inline] + pub fn get_data_at_local_position(&self, x: u8, y: u8) -> Option<u16> { + self.cell_data + .get(&(x as usize + y as usize * CHUNK_SIZE as usize)) + .copied() + } + #[inline] + pub fn set_data_at_local_position(&mut self, x: u8, y: u8, data: u16) { + self.cell_data + .insert(x as usize + y as usize * CHUNK_SIZE as usize, data); + } + + #[inline] + pub fn get_entity_at_local_position(&self, x: u8, y: u8) -> Option<EntityId> { + self.cell_entity + .get(&(x as usize + y as usize * CHUNK_SIZE as usize)) + .copied() + } + #[inline] + pub fn set_entity_at_local_position(&mut self, x: u8, y: u8, entity_id: EntityId) { + self.cell_entity + .insert(x as usize + y as usize * CHUNK_SIZE as usize, entity_id); + } + pub fn void() -> Self { Chunk { cells: Box::new([Cell::void(); CELLS_IN_CHUNK]), + cell_data: FxHashMap::default(), + cell_entity: FxHashMap::default(), sleeping: true, needs_texture_update: true, } diff --git a/src/sim/cell_manager/manager.rs b/src/sim/cell_manager/manager.rs index 6fa5f37..12cc1b7 100644 --- a/src/sim/cell_manager/manager.rs +++ b/src/sim/cell_manager/manager.rs @@ -5,6 +5,7 @@ use crate::{ sim::{ cell::Cell, cell_manager::{chunk::Chunk, sim::sim_tick}, + entity::EntityId, }, }; @@ -45,6 +46,40 @@ impl CellManager { } } + // VERY EXPENSIVE + pub fn get_data_from_game_position(&self, x: i32, y: i32) -> Option<u16> { + 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_data_at_local_position(dx, dy)) + .flatten() + } + + // VERY EXPENSIVE + pub fn set_data_from_game_position(&mut self, x: i32, y: i32, data: u16) { + 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_data_at_local_position(dx, dy, data); + } + } + + // VERY EXPENSIVE + pub fn get_entity_from_game_position(&self, x: i32, y: i32) -> Option<EntityId> { + 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_entity_at_local_position(dx, dy)) + .flatten() + } + + // VERY EXPENSIVE + pub fn set_entity_from_game_position(&mut self, x: i32, y: i32, entity_id: EntityId) { + 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_entity_at_local_position(dx, dy, entity_id); + } + } + pub fn insert(&mut self, x: i32, y: i32, chunk: Chunk) { self.chunk_position_to_chunk_idx .insert((x, y), self.chunks.len()); diff --git a/src/sim/cell_manager/sim.rs b/src/sim/cell_manager/sim.rs index ffa253b..5e369a9 100644 --- a/src/sim/cell_manager/sim.rs +++ b/src/sim/cell_manager/sim.rs @@ -10,6 +10,7 @@ use crate::{ sim::{ cell::Cell, cell_manager::{chunk::Chunk, manager::CellManager}, + entity::EntityId, }, }; @@ -52,9 +53,10 @@ fn neighbourhood_index(x: i8, y: i8) -> usize { (x + 1 + (y + 1) * 3) as usize } -fn get_cell(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<Cell> { +fn internal_get_cell(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<Cell> { let dcx = x.div_euclid(CHUNK_SIZE); let dcy = y.div_euclid(CHUNK_SIZE); + // optimized path to save on the rem calls if dcx != 0 || dcy != 0 { // in a different chunk let nc_x = x.rem_euclid(CHUNK_SIZE) as u8; @@ -70,6 +72,56 @@ fn get_cell(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<Cell> { } } +fn internal_get_cell_data(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<u16> { + let dcx = x.div_euclid(CHUNK_SIZE); + let dcy = y.div_euclid(CHUNK_SIZE); + if dcx != 0 || dcy != 0 { + // in a different chunk + let nc_x = x.rem_euclid(CHUNK_SIZE) as u8; + let nc_y = y.rem_euclid(CHUNK_SIZE) as u8; + + chunks[neighbourhood_index(dcx as i8, dcy as i8)] + .as_ref() + .map(|chunk| chunk.get_data_at_local_position(nc_x, nc_y)) + .flatten() + } else { + chunks[4] + .as_ref() + .map(|target| target.get_data_at_local_position(x as u8, y as u8)) + .flatten() + } +} + +fn internal_set_cell_data(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, data: u16) { + let dcx = x.div_euclid(CHUNK_SIZE); + let dcy = y.div_euclid(CHUNK_SIZE); + let nc_x = x.rem_euclid(CHUNK_SIZE) as u8; + let nc_y = y.rem_euclid(CHUNK_SIZE) as u8; + if let Some(chunk) = &mut chunks[neighbourhood_index(dcx as i8, dcy as i8)] { + chunk.set_data_at_local_position(nc_x, nc_y, data); + } +} + +fn internal_get_cell_entity(chunks: &[Option<&mut Chunk>; 9], x: i32, y: i32) -> Option<EntityId> { + let dcx = x.div_euclid(CHUNK_SIZE); + let dcy = y.div_euclid(CHUNK_SIZE); + if dcx != 0 || dcy != 0 { + // in a different chunk + let nc_x = x.rem_euclid(CHUNK_SIZE) as u8; + let nc_y = y.rem_euclid(CHUNK_SIZE) as u8; + + chunks[neighbourhood_index(dcx as i8, dcy as i8)] + .as_ref() + .map(|chunk| chunk.get_entity_at_local_position(nc_x, nc_y)) + .flatten() + } else { + chunks[4] + .as_ref() + .map(|target| target.get_entity_at_local_position(x as u8, y as u8)) + .flatten() + } +} + fn adjacent_chunks(x: u8, y: u8) -> Vec<usize> { if x == 0 { if y == 0 { @@ -133,29 +185,14 @@ fn internal_set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: let cy = y.div_euclid(CHUNK_SIZE); let lx = x.rem_euclid(CHUNK_SIZE) as u8; let ly = y.rem_euclid(CHUNK_SIZE) as u8; - if cx != 0 || cy != 0 { - // in a different chunk - if let Some(chunk) = &mut chunks[neighbourhood_index(cx as i8, cy as i8)] { - chunk.set_cell_at_local_position(lx, ly, cell); - chunk.needs_texture_update = true; - chunk.sleeping = false; - // if we're at the boundaries of the chunk, wake the adjacent chunk(s) - for idx in adjacent_chunks(lx, ly) { - if let Some(chunk) = chunks[idx].as_mut() { - chunk.sleeping = false; - } - } - } - } else { - if let Some(target) = &mut chunks[4] { - target.set_cell_at_local_position(x as u8, y as u8, cell); - target.needs_texture_update = true; - target.sleeping = false; - // if we're at the boundaries of the chunk, wake the adjacent chunk(s) - for idx in adjacent_chunks(lx, ly) { - if let Some(chunk) = chunks[idx].as_mut() { - chunk.sleeping = false; - } + if let Some(chunk) = &mut chunks[neighbourhood_index(cx as i8, cy as i8)] { + chunk.set_cell_at_local_position(lx, ly, cell); + chunk.needs_texture_update = true; + chunk.sleeping = false; + // if we're at the boundaries of the chunk, wake the adjacent chunk(s) + for idx in adjacent_chunks(lx, ly) { + if let Some(chunk) = chunks[idx].as_mut() { + chunk.sleeping = false; } } } @@ -190,7 +227,7 @@ 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) + internal_get_cell(self.chunks, x, y) } pub fn set_cell(&mut self, dx: i32, dy: i32, cell: Cell) { @@ -203,6 +240,18 @@ impl UpdateCtx<'_, '_, '_> { internal_set_cell(self.chunks, x, y, cell); } + pub fn get_cell_data(&self, dx: i32, dy: i32) -> Option<u16> { + let x = self.x + dx; + let y = self.y + dy; + internal_get_cell_data(self.chunks, x, y) + } + + pub fn set_cell_data(&mut self, dx: i32, dy: i32, data: u16) { + let x = self.x + dx; + let y = self.y + dy; + internal_set_cell_data(self.chunks, x, y, data); + } + 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) @@ -235,7 +284,7 @@ fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) { (CHUNK_SIZE) - i - 1 }; - let mut cell = get_cell(chunks, x, y).unwrap(); + let mut cell = internal_get_cell(chunks, x, y).unwrap(); let material = cell.material.def(); if let Some(update) = material.sim_update diff --git a/src/sim/lib/force.rs b/src/sim/lib/force.rs index e99817c..fd164f1 100644 --- a/src/sim/lib/force.rs +++ b/src/sim/lib/force.rs @@ -4,7 +4,7 @@ use rapier2d::{dynamics::RigidBodyHandle, parry::bounding_volume::Aabb, pipeline use crate::{ config::CELLS_TO_METRES, - content::materials::{MaterialForm, MaterialId, fire::FireCellView}, + content::materials::{MaterialForm, MaterialId}, sim::{cell::Cell, lib::ray::AwDda, particle_manager::particle::Particle, sim_manager::SimCtx}, }; @@ -34,7 +34,8 @@ pub fn apply_explosion( let ipos = pos.round().as_ivec2(); if let Some(cell) = ctx.cell_manager.get_cell_from_game_position(ipos.x, ipos.y) { let mut fire = Cell::from_material(MaterialId::Fire); - fire.set_ticks_lived(300); + ctx.cell_manager + .set_data_from_game_position(ipos.x, ipos.y, 300); fire.match_parity(ctx.cell_manager.seqno); if cell.material == MaterialId::Void && random_range(0.0..1.0) > 0.5 { |
