mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-10 21:38:29 +00:00
Add chunk I/O and unloading
This commit is contained in:
parent
9e2d294d69
commit
01ec312e90
@ -1,21 +1,24 @@
|
||||
use crate::plugins::environment::systems::voxels::atlas::VoxelTextureAtlas;
|
||||
use crate::plugins::environment::systems::voxels::chunk_io::{
|
||||
save_dirty_chunks_system, unload_far_chunks,
|
||||
};
|
||||
use crate::plugins::environment::systems::voxels::debug::{draw_grid, visualize_octree_system};
|
||||
use crate::plugins::environment::systems::voxels::lod::update_chunk_lods;
|
||||
use crate::plugins::environment::systems::voxels::meshing_gpu::{
|
||||
GpuMeshingWorker, queue_gpu_meshing,
|
||||
};
|
||||
use bevy_app_compute::prelude::{AppComputePlugin, AppComputeWorkerPlugin};
|
||||
use crate::plugins::environment::systems::voxels::queue_systems;
|
||||
use crate::plugins::environment::systems::voxels::queue_systems::{
|
||||
enqueue_visible_chunks, process_chunk_queue,
|
||||
};
|
||||
use crate::plugins::environment::systems::voxels::render_chunks::rebuild_dirty_chunks;
|
||||
use crate::plugins::environment::systems::voxels::atlas::{VoxelTextureAtlas};
|
||||
use crate::plugins::environment::systems::voxels::structure::{
|
||||
ChunkBudget, ChunkCullingCfg, ChunkQueue, MeshBufferPool, PrevCameraChunk, SparseVoxelOctree,
|
||||
SpawnedChunks,
|
||||
};
|
||||
use bevy::app::{App, Plugin, PreStartup, PreUpdate, Startup};
|
||||
use bevy::prelude::*;
|
||||
use bevy_app_compute::prelude::{AppComputePlugin, AppComputeWorkerPlugin};
|
||||
|
||||
pub struct EnvironmentPlugin;
|
||||
impl Plugin for EnvironmentPlugin {
|
||||
@ -39,7 +42,7 @@ impl Plugin for EnvironmentPlugin {
|
||||
});
|
||||
app.insert_resource(ChunkBudget { per_frame: 20 });
|
||||
app.init_resource::<PrevCameraChunk>();
|
||||
/* app.add_systems(Update, log_mesh_count);*/
|
||||
/* app.add_systems(Update, log_mesh_count);*/
|
||||
app
|
||||
// ------------------------------------------------------------------------
|
||||
// resources
|
||||
@ -57,8 +60,10 @@ impl Plugin for EnvironmentPlugin {
|
||||
enqueue_visible_chunks,
|
||||
process_chunk_queue.after(enqueue_visible_chunks),
|
||||
update_chunk_lods.after(process_chunk_queue),
|
||||
unload_far_chunks.after(update_chunk_lods),
|
||||
rebuild_dirty_chunks.after(process_chunk_queue), // 4. (re)mesh dirty chunks
|
||||
queue_gpu_meshing.after(rebuild_dirty_chunks),
|
||||
save_dirty_chunks_system.after(rebuild_dirty_chunks),
|
||||
/* ---------- optional debug drawing ------- */
|
||||
visualize_octree_system
|
||||
.run_if(should_visualize_octree)
|
||||
|
||||
35
client/src/plugins/environment/systems/voxels/chunk_io.rs
Normal file
35
client/src/plugins/environment/systems/voxels/chunk_io.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use crate::plugins::environment::systems::voxels::structure::*;
|
||||
use bevy::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
const CHUNK_DIR: &str = "chunks";
|
||||
|
||||
/// Save all dirty chunks to disk.
|
||||
pub fn save_dirty_chunks_system(mut tree_q: Query<&mut SparseVoxelOctree>) {
|
||||
let Ok(mut tree) = tree_q.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
let _ = tree.save_dirty_chunks(Path::new(CHUNK_DIR));
|
||||
}
|
||||
|
||||
/// Unload chunks that reached the maximum LOD distance.
|
||||
pub fn unload_far_chunks(
|
||||
mut commands: Commands,
|
||||
mut tree_q: Query<&mut SparseVoxelOctree>,
|
||||
mut spawned: ResMut<SpawnedChunks>,
|
||||
chunks: Query<(Entity, &Chunk, &ChunkLod)>,
|
||||
) {
|
||||
let Ok(mut tree) = tree_q.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
for (ent, chunk, lod) in chunks.iter() {
|
||||
if lod.0 == tree.max_depth - 1 {
|
||||
if let Err(e) = tree.save_chunk(chunk.key, Path::new(CHUNK_DIR)) {
|
||||
error!("failed to save chunk {:?}: {e}", chunk.key);
|
||||
}
|
||||
tree.unload_chunk(chunk.key);
|
||||
spawned.0.remove(&chunk.key);
|
||||
commands.entity(ent).despawn_recursive();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,11 +3,12 @@ pub mod helper;
|
||||
pub mod octree;
|
||||
pub mod structure;
|
||||
|
||||
pub mod atlas;
|
||||
mod chunk;
|
||||
pub mod chunk_io;
|
||||
pub mod culling;
|
||||
pub mod lod;
|
||||
mod meshing;
|
||||
pub mod meshing_gpu;
|
||||
pub mod queue_systems;
|
||||
pub mod render_chunks;
|
||||
pub mod atlas;
|
||||
|
||||
@ -572,4 +572,83 @@ impl SparseVoxelOctree {
|
||||
self.occupied_chunks.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all voxels contained within a chunk.
|
||||
pub fn voxels_in_chunk(&self, key: ChunkKey) -> Vec<(Vec3, Voxel)> {
|
||||
let step = self.get_spacing_at_depth(self.max_depth);
|
||||
let chunk_size = CHUNK_SIZE as f32 * step;
|
||||
let center = self.chunk_center_world(key);
|
||||
let half = Vec3::splat(chunk_size / 2.0);
|
||||
let min = center - half;
|
||||
let max = center + half;
|
||||
|
||||
Self::collect_voxels_from_node(&self.root, self.size, self.center)
|
||||
.into_iter()
|
||||
.filter_map(|(pos, voxel, _)| {
|
||||
if pos.x >= min.x
|
||||
&& pos.x < max.x
|
||||
&& pos.y >= min.y
|
||||
&& pos.y < max.y
|
||||
&& pos.z >= min.z
|
||||
&& pos.z < max.z
|
||||
{
|
||||
Some((pos, voxel))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Save a single chunk to disk inside the specified directory.
|
||||
pub fn save_chunk<P: AsRef<Path>>(&self, key: ChunkKey, dir: P) -> io::Result<()> {
|
||||
let voxels = self.voxels_in_chunk(key);
|
||||
if voxels.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
let path = dir
|
||||
.as_ref()
|
||||
.join(format!("chunk_{}_{}_{}.bin", key.0, key.1, key.2));
|
||||
let data =
|
||||
bincode::serialize(&voxels).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
std::fs::write(path, data)
|
||||
}
|
||||
|
||||
/// Load a single chunk from disk and insert its voxels.
|
||||
pub fn load_chunk<P: AsRef<Path>>(&mut self, key: ChunkKey, dir: P) -> io::Result<()> {
|
||||
let path = dir
|
||||
.as_ref()
|
||||
.join(format!("chunk_{}_{}_{}.bin", key.0, key.1, key.2));
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
let bytes = std::fs::read(path)?;
|
||||
let voxels: Vec<(Vec3, Voxel)> =
|
||||
bincode::deserialize(&bytes).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
for (pos, voxel) in voxels {
|
||||
self.insert(pos, voxel);
|
||||
}
|
||||
self.clear_dirty_flags();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove all voxels from the specified chunk.
|
||||
pub fn unload_chunk(&mut self, key: ChunkKey) {
|
||||
let voxels = self.voxels_in_chunk(key);
|
||||
for (pos, _) in voxels {
|
||||
self.remove(pos);
|
||||
}
|
||||
self.clear_dirty_flags();
|
||||
}
|
||||
|
||||
/// Save all currently dirty chunks to disk and clear the dirty set.
|
||||
pub fn save_dirty_chunks<P: AsRef<Path>>(&mut self, dir: P) -> io::Result<()> {
|
||||
let keys: Vec<_> = self.dirty_chunks.iter().copied().collect();
|
||||
for key in keys {
|
||||
let _ = self.save_chunk(key, &dir);
|
||||
}
|
||||
self.clear_dirty_flags();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user