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