parent
							
								
									958907228d
								
							
						
					
					
						commit
						5013ded7c9
					
				
				 7 changed files with 193 additions and 54 deletions
			
			
		@ -1,24 +1,10 @@ | 
				
			||||
use bevy::{platform::collections::HashMap, prelude::*}; | 
				
			||||
 | 
				
			||||
use super::{ | 
				
			||||
    math::{ChunkPos, USize3, CHUNK_LENGTH}, | 
				
			||||
    storage::PaletteBloxelStorage, | 
				
			||||
}; | 
				
			||||
use crate::bloxel::math::ChunkPos; | 
				
			||||
 | 
				
			||||
#[derive(Component, Default)] | 
				
			||||
pub struct Chunk; | 
				
			||||
 | 
				
			||||
#[derive(Component, Deref, DerefMut)] | 
				
			||||
#[require(Chunk)] | 
				
			||||
pub struct ChunkData(PaletteBloxelStorage<Entity>); | 
				
			||||
 | 
				
			||||
impl ChunkData { | 
				
			||||
    pub fn new(default: Entity) -> Self { | 
				
			||||
        let len = (CHUNK_LENGTH as u32).try_into().unwrap(); | 
				
			||||
        Self(PaletteBloxelStorage::new(USize3::splat(len), default)) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#[derive(Component, Default, Deref, DerefMut)] | 
				
			||||
#[require(Transform, Visibility)] | 
				
			||||
pub struct ChunkMap(HashMap<ChunkPos, Entity>); | 
				
			||||
 | 
				
			||||
@ -0,0 +1,90 @@ | 
				
			||||
use bevy::{platform::collections::HashMap, prelude::*}; | 
				
			||||
 | 
				
			||||
use crate::bloxel::prelude::*; | 
				
			||||
 | 
				
			||||
pub struct ChunkedBloxelView<'a, T: BloxelStore<Entity>> { | 
				
			||||
    region: BlockRegion, | 
				
			||||
    map: HashMap<ChunkPos, &'a T>, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<'a, T: BloxelStore<Entity>> ChunkedBloxelView<'a, T> { | 
				
			||||
    pub fn new(region: BlockRegion) -> Self { | 
				
			||||
        let map = HashMap::new(); | 
				
			||||
        Self { region, map } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn insert(&mut self, chunk_pos: ChunkPos, data: &'a T) { | 
				
			||||
        self.map.insert(chunk_pos, data); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn get(&self, pos: BlockPos) -> Option<Entity> { | 
				
			||||
        let (chunk_pos, rel_pos) = ChunkPos::from_block_pos(pos); | 
				
			||||
        self.map.get(&chunk_pos).and_then(|d| d.get(rel_pos)) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T: BloxelStore<Entity>> BloxelStore<Entity> for ChunkedBloxelView<'_, T> { | 
				
			||||
    fn size(&self) -> USize3 { | 
				
			||||
        self.region.size() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    fn get(&self, pos: IVec3) -> Option<Entity> { | 
				
			||||
        ChunkedBloxelView::get(self, self.region.min() + pos) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
pub struct ChunkedBloxelViewSingleMut<'a, T: BloxelStore<Entity>, M: BloxelStoreMut<Entity>> { | 
				
			||||
    region: BlockRegion, | 
				
			||||
    main: (ChunkPos, &'a mut M), | 
				
			||||
    map: HashMap<ChunkPos, &'a T>, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<'a, T: BloxelStore<Entity>, M: BloxelStoreMut<Entity>> ChunkedBloxelViewSingleMut<'a, T, M> { | 
				
			||||
    pub fn new(region: BlockRegion, main: (ChunkPos, &'a mut M)) -> Self { | 
				
			||||
        let map = HashMap::new(); | 
				
			||||
        Self { region, main, map } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn insert(&mut self, chunk_pos: ChunkPos, data: &'a T) { | 
				
			||||
        assert!(chunk_pos != self.main.0); | 
				
			||||
        self.map.insert(chunk_pos, data); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn get(&self, pos: BlockPos) -> Option<Entity> { | 
				
			||||
        let (chunk_pos, rel_pos) = ChunkPos::from_block_pos(pos); | 
				
			||||
        if chunk_pos == self.main.0 { | 
				
			||||
            self.main.1.get(rel_pos) | 
				
			||||
        } else { | 
				
			||||
            self.map.get(&chunk_pos).and_then(|d| d.get(rel_pos)) | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn set(&mut self, pos: BlockPos, value: Entity) -> Result<Entity, ()> { | 
				
			||||
        let (chunk_pos, rel_pos) = ChunkPos::from_block_pos(pos); | 
				
			||||
        if chunk_pos == self.main.0 { | 
				
			||||
            self.main.1.set(rel_pos, value) | 
				
			||||
        } else { | 
				
			||||
            Err(()) | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<'a, T: BloxelStore<Entity>, M: BloxelStoreMut<Entity>> BloxelStore<Entity> | 
				
			||||
    for ChunkedBloxelViewSingleMut<'a, T, M> | 
				
			||||
{ | 
				
			||||
    fn size(&self) -> USize3 { | 
				
			||||
        self.region.size() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    fn get(&self, pos: IVec3) -> Option<Entity> { | 
				
			||||
        ChunkedBloxelViewSingleMut::get(self, self.region.min() + pos) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<'a, T: BloxelStore<Entity>, M: BloxelStoreMut<Entity>> BloxelStoreMut<Entity> | 
				
			||||
    for ChunkedBloxelViewSingleMut<'a, T, M> | 
				
			||||
{ | 
				
			||||
    fn set(&mut self, pos: IVec3, value: Entity) -> Result<Entity, ()> { | 
				
			||||
        ChunkedBloxelViewSingleMut::set(self, self.region.min() + pos, value) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,10 +1,12 @@ | 
				
			||||
mod bloxel_array; | 
				
			||||
mod bloxel_store; | 
				
			||||
mod chunked_bloxel_view; | 
				
			||||
mod chunked_octree; | 
				
			||||
mod palette_bloxel_storage; | 
				
			||||
mod palette_storage; | 
				
			||||
 | 
				
			||||
pub use bloxel_array::*; | 
				
			||||
pub use bloxel_store::*; | 
				
			||||
pub use chunked_bloxel_view::*; | 
				
			||||
pub use chunked_octree::*; | 
				
			||||
pub use palette_bloxel_storage::*; | 
				
			||||
 | 
				
			||||
@ -0,0 +1,70 @@ | 
				
			||||
use std::ops::{Deref, DerefMut}; | 
				
			||||
 | 
				
			||||
use bevy::prelude::*; | 
				
			||||
use cart_prod::specs::Hom2FCartProd; | 
				
			||||
 | 
				
			||||
use crate::{ | 
				
			||||
    bloxel::{ | 
				
			||||
        prelude::*, | 
				
			||||
        storage::{ChunkedBloxelViewSingleMut, PaletteBloxelStorage}, | 
				
			||||
        worldgen::TerrainShape, | 
				
			||||
    }, | 
				
			||||
    TerrainBlocks, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
#[derive(Component, Deref, DerefMut)] | 
				
			||||
#[require(Chunk)] | 
				
			||||
pub struct TopLayer(PaletteBloxelStorage<Entity>); | 
				
			||||
 | 
				
			||||
pub fn generate_top_layer( | 
				
			||||
    mut commands: Commands, | 
				
			||||
    terrain_blocks: Option<Res<TerrainBlocks>>, | 
				
			||||
 | 
				
			||||
    mut chunks_without_top_layer: Query< | 
				
			||||
        (Entity, &ChunkPos, &TerrainShape), | 
				
			||||
        (With<Chunk>, Without<TopLayer>), | 
				
			||||
    >, | 
				
			||||
 | 
				
			||||
    chunk_map: Single<&ChunkMap>, | 
				
			||||
    chunks_with_shape: Query<&TerrainShape>, | 
				
			||||
) { | 
				
			||||
    let Some(terrain) = terrain_blocks else { | 
				
			||||
        return; | 
				
			||||
    }; | 
				
			||||
 | 
				
			||||
    for (entity, chunk_pos, shape) in chunks_without_top_layer.iter_mut() { | 
				
			||||
        let above_pos = chunk_pos + Neighbor::Top; | 
				
			||||
        let Some(above_entity) = chunk_map.get(&above_pos).copied() else { | 
				
			||||
            continue; | 
				
			||||
        }; | 
				
			||||
        let Ok(above_shape) = chunks_with_shape.get(above_entity) else { | 
				
			||||
            continue; | 
				
			||||
        }; | 
				
			||||
 | 
				
			||||
        let region = BlockRegion::new_unchecked( | 
				
			||||
            chunk_pos.to_block_pos(IVec3::ZERO), | 
				
			||||
            above_pos.to_block_pos(CHUNK_MAX), | 
				
			||||
        ); | 
				
			||||
        let mut data = TopLayer((*shape.deref()).clone()); | 
				
			||||
        let mut chunks = ChunkedBloxelViewSingleMut::new(region, (*chunk_pos, data.deref_mut())); | 
				
			||||
        chunks.insert(above_pos, above_shape.deref()); | 
				
			||||
 | 
				
			||||
        for [x, z] in Hom2FCartProd::new(0..CHUNK_LENGTH as i32, 0..CHUNK_LENGTH as i32) { | 
				
			||||
            let mut air = CHUNK_LENGTH; // Some large value.
 | 
				
			||||
            for y in (0..20).rev() { | 
				
			||||
                let pos = chunk_pos.to_block_pos(IVec3::new(x, y, z)); | 
				
			||||
                let existing = chunks.get(pos).unwrap(); | 
				
			||||
                if existing == terrain.air { | 
				
			||||
                    air = 0; | 
				
			||||
                } else if y < CHUNK_LENGTH as i32 && air <= 4 { | 
				
			||||
                    let new = (air == 1).then_some(terrain.grass).unwrap_or(terrain.dirt); | 
				
			||||
                    chunks.set(pos, new).unwrap(); | 
				
			||||
                } | 
				
			||||
                air += 1; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        drop(chunks); // Unsure why this is required.
 | 
				
			||||
        commands.entity(entity).insert(data); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue