remove network stack + refactor + readded old voxel system + added skybox

This commit is contained in:
Elias Stepanik 2025-06-07 13:05:49 +02:00
parent e3e3d4d7a2
commit 0329a9015d
21 changed files with 423 additions and 153 deletions

View File

@ -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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

View File

@ -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::<Option<Handle<Image>>>();

View File

@ -1,2 +1,2 @@
pub mod debug_gizmos;
pub mod math;
pub mod math;

View File

@ -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);

View File

@ -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
}

View File

@ -1,2 +1,2 @@
pub mod environment_plugin;
pub mod systems;
pub mod systems;

View File

@ -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<RootGrid>,) {
root: Res<RootGrid>,
asset_server: Res<AssetServer>) {
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<Image>,
}

View File

@ -1,3 +1,5 @@
pub mod environment_system;
pub mod camera_system;
mod planet;
pub mod planet_system;
pub mod voxels;
pub mod voxel_system;

View File

@ -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<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
root: Res<RootGrid>
) {
// Four corner vertices y is up in Bevys 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::<i64>::ZERO,
Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
));
});
}

View File

@ -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<Perlin>);
pub fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
root: Res<RootGrid>,
) {
// 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::<i64>::ZERO,
Transform::default(),
PlanetMaker,
Wireframe,
));
});
}
pub(crate) fn setup_noise(mut commands: Commands) {
let fbm_noise = Fbm::<Perlin>::new(0);
commands.insert_resource(PlanetNoise(fbm_noise));
}
pub fn deform_planet(
mut meshes: ResMut<Assets<Mesh>>,
noise: Res<PlanetNoise>,
query: Query<&Mesh3d, With<PlanetMaker>>,
) {
let frequency = 4.0 / 12_742_000.0;
let amplitude = 100_000.0;
for mesh3d in query.iter() {
let handle: &Handle<Mesh> = &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);
}
}
}

View File

@ -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<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
root: Res<RootGrid>,
) {
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);
}
}
}

View File

@ -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));
}
}

View File

@ -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<ConsoleState>,
) {
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<ConsoleState>,
keys: Res<ButtonInput<KeyCode>>,
) {
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<String>) {
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<String>,
pub output: Vec<String>,
}

View File

@ -1,4 +1,4 @@
pub mod movement;
pub mod flight;
pub mod console;
pub mod ui;
pub mod ui;
pub mod voxels;

View File

@ -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<ButtonInput<KeyCode>>,
mouse_button_input: Res<ButtonInput<MouseButton>>,
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)),
);
}
}
}
}
}
}