From aaab14f0f47669933ada65bcf0f3ca6bf33e03ad Mon Sep 17 00:00:00 2001
From: copygirl <copygirl@mcft.net>
Date: Wed, 26 Mar 2025 19:11:12 +0100
Subject: [PATCH] Add destroy_chunks_away_from_camera system

---
 src/bloxel/worldgen/mod.rs | 46 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/src/bloxel/worldgen/mod.rs b/src/bloxel/worldgen/mod.rs
index 895beb7..a9a14bf 100644
--- a/src/bloxel/worldgen/mod.rs
+++ b/src/bloxel/worldgen/mod.rs
@@ -14,8 +14,14 @@ pub struct WorldGenPlugin;
 
 impl Plugin for WorldGenPlugin {
     fn build(&self, app: &mut App) {
-        app.init_resource::<ExistingChunks>()
-            .add_systems(Update, (create_chunks_around_camera, generate_terrain));
+        app.init_resource::<ExistingChunks>().add_systems(
+            Update,
+            (
+                create_chunks_around_camera,
+                destroy_chunks_away_from_camera,
+                generate_terrain,
+            ),
+        );
     }
 }
 
@@ -60,6 +66,42 @@ fn create_chunks_around_camera(
     }
 }
 
+fn destroy_chunks_away_from_camera(
+    mut commands: Commands,
+    mut octree: ResMut<ExistingChunks>,
+    camera: Single<&Transform, With<ControlledCamera>>,
+    chunk_map: Single<&ChunkMap>,
+) {
+    let block_pos = camera.translation.as_ivec3().into();
+    let (chunk_pos, _) = ChunkPos::from_block_pos(block_pos);
+
+    let distance = |node: OctreeNode| -> i32 {
+        let (min, max) = node.region().into();
+        let a = (chunk_pos - min).length_squared();
+        let b = (chunk_pos - max).length_squared();
+        a.max(b)
+    };
+
+    let to_destroy = octree
+        .find(|node, exist| (exist != Existing::None).then(|| distance(node)))
+        .take_while(|(_, _, neg_sqr_distance)| *neg_sqr_distance > 16 * 16)
+        .map(|(chunk_pos, _, _)| chunk_pos)
+        .collect::<Vec<_>>();
+
+    for chunk_pos in to_destroy {
+        let chunk = *chunk_map.get(&chunk_pos).unwrap();
+        commands.entity(chunk).despawn_recursive();
+        octree.update(chunk_pos, |_, children, parent| {
+            let children = children.map(|a| a.as_slice()).unwrap_or_default();
+            *parent = if children.iter().all(|c| *c == Existing::None) {
+                Existing::None
+            } else {
+                Existing::Some
+            }
+        });
+    }
+}
+
 fn generate_terrain(
     mut commands: Commands,
     terrain_blocks: Option<Res<TerrainBlocks>>,