mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-11 13:58:30 +00:00
Add compute-based LOD update and fix culling output
This commit is contained in:
parent
95c088cabb
commit
0401b6076f
29
client/assets/shaders/chunk_lod.wgsl
Normal file
29
client/assets/shaders/chunk_lod.wgsl
Normal file
@ -0,0 +1,29 @@
|
||||
struct Params {
|
||||
centre: vec3<i32>;
|
||||
max_level: u32;
|
||||
range_step: f32;
|
||||
count: u32;
|
||||
_pad: u32;
|
||||
};
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> params: Params;
|
||||
|
||||
@group(0) @binding(1)
|
||||
var<storage, read> keys_in: array<vec3<i32>>;
|
||||
|
||||
@group(0) @binding(2)
|
||||
var<storage, read_write> lod_out: array<u32>;
|
||||
|
||||
@compute @workgroup_size(64)
|
||||
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
|
||||
let idx = id.x;
|
||||
if (idx >= params.count) { return; }
|
||||
let key = keys_in[idx];
|
||||
let dx = f32(key.x - params.centre.x);
|
||||
let dy = f32(key.y - params.centre.y);
|
||||
let dz = f32(key.z - params.centre.z);
|
||||
var level = floor(length(vec3<f32>(dx, dy, dz)) / params.range_step);
|
||||
if (level > f32(params.max_level)) { level = f32(params.max_level); }
|
||||
lod_out[idx] = u32(level);
|
||||
}
|
||||
@ -4,6 +4,7 @@ use bevy_easy_compute::prelude::{AppComputePlugin, AppComputeWorkerPlugin};
|
||||
use crate::plugins::environment::systems::voxels::sphere_compute::SphereWorker;
|
||||
use crate::plugins::environment::systems::voxels::visible_chunks_compute::VisibleChunksWorker;
|
||||
use crate::plugins::environment::systems::voxels::chunk_mesh_compute::ChunkMeshWorker;
|
||||
use crate::plugins::environment::systems::voxels::lod_compute::ChunkLodWorker;
|
||||
use bevy::prelude::*;
|
||||
pub struct AppPlugin;
|
||||
|
||||
@ -16,6 +17,7 @@ impl Plugin for AppPlugin {
|
||||
app.add_plugins(AppComputeWorkerPlugin::<SphereWorker>::default());
|
||||
app.add_plugins(AppComputeWorkerPlugin::<VisibleChunksWorker>::default());
|
||||
app.add_plugins(AppComputeWorkerPlugin::<ChunkMeshWorker>::default());
|
||||
app.add_plugins(AppComputeWorkerPlugin::<ChunkLodWorker>::default());
|
||||
//app.add_plugins(crate::plugins::network::network_plugin::NetworkPlugin);
|
||||
app.add_plugins(crate::plugins::input::input_plugin::InputPlugin);
|
||||
app.add_plugins(WireframePlugin);
|
||||
|
||||
@ -1,38 +1,60 @@
|
||||
use bevy::prelude::*;
|
||||
use crate::plugins::environment::systems::voxels::helper::chunk_center_world;
|
||||
use crate::plugins::environment::systems::voxels::structure::{Chunk, ChunkLod, ChunkCullingCfg, SparseVoxelOctree, CHUNK_SIZE};
|
||||
use crate::plugins::environment::systems::voxels::helper::world_to_chunk;
|
||||
use crate::plugins::environment::systems::voxels::structure::{Chunk, ChunkLod, ChunkCullingCfg, SparseVoxelOctree, ChunkKey};
|
||||
use super::lod_compute::{ChunkLodWorker, LodParams, MAX_LOD_CHUNKS};
|
||||
use super::visible_chunks_compute::IVec3Pod;
|
||||
use bevy_easy_compute::prelude::AppComputeWorker;
|
||||
|
||||
/// 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)>,
|
||||
chunks: Query<(Entity, &Chunk, &ChunkLod)>,
|
||||
mut chunks_mut: Query<&mut ChunkLod>,
|
||||
mut tree_q: Query<&mut SparseVoxelOctree>,
|
||||
mut worker: ResMut<AppComputeWorker<ChunkLodWorker>>,
|
||||
cfg: Res<ChunkCullingCfg>,
|
||||
) {
|
||||
let cam_pos = cam_q.single().translation();
|
||||
|
||||
// Borrow the octree only once to avoid repeated query lookups
|
||||
let mut tree = tree_q.single_mut();
|
||||
let tree = tree_q.single();
|
||||
let max_depth = tree.max_depth - 1;
|
||||
let max_level = max_depth;
|
||||
let range_step = cfg.view_distance_chunks as f32 / (max_depth as f32 - 1.0);
|
||||
let chunk_size = CHUNK_SIZE as f32 * tree.get_spacing_at_depth(max_depth);
|
||||
let centre = world_to_chunk(tree, cam_pos);
|
||||
drop(tree);
|
||||
|
||||
let mut changed = Vec::new();
|
||||
for (chunk, mut lod) in chunks.iter_mut() {
|
||||
let center = chunk_center_world(&tree, chunk.key);
|
||||
let dist_chunks = cam_pos.distance(center) / chunk_size;
|
||||
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);
|
||||
}
|
||||
let mut keys: Vec<IVec3Pod> = Vec::new();
|
||||
let mut entities: Vec<(Entity, ChunkKey)> = Vec::new();
|
||||
for (ent, chunk, _lod) in chunks.iter() {
|
||||
keys.push(IVec3Pod { x: chunk.key.0, y: chunk.key.1, z: chunk.key.2, _pad: 0 });
|
||||
entities.push((ent, chunk.key));
|
||||
}
|
||||
|
||||
for key in changed {
|
||||
tree.dirty_chunks.insert(key);
|
||||
let count = keys.len().min(MAX_LOD_CHUNKS);
|
||||
worker.write_slice("keys_in", &keys[..count]);
|
||||
let params = LodParams {
|
||||
centre: IVec3Pod { x: centre.0, y: centre.1, z: centre.2, _pad: 0 },
|
||||
max_level,
|
||||
count: count as u32,
|
||||
range_step,
|
||||
_pad0: 0,
|
||||
};
|
||||
worker.write("params", ¶ms);
|
||||
worker.execute();
|
||||
|
||||
if !worker.ready() {
|
||||
return;
|
||||
}
|
||||
|
||||
let results: Vec<u32> = worker.read_vec("lod_out");
|
||||
|
||||
let mut tree = tree_q.single_mut();
|
||||
for ((ent, key), level) in entities.into_iter().zip(results.into_iter().take(count)) {
|
||||
if let Ok(mut lod) = chunks_mut.get_mut(ent) {
|
||||
if lod.0 != level {
|
||||
lod.0 = level;
|
||||
tree.dirty_chunks.insert(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
client/src/plugins/environment/systems/voxels/lod_compute.rs
Normal file
49
client/src/plugins/environment/systems/voxels/lod_compute.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_easy_compute::prelude::*;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use super::visible_chunks_compute::IVec3Pod;
|
||||
|
||||
pub const MAX_LOD_CHUNKS: usize = 4096;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Pod, Zeroable, ShaderType)]
|
||||
pub struct LodParams {
|
||||
pub centre: IVec3Pod,
|
||||
pub max_level: u32,
|
||||
pub count: u32,
|
||||
pub range_step: f32,
|
||||
pub _pad0: u32,
|
||||
}
|
||||
|
||||
#[derive(TypePath)]
|
||||
struct LodShader;
|
||||
|
||||
impl ComputeShader for LodShader {
|
||||
fn shader() -> ShaderRef {
|
||||
"shaders/chunk_lod.wgsl".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct ChunkLodWorker;
|
||||
|
||||
impl ComputeWorker for ChunkLodWorker {
|
||||
fn build(world: &mut World) -> AppComputeWorker<Self> {
|
||||
let params = LodParams {
|
||||
centre: IVec3Pod { x: 0, y: 0, z: 0, _pad: 0 },
|
||||
max_level: 0,
|
||||
count: 0,
|
||||
range_step: 1.0,
|
||||
_pad0: 0,
|
||||
};
|
||||
AppComputeWorkerBuilder::new(world)
|
||||
.add_uniform("params", ¶ms)
|
||||
.add_staging("keys_in", &[IVec3Pod { x: 0, y: 0, z: 0, _pad: 0 }; MAX_LOD_CHUNKS])
|
||||
.add_staging("lod_out", &[0u32; MAX_LOD_CHUNKS])
|
||||
.add_pass::<LodShader>([((MAX_LOD_CHUNKS as u32 + 63) / 64), 1, 1], &["params", "keys_in", "lod_out"])
|
||||
.one_shot()
|
||||
.synchronous()
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@ -13,3 +13,4 @@ pub mod noise_compute;
|
||||
pub mod sphere_compute;
|
||||
pub mod visible_chunks_compute;
|
||||
pub mod chunk_mesh_compute;
|
||||
pub mod lod_compute;
|
||||
|
||||
@ -79,7 +79,7 @@ pub fn apply_visible_chunk_results(
|
||||
.into_iter()
|
||||
.filter_map(|r| {
|
||||
if r.dist2 < 0 { return None; }
|
||||
let key = ChunkKey(r.key.x, r.key.y, r.key.z);
|
||||
let key = ChunkKey(r.x, r.y, r.z);
|
||||
if spawned.0.contains_key(&key) { return None; }
|
||||
Some((key, r.dist2))
|
||||
})
|
||||
|
||||
@ -25,7 +25,9 @@ pub struct VisibleParams {
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Pod, Zeroable, ShaderType)]
|
||||
pub struct ChunkResult {
|
||||
pub key: IVec3Pod,
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub z: i32,
|
||||
pub dist2: i32,
|
||||
}
|
||||
|
||||
@ -52,7 +54,7 @@ impl ComputeWorker for VisibleChunksWorker {
|
||||
AppComputeWorkerBuilder::new(world)
|
||||
.add_uniform("params", &default_params)
|
||||
.add_staging("keys_in", &[IVec3Pod { x: 0, y: 0, z: 0, _pad: 0 }; MAX_VISIBLE_CHUNKS])
|
||||
.add_staging("results", &[ChunkResult { key: IVec3Pod { x: 0, y: 0, z: 0, _pad: 0 }, dist2: 0 }; MAX_VISIBLE_CHUNKS])
|
||||
.add_staging("results", &[ChunkResult { x: 0, y: 0, z: 0, dist2: 0 }; MAX_VISIBLE_CHUNKS])
|
||||
.add_pass::<VisibleShader>([((MAX_VISIBLE_CHUNKS as u32 + 63) / 64), 1, 1], &["params", "keys_in", "results"])
|
||||
.one_shot()
|
||||
.synchronous()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user