mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-11 13:58:30 +00:00
Add distance-based LOD for voxel chunks
This commit is contained in:
parent
3440093284
commit
4d4446f964
@ -4,7 +4,9 @@ use crate::plugins::environment::systems::voxels::culling::{despawn_distant_chun
|
|||||||
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::queue_systems;
|
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::queue_systems::{enqueue_visible_chunks, 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::structure::{ChunkBudget, ChunkCullingCfg, ChunkQueue, SparseVoxelOctree, SpawnedChunks};
|
use crate::plugins::environment::systems::voxels::structure::{ChunkBudget, ChunkCullingCfg, ChunkQueue, SparseVoxelOctree, SpawnedChunks};
|
||||||
|
|
||||||
pub struct EnvironmentPlugin;
|
pub struct EnvironmentPlugin;
|
||||||
@ -40,6 +42,7 @@ impl Plugin for EnvironmentPlugin {
|
|||||||
despawn_distant_chunks, // 1. remove too-far chunks
|
despawn_distant_chunks, // 1. remove too-far chunks
|
||||||
enqueue_visible_chunks.after(despawn_distant_chunks), // 2. find new visible ones
|
enqueue_visible_chunks.after(despawn_distant_chunks), // 2. find new visible ones
|
||||||
process_chunk_queue .after(enqueue_visible_chunks), // 3. spawn ≤ budget per frame
|
process_chunk_queue .after(enqueue_visible_chunks), // 3. spawn ≤ budget per frame
|
||||||
|
update_chunk_lods.after(process_chunk_queue),
|
||||||
rebuild_dirty_chunks .after(process_chunk_queue), // 4. (re)mesh dirty chunks
|
rebuild_dirty_chunks .after(process_chunk_queue), // 4. (re)mesh dirty chunks
|
||||||
|
|
||||||
/* ---------- optional debug drawing ------- */
|
/* ---------- optional debug drawing ------- */
|
||||||
|
|||||||
@ -269,6 +269,16 @@ pub fn world_to_chunk(tree: &SparseVoxelOctree, p: Vec3) -> ChunkKey {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chunk_center_world(tree: &SparseVoxelOctree, key: ChunkKey) -> Vec3 {
|
||||||
|
let half = tree.size * 0.5;
|
||||||
|
let step = tree.get_spacing_at_depth(tree.max_depth);
|
||||||
|
Vec3::new(
|
||||||
|
(key.0 as f32 + 0.5) * CHUNK_SIZE as f32 * step - half,
|
||||||
|
(key.1 as f32 + 0.5) * CHUNK_SIZE as f32 * step - half,
|
||||||
|
(key.2 as f32 + 0.5) * CHUNK_SIZE as f32 * step - half,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl AABB {
|
impl AABB {
|
||||||
pub fn intersects_aabb(&self, other: &AABB) -> bool {
|
pub fn intersects_aabb(&self, other: &AABB) -> bool {
|
||||||
self.min.x <= other.max.x &&
|
self.min.x <= other.max.x &&
|
||||||
|
|||||||
42
client/src/plugins/environment/systems/voxels/lod.rs
Normal file
42
client/src/plugins/environment/systems/voxels/lod.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use crate::plugins::environment::systems::voxels::helper::chunk_center_world;
|
||||||
|
use crate::plugins::environment::systems::voxels::structure::{Chunk, ChunkLod, ChunkCullingCfg, SparseVoxelOctree};
|
||||||
|
|
||||||
|
/// Update each chunk's LOD level based on its distance from the camera.
|
||||||
|
/// Chunks farther away get a higher LOD value (coarser mesh).
|
||||||
|
pub fn update_chunk_lods(
|
||||||
|
cam_q: Query<&GlobalTransform, With<Camera>>,
|
||||||
|
mut chunks: Query<(&Chunk, &mut ChunkLod)>,
|
||||||
|
mut tree_q: Query<&mut SparseVoxelOctree>,
|
||||||
|
cfg: Res<ChunkCullingCfg>,
|
||||||
|
) {
|
||||||
|
let cam_pos = cam_q.single().translation();
|
||||||
|
let max_depth;
|
||||||
|
let range_step;
|
||||||
|
{
|
||||||
|
let tree = tree_q.single();
|
||||||
|
max_depth = tree.max_depth;
|
||||||
|
range_step = cfg.view_distance_chunks as f32 / max_depth as f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut changed = Vec::new();
|
||||||
|
for (chunk, mut lod) in chunks.iter_mut() {
|
||||||
|
let tree = tree_q.single();
|
||||||
|
let center = chunk_center_world(&tree, chunk.key);
|
||||||
|
let dist_chunks = cam_pos.distance(center)
|
||||||
|
/ (cfg.view_distance_chunks as f32);
|
||||||
|
let mut level = (dist_chunks / range_step).floor() as u32;
|
||||||
|
if level > max_depth { level = max_depth; }
|
||||||
|
if lod.0 != level {
|
||||||
|
lod.0 = level;
|
||||||
|
changed.push(chunk.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !changed.is_empty() {
|
||||||
|
let mut tree = tree_q.single_mut();
|
||||||
|
for key in changed {
|
||||||
|
tree.dirty_chunks.insert(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,4 +7,5 @@ mod chunk;
|
|||||||
mod meshing;
|
mod meshing;
|
||||||
pub mod render_chunks;
|
pub mod render_chunks;
|
||||||
pub mod culling;
|
pub mod culling;
|
||||||
pub mod queue_systems;
|
pub mod queue_systems;
|
||||||
|
pub mod lod;
|
||||||
|
|||||||
@ -18,15 +18,16 @@ pub fn rebuild_dirty_chunks(
|
|||||||
chunk_q : Query<(Entity,
|
chunk_q : Query<(Entity,
|
||||||
&Chunk,
|
&Chunk,
|
||||||
&Mesh3d,
|
&Mesh3d,
|
||||||
&MeshMaterial3d<StandardMaterial>)>,
|
&MeshMaterial3d<StandardMaterial>,
|
||||||
|
&ChunkLod)>,
|
||||||
mut spawned : ResMut<SpawnedChunks>,
|
mut spawned : ResMut<SpawnedChunks>,
|
||||||
root : Res<RootGrid>,
|
root : Res<RootGrid>,
|
||||||
) {
|
) {
|
||||||
// map ChunkKey → (entity, mesh-handle, material-handle)
|
// map ChunkKey → (entity, mesh-handle, material-handle)
|
||||||
let existing: HashMap<ChunkKey, (Entity, Handle<Mesh>, Handle<StandardMaterial>)> =
|
let existing: HashMap<ChunkKey, (Entity, Handle<Mesh>, Handle<StandardMaterial>, u32)> =
|
||||||
chunk_q
|
chunk_q
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(e, c, m, mat)| (c.key, (e, m.0.clone(), mat.0.clone())))
|
.map(|(e, c, m, mat, lod)| (c.key, (e, m.0.clone(), mat.0.clone(), lod.0)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for mut tree in &mut octrees {
|
for mut tree in &mut octrees {
|
||||||
@ -37,6 +38,7 @@ pub fn rebuild_dirty_chunks(
|
|||||||
//------------------------------------------------ collect voxel data
|
//------------------------------------------------ collect voxel data
|
||||||
let mut bufs = Vec::new();
|
let mut bufs = Vec::new();
|
||||||
for key in tree.dirty_chunks.iter().copied() {
|
for key in tree.dirty_chunks.iter().copied() {
|
||||||
|
let lod = existing.get(&key).map(|v| v.3).unwrap_or(0);
|
||||||
let mut buf =
|
let mut buf =
|
||||||
[[[None; CHUNK_SIZE as usize]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize];
|
[[[None; CHUNK_SIZE as usize]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize];
|
||||||
|
|
||||||
@ -48,24 +50,40 @@ pub fn rebuild_dirty_chunks(
|
|||||||
key.2 as f32 * CHUNK_SIZE as f32 * step - half,
|
key.2 as f32 * CHUNK_SIZE as f32 * step - half,
|
||||||
);
|
);
|
||||||
|
|
||||||
for lx in 0..CHUNK_SIZE {
|
let mult = 1 << lod;
|
||||||
for ly in 0..CHUNK_SIZE {
|
for gx in (0..CHUNK_SIZE).step_by(mult as usize) {
|
||||||
for lz in 0..CHUNK_SIZE {
|
for gy in (0..CHUNK_SIZE).step_by(mult as usize) {
|
||||||
let world = origin
|
for gz in (0..CHUNK_SIZE).step_by(mult as usize) {
|
||||||
+ Vec3::new(lx as f32 * step, ly as f32 * step, lz as f32 * step);
|
let center = origin
|
||||||
if let Some(v) = tree.get_voxel_at_world_coords(world) {
|
+ Vec3::new(
|
||||||
buf[lx as usize][ly as usize][lz as usize] = Some(*v);
|
(gx + mult / 2) as f32 * step,
|
||||||
|
(gy + mult / 2) as f32 * step,
|
||||||
|
(gz + mult / 2) as f32 * step,
|
||||||
|
);
|
||||||
|
if let Some(v) = tree.get_voxel_at_world_coords(center) {
|
||||||
|
for lx in 0..mult {
|
||||||
|
for ly in 0..mult {
|
||||||
|
for lz in 0..mult {
|
||||||
|
let ix = gx + lx;
|
||||||
|
let iy = gy + ly;
|
||||||
|
let iz = gz + lz;
|
||||||
|
if ix < CHUNK_SIZE && iy < CHUNK_SIZE && iz < CHUNK_SIZE {
|
||||||
|
buf[ix as usize][iy as usize][iz as usize] = Some(*v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bufs.push((key, buf, origin, step));
|
bufs.push((key, buf, origin, step, lod));
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------ create / update
|
//------------------------------------------------ create / update
|
||||||
for (key, buf, origin, step) in bufs {
|
for (key, buf, origin, step, lod) in bufs {
|
||||||
if let Some((ent, mesh_h, _mat_h)) = existing.get(&key).cloned() {
|
if let Some((ent, mesh_h, _mat_h, _)) = existing.get(&key).cloned() {
|
||||||
// update mesh in-place; keeps old asset id
|
// update mesh in-place; keeps old asset id
|
||||||
if let Some(mesh) = meshes.get_mut(&mesh_h) {
|
if let Some(mesh) = meshes.get_mut(&mesh_h) {
|
||||||
*mesh = mesh_chunk(&buf, origin, step, &tree);
|
*mesh = mesh_chunk(&buf, origin, step, &tree);
|
||||||
@ -84,6 +102,7 @@ pub fn rebuild_dirty_chunks(
|
|||||||
Transform::default(),
|
Transform::default(),
|
||||||
GridCell::<i64>::ZERO,
|
GridCell::<i64>::ZERO,
|
||||||
Chunk { key, voxels: Vec::new(), dirty: false },
|
Chunk { key, voxels: Vec::new(), dirty: false },
|
||||||
|
ChunkLod(lod),
|
||||||
/*Wireframe,*/
|
/*Wireframe,*/
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
|
|||||||
@ -93,9 +93,12 @@ pub struct Chunk {
|
|||||||
pub key: ChunkKey,
|
pub key: ChunkKey,
|
||||||
pub voxels: Vec<(IVec3, Voxel)>, // local coords 0‥15
|
pub voxels: Vec<(IVec3, Voxel)>, // local coords 0‥15
|
||||||
pub dirty: bool,
|
pub dirty: bool,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Clone, Copy)]
|
||||||
|
pub struct ChunkLod(pub u32);
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub struct ChunkKey(pub i32, pub i32, pub i32);
|
pub struct ChunkKey(pub i32, pub i32, pub i32);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user