feat: Box brush

This commit is contained in:
Rendo 2026-08-05 21:15:39 +05:00
commit 4373e671a0
3 changed files with 88 additions and 35 deletions

View file

@ -15,10 +15,14 @@ impl<'a> GridManipulationTools<'a> {
}; };
self.grid.data[index] = element; self.grid.data[index] = element;
} }
pub fn fill_xyz(&mut self,x1: u32, y1: u32, z1: u32, x2: u32, y2: u32, z2: u32, element: GridElement) { pub fn set_vec(&mut self, vec: UVec3, element: GridElement) {
if x1 > x2 || y1 > y2 || z1 > z2 { let Some(index) = self.grid.indexify(vec) else {
return; return;
} };
self.grid.data[index] = element;
}
pub fn fill_xyz(&mut self,x1: u32, y1: u32, z1: u32, x2: u32, y2: u32, z2: u32, element: GridElement) {
let (x1,y1,z1,x2,y2,z2) = (x1.min(x2), y1.min(y2), z1.min(z2), x1.max(x2), y1.max(y2), z1.max(z2));
for x in x1..=x2 { for x in x1..=x2 {
for y in y1..=y2 { for y in y1..=y2 {
@ -27,6 +31,21 @@ impl<'a> GridManipulationTools<'a> {
continue; continue;
}; };
self.grid.data[index] = element.clone();
}
}
}
}
pub fn fill_vec(&mut self,vec1: UVec3, vec2: UVec3, element: GridElement) {
let (min_corner, max_corner) = (vec1.min(vec2), vec1.max(vec2));
for x in min_corner.x..=max_corner.x {
for y in min_corner.y..=max_corner.y {
for z in min_corner.z..=max_corner.z {
let Some(index) = self.grid.indexify(UVec3::new(x,y,z)) else {
continue;
};
self.grid.data[index] = element.clone(); self.grid.data[index] = element.clone();
} }
} }

View file

@ -15,7 +15,7 @@ impl Plugin for EditorPlugin {
} }
} }
#[derive(Default, Clone, PartialEq, Eq)] #[derive(Default, PartialEq, Eq)]
pub enum Confirmation<T> { pub enum Confirmation<T> {
#[default] #[default]
None, None,
@ -23,6 +23,32 @@ pub enum Confirmation<T> {
Confirmed(T) Confirmed(T)
} }
impl<T: Clone> Confirmation<T> {
pub fn from_option(option: &Option<T>) -> Self {
match option {
Some(item) => Confirmation::Suggestion(item.clone()),
None => Confirmation::None,
}
}
pub fn to_option(&self) -> Option<T> {
match self {
Confirmation::None => None,
Confirmation::Suggestion(item) => Some(item.clone()),
Confirmation::Confirmed(item) => Some(item.clone()),
}
}
}
impl<T: Clone> Clone for Confirmation<T> {
fn clone(&self) -> Self {
match self {
Self::None => Self::None,
Self::Suggestion(arg0) => Self::Suggestion(arg0.clone()),
Self::Confirmed(arg0) => Self::Confirmed(arg0.clone()),
}
}
}
#[derive(Default, Clone, PartialEq, Eq,Resource)] #[derive(Default, Clone, PartialEq, Eq,Resource)]
pub struct EditorTool { pub struct EditorTool {
pub block: GridElement, pub block: GridElement,
@ -34,14 +60,16 @@ pub enum ToolType {
#[default] #[default]
None, None,
Paint{ position: Option<UVec3> }, Paint{ position: Option<UVec3> },
//Box { first_corner: Confirmation<UVec3>, second_corner: Option<UVec3> } Box { first_corner: Confirmation<UVec3>, second_corner: Option<UVec3> }
} }
impl Display for ToolType { impl Display for ToolType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{}",match self { write!(f,"{}",match self {
ToolType::None => "None", ToolType::None => "None",
ToolType::Paint { position: _ } => "Paint" ToolType::Paint { position: _ } => "Paint",
ToolType::Box { first_corner: _, second_corner: _ } => "Box",
}) })
} }
} }
@ -51,15 +79,8 @@ impl Display for ToolType {
pub struct ViewCube; pub struct ViewCube;
#[derive(Event)] #[derive(Event)]
pub struct ApplyTool{ pub struct ApplyTool;
pub tool: EditorTool
}
impl ApplyTool {
pub fn new(tool: EditorTool) -> Self {
Self { tool }
}
}
pub fn calculate_tools( pub fn calculate_tools(
mut commands: Commands, mut commands: Commands,
@ -69,38 +90,50 @@ pub fn calculate_tools(
mut tool: ResMut<EditorTool>, mut tool: ResMut<EditorTool>,
grid: Res<Grid> grid: Res<Grid>
) { ) {
let tool_change: ToolType = match tool.kind { // Calculating tile cursor tile placement
let mut cursor_position: Option<UVec3> = None;
for (_, ray) in ray_map.iter() {
let Some((_, hit)) = raycast.cast_ray(*ray, &MeshRayCastSettings::default()).first() else {continue;};
let placement_position = if let GridElement::Air = tool.block { hit.point - 0.51 * hit.normal.normalize() } else { hit.point + 0.51 * hit.normal.normalize() }.round();
cursor_position = Some(placement_position.as_ivec3().max(IVec3::ZERO).as_uvec3().min(grid.size-UVec3::ONE));
}
let tool_change: ToolType = match tool.kind.clone() {
ToolType::None => ToolType::None, ToolType::None => ToolType::None,
ToolType::Paint { position: _ } => { ToolType::Paint { position: _ } => ToolType::Paint { position: cursor_position },
let mut new_position: Option<UVec3> = None; ToolType::Box { first_corner, second_corner: _ } => {
if let Confirmation::Confirmed(_) = first_corner {
for (_, ray) in ray_map.iter() { ToolType::Box { first_corner, second_corner: cursor_position }
let Some((_, hit)) = raycast.cast_ray(*ray, &MeshRayCastSettings::default()).first() else {continue;}; } else {
ToolType::Box { first_corner: Confirmation::from_option(&cursor_position), second_corner: None }
let placement_position = if let GridElement::Air = tool.block { hit.point - 0.51 * hit.normal.normalize() } else { hit.point + 0.51 * hit.normal.normalize() }.round();
new_position = Some(placement_position.as_ivec3().max(IVec3::ZERO).as_uvec3().min(grid.size-UVec3::ONE));
} }
}
ToolType::Paint { position: new_position }
},
}; };
tool.kind = tool_change; tool.kind = tool_change;
if mouse_button.just_pressed(MouseButton::Left) { if mouse_button.just_pressed(MouseButton::Left) {
commands.trigger(ApplyTool::new(tool.clone())); commands.trigger(ApplyTool);
} }
} }
pub fn apply_tool(params: On<ApplyTool>, mut grid: ResMut<Grid>) { pub fn apply_tool(_params: On<ApplyTool>, mut grid: ResMut<Grid>, mut tool: ResMut<EditorTool>) {
match params.tool.kind { let mut editor = grid.manipulate();
ToolType::None => {}, match tool.kind {
ToolType::Paint { position } => { ToolType::Paint { position: Some(position) } => {
let Some(position) = position else { return; }; editor.set_vec(position, tool.block);
let index = grid.indexify(position).unwrap();
grid.data[index] = params.tool.block;
}, },
ToolType::Box { first_corner: Confirmation::Suggestion(first_corner), second_corner } => {
tool.kind = ToolType::Box { first_corner: Confirmation::Confirmed(first_corner), second_corner: second_corner };
},
ToolType::Box { first_corner: Confirmation::Confirmed(first_corner), second_corner: Some(second_corner) } => {
editor.fill_vec(first_corner, second_corner, tool.block);
tool.kind = ToolType::Box { first_corner: Confirmation::None, second_corner: None };
},
_ => {},
} }
} }

View file

@ -5,7 +5,7 @@ use bevy::prelude::*;
use crate::MAX_FLUID_AMOUNT; use crate::MAX_FLUID_AMOUNT;
use crate::grid::{Grid, GridElement}; use crate::grid::{Grid, GridElement};
use crate::simulation::SimulationStatus; use crate::simulation::SimulationStatus;
use crate::view::editor::{EditorTool,ToolType}; use crate::view::editor::{EditorTool,ToolType,Confirmation};
pub struct UIPlugin; pub struct UIPlugin;
@ -105,7 +105,8 @@ fn editor_tools_column() -> impl Scene {
label("Tools") label("Tools")
ToolLabel, ToolLabel,
tool_button(ToolType::None), tool_button(ToolType::None),
tool_button(ToolType::Paint{position: None}) tool_button(ToolType::Paint{position: None}),
tool_button(ToolType::Box { first_corner: Confirmation::None, second_corner: None })
] ]
} }
} }