use crate::plugins::big_space::big_space_plugin::RootGrid; use crate::plugins::environment::systems::voxels::meshing::mesh_chunk; use crate::plugins::environment::systems::voxels::structure::*; use crate::plugins::environment::systems::voxels::atlas::VoxelTextureAtlas; use bevy::pbr::wireframe::Wireframe; use bevy::prelude::*; use bevy::render::mesh::Mesh; use big_space::prelude::GridCell; use itertools::Itertools; use std::collections::HashMap; use std::fmt::format; /// rebuilds meshes only for chunks flagged dirty by the octree pub fn rebuild_dirty_chunks( mut commands: Commands, mut octrees: Query<&mut SparseVoxelOctree>, mut meshes: ResMut>, mut materials: ResMut>, chunk_q: Query<( Entity, &Chunk, &Mesh3d, &MeshMaterial3d, &ChunkLod, )>, mut spawned: ResMut, mut pool: ResMut, root: Res, atlas: Res, ) { // map ChunkKey → (entity, mesh-handle, material-handle) let existing: HashMap, Handle, u32)> = chunk_q .iter() .map(|(e, c, m, mat, lod)| (c.key, (e, m.0.clone(), mat.0.clone(), lod.0))) .collect(); for mut tree in &mut octrees { if tree.dirty_chunks.is_empty() { continue; } //------------------------------------------------ collect voxel data let mut bufs = Vec::new(); for key in tree.dirty_chunks.iter().copied() { let lod = existing.get(&key).map(|v| v.3).unwrap_or(0); let mut buf = [[[None; CHUNK_SIZE as usize]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize]; let half = tree.size * 0.5; let step = tree.get_spacing_at_depth(tree.max_depth); let origin = Vec3::new( key.0 as f32 * CHUNK_SIZE as f32 * step - half, key.1 as f32 * CHUNK_SIZE as f32 * step - half, key.2 as f32 * CHUNK_SIZE as f32 * step - half, ); let mult = 1 << lod; for gx in (0..CHUNK_SIZE).step_by(mult as usize) { for gy in (0..CHUNK_SIZE).step_by(mult as usize) { for gz in (0..CHUNK_SIZE).step_by(mult as usize) { let center = origin + Vec3::new( (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, lod)); } //------------------------------------------------ create / update for (key, buf, origin, step, lod) in bufs { if let Some((ent, mesh_h, _mat_h, _)) = existing.get(&key).cloned() { // update mesh in-place; keeps old asset id match mesh_chunk(&buf, origin, step, &tree, &mut pool, &atlas) { Some(new_mesh) => { if let Some(mesh) = meshes.get_mut(&mesh_h) { *mesh = new_mesh; } spawned.0.insert(key, ent); } None => { meshes.remove(&mesh_h); commands.entity(ent).despawn_recursive(); spawned.0.remove(&key); } } } else if let Some(mesh) = mesh_chunk(&buf, origin, step, &tree, &mut pool, &atlas) { // spawn brand-new chunk only if mesh has faces let mesh_h = meshes.add(mesh); let mat_h = materials.add(StandardMaterial { base_color_texture: Some(atlas.handle.clone()), ..Default::default() }); commands.entity(root.0).with_children(|p| { let e = p .spawn(( Mesh3d::from(mesh_h.clone()), MeshMaterial3d(mat_h.clone()), Transform::default(), GridCell::ZERO, Chunk { key, voxels: Vec::new(), dirty: false, }, ChunkLod(lod), /*Wireframe,*/ )) .id(); spawned.0.insert(key, e); }); } } tree.clear_dirty_flags(); } }