Add octree serialization and sorted chunk loading

This commit is contained in:
Elias Stepanik 2025-06-09 19:17:15 +02:00
parent 2f7128c55f
commit 78ee3483d7
4 changed files with 78 additions and 16 deletions

View File

@ -19,4 +19,5 @@ itertools = "0.13.0"
bitvec = "1.0.1"
smallvec = "1.14.0"
once_cell = "1.21.3"
rayon = "1.10.0"
rayon = "1.10.0"
bincode = "1.3"

View File

@ -1,4 +1,7 @@
use std::collections::{HashMap, HashSet};
use std::path::Path;
use std::io;
use bincode;
use bevy::asset::Assets;
use bevy::color::Color;
use bevy::math::{DQuat, DVec3};
@ -457,8 +460,33 @@ impl SparseVoxelOctree {
None
}
/// Save the octree to a file using bincode serialization.
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let data = bincode::serialize(self)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
std::fs::write(path, data)
}
/// Load an octree from a file and rebuild runtime caches.
pub fn load_from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let bytes = std::fs::read(path)?;
let mut tree: Self = bincode::deserialize(&bytes)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
tree.rebuild_cache();
Ok(tree)
}
/// Rebuild runtime caches like occupied_chunks after loading.
pub fn rebuild_cache(&mut self) {
self.dirty.clear();
self.dirty_chunks.clear();
self.occupied_chunks.clear();
let voxels = Self::collect_voxels_from_node(&self.root, self.size);
for (pos, _voxel, _depth) in voxels {
let key = chunk_key_from_world(self, pos);
self.occupied_chunks.insert(key);
}
}
}

View File

@ -24,15 +24,27 @@ pub fn enqueue_visible_chunks(
prev_cam.0 = Some(centre);
let r = cfg.view_distance_chunks;
for key in &tree.occupied_chunks {
let dx = key.0 - centre.0;
let dy = key.1 - centre.1;
let dz = key.2 - centre.2;
if dx.abs() > r || dy.abs() > r || dz.abs() > r { continue; }
if spawned.0.contains_key(key) { continue; }
if queue.set.contains(key) { continue; }
queue.keys.push_back(*key);
queue.set.insert(*key);
let mut keys: Vec<(ChunkKey, i32)> = tree
.occupied_chunks
.iter()
.filter_map(|key| {
let dx = key.0 - centre.0;
let dy = key.1 - centre.1;
let dz = key.2 - centre.2;
if dx.abs() > r || dy.abs() > r || dz.abs() > r { return None; }
if spawned.0.contains_key(key) { return None; }
Some((*key, dx*dx + dy*dy + dz*dz))
})
.collect();
keys.sort_by_key(|(_, d)| *d);
queue.keys.clear();
queue.set.clear();
for (key, _) in keys {
queue.keys.push_back(key);
queue.set.insert(key);
}
}

View File

@ -1,11 +1,29 @@
use std::collections::{HashMap, HashSet, VecDeque};
use bevy::color::Color;
use bevy::prelude::*;
use serde::{Serialize, Deserialize, Serializer, Deserializer};
fn serialize_color<S>(color: &Color, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let [r, g, b, a] = color.as_linear_rgba_f32();
[r, g, b, a].serialize(serializer)
}
fn deserialize_color<'de, D>(deserializer: D) -> Result<Color, D::Error>
where
D: Deserializer<'de>,
{
let arr: [f32; 4] = Deserialize::deserialize(deserializer)?;
Ok(Color::rgba_linear(arr[0], arr[1], arr[2], arr[3]))
}
/// Represents a single voxel with a color.
#[derive(Debug, Clone, Copy, Component, PartialEq, Default)]
#[derive(Debug, Clone, Copy, Component, PartialEq, Default, Serialize, Deserialize)]
pub struct Voxel {
#[serde(serialize_with = "serialize_color", deserialize_with = "deserialize_color")]
pub color: Color,
}
@ -16,7 +34,7 @@ pub struct DirtyVoxel {
/// Represents a node in the sparse voxel octree.
#[derive(Debug, Component, Clone)]
#[derive(Debug, Component, Clone, Serialize, Deserialize)]
pub struct OctreeNode {
pub children: Option<Box<[OctreeNode; 8]>>,
pub voxel: Option<Voxel>,
@ -24,7 +42,7 @@ pub struct OctreeNode {
}
/// Represents the root of the sparse voxel octree.
/// Represents the root of the sparse voxel octree.
#[derive(Debug, Component)]
#[derive(Debug, Component, Serialize, Deserialize)]
pub struct SparseVoxelOctree {
pub root: OctreeNode,
@ -34,8 +52,11 @@ pub struct SparseVoxelOctree {
pub show_world_grid: bool,
pub show_chunks: bool,
#[serde(skip)]
pub dirty: Vec<DirtyVoxel>,
#[serde(skip)]
pub dirty_chunks: HashSet<ChunkKey>,
#[serde(skip)]
pub occupied_chunks: HashSet<ChunkKey>,
}
@ -101,7 +122,7 @@ pub struct Chunk {
pub struct ChunkLod(pub u32);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChunkKey(pub i32, pub i32, pub i32);