diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-15 22:46:53 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-15 22:46:53 -0700 |
| commit | d7eccba2055cd7784a0db6d9ee8692a4f3510931 (patch) | |
| tree | 37f4c19781c788fa2af1ee4eeb7195f49a6d2d7a /src/sim | |
| parent | 05135beb87bbf0edace544b80ba40e327d9a89ac (diff) | |
fire, optimize viewport rendering
Diffstat (limited to 'src/sim')
| -rw-r--r-- | src/sim/cell.rs | 21 | ||||
| -rw-r--r-- | src/sim/materials/fire.rs | 53 | ||||
| -rw-r--r-- | src/sim/materials/mod.rs | 14 | ||||
| -rw-r--r-- | src/sim/sim.rs | 65 | ||||
| -rw-r--r-- | src/sim/world.rs | 5 |
5 files changed, 123 insertions, 35 deletions
diff --git a/src/sim/cell.rs b/src/sim/cell.rs index 2e26ddc..d3ca302 100644 --- a/src/sim/cell.rs +++ b/src/sim/cell.rs @@ -4,6 +4,20 @@ use crate::sim::materials::MaterialId; pub struct Cell { pub material: MaterialId, pub flags: u8, + pub data: u16, +} + +impl Cell { + const FLAG_PARITY: u8 = 0b0000_0001; + + #[inline] + pub fn parity(self) -> u8 { + self.flags & Self::FLAG_PARITY + } + #[inline] + pub fn flip_parity(&mut self) { + self.flags ^= Self::FLAG_PARITY; + } } impl Cell { @@ -11,9 +25,14 @@ impl Cell { Cell { material: MaterialId::Void, flags: 0, + data: 0, } } pub fn from_material(material: MaterialId) -> Cell { - Cell { material, flags: 0 } + Cell { + material, + flags: 0, + data: 0, + } } } diff --git a/src/sim/materials/fire.rs b/src/sim/materials/fire.rs new file mode 100644 index 0000000..b155986 --- /dev/null +++ b/src/sim/materials/fire.rs @@ -0,0 +1,53 @@ +use rand::RngExt; + +use crate::sim::{cell::Cell, materials::MaterialId, sim::UpdateCtx}; + +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) + } +} + +#[inline] +pub fn sim_update(ctx: &mut UpdateCtx) { + let ticks_lived = ctx.cell.get_ticks_lived(); + // 2 seconds + if ticks_lived > 240 { + // kill ourselves, with a chance to turn into ash + if ctx.rng.random_range(0.0..1.0) > 0.8 { + // TODO ash material + ctx.set_cell(0, 0, Cell::from_material(MaterialId::Sand)); + } else { + ctx.set_cell(0, 0, Cell::void()); + } + return; + } + + // 4 times in our lifespan, try to spread in a random direction + // since it's random, fire may die out if there's nothing in that direction + // a 1-thick line of flammable cells has a 31% chance to die each iteration + if ticks_lived % 60 == 0 { + let (dx, dy) = (ctx.rng.random_range(-1..=1), ctx.rng.random_range(-1..=1)); + if let Some(target) = ctx.get_cell(dx, dy) + && target.is_flammable() + { + ctx.set_cell(dx, dy, Cell::from_material(MaterialId::Fire)); + } + } + + ctx.cell.set_ticks_lived(ticks_lived + 1); + ctx.set_cell(0, 0, *ctx.cell); +} diff --git a/src/sim/materials/mod.rs b/src/sim/materials/mod.rs index d2d0e0e..764318a 100644 --- a/src/sim/materials/mod.rs +++ b/src/sim/materials/mod.rs @@ -1,5 +1,6 @@ use crate::sim::sim::UpdateCtx; +mod fire; mod gas; mod sand; mod water; @@ -12,6 +13,7 @@ pub enum MaterialId { Wood, Water, Gas, + Fire, } pub struct MaterialDef { @@ -21,7 +23,7 @@ pub struct MaterialDef { pub sim_update: Option<fn(ctx: &mut UpdateCtx) -> ()>, } -static MATERIALS: [MaterialDef; 5] = [ +static MATERIALS: [MaterialDef; 6] = [ MaterialDef { name: "Void", color: (0x00, 0x00, 0x00, 0x00), @@ -52,15 +54,23 @@ static MATERIALS: [MaterialDef; 5] = [ density: 10, sim_update: Some(gas::sim_update), }, + MaterialDef { + name: "Fire", + color: (0xFC, 0x66, 0x00, 0xAA), + // for now this matches wood + density: 50, + sim_update: Some(fire::sim_update), + }, ]; impl MaterialId { - pub const ALL: [MaterialId; 5] = [ + pub const ALL: [MaterialId; 6] = [ MaterialId::Void, MaterialId::Sand, MaterialId::Wood, MaterialId::Water, MaterialId::Gas, + MaterialId::Fire, ]; #[inline] pub fn def(self) -> &'static MaterialDef { diff --git a/src/sim/sim.rs b/src/sim/sim.rs index 4c6d908..84c3126 100644 --- a/src/sim/sim.rs +++ b/src/sim/sim.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; use fxhash::FxHashMap; +use rand::{Rng, SeedableRng, rngs::SmallRng}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use crate::{ @@ -30,17 +31,6 @@ impl<'a> ChunkAccess<'a> { unsafe impl Sync for ChunkAccess<'_> {} -pub struct UpdateCtx<'a, 'b, 'c> { - pub chunks: &'a mut [Option<&'b mut Chunk>; 9], - pub seqno: u64, - pub seqno_parity: u8, - - 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); let dcy = y.div_euclid(CHUNK_SIZE); @@ -74,17 +64,28 @@ pub fn set_cell(chunks: &mut [Option<&mut Chunk>; 9], x: i32, y: i32, cell: Cell if let Some(chunk) = &mut chunks[(dcx + 1 + (dcy + 1) * 3) as usize] { chunk.set_cell_at_local_position(nc_x, nc_y, cell); chunk.needs_texture_update = true; - 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; } } } +pub struct UpdateCtx<'a, 'b, 'c> { + pub chunks: &'a mut [Option<&'b mut Chunk>; 9], + pub seqno: u64, + pub seqno_parity: u8, + + pub x: i32, + pub y: i32, + pub cell: &'c mut Cell, + pub material: &'c MaterialDef, + + pub rng: &'c mut dyn Rng, +} + impl UpdateCtx<'_, '_, '_> { pub fn get_cell(&self, dx: i32, dy: i32) -> Option<Cell> { let x = self.x + dx; @@ -92,7 +93,7 @@ impl UpdateCtx<'_, '_, '_> { get_cell(self.chunks, x, y) } - fn set_cell(&mut self, dx: i32, dy: i32, cell: Cell) { + pub fn set_cell(&mut self, dx: i32, dy: i32, cell: Cell) { // cannot move out of the neighbourhood, but also cannot move to the edge of the neighbourhood // as this would wake a chunk outside of the neighbourhood debug_assert!(dx > -15 && dx < 15); @@ -122,8 +123,8 @@ impl UpdateCtx<'_, '_, '_> { 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 mut rng = SmallRng::seed_from_u64(seqno); if chunks[4].is_some() { for y in (0..CHUNK_SIZE).rev() { @@ -135,24 +136,28 @@ pub fn sim_tick_chunk(chunks: &mut [Option<&mut Chunk>; 9], seqno: u64) { }; 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(); + let material = cell.material.def(); + + if let Some(update) = material.sim_update { + if cell.parity() == seqno_parity { + cell.flip_parity(); + // apply the flipped parity in case the sim target doesn't + set_cell(chunks, x, y, cell); + + let mut update_ctx = UpdateCtx { + chunks, + seqno, + seqno_parity, - let mut update_ctx = UpdateCtx { - chunks, - seqno, - seqno_parity, + x, + y, + cell: &mut cell, + material, - x, - y, - cell: &mut cell, - material, - }; + // TODO this is platform-dependent, will break for multiplayer + rng: &mut rng, + }; - if let Some(update) = material.sim_update { update(&mut update_ctx); } } diff --git a/src/sim/world.rs b/src/sim/world.rs index 6645d4e..5993b10 100644 --- a/src/sim/world.rs +++ b/src/sim/world.rs @@ -48,6 +48,7 @@ impl World { self.chunks[idx].set_cell_at_local_position(dx, dy, cell); // this is a temporary hack self.chunks[idx].sleeping = sleeping; + self.chunks[idx].needs_texture_update = true; } } @@ -63,8 +64,8 @@ impl World { chunk_position_to_chunk_idx: FxHashMap::default(), }; - for y in -10..10 { - for x in -10..10 { + for y in -10..1 { + for x in -100..100 { world.insert(x, y, Chunk::void()); } } |
