Load and Unload System working

This commit is contained in:
Elias Stepanik 2025-06-09 21:44:47 +02:00
parent e406ac15cb
commit 85699338a9
6 changed files with 115 additions and 23 deletions

View File

@ -71,7 +71,3 @@ fn should_visualize_octree(octree_query: Query<&SparseVoxelOctree>,) -> bool {
fn should_draw_grid(octree_query: Query<&SparseVoxelOctree>,) -> bool { fn should_draw_grid(octree_query: Query<&SparseVoxelOctree>,) -> bool {
octree_query.single().show_world_grid octree_query.single().show_world_grid
} }
fn should_visualize_chunks(octree_query: Query<&SparseVoxelOctree>,) -> bool {
octree_query.single().show_chunks
}

View File

@ -1,32 +1,106 @@
use std::path::Path;
use rayon::prelude::*;
use crate::plugins::big_space::big_space_plugin::RootGrid; use crate::plugins::big_space::big_space_plugin::RootGrid;
use crate::plugins::environment::systems::voxels::structure::*; use crate::plugins::environment::systems::voxels::structure::*;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::mesh::*; use bevy::render::mesh::*;
use noise::{NoiseFn, Perlin}; use noise::{NoiseFn, Perlin};
use rand::{thread_rng, Rng};
pub fn setup( pub fn setup(
mut commands: Commands, mut commands: Commands,
root: Res<RootGrid>, root: Res<RootGrid>,
) { ) {
// Octree parameters
let unit_size = 1.0_f32; let unit_size = 1.0_f32;
let octree_base_size = 64.0 * unit_size; let octree_base_size = 64.0 * unit_size;
let octree_depth = 10; let octree_depth = 10;
// 1. Create octree and wrap in Arc<Mutex<>> for thread-safe generation let path = Path::new("octree.bin");
let mut octree = SparseVoxelOctree::new(octree_depth, octree_base_size, false, false, false);
// 2. Generate sphere in parallel, dropping the cloned Arc inside the function
let mut octree = if path.exists() {
match SparseVoxelOctree::load_from_file(path) {
Ok(tree) => tree,
Err(err) => {
error!("failed to load octree: {err}");
SparseVoxelOctree::new(octree_depth, octree_base_size, false, false, false)
}
}
} else {
let mut tree = SparseVoxelOctree::new(octree_depth, octree_base_size, false, false, false);
let color = Color::rgb(0.2, 0.8, 0.2); let color = Color::rgb(0.2, 0.8, 0.2);
// How many random spheres?
/*const NUM_SPHERES: usize = 5;
let mut rng = thread_rng();
generate_voxel_sphere(&mut octree, 110, color); for _ in 0..NUM_SPHERES {
let center = Vec3::new(
rng.gen_range(-1000.0..1000.0),
rng.gen_range(-1000.0..1000.0),
rng.gen_range(-1000.0..1000.0),
);
// 4. Spawn entity with both Transform and the real octree component let radius = rng.gen_range(20..=150); // voxels
generate_voxel_sphere_parallel(&mut tree, center, radius, color);
}*/
generate_voxel_sphere(&mut tree, 200, color);
tree
};
// Attach octree to the scene graph
commands.entity(root.0).with_children(|parent| { commands.entity(root.0).with_children(|parent| {
parent.spawn((Transform::default(), octree)); parent.spawn((Transform::default(), octree));
}); });
} }
pub fn generate_voxel_sphere_parallel(
octree: &mut SparseVoxelOctree,
center: Vec3,
radius: i32,
color: Color,
) {
let step = octree.get_spacing_at_depth(octree.max_depth);
let radius_sq = radius * radius;
// 1. Collect voxel positions in parallel
let voxels: Vec<(Vec3, Voxel)> = (-radius..=radius)
.into_par_iter()
.flat_map_iter(|ix| {
let dx2 = ix * ix;
(-radius..=radius).flat_map(move |iy| {
let dy2 = iy * iy;
let r2_xy = dx2 + dy2;
if r2_xy > radius_sq {
return Vec::new(); // this (x,y) column is outside
}
let max_z = ((radius_sq - r2_xy) as f32).sqrt() as i32;
(-max_z..=max_z).map(move |iz| {
let pos = Vec3::new(
center.x + ix as f32 * step,
center.y + iy as f32 * step,
center.z + iz as f32 * step,
);
(pos, Voxel { color })
}).collect::<Vec<_>>()
})
})
.collect();
// 2. Single-threaded insert (keeps `SparseVoxelOctree` API unchanged)
for (pos, voxel) in voxels {
octree.insert(pos, voxel);
}
}
fn generate_voxel_sphere( fn generate_voxel_sphere(
octree: &mut SparseVoxelOctree, octree: &mut SparseVoxelOctree,

View File

@ -14,8 +14,8 @@ pub fn update_chunk_lods(
// Borrow the octree only once to avoid repeated query lookups // Borrow the octree only once to avoid repeated query lookups
let mut tree = tree_q.single_mut(); let mut tree = tree_q.single_mut();
let max_depth = tree.max_depth; let max_depth = tree.max_depth - 1;
let range_step = cfg.view_distance_chunks as f32 / max_depth as f32; let range_step = cfg.view_distance_chunks as f32 / (max_depth as f32 - 1.0);
let chunk_size = CHUNK_SIZE as f32 * tree.get_spacing_at_depth(max_depth); let chunk_size = CHUNK_SIZE as f32 * tree.get_spacing_at_depth(max_depth);
let mut changed = Vec::new(); let mut changed = Vec::new();

View File

@ -20,7 +20,6 @@ impl SparseVoxelOctree {
size, size,
show_wireframe, show_wireframe,
show_world_grid, show_world_grid,
show_chunks,
dirty: Vec::new(), dirty: Vec::new(),
dirty_chunks: Default::default(), dirty_chunks: Default::default(),
occupied_chunks: Default::default(), occupied_chunks: Default::default(),
@ -482,6 +481,7 @@ impl SparseVoxelOctree {
self.dirty.clear(); self.dirty.clear();
self.dirty_chunks.clear(); self.dirty_chunks.clear();
self.occupied_chunks.clear(); self.occupied_chunks.clear();
let voxels = Self::collect_voxels_from_node(&self.root, self.size); let voxels = Self::collect_voxels_from_node(&self.root, self.size);
for (pos, _voxel, _depth) in voxels { for (pos, _voxel, _depth) in voxels {
let key = chunk_key_from_world(self, pos); let key = chunk_key_from_world(self, pos);

View File

@ -42,7 +42,7 @@ pub struct OctreeNode {
} }
/// Represents the root of the sparse voxel octree. /// Represents the root of the sparse voxel octree.
/// Represents the root of the sparse voxel octree. /// Represents the root of the sparse voxel octree.
#[derive(Debug, Component, Serialize, Deserialize)] #[derive(Debug, Component, Serialize, Deserialize, Clone)]
pub struct SparseVoxelOctree { pub struct SparseVoxelOctree {
pub root: OctreeNode, pub root: OctreeNode,
@ -50,7 +50,6 @@ pub struct SparseVoxelOctree {
pub size: f32, pub size: f32,
pub show_wireframe: bool, pub show_wireframe: bool,
pub show_world_grid: bool, pub show_world_grid: bool,
pub show_chunks: bool,
#[serde(skip)] #[serde(skip)]
pub dirty: Vec<DirtyVoxel>, pub dirty: Vec<DirtyVoxel>,

View File

@ -1,5 +1,7 @@
use std::path::Path;
use bevy::prelude::*; use bevy::prelude::*;
use crate::plugins::environment::systems::camera_system::CameraController; use crate::plugins::environment::systems::camera_system::CameraController;
use crate::plugins::environment::systems::voxels::octree;
use crate::plugins::environment::systems::voxels::structure::*; use crate::plugins::environment::systems::voxels::structure::*;
///TODO ///TODO
@ -28,16 +30,37 @@ pub fn voxel_system(
octree.show_world_grid = !octree.show_world_grid; 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{ if keyboard_input.just_pressed(KeyCode::KeyQ) && window.cursor_options.visible == false{
for mut octree in octree_query.iter_mut() { for mut octree in octree_query.iter_mut() {
octree.insert(transform.translation, Voxel::new(Color::srgb(1.0, 0.0, 0.0))); octree.insert(transform.translation, Voxel::new(Color::srgb(1.0, 0.0, 0.0)));
} }
} }
if keyboard_input.just_pressed(KeyCode::F4){
let path = Path::new("octree.bin");
for octree in octree_query.iter() {
if let Err(e) = octree.save_to_file(path) {
error!("failed to save octree: {e}");
}
}
}
/* if keyboard_input.just_pressed(KeyCode::F5){
let path = Path::new("octree.bin");
if path.exists() {
let path = Path::new("octree.bin");
let mut octree = if path.exists() {
match SparseVoxelOctree::load_from_file(path) {
Ok(tree) => tree,
Err(err) => {
error!("failed to load octree: {err}");
}
}
}
}
}*/
// ======================= // =======================
// 6) Building // 6) Building