mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-26 21:18:56 +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::debug::{draw_grid, visualize_octree_system};
|
||||||
use crate::plugins::environment::systems::voxels::lod::update_chunk_lods;
|
use crate::plugins::environment::systems::voxels::lod::update_chunk_lods;
|
||||||
use crate::plugins::environment::systems::voxels::meshing_gpu::{
|
use crate::plugins::environment::systems::voxels::meshing_gpu::{
|
||||||
GpuMeshingWorker, queue_gpu_meshing,
|
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;
|
||||||
use crate::plugins::environment::systems::voxels::queue_systems::{
|
use crate::plugins::environment::systems::voxels::queue_systems::{
|
||||||
enqueue_visible_chunks, process_chunk_queue,
|
enqueue_visible_chunks, process_chunk_queue,
|
||||||
};
|
};
|
||||||
use crate::plugins::environment::systems::voxels::render_chunks::rebuild_dirty_chunks;
|
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::{
|
use crate::plugins::environment::systems::voxels::structure::{
|
||||||
ChunkBudget, ChunkCullingCfg, ChunkQueue, MeshBufferPool, PrevCameraChunk, SparseVoxelOctree,
|
ChunkBudget, ChunkCullingCfg, ChunkQueue, MeshBufferPool, PrevCameraChunk, SparseVoxelOctree,
|
||||||
SpawnedChunks,
|
SpawnedChunks,
|
||||||
};
|
};
|
||||||
use bevy::app::{App, Plugin, PreStartup, PreUpdate, Startup};
|
use bevy::app::{App, Plugin, PreStartup, PreUpdate, Startup};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy_app_compute::prelude::{AppComputePlugin, AppComputeWorkerPlugin};
|
||||||
|
|
||||||
pub struct EnvironmentPlugin;
|
pub struct EnvironmentPlugin;
|
||||||
impl Plugin for EnvironmentPlugin {
|
impl Plugin for EnvironmentPlugin {
|
||||||
@ -39,7 +42,7 @@ impl Plugin for EnvironmentPlugin {
|
|||||||
});
|
});
|
||||||
app.insert_resource(ChunkBudget { per_frame: 20 });
|
app.insert_resource(ChunkBudget { per_frame: 20 });
|
||||||
app.init_resource::<PrevCameraChunk>();
|
app.init_resource::<PrevCameraChunk>();
|
||||||
/* app.add_systems(Update, log_mesh_count);*/
|
/* app.add_systems(Update, log_mesh_count);*/
|
||||||
app
|
app
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// resources
|
// resources
|
||||||
@ -57,8 +60,10 @@ impl Plugin for EnvironmentPlugin {
|
|||||||
enqueue_visible_chunks,
|
enqueue_visible_chunks,
|
||||||
process_chunk_queue.after(enqueue_visible_chunks),
|
process_chunk_queue.after(enqueue_visible_chunks),
|
||||||
update_chunk_lods.after(process_chunk_queue),
|
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
|
rebuild_dirty_chunks.after(process_chunk_queue), // 4. (re)mesh dirty chunks
|
||||||
queue_gpu_meshing.after(rebuild_dirty_chunks),
|
queue_gpu_meshing.after(rebuild_dirty_chunks),
|
||||||
|
save_dirty_chunks_system.after(rebuild_dirty_chunks),
|
||||||
/* ---------- optional debug drawing ------- */
|
/* ---------- optional debug drawing ------- */
|
||||||
visualize_octree_system
|
visualize_octree_system
|
||||||
.run_if(should_visualize_octree)
|
.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 octree;
|
||||||
pub mod structure;
|
pub mod structure;
|
||||||
|
|
||||||
|
pub mod atlas;
|
||||||
mod chunk;
|
mod chunk;
|
||||||
|
pub mod chunk_io;
|
||||||
pub mod culling;
|
pub mod culling;
|
||||||
pub mod lod;
|
pub mod lod;
|
||||||
mod meshing;
|
mod meshing;
|
||||||
pub mod meshing_gpu;
|
pub mod meshing_gpu;
|
||||||
pub mod queue_systems;
|
pub mod queue_systems;
|
||||||
pub mod render_chunks;
|
pub mod render_chunks;
|
||||||
pub mod atlas;
|
|
||||||
|
|||||||
@ -572,4 +572,83 @@ impl SparseVoxelOctree {
|
|||||||
self.occupied_chunks.insert(key);
|
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