parent
							
								
									37c83ffb7f
								
							
						
					
					
						commit
						c3077d3ddf
					
				
				 3 changed files with 160 additions and 148 deletions
			
			
		@ -0,0 +1,112 @@ | 
				
			|||||||
 | 
					use bevy::prelude::*; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{ | 
				
			||||||
 | 
					    bloxel::{ | 
				
			||||||
 | 
					        prelude::*, | 
				
			||||||
 | 
					        storage::{ChunkedOctree, OctreeNode}, | 
				
			||||||
 | 
					    }, | 
				
			||||||
 | 
					    camera_controller::ControlledCamera, | 
				
			||||||
 | 
					}; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const LOAD_DISTANCE: usize = 12; | 
				
			||||||
 | 
					const UNLOAD_DISTANCE: usize = 16; | 
				
			||||||
 | 
					const CHUNKS_PER_ITERATION: usize = 12; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn create_chunks_around_camera( | 
				
			||||||
 | 
					    mut commands: Commands, | 
				
			||||||
 | 
					    mut octree: ResMut<ExistingChunks>, | 
				
			||||||
 | 
					    camera: Single<&Transform, With<ControlledCamera>>, | 
				
			||||||
 | 
					    chunk_map: Single<Entity, With<ChunkMap>>, | 
				
			||||||
 | 
					) { | 
				
			||||||
 | 
					    let block_pos = camera.translation.as_ivec3().into(); | 
				
			||||||
 | 
					    let (chunk_pos, _) = ChunkPos::from_block_pos(block_pos); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut create_chunk = |octree: &mut ExistingChunks, pos: ChunkPos| { | 
				
			||||||
 | 
					        commands.entity(*chunk_map).with_child((Chunk, pos)); | 
				
			||||||
 | 
					        octree.update(pos, |_, children, parent| { | 
				
			||||||
 | 
					            let children = children.map(|a| a.as_slice()).unwrap_or_default(); | 
				
			||||||
 | 
					            *parent = if children.iter().all(|c| *c == Existing::All) { | 
				
			||||||
 | 
					                Existing::All | 
				
			||||||
 | 
					            } else { | 
				
			||||||
 | 
					                Existing::Some | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					    }; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create chunk at camera's position, if it's not already. This is necessary because we
 | 
				
			||||||
 | 
					    // need to "seed" the octree with a region so we have something to begin the search from.
 | 
				
			||||||
 | 
					    if octree.get(chunk_pos) == Existing::None { | 
				
			||||||
 | 
					        create_chunk(&mut octree, chunk_pos); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let sqr_load_distance = (LOAD_DISTANCE * LOAD_DISTANCE) as i32; | 
				
			||||||
 | 
					    let to_create = octree | 
				
			||||||
 | 
					        .find(|node, exist| { | 
				
			||||||
 | 
					            (exist != Existing::All).then(|| -node.region().distance_to_squared(chunk_pos)) | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					        .take_while(|(_, _, neg_sqr_distance)| *neg_sqr_distance > -sqr_load_distance) | 
				
			||||||
 | 
					        .map(|(chunk_pos, _, _)| chunk_pos) | 
				
			||||||
 | 
					        .take(CHUNKS_PER_ITERATION) // Create up to this many chunks per system iteration.
 | 
				
			||||||
 | 
					        .collect::<Vec<_>>(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for chunk_pos in to_create { | 
				
			||||||
 | 
					        create_chunk(&mut octree, chunk_pos); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub 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 sqr_unload_distance = (UNLOAD_DISTANCE * UNLOAD_DISTANCE) as i32; | 
				
			||||||
 | 
					    let to_destroy = octree | 
				
			||||||
 | 
					        .find(|node, exist| (exist != Existing::None).then(|| distance(node))) | 
				
			||||||
 | 
					        .take_while(|(_, _, sqr_distance)| *sqr_distance > sqr_unload_distance) | 
				
			||||||
 | 
					        .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(); | 
				
			||||||
 | 
					        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 | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Resource, Deref, DerefMut)] | 
				
			||||||
 | 
					pub struct ExistingChunks { | 
				
			||||||
 | 
					    octree: ChunkedOctree<Existing>, | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for ExistingChunks { | 
				
			||||||
 | 
					    fn default() -> Self { | 
				
			||||||
 | 
					        let octree = ChunkedOctree::new(5); | 
				
			||||||
 | 
					        Self { octree } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug)] | 
				
			||||||
 | 
					pub enum Existing { | 
				
			||||||
 | 
					    #[default] | 
				
			||||||
 | 
					    None, | 
				
			||||||
 | 
					    Some, | 
				
			||||||
 | 
					    All, | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,40 @@ | 
				
			|||||||
 | 
					use bevy::prelude::*; | 
				
			||||||
 | 
					use noise_functions::{Noise, Simplex}; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{bloxel::prelude::*, TerrainBlocks}; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn generate_terrain_shape( | 
				
			||||||
 | 
					    mut commands: Commands, | 
				
			||||||
 | 
					    terrain_blocks: Option<Res<TerrainBlocks>>, | 
				
			||||||
 | 
					    chunks_without_data: Query<(Entity, &ChunkPos), (With<Chunk>, Without<ChunkData>)>, | 
				
			||||||
 | 
					) { | 
				
			||||||
 | 
					    let Some(terrain) = terrain_blocks else { | 
				
			||||||
 | 
					        return; | 
				
			||||||
 | 
					    }; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let slices = [terrain.rock, terrain.dirt, terrain.grass, terrain.sand]; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (entity, chunk_pos) in chunks_without_data.iter() { | 
				
			||||||
 | 
					        let mut data = ChunkData::new(terrain.air); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data.update(|relative, _| { | 
				
			||||||
 | 
					            let block_pos = chunk_pos.to_block_pos(relative); | 
				
			||||||
 | 
					            let float_pos = IVec3::from(block_pos).as_vec3(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let bias = ((float_pos.y + 32.) / 64.).clamp(-0.25, 1.); | 
				
			||||||
 | 
					            let sample = Simplex | 
				
			||||||
 | 
					                .fbm(3, 0.65, 2.0) | 
				
			||||||
 | 
					                .weighted(0.4) | 
				
			||||||
 | 
					                .frequency(0.01) | 
				
			||||||
 | 
					                .sample3(float_pos); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if sample > bias { | 
				
			||||||
 | 
					                slices[relative.y as usize % slices.len()] | 
				
			||||||
 | 
					            } else { | 
				
			||||||
 | 
					                terrain.air | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        commands.entity(entity).insert(data); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue