diff --git a/client/Cargo.toml b/client/Cargo.toml index 79943b6..82a5bbb 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -10,11 +10,8 @@ build = "build.rs" [dependencies] bevy = { version = "0.15.1", features = ["jpeg", "trace_tracy", "trace_tracy_memory"] } -bevy_egui = "0.33.0" -random_word = { version = "0.5.0", features = ["en"] } rand = "0.8.5" serde = { version = "1.0", features = ["derive"] } toml = "0.8" big_space = "0.9.1" - - +noise = "0.9.0" \ No newline at end of file diff --git a/client/assets/textures/skybox_space_1024/back.png b/client/assets/textures/skybox_space_1024/back.png new file mode 100644 index 0000000..234622d Binary files /dev/null and b/client/assets/textures/skybox_space_1024/back.png differ diff --git a/client/assets/textures/skybox_space_1024/bottom.png b/client/assets/textures/skybox_space_1024/bottom.png new file mode 100644 index 0000000..0defe2d Binary files /dev/null and b/client/assets/textures/skybox_space_1024/bottom.png differ diff --git a/client/assets/textures/skybox_space_1024/front.png b/client/assets/textures/skybox_space_1024/front.png new file mode 100644 index 0000000..3d77033 Binary files /dev/null and b/client/assets/textures/skybox_space_1024/front.png differ diff --git a/client/assets/textures/skybox_space_1024/left.png b/client/assets/textures/skybox_space_1024/left.png new file mode 100644 index 0000000..13c24d2 Binary files /dev/null and b/client/assets/textures/skybox_space_1024/left.png differ diff --git a/client/assets/textures/skybox_space_1024/right.png b/client/assets/textures/skybox_space_1024/right.png new file mode 100644 index 0000000..ec13d41 Binary files /dev/null and b/client/assets/textures/skybox_space_1024/right.png differ diff --git a/client/assets/textures/skybox_space_1024/top.png b/client/assets/textures/skybox_space_1024/top.png new file mode 100644 index 0000000..a52961b Binary files /dev/null and b/client/assets/textures/skybox_space_1024/top.png differ diff --git a/client/src/app.rs b/client/src/app.rs index f75ac1a..958713e 100644 --- a/client/src/app.rs +++ b/client/src/app.rs @@ -1,3 +1,4 @@ +use bevy::pbr::wireframe::WireframePlugin; use crate::helper::debug_gizmos::debug_gizmos; use bevy::prelude::*; pub struct AppPlugin; @@ -9,6 +10,7 @@ impl Plugin for AppPlugin { app.add_plugins(crate::plugins::environment::environment_plugin::EnvironmentPlugin); //app.add_plugins(crate::plugins::network::network_plugin::NetworkPlugin); app.add_plugins(crate::plugins::input::input_plugin::InputPlugin); + app.add_plugins(WireframePlugin); app.add_systems(Update, (debug_gizmos)); app.register_type::>>(); diff --git a/client/src/helper/mod.rs b/client/src/helper/mod.rs index 4e5cfd9..219d6f7 100644 --- a/client/src/helper/mod.rs +++ b/client/src/helper/mod.rs @@ -1,2 +1,2 @@ pub mod debug_gizmos; -pub mod math; +pub mod math; \ No newline at end of file diff --git a/client/src/main.rs b/client/src/main.rs index 66a81d5..ba52ed6 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -13,7 +13,6 @@ use bevy::render::RenderPlugin; use bevy::DefaultPlugins; use bevy::input::gamepad::AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound; use bevy::window::PresentMode; -use bevy_egui::EguiPlugin; use big_space::plugin::BigSpacePlugin; use toml; use crate::config::Config; @@ -42,7 +41,7 @@ fn main() { register_platform_plugins(&mut app); app.add_plugins(AppPlugin); - app.add_plugins(EguiPlugin); + diff --git a/client/src/plugins/environment/environment_plugin.rs b/client/src/plugins/environment/environment_plugin.rs index 836c6e0..c759962 100644 --- a/client/src/plugins/environment/environment_plugin.rs +++ b/client/src/plugins/environment/environment_plugin.rs @@ -1,5 +1,6 @@ use bevy::app::{App, Plugin, PreStartup, PreUpdate, Startup}; -use bevy::prelude::IntoSystemConfigs; +use bevy::prelude::*; +use crate::plugins::environment::systems::voxels::structure::SparseVoxelOctree; pub struct EnvironmentPlugin; impl Plugin for EnvironmentPlugin { @@ -7,9 +8,31 @@ impl Plugin for EnvironmentPlugin { app.add_systems( Startup, - (crate::plugins::environment::systems::camera_system::setup,crate::plugins::environment::systems::environment_system::setup.after(crate::plugins::environment::systems::camera_system::setup) ), + ( + crate::plugins::environment::systems::camera_system::setup, + crate::plugins::environment::systems::environment_system::setup.after(crate::plugins::environment::systems::camera_system::setup), + crate::plugins::environment::systems::voxel_system::setup + + ), ); - + app.add_systems(Update, (crate::plugins::environment::systems::voxels::rendering::render,crate::plugins::environment::systems::voxels::debug::visualize_octree_system.run_if(should_visualize_octree), crate::plugins::environment::systems::voxels::debug::draw_grid.run_if(should_draw_grid)).chain()); + + + + } } + + +fn should_visualize_octree(octree_query: Query<&SparseVoxelOctree>,) -> bool { + octree_query.single().show_wireframe +} + +fn should_draw_grid(octree_query: Query<&SparseVoxelOctree>,) -> bool { + octree_query.single().show_world_grid +} + +fn should_visualize_chunks(octree_query: Query<&SparseVoxelOctree>,) -> bool { + octree_query.single().show_chunks +} \ No newline at end of file diff --git a/client/src/plugins/environment/mod.rs b/client/src/plugins/environment/mod.rs index 252afee..372a5e4 100644 --- a/client/src/plugins/environment/mod.rs +++ b/client/src/plugins/environment/mod.rs @@ -1,2 +1,2 @@ pub mod environment_plugin; -pub mod systems; +pub mod systems; \ No newline at end of file diff --git a/client/src/plugins/environment/systems/camera_system.rs b/client/src/plugins/environment/systems/camera_system.rs index d11654c..cabaf58 100644 --- a/client/src/plugins/environment/systems/camera_system.rs +++ b/client/src/plugins/environment/systems/camera_system.rs @@ -1,4 +1,4 @@ - +use bevy::core_pipeline::Skybox; use bevy::input::mouse::{MouseMotion, MouseWheel}; use bevy::math::Vec3; use bevy::prelude::*; @@ -26,11 +26,13 @@ impl Default for CameraController { } } pub fn setup(mut commands: Commands, - root: Res,) { + root: Res, + asset_server: Res) { - + let cubemap_handle = asset_server.load("textures/skybox_space_1024/sky.ktx2"); + commands.insert_resource(PendingSkybox { handle: cubemap_handle.clone() }); commands.entity(root.0).with_children(|parent| { parent.spawn(( @@ -51,7 +53,20 @@ pub fn setup(mut commands: Commands, sensor_height: 0.01866, }), FloatingOrigin, + Skybox { + image: cubemap_handle.clone(), + brightness: 1000.0, + ..default() + }, )); }); } + + + +#[derive(Resource)] +struct PendingSkybox { + handle: Handle, +} + diff --git a/client/src/plugins/environment/systems/mod.rs b/client/src/plugins/environment/systems/mod.rs index 2ad0fa5..7a87ad1 100644 --- a/client/src/plugins/environment/systems/mod.rs +++ b/client/src/plugins/environment/systems/mod.rs @@ -1,3 +1,5 @@ pub mod environment_system; pub mod camera_system; -mod planet; \ No newline at end of file +pub mod planet_system; +pub mod voxels; +pub mod voxel_system; \ No newline at end of file diff --git a/client/src/plugins/environment/systems/planet.rs b/client/src/plugins/environment/systems/planet.rs deleted file mode 100644 index ae4048a..0000000 --- a/client/src/plugins/environment/systems/planet.rs +++ /dev/null @@ -1,71 +0,0 @@ -use bevy::asset::RenderAssetUsages; -use bevy::prelude::*; -use bevy::render::mesh::*; -use big_space::floating_origins::FloatingOrigin; -use big_space::prelude::GridCell; -use crate::plugins::big_space::big_space_plugin::RootGrid; -use crate::plugins::environment::systems::camera_system::CameraController; - -pub struct PlanetMaker {} - - -fn setup( - mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, - root: Res -) { - // Four corner vertices – y is up in Bevy’s 3-D coordinate system - let positions = vec![ - [-0.5, 0.0, -0.5], - [ 0.5, 0.0, -0.5], - [ 0.5, 0.0, 0.5], - [-0.5, 0.0, 0.5], - ]; - - // Single normal for all vertices (pointing up) - let normals = vec![[0.0, 1.0, 0.0]; 4]; - - // UVs for a full-size texture - let uvs = vec![ - [0.0, 0.0], - [1.0, 0.0], - [1.0, 1.0], - [0.0, 1.0], - ]; - - // Two triangles: (0,1,2) and (0,2,3) - let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]); - - let plane = Mesh::new( - PrimitiveTopology::TriangleList, - RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD, - ) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) - .with_inserted_indices(indices); - - let mesh_handle = meshes.add(plane); - let material_handle = materials.add(StandardMaterial::from_color(Color::srgb(0.3, 0.6, 1.0))); - - - let sphere = meshes.add( - SphereMeshBuilder::new(1.0, SphereKind::Ico { subdivisions: 5 }) - .build() - ); - - - - commands.entity(root.0).with_children(|parent| { - - parent.spawn(( - Name::new("Planet"), - Mesh3d(sphere), - MeshMaterial3d(material_handle), - GridCell::::ZERO, - Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)), - )); - }); - -} \ No newline at end of file diff --git a/client/src/plugins/environment/systems/planet_system.rs b/client/src/plugins/environment/systems/planet_system.rs new file mode 100644 index 0000000..cbd490c --- /dev/null +++ b/client/src/plugins/environment/systems/planet_system.rs @@ -0,0 +1,116 @@ +use bevy::asset::RenderAssetUsages; +use bevy::pbr::wireframe::{Wireframe, WireframeColor}; +use bevy::prelude::*; +use bevy::render::mesh::*; +use big_space::floating_origins::FloatingOrigin; +use big_space::prelude::GridCell; +use noise::{Fbm, NoiseFn, Perlin}; +use crate::plugins::big_space::big_space_plugin::RootGrid; +use crate::plugins::environment::systems::camera_system::CameraController; + + +#[derive(Component)] +pub struct PlanetMaker; + + + +#[derive(Resource)] +pub struct PlanetNoise(pub Fbm); + +pub fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + root: Res, +) { + // Diameter ~ Earth (~12,742 km) × 2 to exaggerate terrain if desired + let radius = 12_742_000.0; + let sphere_mesh = meshes.add( + SphereMeshBuilder::new(radius, SphereKind::Ico { subdivisions: 100 }) + .build(), + ); + let material_handle = materials.add(StandardMaterial::from(Color::rgb(0.3, 0.6, 1.0))); + + commands.entity(root.0).with_children(|parent| { + parent.spawn(( + Name::new("Planet"), + Mesh3d(sphere_mesh.clone()), + MeshMaterial3d(material_handle), + GridCell::::ZERO, + Transform::default(), + PlanetMaker, + Wireframe, + )); + }); +} + + +pub(crate) fn setup_noise(mut commands: Commands) { + let fbm_noise = Fbm::::new(0); + commands.insert_resource(PlanetNoise(fbm_noise)); +} + +pub fn deform_planet( + mut meshes: ResMut>, + noise: Res, + query: Query<&Mesh3d, With>, +) { + let frequency = 4.0 / 12_742_000.0; + let amplitude = 100_000.0; + + for mesh3d in query.iter() { + let handle: &Handle = &mesh3d.0; + + if let Some(mesh) = meshes.get_mut(handle) { + // 1. Immutable borrow to extract normals (or default) + let normals: Vec<[f32; 3]> = if let Some(VertexAttributeValues::Float32x3(vals)) = + mesh.attribute(Mesh::ATTRIBUTE_NORMAL) + { + vals.clone() + } else { + // default normals if none exist + let count = mesh + .attribute(Mesh::ATTRIBUTE_POSITION) + .and_then(|attr| match attr { + VertexAttributeValues::Float32x3(v) => Some(v.len()), + _ => None, + }) + .unwrap_or(0); + vec![[0.0, 1.0, 0.0]; count] + }; + + // 2. Drop the immutable borrow, then mutable-borrow positions + if let Some(VertexAttributeValues::Float32x3(positions)) = + mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION) + { + // Now mutate positions using the pre-fetched normals + for (i, pos) in positions.iter_mut().enumerate() { + let mut vertex = Vec3::new(pos[0], pos[1], pos[2]); + let normal = Vec3::new( + normals[i][0], + normals[i][1], + normals[i][2], + ); + + let unit_dir = vertex.normalize(); + let sample = [ + unit_dir.x as f64 * frequency as f64, + unit_dir.y as f64 * frequency as f64, + unit_dir.z as f64 * frequency as f64, + ]; + let noise_value = noise.0.get(sample) as f32; + let offset = normal * (noise_value * amplitude); + + let new_pos = unit_dir * (vertex.length() + offset.length()); + *pos = [new_pos.x, new_pos.y, new_pos.z]; + } + + + mesh.compute_smooth_normals(); + } + + // Force AABB recalc + mesh.remove_attribute(Mesh::ATTRIBUTE_COLOR); + } + } +} \ No newline at end of file diff --git a/client/src/plugins/environment/systems/voxel_system.rs b/client/src/plugins/environment/systems/voxel_system.rs new file mode 100644 index 0000000..6a41451 --- /dev/null +++ b/client/src/plugins/environment/systems/voxel_system.rs @@ -0,0 +1,152 @@ +use bevy::asset::RenderAssetUsages; +use bevy::pbr::wireframe::{Wireframe, WireframeColor}; +use bevy::prelude::*; +use bevy::render::mesh::*; +use big_space::floating_origins::FloatingOrigin; +use big_space::prelude::GridCell; +use noise::{Fbm, NoiseFn, Perlin}; +use crate::plugins::big_space::big_space_plugin::RootGrid; +use crate::plugins::environment::systems::camera_system::CameraController; +use crate::plugins::environment::systems::voxels::structure::*; + +pub fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + root: Res, +) { + let unit_size = 1.0; + + let octree_base_size = 64.0 * unit_size; // Octree's total size in your world space + let octree_depth = 10; + + + let mut octree = SparseVoxelOctree::new(octree_depth, octree_base_size as f32, false, false, false); + + + let color = Color::rgb(0.2, 0.8, 0.2); + /*generate_voxel_rect(&mut octree,color);*/ + generate_voxel_sphere(&mut octree, 10, color); + + commands.spawn( + ( + Transform::default(), + octree + ) + ); +} + +fn generate_voxel_sphere( + octree: &mut SparseVoxelOctree, + planet_radius: i32, + voxel_color: Color, +) { + // For simplicity, we center the sphere around (0,0,0). + // We'll loop over a cubic region [-planet_radius, +planet_radius] in x, y, z + let min = -planet_radius; + let max = planet_radius; + + let step = octree.get_spacing_at_depth(octree.max_depth); + + for ix in min..=max { + let x = ix; + for iy in min..=max { + let y = iy; + for iz in min..=max { + let z = iz; + + // Check if within sphere of radius `planet_radius` + let dist2 = x * x + y * y + z * z; + if dist2 <= planet_radius * planet_radius { + // Convert (x,y,z) to world space, stepping by `voxel_step`. + let wx = x as f32 * step; + let wy = y as f32 * step; + let wz = z as f32 * step; + let position = Vec3::new(wx, wy, wz); + + // Insert the voxel + let voxel = Voxel { + color: voxel_color, + }; + octree.insert(position, voxel); + } + } + } + } +} + + + +/// Inserts a 16x256x16 "column" of voxels into the octree at (0,0,0) corner. +/// If you want it offset or centered differently, just adjust the for-loop ranges or offsets. +fn generate_voxel_rect( + octree: &mut SparseVoxelOctree, + voxel_color: Color, +) { + // The dimensions of our rectangle: 16 x 256 x 16 + let size_x = 16; + let size_y = 256; + let size_z = 16; + + // We'll get the voxel spacing (size at the deepest level), same as in your sphere code. + let step = octree.get_spacing_at_depth(octree.max_depth); + + // Triple-nested loop for each voxel in [0..16, 0..256, 0..16] + for ix in 0..size_x { + let x = ix as f32; + for iy in 0..size_y { + let y = iy as f32; + for iz in 0..size_z { + let z = iz as f32; + + // Convert (x,y,z) to world coordinates + let wx = x * step; + let wy = y * step; + let wz = z * step; + + let position = Vec3::new(wx, wy, wz); + + // Insert the voxel + let voxel = Voxel { + color: voxel_color, + }; + octree.insert(position, voxel); + } + } + } +} + +fn generate_large_plane( + octree: &mut SparseVoxelOctree, + width: usize, + depth: usize, + color: Color, +) { + // We'll get the voxel spacing (size at the deepest level). + let step = octree.get_spacing_at_depth(octree.max_depth); + + // Double-nested loop for each voxel in [0..width, 0..depth], + // with y=0. + for ix in 0..width { + let x = ix as f32; + for iz in 0..depth { + let z = iz as f32; + // y is always 0. + let y = 0.0; + + // Convert (x,0,z) to world coordinates + let wx = x * step; + let wy = y * step; + let wz = z * step; + + let position = Vec3::new(wx, wy, wz); + + // Insert the voxel + let voxel = Voxel { + color, + }; + octree.insert(position, voxel); + } + } +} + diff --git a/client/src/plugins/input/input_plugin.rs b/client/src/plugins/input/input_plugin.rs index ae7752f..b64cc76 100644 --- a/client/src/plugins/input/input_plugin.rs +++ b/client/src/plugins/input/input_plugin.rs @@ -1,7 +1,6 @@ use bevy::app::{App, Plugin, PreUpdate, Startup}; use bevy::prelude::{IntoSystemConfigs, Update}; -use crate::plugins::input::systems::console::{console_system, toggle_console, ConsoleState}; pub struct InputPlugin; impl Plugin for InputPlugin { @@ -9,17 +8,14 @@ impl Plugin for InputPlugin { _app.add_systems( Update, ( - crate::plugins::input::systems::console::console_system, crate::plugins::input::systems::flight::flight_systems, crate::plugins::input::systems::ui::ui_system, //crate::plugins::input::systems::network::network_system, crate::plugins::input::systems::movement::movement_system, + crate::plugins::input::systems::voxels::voxel_system ), ); - - _app.insert_resource(ConsoleState::default()); - _app.add_systems(Update, (toggle_console, console_system)); } } diff --git a/client/src/plugins/input/systems/console.rs b/client/src/plugins/input/systems/console.rs deleted file mode 100644 index 372b79c..0000000 --- a/client/src/plugins/input/systems/console.rs +++ /dev/null @@ -1,60 +0,0 @@ -use bevy::app::AppExit; -use bevy::input::ButtonInput; -use bevy::input::mouse::{MouseMotion, MouseWheel}; -use bevy::prelude::*; -use bevy_egui::{egui, EguiContexts}; -use crate::plugins::environment::systems::camera_system::CameraController; -pub fn console_system( - mut ctxs: EguiContexts, - mut state: ResMut, -) { - if !state.open { return; } - - egui::Window::new("Console") - .resizable(true) - .vscroll(true) - .show(ctxs.ctx_mut(), |ui| { - // Output - for line in &state.output { - ui.label(line); - } - - // Input line - let resp = ui.text_edit_singleline(&mut state.input); - if resp.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { - let cmd = state.input.trim().to_string(); - if !cmd.is_empty() { - state.history.push(cmd.clone()); - handle_command(&cmd, &mut state.output); - state.input.clear(); - } - } - }); -} -/// Press ` to open / close -pub fn toggle_console( - mut state: ResMut, - keys: Res>, -) { - if keys.just_pressed(KeyCode::KeyC) { - state.open = !state.open; - } -} - -/// Add your own commands here. -/// For demo purposes we just echo the input. -fn handle_command(cmd: &str, out: &mut Vec) { - match cmd.trim() { - "help" => out.push("Available: help, clear, echo …".into()), - "clear" => out.clear(), - _ => out.push(format!("> {cmd}")), - } -} - -#[derive(Resource, Default)] -pub struct ConsoleState { - pub open: bool, - pub input: String, - pub history: Vec, - pub output: Vec, -} diff --git a/client/src/plugins/input/systems/mod.rs b/client/src/plugins/input/systems/mod.rs index 5214e6d..10937c6 100644 --- a/client/src/plugins/input/systems/mod.rs +++ b/client/src/plugins/input/systems/mod.rs @@ -1,4 +1,4 @@ pub mod movement; pub mod flight; -pub mod console; -pub mod ui; \ No newline at end of file +pub mod ui; +pub mod voxels; \ No newline at end of file diff --git a/client/src/plugins/input/systems/voxels.rs b/client/src/plugins/input/systems/voxels.rs new file mode 100644 index 0000000..cbc33ed --- /dev/null +++ b/client/src/plugins/input/systems/voxels.rs @@ -0,0 +1,99 @@ +use bevy::prelude::*; +use crate::plugins::environment::systems::camera_system::CameraController; +use crate::plugins::environment::systems::voxels::structure::*; + +///TODO +pub fn voxel_system( + + keyboard_input: Res>, + mouse_button_input: Res>, + mut octree_query: Query<&mut SparseVoxelOctree>, + + mut query: Query<(&mut Transform, &mut CameraController)>, + mut windows: Query<&mut Window>, +) { + let mut window = windows.single_mut(); + let (mut transform, _) = query.single_mut(); + + // ======================= + // 5) Octree Keys + // ======================= + if keyboard_input.just_pressed(KeyCode::F2){ + for mut octree in octree_query.iter_mut() { + octree.show_wireframe = !octree.show_wireframe; + } + } + if keyboard_input.just_pressed(KeyCode::F3){ + for mut octree in octree_query.iter_mut() { + octree.show_world_grid = !octree.show_world_grid; + } + } + if keyboard_input.just_pressed(KeyCode::F4){ + for mut octree in octree_query.iter_mut() { + octree.show_chunks = !octree.show_chunks; + } + } + if keyboard_input.just_pressed(KeyCode::KeyQ) && window.cursor_options.visible == false{ + for mut octree in octree_query.iter_mut() { + octree.insert(transform.translation, Voxel::new(Color::srgb(1.0, 0.0, 0.0))); + } + } + + // ======================= + // 6) Building + // ======================= + + if (mouse_button_input.just_pressed(MouseButton::Left) || mouse_button_input.just_pressed(MouseButton::Right)) && !window.cursor_options.visible { + + // Get the mouse position in normalized device coordinates (-1 to 1) + if let Some(_) = window.cursor_position() { + // Set the ray direction to the camera's forward vector + let ray_origin = transform.translation; + let ray_direction = transform.forward().normalize(); + + let ray = Ray { + origin: ray_origin, + direction: ray_direction, + }; + + + + for mut octree in octree_query.iter_mut() { + if let Some((hit_x, hit_y, hit_z, depth,normal)) = octree.raycast(&ray) { + + + if mouse_button_input.just_pressed(MouseButton::Right) { + + let voxel_size = octree.get_spacing_at_depth(depth); + let hit_position = Vec3::new(hit_x as f32, hit_y as f32, hit_z as f32); + let epsilon = voxel_size * 0.1; // Adjust this value as needed (e.g., 0.1 times the voxel size) + + // Offset position by epsilon in the direction of the normal + let offset_position = hit_position - (normal * Vec3::new(epsilon as f32, epsilon as f32, epsilon as f32)); + + // Remove the voxel + octree.remove(offset_position); + + + } + else if mouse_button_input.just_pressed(MouseButton::Left) { + + let voxel_size = octree.get_spacing_at_depth(depth); + let hit_position = Vec3::new(hit_x as f32, hit_y as f32, hit_z as f32); + let epsilon = voxel_size * 0.1; // Adjust this value as needed (e.g., 0.1 times the voxel size) + + // Offset position by epsilon in the direction of the normal + let offset_position = hit_position + (normal * Vec3::new(epsilon as f32, epsilon as f32, epsilon as f32)); + + // Insert the new voxel + octree.insert( + offset_position, + Voxel::new(Color::srgb(1.0, 0.0, 0.0)), + ); + } + } + } + } + } + +} \ No newline at end of file