diff --git a/src/grid/element.rs b/src/grid/element.rs index 385677d..10ca4a7 100644 --- a/src/grid/element.rs +++ b/src/grid/element.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use crate::MAX_FLUID_AMOUNT; #[derive(Default,Clone,Copy)] @@ -6,9 +8,20 @@ pub enum GridElement{ Air, Water{amount: u32}, Solid, + Drain, + Source{source_amount: u32}, } impl GridElement { + pub fn can_fill(&self) -> bool { + match self { + GridElement::Air => true, + GridElement::Water { amount: _ } => true, + GridElement::Solid => false, + GridElement::Drain => true, + GridElement::Source { source_amount: _ } => false, + } + } pub fn try_fill(&self, fill_amount: u32) -> Option { return match self { GridElement::Water { amount } => { @@ -23,7 +36,20 @@ impl GridElement { } }, GridElement::Air => Some(fill_amount), + GridElement::Drain => Some(fill_amount), _ => None, } } } + +impl Display for GridElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f,"{}",match self { + GridElement::Air => "Air", + GridElement::Water { amount: _ } => "Water", + GridElement::Solid => "Solid", + GridElement::Drain => "Drain", + GridElement::Source { source_amount: _ } => "Source", + }) + } +} diff --git a/src/grid/mod.rs b/src/grid/mod.rs index 4c114aa..5f33e2e 100644 --- a/src/grid/mod.rs +++ b/src/grid/mod.rs @@ -53,10 +53,11 @@ impl Grid { let to_new_value; match (self.data[to],self.data[from]) { - (GridElement::Air,GridElement::Water { amount: mut water_amount }) => { - to_new_value = Some(GridElement::Water { amount: transfer_amount }); - water_amount = water_amount.checked_sub(transfer_amount).unwrap_or(0); - from_new_value = if water_amount == 0 {Some(GridElement::Air)} else {Some(GridElement::Water { amount: water_amount })}; + (GridElement::Air,GridElement::Water { amount: water_amount }) => { + let moved_amount = water_amount.min(transfer_amount); + let remaining_amount = water_amount-moved_amount; + to_new_value = Some(GridElement::Water { amount: moved_amount }); + from_new_value = if remaining_amount == 0 {Some(GridElement::Air)} else {Some(GridElement::Water { amount: remaining_amount })}; }, (GridElement::Water { amount: mut to_amount }, GridElement::Water { amount: mut from_amount }) => { let transfer_amount = from_amount.min(transfer_amount); @@ -71,7 +72,13 @@ impl Grid { } to_new_value = Some(GridElement::Water { amount: to_amount }); from_new_value = if from_amount == 0 {Some(GridElement::Air)} else {Some(GridElement::Water { amount: from_amount })}; - } + }, + (GridElement::Drain, GridElement::Water { amount: from_amount }) => { + let transfer_amount = from_amount.min(transfer_amount); + let new_amount = from_amount - transfer_amount; + from_new_value = if new_amount == 0 {Some(GridElement::Air)} else {Some(GridElement::Water { amount: new_amount })}; + to_new_value = None; + }, _ => {return;}, } @@ -84,6 +91,28 @@ impl Grid { } } + pub fn fill(&mut self, at: usize, amount: u32) -> u32 { + let transformed_value; + let return_amount; + match self.data[at] { + GridElement::Air => { + transformed_value = GridElement::Water { amount }; + return_amount = 0; + }, + GridElement::Water { amount: water_amount } => { + let diff = MAX_FLUID_AMOUNT - water_amount; + let fill_amount = diff.min(amount); + return_amount = amount - fill_amount; + transformed_value = GridElement::Water { amount: water_amount + fill_amount }; + }, + GridElement::Drain => { return 0; }, + _ => { return amount; } + } + + self.data[at] = transformed_value; + + return return_amount; + } pub fn manipulate<'a>(&'a mut self) -> GridManipulationTools<'a> { GridManipulationTools::new(self) } diff --git a/src/simulation.rs b/src/simulation.rs index 31dca7f..531f544 100644 --- a/src/simulation.rs +++ b/src/simulation.rs @@ -16,6 +16,10 @@ enum GridAction{ from: UVec3, to: UVec3, amount: u32, + }, + SpawnLiquid { + at: UVec3, + amount: u32, } } @@ -52,29 +56,41 @@ pub fn tick(mut grid: ResMut) { transfer_amount = transfer_amount.min(MAX_HORIZONTAL_FLOW) / 4; if transfer_amount > 0 { - for lookup_vector in [UVec3::new(1,0,0),UVec3::new(0,0,1)] { - if let Some(toward_vector) = vector.checked_add(lookup_vector) { - if let Some(index) = grid.indexify(toward_vector) { - if let Some(toward_amount) = grid.data - [index] - .try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) { - stack.push(GridAction::MoveLiquid { from: vector, to: toward_vector, amount: toward_amount }); - } - } + for lookup_vector in [IVec3::new(1,0,0),IVec3::new(0,0,1),IVec3::new(-1,0,0),IVec3::new(0,0,-1)] { + let push_vector = (vector.as_ivec3() + lookup_vector).max(IVec3::ZERO).as_uvec3(); + if push_vector == vector { + continue; } + let Some(push_index) = grid.indexify(push_vector) else { + continue; + }; - if let Some(toward_vector) = vector.checked_sub(lookup_vector) { - if let Some(index) = grid.indexify(toward_vector) { - if let Some(toward_amount) = grid.data - [index] - .try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) { - stack.push(GridAction::MoveLiquid { from: vector, to: toward_vector, amount: toward_amount }); - } - } - } + let Some(push_amount) = grid.data[push_index].try_fill(transfer_amount) else { + continue; + }; + stack.push(GridAction::MoveLiquid { from: vector, to: push_vector, amount: push_amount }); } } }, + GridElement::Source { source_amount } => { + if let Some(gravity_vector) = vector.checked_sub(UVec3::new(0,1,0)) { + if grid.data[grid.indexify(gravity_vector).unwrap()].can_fill() { + stack.push(GridAction::SpawnLiquid { at: gravity_vector, amount: source_amount }); + } + } + + for lookup_vector in [IVec3::new(1,0,0),IVec3::new(0,0,1),IVec3::new(-1,0,0),IVec3::new(0,0,-1)] { + let push_vector = (vector.as_ivec3() + lookup_vector).max(IVec3::ZERO).as_uvec3(); + let Some(push_index) = grid.indexify(push_vector) else { + continue; + }; + + if grid.data[push_index].can_fill() == false { + continue; + } + stack.push(GridAction::SpawnLiquid { at: push_vector, amount: source_amount }); + } + }, _ => {}, } @@ -88,6 +104,10 @@ pub fn tick(mut grid: ResMut) { let (from_index, to_index) = (grid.indexify(from).unwrap(), grid.indexify(to).unwrap()); grid.tranfer_fluid(from_index, to_index, amount); }, + GridAction::SpawnLiquid { at, amount } => { + let at_index = grid.indexify(at).unwrap(); + grid.fill(at_index, amount); + } } } diff --git a/src/view/game.rs b/src/view/game.rs index 1469186..a5fcc43 100644 --- a/src/view/game.rs +++ b/src/view/game.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use crate::grid::Grid; +use crate::grid::{Grid,GridElement}; use crate::MAX_FLUID_AMOUNT; const FLUID_FULLNES_STAGES: usize = 7; @@ -26,6 +26,8 @@ pub struct TileWatch(pub usize); pub struct TileMaterials { pub solid: Handle, pub water: Handle, + pub drain: Handle, + pub source: Handle, } pub fn debug_setup_grid(mut grid: ResMut) { @@ -33,6 +35,8 @@ pub fn debug_setup_grid(mut grid: ResMut) { let mut tool = grid.manipulate(); tool.fill_xyz(5, 0, 1, 5, 0, 14, GridElement::Solid); tool.fill_xyz(0, 0, 0, 4, 0, 15, GridElement::Water { amount: MAX_FLUID_AMOUNT }); + //tool.set_xyz(0, 0, 6, GridElement::Source { source_amount: MAX_FLUID_AMOUNT }); + tool.set_xyz(15,0, 6, GridElement::Drain); } pub fn setup_view_cubes( @@ -47,17 +51,26 @@ pub fn setup_view_cubes( let mesh = meshes.add(Cuboid::new(1., tile_height, 1.)); stages.0.push(mesh); } - - tile_materials.water = materials.add(StandardMaterial { - base_color: Color::hsl(172.,0.502,0.571), - ..default() - }); - - tile_materials.solid = materials.add(StandardMaterial { + + *tile_materials = TileMaterials { + solid: materials.add(StandardMaterial { base_color: Color::hsl(268., 0.778, 0.499), ..default() - }); - + }), + water: materials.add(StandardMaterial { + base_color: Color::hsl(172.,0.502,0.571), + ..default() + }), + drain: materials.add(StandardMaterial { + base_color: Color::hsl(342., 0.628, 0.685), + ..default() + }), + source: materials.add(StandardMaterial { + base_color: Color::hsl(172., 0.502, 0.371), + ..default() + }) + }; + let mesh = stages.0[FLUID_FULLNES_STAGES-1].clone(); for i in 0..grid.cell_amount() { @@ -123,10 +136,10 @@ pub fn update_cubes(query: Query<(&mut Mesh3d,&mut Visibility,&mut MeshMaterial3 let state = grid.data[index.0]; match state { - crate::grid::GridElement::Air => { + GridElement::Air => { *visibility = Visibility::Hidden; }, - crate::grid::GridElement::Water { amount } => { + GridElement::Water { amount } => { *visibility = Visibility::Visible; material.0 = materials.water.clone(); let stage = ((amount as f32)/(MAX_FLUID_AMOUNT as f32) * (FLUID_FULLNES_STAGES-1) as f32).ceil() as usize; @@ -135,11 +148,26 @@ pub fn update_cubes(query: Query<(&mut Mesh3d,&mut Visibility,&mut MeshMaterial3 mesh.0 = stages.0[stage].clone(); }, - crate::grid::GridElement::Solid => { + GridElement::Solid => { *visibility = Visibility::Visible; material.0 = materials.solid.clone(); mesh.0 = stages.0[FLUID_FULLNES_STAGES-1].clone(); }, + GridElement::Drain => { + *visibility = Visibility::Visible; + material.0 = materials.drain.clone(); + mesh.0 = stages.0[FLUID_FULLNES_STAGES-1].clone(); + }, + GridElement::Source { source_amount } => { + *visibility = Visibility::Visible; + material.0 = materials.source.clone(); + let stage = ((source_amount as f32)/(MAX_FLUID_AMOUNT as f32) * (FLUID_FULLNES_STAGES-1) as f32).ceil() as usize; + + transform.translation = grid.deindexify(index.0 as u32).as_vec3() - vec3(0.,((FLUID_FULLNES_STAGES-1-stage) as f32 / (FLUID_FULLNES_STAGES as f32))/2.,0.); + + mesh.0 = stages.0[stage].clone(); + } + } } } diff --git a/src/view/ui.rs b/src/view/ui.rs index 131d2b3..c14f48e 100644 --- a/src/view/ui.rs +++ b/src/view/ui.rs @@ -1,4 +1,4 @@ -use bevy::feathers::{FeathersPlugins, containers::*, controls::*, dark_theme::create_dark_theme, display::*, palette, rounded_corners::RoundedCorners, theme::UiTheme}; +use bevy::feathers::{FeathersPlugins, containers::*, controls::*, dark_theme::create_dark_theme, display::*, theme::UiTheme}; use bevy::ui_widgets::{Activate}; use bevy::prelude::*; @@ -22,6 +22,9 @@ struct WaterLabel; #[derive(Component, Clone, Default)] struct SpeedLabel; +const MAX_FREQUENCY: u32 = 512; +const MIN_FREQUENCY: u32 = 1; + fn count_water_amount(grid: Res, text: Single<&mut Text, With>) { let mut sum: u32 = 0; for i in 0..grid.cell_amount() { @@ -119,7 +122,7 @@ fn simulation_speed() -> impl Scene { } on(|_activate: On, mut time: ResMut>, mut label: Single<&mut Text, With>|{ let mut current_frequency = (1./time.timestep().as_secs_f64()) as u32; - current_frequency = if current_frequency == 1 {1} else {current_frequency / 2}; + current_frequency = if current_frequency <= MIN_FREQUENCY {MIN_FREQUENCY} else {current_frequency / 2}; time.set_timestep_hz(current_frequency as f64); label.0 = format!("{} hz", current_frequency); }) @@ -134,7 +137,7 @@ fn simulation_speed() -> impl Scene { } on(|_activate: On, mut time: ResMut>, mut label: Single<&mut Text, With>|{ let mut current_frequency = (1./time.timestep().as_secs_f64()) as u32; - current_frequency = if current_frequency >= 128 {128} else {current_frequency * 2}; + current_frequency = if current_frequency >= MAX_FREQUENCY {MAX_FREQUENCY} else {current_frequency * 2}; time.set_timestep_hz(current_frequency as f64); label.0 = format!("{} hz", current_frequency); }),