mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-26 13:08:29 +00:00
Optimize chunk visibility queue
This commit is contained in:
parent
4d4446f964
commit
9a74d8d0da
@ -7,7 +7,7 @@ use crate::plugins::environment::systems::voxels::queue_systems::{enqueue_visibl
|
|||||||
update_chunk_lods.after(process_chunk_queue),
|
update_chunk_lods.after(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::lod::update_chunk_lods;
|
use crate::plugins::environment::systems::voxels::lod::update_chunk_lods;
|
||||||
use crate::plugins::environment::systems::voxels::structure::{ChunkBudget, ChunkCullingCfg, ChunkQueue, SparseVoxelOctree, SpawnedChunks};
|
use crate::plugins::environment::systems::voxels::structure::{ChunkBudget, ChunkCullingCfg, ChunkQueue, SparseVoxelOctree, SpawnedChunks, ChunkOffsets, PrevCameraChunk};
|
||||||
|
|
||||||
pub struct EnvironmentPlugin;
|
pub struct EnvironmentPlugin;
|
||||||
impl Plugin for EnvironmentPlugin {
|
impl Plugin for EnvironmentPlugin {
|
||||||
@ -23,8 +23,11 @@ impl Plugin for EnvironmentPlugin {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.insert_resource(ChunkCullingCfg { view_distance_chunks: 10 });
|
let view_distance_chunks = 10;
|
||||||
|
app.insert_resource(ChunkCullingCfg { view_distance_chunks });
|
||||||
app.insert_resource(ChunkBudget { per_frame: 20 });
|
app.insert_resource(ChunkBudget { per_frame: 20 });
|
||||||
|
app.insert_resource(ChunkOffsets::new(view_distance_chunks));
|
||||||
|
app.init_resource::<PrevCameraChunk>();
|
||||||
app.add_systems(Update, log_mesh_count);
|
app.add_systems(Update, log_mesh_count);
|
||||||
app
|
app
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|||||||
@ -20,6 +20,7 @@ impl SparseVoxelOctree {
|
|||||||
show_chunks,
|
show_chunks,
|
||||||
dirty: Vec::new(),
|
dirty: Vec::new(),
|
||||||
dirty_chunks: Default::default(),
|
dirty_chunks: Default::default(),
|
||||||
|
occupied_chunks: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn insert(&mut self, position: Vec3, voxel: Voxel) {
|
pub fn insert(&mut self, position: Vec3, voxel: Voxel) {
|
||||||
@ -40,7 +41,9 @@ impl SparseVoxelOctree {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.dirty.push(dirty_voxel);
|
self.dirty.push(dirty_voxel);
|
||||||
self.dirty_chunks.insert(chunk_key_from_world(self, position));
|
let key = chunk_key_from_world(self, position);
|
||||||
|
self.dirty_chunks.insert(key);
|
||||||
|
self.occupied_chunks.insert(key);
|
||||||
|
|
||||||
|
|
||||||
Self::insert_recursive(&mut self.root, aligned, voxel, self.max_depth);
|
Self::insert_recursive(&mut self.root, aligned, voxel, self.max_depth);
|
||||||
@ -87,7 +90,8 @@ impl SparseVoxelOctree {
|
|||||||
self.dirty.push(DirtyVoxel { position: aligned });
|
self.dirty.push(DirtyVoxel { position: aligned });
|
||||||
|
|
||||||
// mark the chunk
|
// mark the chunk
|
||||||
self.dirty_chunks.insert(chunk_key_from_world(self, position));
|
let key = chunk_key_from_world(self, position);
|
||||||
|
self.dirty_chunks.insert(key);
|
||||||
|
|
||||||
Self::remove_recursive(
|
Self::remove_recursive(
|
||||||
&mut self.root,
|
&mut self.root,
|
||||||
@ -96,6 +100,10 @@ impl SparseVoxelOctree {
|
|||||||
aligned.z,
|
aligned.z,
|
||||||
self.max_depth,
|
self.max_depth,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if !self.chunk_has_any_voxel(key) {
|
||||||
|
self.occupied_chunks.remove(&key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_dirty_flags(&mut self) {
|
pub fn clear_dirty_flags(&mut self) {
|
||||||
|
|||||||
@ -9,6 +9,8 @@ use crate::plugins::environment::systems::voxels::structure::*;
|
|||||||
pub fn enqueue_visible_chunks(
|
pub fn enqueue_visible_chunks(
|
||||||
mut queue : ResMut<ChunkQueue>,
|
mut queue : ResMut<ChunkQueue>,
|
||||||
spawned : Res<SpawnedChunks>,
|
spawned : Res<SpawnedChunks>,
|
||||||
|
mut prev_cam : ResMut<PrevCameraChunk>,
|
||||||
|
offsets : Res<ChunkOffsets>,
|
||||||
cfg : Res<ChunkCullingCfg>,
|
cfg : Res<ChunkCullingCfg>,
|
||||||
cam_q : Query<&GlobalTransform, With<Camera>>,
|
cam_q : Query<&GlobalTransform, With<Camera>>,
|
||||||
tree_q : Query<&SparseVoxelOctree>,
|
tree_q : Query<&SparseVoxelOctree>,
|
||||||
@ -16,35 +18,17 @@ pub fn enqueue_visible_chunks(
|
|||||||
let tree = tree_q.single();
|
let tree = tree_q.single();
|
||||||
let cam_pos = cam_q.single().translation();
|
let cam_pos = cam_q.single().translation();
|
||||||
let centre = world_to_chunk(tree, cam_pos);
|
let centre = world_to_chunk(tree, cam_pos);
|
||||||
let r = cfg.view_distance_chunks;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
if prev_cam.0 == Some(centre) {
|
||||||
// 1. gather every *new* candidate chunk together with its distance²
|
return;
|
||||||
// ------------------------------------------------------------------
|
|
||||||
let mut candidates: Vec<(i32 /*dist²*/, ChunkKey)> = Vec::new();
|
|
||||||
|
|
||||||
for dx in -r..=r {
|
|
||||||
for dy in -r..=r {
|
|
||||||
for dz in -r..=r {
|
|
||||||
let key = ChunkKey(centre.0 + dx, centre.1 + dy, centre.2 + dz);
|
|
||||||
|
|
||||||
if spawned.0.contains_key(&key) { continue; } // already spawned
|
|
||||||
if queue.0.contains(&key) { continue; } // already queued
|
|
||||||
if !tree.chunk_has_any_voxel(key) { continue; } // empty air
|
|
||||||
|
|
||||||
let dist2 = dx*dx + dy*dy + dz*dz; // squared distance
|
|
||||||
candidates.push((dist2, key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
prev_cam.0 = Some(centre);
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
for offset in &offsets.0 {
|
||||||
// 2. sort by distance so nearest chunks enter the queue first
|
let key = ChunkKey(centre.0 + offset.x, centre.1 + offset.y, centre.2 + offset.z);
|
||||||
// ------------------------------------------------------------------
|
if spawned.0.contains_key(&key) { continue; }
|
||||||
candidates.sort_by_key(|&(d2, _)| d2);
|
if queue.0.contains(&key) { continue; }
|
||||||
|
if !tree.occupied_chunks.contains(&key) { continue; }
|
||||||
// push into FIFO queue in that order
|
|
||||||
for (_, key) in candidates {
|
|
||||||
queue.0.push_back(key);
|
queue.0.push_back(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ pub struct SparseVoxelOctree {
|
|||||||
|
|
||||||
pub dirty: Vec<DirtyVoxel>,
|
pub dirty: Vec<DirtyVoxel>,
|
||||||
pub dirty_chunks: HashSet<ChunkKey>,
|
pub dirty_chunks: HashSet<ChunkKey>,
|
||||||
|
pub occupied_chunks: HashSet<ChunkKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OctreeNode {
|
impl OctreeNode {
|
||||||
@ -127,3 +128,24 @@ pub struct SpawnedChunks(pub HashMap<ChunkKey, Entity>);
|
|||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct ChunkCullingCfg { pub view_distance_chunks: i32 }
|
pub struct ChunkCullingCfg { pub view_distance_chunks: i32 }
|
||||||
impl Default for ChunkCullingCfg { fn default() -> Self { Self { view_distance_chunks: 6 } } }
|
impl Default for ChunkCullingCfg { fn default() -> Self { Self { view_distance_chunks: 6 } } }
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct PrevCameraChunk(pub Option<ChunkKey>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct ChunkOffsets(pub Vec<IVec3>);
|
||||||
|
|
||||||
|
impl ChunkOffsets {
|
||||||
|
pub fn new(radius: i32) -> Self {
|
||||||
|
let mut offsets = Vec::new();
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
for dy in -radius..=radius {
|
||||||
|
for dz in -radius..=radius {
|
||||||
|
offsets.push(IVec3::new(dx, dy, dz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsets.sort_by_key(|v| v.x * v.x + v.y * v.y + v.z * v.z);
|
||||||
|
Self(offsets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user