From fb0e374f17ccd9fc5be1456c53ff6dcbd99a2fa5 Mon Sep 17 00:00:00 2001 From: Rendo Date: Tue, 4 Aug 2026 03:33:19 +0500 Subject: [PATCH] feat: editor paint undone --- src/view/editor.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/view/game.rs | 39 ++++++++++++++++++------- src/view/mod.rs | 5 ++-- src/view/ui.rs | 41 ++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 13 deletions(-) create mode 100644 src/view/editor.rs diff --git a/src/view/editor.rs b/src/view/editor.rs new file mode 100644 index 0000000..6c64554 --- /dev/null +++ b/src/view/editor.rs @@ -0,0 +1,72 @@ +use bevy::prelude::*; +use bevy::picking::backend::ray::RayMap; + +use crate::grid::{Grid, GridElement}; + +pub struct EditorPlugin; + +impl Plugin for EditorPlugin { + fn build(&self, app: &mut App) { + app.init_resource::().add_observer(apply_tool).add_systems(Update, calculate_tools); + } +} + +#[derive(Default, Clone, PartialEq, Eq,Resource)] +pub enum EditorTools { + #[default] + None, + Paint{ position: UVec3, block: GridElement }, +} + + +#[derive(Component,Default,Clone)] +pub struct ViewCube; + +#[derive(Event)] +pub struct ApplyTool(EditorTools); + +pub fn calculate_tools( + mut commands: Commands, + mouse_button: Res>, + ray_map: Res, + mut raycast: MeshRayCast, + mut tool: ResMut, + mut gizmos: Gizmos + ) { + let new_tool: Option = match tool.clone() { + EditorTools::None => None, + EditorTools::Paint{position: _, block} => { + let mut new_position: Option = None; + for (_, ray) in ray_map.iter() { + let Some((_, hit)) = raycast.cast_ray(*ray, &MeshRayCastSettings::default()).first() else {continue;}; + gizmos.sphere(hit.point.round(), 0.5, Color::BLACK); + new_position = Some(hit.point.floor().as_uvec3()); + } + + new_position.map(|pos|{ + EditorTools::Paint { position: pos , block }} + ) + }, + }; + if let Some(new_tool) = new_tool { + *tool = new_tool; + } + if mouse_button.just_pressed(MouseButton::Left) { + match tool.clone() { + EditorTools::None => {}, + EditorTools::Paint { position, block } => { + commands.trigger(ApplyTool(EditorTools::Paint { position, block })); + }, + } + } +} + +pub fn apply_tool(params: On, mut grid: ResMut) { + match params.0 { + EditorTools::None => {}, + EditorTools::Paint { position, block } => { + let index = grid.indexify(position).unwrap(); + grid.data[index] = block; + }, + } +} diff --git a/src/view/game.rs b/src/view/game.rs index ec42fcf..233f11d 100644 --- a/src/view/game.rs +++ b/src/view/game.rs @@ -8,10 +8,10 @@ pub struct GameViewPlugin; impl Plugin for GameViewPlugin { fn build(&self, app: &mut App) { - app.add_systems(PreStartup, debug_setup_grid) + app .init_resource::() .init_resource::() - .add_systems(Startup, setup_view_cubes) + .add_systems(Startup, (setup_building_planes,setup_view_cubes)) .add_systems(Update, update_cubes); } } @@ -33,14 +33,31 @@ pub struct TileMaterials { pub source: Handle, } -pub fn debug_setup_grid(mut grid: ResMut) { - use crate::grid::GridElement; - 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(0, 8, 6, GridElement::Source { source_amount: MAX_FLUID_AMOUNT }); - //tool.set_xyz(15,0, 6, GridElement::Drain); +pub fn setup_building_planes( + mut commands: Commands, + mut meshes: ResMut>, + grid: Res +) { + let mut mesh: Mesh = (Cuboid::new(grid.size.x as f32, grid.size.y as f32, grid.size.z as f32).mesh()).into(); + let _ = mesh.invert_winding(); + + + let mesh_handle = meshes.add(mesh); + let center = (grid.size.as_vec3() - Vec3::ONE)/2.; + commands.spawn_scene(bsn!{ + Mesh3d(mesh_handle) + Transform { + translation: center + } + MeshMaterial3d(asset_value( + StandardMaterial { + base_color: Color::WHITE, + ..default() + } + )) + Visibility::Visible + }); + } pub fn setup_view_cubes( @@ -81,7 +98,7 @@ pub fn setup_view_cubes( commands.spawn_scene( cube( grid.deindexify(i as u32).as_vec3(), - i as usize, + i as usize, mesh.clone() ) ); diff --git a/src/view/mod.rs b/src/view/mod.rs index 7e80b34..8b8387c 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -2,15 +2,16 @@ use bevy::prelude::*; use game::GameViewPlugin; -use crate::view::ui::UIPlugin; +use crate::view::{editor::EditorPlugin, ui::UIPlugin}; pub mod ui; pub mod game; +pub mod editor; pub struct ViewPlugin; impl Plugin for ViewPlugin { fn build(&self, app: &mut App) { - app.add_plugins((GameViewPlugin,UIPlugin)); + app.add_plugins((GameViewPlugin,UIPlugin,EditorPlugin)); } } diff --git a/src/view/ui.rs b/src/view/ui.rs index c14f48e..3af88c0 100644 --- a/src/view/ui.rs +++ b/src/view/ui.rs @@ -4,6 +4,7 @@ use bevy::prelude::*; use crate::grid::{Grid, GridElement}; use crate::simulation::SimulationStatus; +use crate::view::editor::EditorTools; pub struct UIPlugin; @@ -22,6 +23,9 @@ struct WaterLabel; #[derive(Component, Clone, Default)] struct SpeedLabel; +#[derive(Component, Clone, Default)] +struct ToolLabel; + const MAX_FREQUENCY: u32 = 512; const MIN_FREQUENCY: u32 = 1; @@ -52,6 +56,7 @@ fn ui() -> impl Scene { } Children [ debug_column() + editor_column() ] } } @@ -79,6 +84,42 @@ fn debug_column() -> impl Scene { } } +fn editor_column() -> impl Scene { + bsn! { + Node { + display: Display::Flex, + flex_direction: FlexDirection::Column, + align_items: AlignItems::Stretch, + justify_content: JustifyContent::Start, + padding: px(8), + row_gap: px(8), + width: px(256), + min_width: px(256), + } + pane() + pane_header() + pane_body() + Children [ + label("Tools") + ToolLabel, + @FeathersButton { + @caption: bsn!{label("None")} + } + on(|_activate: On, mut tool: ResMut, mut label: Single<&mut Text, With>| { + *tool = EditorTools::None; + label.0 = "Current tool: None".to_string(); + }), + @FeathersButton { + @caption: bsn!{label("Paint")} + } + on(|_activate: On, mut tool: ResMut, mut label: Single<&mut Text, With>| { + *tool = EditorTools::Paint { position: UVec3::default(), block: GridElement::Solid }; + label.0 = "Current tool: Paint".to_string(); + }), + ] + } +} + fn water_amount_label() -> impl Scene { bsn! { Node {