From f8732abb5d07f20b816fdad7c1af47ebb3e4ac83 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Thu, 16 Feb 2023 03:54:57 +0100 Subject: [PATCH] Placement queue, binary transparency, WIP trees --- kubi-shared/src/block.rs | 22 +++++++++++++++++++ kubi-shared/src/worldgen.rs | 42 ++++++++++++++++++++++++++++++------- kubi/src/block_placement.rs | 1 + kubi/src/world/loading.rs | 16 +++++++++++--- kubi/src/world/mesh.rs | 8 +++++-- kubi/src/world/tasks.rs | 19 ++++++++++++----- 6 files changed, 91 insertions(+), 17 deletions(-) diff --git a/kubi-shared/src/block.rs b/kubi-shared/src/block.rs index 7244b2c..dae5489 100644 --- a/kubi-shared/src/block.rs +++ b/kubi-shared/src/block.rs @@ -32,6 +32,9 @@ pub enum Block { Cobblestone, TallGrass, Planks, + Torch, + Wood, + Leaf, } impl Block { @@ -90,6 +93,24 @@ impl Block { collision: CollisionType::Solid, raycast_collision: true, }, + Self::Torch => BlockDescriptor { + name: "torch", + render: RenderType::CrossShape(CrossTexture::all(BlockTexture::Torch)), + collision: CollisionType::None, + raycast_collision: true, + }, + Self::Wood => BlockDescriptor { + name: "leaf", + render: RenderType::SolidBlock(CubeTexture::horizontal_vertical(BlockTexture::Wood, BlockTexture::WoodTop)), + collision: CollisionType::Solid, + raycast_collision: true, + }, + Self::Leaf => BlockDescriptor { + name: "leaf", + render: RenderType::BinaryTransparency(CubeTexture::all(BlockTexture::Leaf)), + collision: CollisionType::Solid, + raycast_collision: true, + }, } } } @@ -170,5 +191,6 @@ pub enum CollisionType { pub enum RenderType { None, SolidBlock(CubeTexture), + BinaryTransparency(CubeTexture), CrossShape(CrossTexture), } diff --git a/kubi-shared/src/worldgen.rs b/kubi-shared/src/worldgen.rs index 63a3dff..782ba91 100644 --- a/kubi-shared/src/worldgen.rs +++ b/kubi-shared/src/worldgen.rs @@ -29,10 +29,27 @@ fn local_y_position(height: i32, chunk_position: IVec3) -> Option { (0..CHUNK_SIZE as i32).contains(&position).then_some(position as usize) } -pub fn generate_world(chunk_position: IVec3, seed: u64) -> BlockData { +pub struct QueuedBlock { + pub position: IVec3, + pub block_type: Block, +} + +pub fn generate_world(chunk_position: IVec3, seed: u64) -> (BlockData, Vec) { let offset = chunk_position * CHUNK_SIZE as i32; let mut blocks = Box::new([[[Block::Air; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]); - + let mut queue = Vec::with_capacity(0); + + let mut smart_place = |blocks: &mut BlockData, position: IVec3, block: Block| { + if position.to_array().iter().any(|&x| !(0..CHUNK_SIZE).contains(&(x as usize))) { + queue.push(QueuedBlock { + position: offset + position, + block_type: block + }); + } else { + blocks[position.x as usize][position.y as usize][position.z as usize] = block; + } + }; + let mut height_noise = FastNoise::seeded(seed); height_noise.set_fractal_type(FractalType::FBM); height_noise.set_fractal_octaves(4); @@ -46,10 +63,10 @@ pub fn generate_world(chunk_position: IVec3, seed: u64) -> BlockData { let mut rng = Xoshiro256StarStar::seed_from_u64( seed ^ ((chunk_position.x as u32 as u64) << 0) - ^ ((chunk_position.y as u32 as u64) << 16) ^ ((chunk_position.z as u32 as u64) << 32) ); - let tall_grass_map: [[u8; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); + let rng_map_a: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); + let rng_map_b: [[f32; CHUNK_SIZE]; CHUNK_SIZE] = rng.gen(); // let mut cave_noise = FastNoise::seeded(seed.rotate_left(1)); // cave_noise.set_fractal_type(FractalType::FBM); @@ -86,11 +103,22 @@ pub fn generate_world(chunk_position: IVec3, seed: u64) -> BlockData { blocks[x][y][z] = Block::Grass; } //place tall grass - if tall_grass_map[x][z] < 10 { + if rng_map_a[x][z] < 0.03 { if let Some(y) = local_y_position(height + 1, chunk_position) { blocks[x][y][z] = Block::TallGrass; } } + //place trees! + if rng_map_a[x][z] < 0.001 { + if let Some(y) = local_y_position(height + 1, chunk_position) { + let tree_pos = ivec3(x as i32, y as i32, z as i32); + let tree_height = 6 + (rng_map_b[x][z] * 6.).round() as i32; + for tree_y in 0..tree_height { + smart_place(&mut blocks, tree_pos + IVec3::Y * tree_y, Block::Wood); + } + smart_place(&mut blocks, tree_pos + IVec3::Y * tree_height, Block::Leaf); + } + } } } @@ -111,8 +139,8 @@ pub fn generate_world(chunk_position: IVec3, seed: u64) -> BlockData { // } } - blocks - + (blocks, queue) + // let mut cave_noise = FastNoise::seeded(seed); // cave_noise.set_fractal_type(FractalType::FBM); // cave_noise.set_frequency(0.1); diff --git a/kubi/src/block_placement.rs b/kubi/src/block_placement.rs index 36605d2..7b2b93c 100644 --- a/kubi/src/block_placement.rs +++ b/kubi/src/block_placement.rs @@ -23,6 +23,7 @@ const BLOCK_KEY_MAP: &[(VirtualKeyCode, Block)] = &[ (VirtualKeyCode::Key4, Block::Grass), (VirtualKeyCode::Key5, Block::Sand), (VirtualKeyCode::Key6, Block::Stone), + (VirtualKeyCode::Key7, Block::Torch), ]; fn pick_block_with_number_keys( diff --git a/kubi/src/world/loading.rs b/kubi/src/world/loading.rs index 18c0172..8866058 100644 --- a/kubi/src/world/loading.rs +++ b/kubi/src/world/loading.rs @@ -11,7 +11,8 @@ use crate::{ use super::{ ChunkStorage, ChunkMeshStorage, chunk::{Chunk, DesiredChunkState, CHUNK_SIZE, ChunkMesh, CurrentChunkState, ChunkData}, - tasks::{ChunkTaskManager, ChunkTaskResponse, ChunkTask}, + tasks::{ChunkTaskManager, ChunkTaskResponse, ChunkTask}, + queue::{BlockUpdateQueue, BlockUpdateEvent}, }; const MAX_CHUNK_OPS_INGAME: usize = 6; @@ -170,12 +171,13 @@ fn process_completed_tasks( mut world: UniqueViewMut, mut meshes: NonSendSync>, renderer: NonSendSync>, - state: UniqueView + state: UniqueView, + mut queue: UniqueViewMut, ) { let mut ops: usize = 0; while let Some(res) = task_manager.receive() { match res { - ChunkTaskResponse::LoadedChunk { position, chunk_data } => { + ChunkTaskResponse::LoadedChunk { position, chunk_data, queued } => { //check if chunk exists let Some(chunk) = world.chunks.get_mut(&position) else { log::warn!("blocks data discarded: chunk doesn't exist"); @@ -196,6 +198,14 @@ fn process_completed_tasks( //update chunk state chunk.current_state = CurrentChunkState::Loaded; + //push queued blocks + for event in queued { + queue.push(BlockUpdateEvent { + position: event.position, + value: event.block_type, + }); + } + //increase ops counter ops += 1; }, diff --git a/kubi/src/world/mesh.rs b/kubi/src/world/mesh.rs index 4f81538..93cd762 100644 --- a/kubi/src/world/mesh.rs +++ b/kubi/src/world/mesh.rs @@ -39,12 +39,16 @@ pub fn generate_mesh(data: MeshGenData) -> (Vec, Vec) { let descriptor = block.descriptor(); match descriptor.render { RenderType::None => continue, - RenderType::SolidBlock(textures) => { + RenderType::SolidBlock(textures) | RenderType::BinaryTransparency(textures) => { for face in CubeFace::iter() { let facing_direction = face.normal(); let facing_coord = coord + facing_direction; let facing_descriptor = get_block(facing_coord).descriptor(); - let face_obstructed = matches!(facing_descriptor.render, RenderType::SolidBlock(_)); + let face_obstructed = match descriptor.render { + RenderType::SolidBlock(_) => matches!(facing_descriptor.render, RenderType::SolidBlock(_)), + RenderType::BinaryTransparency(_) => matches!(facing_descriptor.render, RenderType::SolidBlock(_) | RenderType::BinaryTransparency(_)), + _ => unreachable!(), + }; if !face_obstructed { let face_texture = match face { CubeFace::Top => textures.top, diff --git a/kubi/src/world/tasks.rs b/kubi/src/world/tasks.rs index 82da68a..60b39e0 100644 --- a/kubi/src/world/tasks.rs +++ b/kubi/src/world/tasks.rs @@ -1,6 +1,9 @@ use flume::{Sender, Receiver}; use glam::IVec3; -use kubi_shared::networking::messages::{ClientToServerMessage, ServerToClientMessage}; +use kubi_shared::{ + networking::messages::{ClientToServerMessage, ServerToClientMessage}, + worldgen::QueuedBlock +}; use shipyard::{Unique, UniqueView, View, IntoIter}; use rayon::{ThreadPool, ThreadPoolBuilder}; use super::{ @@ -8,7 +11,10 @@ use super::{ mesh::{generate_mesh, data::MeshGenData}, worldgen::generate_world, }; -use crate::{rendering::world::ChunkVertex, networking::{UdpClient, NetworkEvent}}; +use crate::{ + rendering::world::ChunkVertex, + networking::{UdpClient, NetworkEvent} +}; use kubi_udp::client::ClientEvent; pub enum ChunkTask { @@ -25,6 +31,7 @@ pub enum ChunkTaskResponse { LoadedChunk { position: IVec3, chunk_data: BlockData, + queued: Vec }, GeneratedMesh { position: IVec3, @@ -59,8 +66,8 @@ impl ChunkTaskManager { ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } }, ChunkTask::LoadChunk { position, seed } => { - let chunk_data = generate_world(position, seed); - ChunkTaskResponse::LoadedChunk { position, chunk_data } + let (chunk_data, queued) = generate_world(position, seed); + ChunkTaskResponse::LoadedChunk { position, chunk_data, queued } } }); }); @@ -97,7 +104,9 @@ pub fn inject_network_responses_into_manager_queue( if let ClientEvent::MessageReceived(ServerToClientMessage::ChunkResponse { chunk, data }) = &event.0 { let position = IVec3::from_array(*chunk); manager.add_sussy_response(ChunkTaskResponse::LoadedChunk { - position, chunk_data: data.clone() + position, + chunk_data: data.clone(), + queued: Vec::with_capacity(0) }); } }