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;
}
pub fn fill_xyz(&mut self,x1: u32, y1: u32, z1: u32, x2: u32, y2: u32, z2: u32, element: GridElement) {
if x1 > x2 || y1 > y2 || z1 > z2 {
pub fn set_vec(&mut self, vec: UVec3, element: GridElement) {
let Some(index) = self.grid.indexify(vec) else {
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 y in y1..=y2 {
@ -27,6 +31,21 @@ impl<'a> GridManipulationTools<'a> {
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();
}
}

View file

@ -15,7 +15,7 @@ impl Plugin for EditorPlugin {
}
}
#[derive(Default, Clone, PartialEq, Eq)]
#[derive(Default, PartialEq, Eq)]
pub enum Confirmation<T> {
#[default]
None,
@ -23,6 +23,32 @@ pub enum Confirmation<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)]
pub struct EditorTool {
pub block: GridElement,
@ -34,14 +60,16 @@ pub enum ToolType {
#[default]
None,
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 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{}",match self {
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;
#[derive(Event)]
pub struct ApplyTool{
pub tool: EditorTool
}
pub struct ApplyTool;
impl ApplyTool {
pub fn new(tool: EditorTool) -> Self {
Self { tool }
}
}
pub fn calculate_tools(
mut commands: Commands,
@ -69,38 +90,50 @@ pub fn calculate_tools(
mut tool: ResMut<EditorTool>,
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::Paint { position: _ } => {
let mut new_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();
new_position = Some(placement_position.as_ivec3().max(IVec3::ZERO).as_uvec3().min(grid.size-UVec3::ONE));
ToolType::Paint { position: _ } => ToolType::Paint { position: cursor_position },
ToolType::Box { first_corner, second_corner: _ } => {
if let Confirmation::Confirmed(_) = first_corner {
ToolType::Box { first_corner, second_corner: cursor_position }
} else {
ToolType::Box { first_corner: Confirmation::from_option(&cursor_position), second_corner: None }
}
ToolType::Paint { position: new_position }
},
}
};
tool.kind = tool_change;
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>) {
match params.tool.kind {
ToolType::None => {},
ToolType::Paint { position } => {
let Some(position) = position else { return; };
let index = grid.indexify(position).unwrap();
grid.data[index] = params.tool.block;
pub fn apply_tool(_params: On<ApplyTool>, mut grid: ResMut<Grid>, mut tool: ResMut<EditorTool>) {
let mut editor = grid.manipulate();
match tool.kind {
ToolType::Paint { position: Some(position) } => {
editor.set_vec(position, 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::grid::{Grid, GridElement};
use crate::simulation::SimulationStatus;
use crate::view::editor::{EditorTool,ToolType};
use crate::view::editor::{EditorTool,ToolType,Confirmation};
pub struct UIPlugin;
@ -105,7 +105,8 @@ fn editor_tools_column() -> impl Scene {
label("Tools")
ToolLabel,
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 })
]
}
}