mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-10 05:18:30 +00:00
Merge pull request #40 from eliasstepanik/codex/optimize-und-refaktoriere-voxel-system
Refactor chunk utility functions
This commit is contained in:
commit
970545dc16
@ -1,36 +1,35 @@
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use bevy::prelude::*;
|
||||
use crate::plugins::environment::systems::voxels::helper::world_to_chunk;
|
||||
use crate::plugins::environment::systems::voxels::structure::*;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
/// despawn (or hide) every chunk entity whose centre is farther away than the
|
||||
/// configured radius
|
||||
|
||||
pub fn despawn_distant_chunks(
|
||||
mut commands : Commands,
|
||||
cam_q : Query<&GlobalTransform, With<Camera>>,
|
||||
tree_q : Query<&SparseVoxelOctree>,
|
||||
mut spawned : ResMut<SpawnedChunks>,
|
||||
chunk_q : Query<(Entity,
|
||||
&Chunk,
|
||||
&Mesh3d,
|
||||
&MeshMaterial3d<StandardMaterial>)>,
|
||||
mut meshes : ResMut<Assets<Mesh>>,
|
||||
mut commands: Commands,
|
||||
cam_q: Query<&GlobalTransform, With<Camera>>,
|
||||
tree_q: Query<&SparseVoxelOctree>,
|
||||
mut spawned: ResMut<SpawnedChunks>,
|
||||
chunk_q: Query<(Entity, &Chunk, &Mesh3d, &MeshMaterial3d<StandardMaterial>)>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
cfg : Res<ChunkCullingCfg>,
|
||||
cfg: Res<ChunkCullingCfg>,
|
||||
) {
|
||||
let Ok(tree) = tree_q.get_single() else { return };
|
||||
let Ok(cam_tf) = cam_q.get_single() else { return };
|
||||
let cam = cam_tf.translation();
|
||||
let centre = world_to_chunk(tree, cam);
|
||||
let Ok(tree) = tree_q.get_single() else {
|
||||
return;
|
||||
};
|
||||
let Ok(cam_tf) = cam_q.get_single() else {
|
||||
return;
|
||||
};
|
||||
let cam = cam_tf.translation();
|
||||
let centre = tree.world_to_chunk(cam);
|
||||
|
||||
for (ent, chunk, mesh3d, mat3d) in chunk_q.iter() {
|
||||
let ChunkKey(x, y, z) = chunk.key;
|
||||
if (x - centre.0).abs() > cfg.view_distance_chunks ||
|
||||
(y - centre.1).abs() > cfg.view_distance_chunks ||
|
||||
(z - centre.2).abs() > cfg.view_distance_chunks {
|
||||
|
||||
if (x - centre.0).abs() > cfg.view_distance_chunks
|
||||
|| (y - centre.1).abs() > cfg.view_distance_chunks
|
||||
|| (z - centre.2).abs() > cfg.view_distance_chunks
|
||||
{
|
||||
// free assets – borrow, don't move
|
||||
meshes.remove(&mesh3d.0);
|
||||
materials.remove(&mat3d.0);
|
||||
@ -39,4 +38,4 @@ pub fn despawn_distant_chunks(
|
||||
spawned.0.remove(&chunk.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use bevy::prelude::*;
|
||||
use crate::plugins::environment::systems::voxels::structure::*;
|
||||
use bevy::prelude::*;
|
||||
|
||||
impl SparseVoxelOctree {
|
||||
pub fn ray_intersects_aabb(&self,ray: &Ray, aabb: &AABB) -> bool {
|
||||
pub fn ray_intersects_aabb(&self, ray: &Ray, aabb: &AABB) -> bool {
|
||||
let inv_dir = 1.0 / ray.direction;
|
||||
let t1 = (aabb.min - ray.origin) * inv_dir;
|
||||
let t2 = (aabb.max - ray.origin) * inv_dir;
|
||||
@ -16,14 +16,12 @@ impl SparseVoxelOctree {
|
||||
t_enter <= t_exit && t_exit >= 0.0
|
||||
}
|
||||
|
||||
|
||||
/// Returns the size of one voxel at the given depth.
|
||||
pub fn get_spacing_at_depth(&self, depth: u32) -> f32 {
|
||||
let effective = depth.min(self.max_depth);
|
||||
self.size / (2_u32.pow(effective)) as f32
|
||||
}
|
||||
|
||||
|
||||
/// Center-based: [-size/2..+size/2]. Shift +half_size => [0..size], floor, shift back.
|
||||
pub fn normalize_to_voxel_at_depth(&self, position: Vec3, depth: u32) -> Vec3 {
|
||||
// Convert world coordinate to normalized [0,1] space.
|
||||
@ -43,6 +41,28 @@ impl SparseVoxelOctree {
|
||||
voxel_center * self.size - Vec3::splat(half_size) + self.center
|
||||
}
|
||||
|
||||
/// Convert a world position to the key of the chunk containing it.
|
||||
pub fn world_to_chunk(&self, pos: Vec3) -> ChunkKey {
|
||||
let step = self.get_spacing_at_depth(self.max_depth);
|
||||
let half = self.size * 0.5;
|
||||
let scale = CHUNK_SIZE as f32 * step; // metres per chunk
|
||||
ChunkKey(
|
||||
((pos.x - self.center.x + half) / scale).floor() as i32,
|
||||
((pos.y - self.center.y + half) / scale).floor() as i32,
|
||||
((pos.z - self.center.z + half) / scale).floor() as i32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the world-space center for a given chunk.
|
||||
pub fn chunk_center_world(&self, key: ChunkKey) -> Vec3 {
|
||||
let half = self.size * 0.5;
|
||||
let step = self.get_spacing_at_depth(self.max_depth);
|
||||
Vec3::new(
|
||||
self.center.x - half + (key.0 as f32 + 0.5) * CHUNK_SIZE as f32 * step,
|
||||
self.center.y - half + (key.1 as f32 + 0.5) * CHUNK_SIZE as f32 * step,
|
||||
self.center.z - half + (key.2 as f32 + 0.5) * CHUNK_SIZE as f32 * step,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn compute_child_bounds(&self, bounds: &AABB, index: usize) -> AABB {
|
||||
let min = bounds.min;
|
||||
@ -126,8 +146,6 @@ impl SparseVoxelOctree {
|
||||
(local_pos - Vec3::splat(0.5)) * self.size + self.center
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Helper function to recursively traverse the octree to a specific depth.
|
||||
pub(crate) fn get_node_at_depth(
|
||||
node: &OctreeNode,
|
||||
@ -186,9 +204,6 @@ impl SparseVoxelOctree {
|
||||
// If no voxel found in this node or its children
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// Returns the (face_normal, local_offset) for the given neighbor direction.
|
||||
@ -235,53 +250,21 @@ pub fn face_orientation(dx: f32, dy: f32, dz: f32, voxel_size_f: f32) -> (Vec3,
|
||||
}
|
||||
// If the direction is not one of the 6 axis directions, you might skip or handle differently
|
||||
_ => {
|
||||
// For safety, we can panic or return a default.
|
||||
// For safety, we can panic or return a default.
|
||||
// But typically you won't call face_orientation with an invalid direction
|
||||
panic!("Invalid face direction: ({}, {}, {})", dx, dy, dz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn chunk_key_from_world(tree: &SparseVoxelOctree, pos: Vec3) -> ChunkKey {
|
||||
let half = tree.size * 0.5;
|
||||
let step = tree.get_spacing_at_depth(tree.max_depth);
|
||||
let scale = CHUNK_SIZE as f32 * step; // metres per chunk
|
||||
ChunkKey(
|
||||
((pos.x - tree.center.x + half) / scale).floor() as i32,
|
||||
((pos.y - tree.center.y + half) / scale).floor() as i32,
|
||||
((pos.z - tree.center.z + half) / scale).floor() as i32,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn world_to_chunk(tree: &SparseVoxelOctree, p: Vec3) -> ChunkKey {
|
||||
let step = tree.get_spacing_at_depth(tree.max_depth);
|
||||
let half = tree.size * 0.5;
|
||||
let scale = CHUNK_SIZE as f32 * step;
|
||||
ChunkKey(
|
||||
((p.x - tree.center.x + half) / scale).floor() as i32,
|
||||
((p.y - tree.center.y + half) / scale).floor() as i32,
|
||||
((p.z - tree.center.z + half) / scale).floor() as i32,
|
||||
)
|
||||
}
|
||||
|
||||
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(
|
||||
tree.center.x - half + (key.0 as f32 + 0.5) * CHUNK_SIZE as f32 * step,
|
||||
tree.center.y - half + (key.1 as f32 + 0.5) * CHUNK_SIZE as f32 * step,
|
||||
tree.center.z - half + (key.2 as f32 + 0.5) * CHUNK_SIZE as f32 * step,
|
||||
)
|
||||
}
|
||||
|
||||
impl AABB {
|
||||
pub fn intersects_aabb(&self, other: &AABB) -> bool {
|
||||
self.min.x <= other.max.x &&
|
||||
self.max.x >= other.min.x &&
|
||||
self.min.y <= other.max.y &&
|
||||
self.max.y >= other.min.y &&
|
||||
self.min.z <= other.max.z &&
|
||||
self.max.z >= other.min.z
|
||||
self.min.x <= other.max.x
|
||||
&& self.max.x >= other.min.x
|
||||
&& self.min.y <= other.max.y
|
||||
&& self.max.y >= other.min.y
|
||||
&& self.min.z <= other.max.z
|
||||
&& self.max.z >= other.min.z
|
||||
}
|
||||
|
||||
pub fn center(&self) -> Vec3 {
|
||||
@ -316,9 +299,12 @@ impl SparseVoxelOctree {
|
||||
if node.is_leaf {
|
||||
if let Some(voxel) = &node.voxel {
|
||||
let center = node_bounds.center();
|
||||
if center.x >= min.x && center.x <= max.x &&
|
||||
center.y >= min.y && center.y <= max.y &&
|
||||
center.z >= min.z && center.z <= max.z
|
||||
if center.x >= min.x
|
||||
&& center.x <= max.x
|
||||
&& center.y >= min.y
|
||||
&& center.y <= max.y
|
||||
&& center.z >= min.z
|
||||
&& center.z <= max.z
|
||||
{
|
||||
out.push((center, *voxel));
|
||||
}
|
||||
@ -332,4 +318,4 @@ impl SparseVoxelOctree {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::plugins::environment::systems::voxels::structure::{
|
||||
CHUNK_SIZE, Chunk, ChunkCullingCfg, ChunkLod, SparseVoxelOctree,
|
||||
};
|
||||
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};
|
||||
|
||||
/// Update each chunk's LOD level based on its distance from the camera.
|
||||
/// Chunks farther away get a higher LOD value (coarser mesh).
|
||||
@ -10,18 +11,22 @@ pub fn update_chunk_lods(
|
||||
mut tree_q: Query<&mut SparseVoxelOctree>,
|
||||
cfg: Res<ChunkCullingCfg>,
|
||||
) {
|
||||
let Ok(cam_tf) = cam_q.get_single() else { return };
|
||||
let Ok(cam_tf) = cam_q.get_single() else {
|
||||
return;
|
||||
};
|
||||
let cam_pos = cam_tf.translation();
|
||||
|
||||
// Borrow the octree only once to avoid repeated query lookups
|
||||
let Ok(mut tree) = tree_q.get_single_mut() else { return };
|
||||
let Ok(mut tree) = tree_q.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
let max_depth = tree.max_depth - 1;
|
||||
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 mut changed = Vec::new();
|
||||
for (chunk, mut lod) in chunks.iter_mut() {
|
||||
let center = chunk_center_world(&tree, chunk.key);
|
||||
let center = tree.chunk_center_world(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 {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use crate::plugins::environment::systems::voxels::helper::chunk_key_from_world;
|
||||
use crate::plugins::environment::systems::voxels::structure::{
|
||||
AABB, CHUNK_SIZE, ChunkKey, DirtyVoxel, NEIGHBOR_OFFSETS, OctreeNode, Ray, SparseVoxelOctree,
|
||||
Voxel,
|
||||
@ -50,7 +49,7 @@ impl SparseVoxelOctree {
|
||||
let dirty_voxel = DirtyVoxel { position: aligned };
|
||||
|
||||
self.dirty.push(dirty_voxel);
|
||||
let key = chunk_key_from_world(self, position);
|
||||
let key = self.world_to_chunk(position);
|
||||
self.dirty_chunks.insert(key);
|
||||
self.mark_neighbor_chunks_dirty(position);
|
||||
self.occupied_chunks.insert(key);
|
||||
@ -99,7 +98,7 @@ impl SparseVoxelOctree {
|
||||
self.dirty.push(DirtyVoxel { position: aligned });
|
||||
|
||||
// mark the chunk
|
||||
let key = chunk_key_from_world(self, position);
|
||||
let key = self.world_to_chunk(position);
|
||||
self.dirty_chunks.insert(key);
|
||||
self.mark_neighbor_chunks_dirty(position);
|
||||
|
||||
@ -122,7 +121,7 @@ impl SparseVoxelOctree {
|
||||
}
|
||||
|
||||
fn mark_neighbor_chunks_dirty(&mut self, position: Vec3) {
|
||||
let key = chunk_key_from_world(self, position);
|
||||
let key = self.world_to_chunk(position);
|
||||
let step = self.get_spacing_at_depth(self.max_depth);
|
||||
let half = self.size * 0.5;
|
||||
|
||||
@ -317,7 +316,11 @@ impl SparseVoxelOctree {
|
||||
|
||||
/// Helper: Collect all voxels from a given octree node recursively.
|
||||
/// The coordinate system here assumes the node covers [–old_size/2, +old_size/2] in each axis.
|
||||
fn collect_voxels_from_node(node: &OctreeNode, old_size: f32, center: Vec3) -> Vec<(Vec3, Voxel, u32)> {
|
||||
fn collect_voxels_from_node(
|
||||
node: &OctreeNode,
|
||||
old_size: f32,
|
||||
center: Vec3,
|
||||
) -> Vec<(Vec3, Voxel, u32)> {
|
||||
let mut voxels = Vec::new();
|
||||
Self::collect_voxels_recursive(
|
||||
node,
|
||||
@ -565,7 +568,7 @@ impl SparseVoxelOctree {
|
||||
|
||||
let voxels = Self::collect_voxels_from_node(&self.root, self.size, self.center);
|
||||
for (pos, _voxel, _depth) in voxels {
|
||||
let key = chunk_key_from_world(self, pos);
|
||||
let key = self.world_to_chunk(pos);
|
||||
self.occupied_chunks.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use crate::plugins::environment::systems::voxels::helper::world_to_chunk;
|
||||
use crate::plugins::environment::systems::voxels::structure::*;
|
||||
use bevy::prelude::*;
|
||||
use rayon::prelude::*;
|
||||
@ -20,7 +19,7 @@ pub fn enqueue_visible_chunks(
|
||||
return;
|
||||
};
|
||||
let cam_pos = cam_tf.translation();
|
||||
let centre = world_to_chunk(tree, cam_pos);
|
||||
let centre = tree.world_to_chunk(cam_pos);
|
||||
|
||||
if prev_cam.0 == Some(centre) {
|
||||
return;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user