From 8228442387678acb41f13e9e4f3d3c7917176792 Mon Sep 17 00:00:00 2001 From: Daniel Bengl Date: Fri, 7 Mar 2025 20:21:29 +0100 Subject: [PATCH 01/45] Add tallgrass texture/block definition --- src/client/terrain/util/blocks.rs | 6 ++++-- src/server/terrain/resources.rs | 8 ++++++++ src/server/terrain/util/generator.rs | 16 ++++++++++++++++ src/shared/blocks.rs | 3 ++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/client/terrain/util/blocks.rs b/src/client/terrain/util/blocks.rs index 65699f2..09b2b6b 100644 --- a/src/client/terrain/util/blocks.rs +++ b/src/client/terrain/util/blocks.rs @@ -15,6 +15,7 @@ pub enum TextureName { OakLeaves, OakLogTop, OakLogSide, + Tallgrass } use TextureName::*; @@ -55,7 +56,7 @@ impl TextureManager { [Stone, CobbleStone, GrassTop, OakLeaves], [IronOre, Sand, GrassSide, OakLogTop], [CoalOre, Bedrock, Dirt, OakLogSide], - [Air, Air, Air, Air], + [Tallgrass, Air, Air, Air], ]; let mut texture_positions = Vec::new(); @@ -93,7 +94,7 @@ macro_rules! add_block { }; } -pub static BLOCKS: [Block; 10] = [ +pub static BLOCKS: [Block; 11] = [ add_block!(BlockId::Air, [TextureName::Air; 6], false), add_block!( BlockId::Grass, @@ -126,6 +127,7 @@ pub static BLOCKS: [Block; 10] = [ ], true ), + add_block!(BlockId::Tallgrass, [TextureName::Tallgrass; 6], true) ]; type TextureUV = [f32; 2]; diff --git a/src/server/terrain/resources.rs b/src/server/terrain/resources.rs index bd2df78..bb125ca 100644 --- a/src/server/terrain/resources.rs +++ b/src/server/terrain/resources.rs @@ -49,6 +49,10 @@ pub struct HeightAdjustParams { pub noise: NoiseFunctionParams, } +pub struct GrassParams { + pub spawn_attempts_per_chunk: u32, +} + #[derive(Debug)] pub struct NoiseFunctionParams { pub octaves: u32, @@ -79,6 +83,7 @@ pub struct TerrainGeneratorParams { pub density: DensityParams, pub cave: CaveParams, pub tree: TreeParams, + pub grass: GrassParams, } impl Default for TerrainGeneratorParams { @@ -142,6 +147,9 @@ impl Default for TerrainGeneratorParams { min_bush_radius: 3, max_bush_radius: 5, }, + grass: GrassParams { + spawn_attempts_per_chunk: 500 + } } } } diff --git a/src/server/terrain/util/generator.rs b/src/server/terrain/util/generator.rs index 58f9fb7..45ea605 100644 --- a/src/server/terrain/util/generator.rs +++ b/src/server/terrain/util/generator.rs @@ -1,4 +1,5 @@ use terrain_resources::{Generator, NoiseFunctionParams, TerrainGeneratorParams}; +use terrain_util::Block; use crate::prelude::*; @@ -66,6 +67,21 @@ impl Generator { for _ in 0..self.params.tree.spawn_attempts_per_chunk { self.attempt_spawn_tree(chunk); } + + for _ in 0..self.params.grass.spawn_attempts_per_chunk { + self.attempt_spawn_grass(chunk); + } + } + + fn attempt_spawn_grass(&self, chunk: &mut Chunk) { + let chunk_range = 0..CHUNK_SIZE; + let x = rand::random_range(chunk_range.clone()); + let y = rand::random_range(0..(CHUNK_SIZE - 1)); + let z = rand::random_range(chunk_range.clone()); + + if chunk.get(x,y,z) == BlockId::Grass { + chunk.set(x,y + 1, z, BlockId::Tallgrass); + } } fn attempt_spawn_tree(&self, chunk: &mut Chunk) { diff --git a/src/shared/blocks.rs b/src/shared/blocks.rs index 457e18b..0619cc5 100644 --- a/src/shared/blocks.rs +++ b/src/shared/blocks.rs @@ -34,6 +34,7 @@ enum_from_u8! { IronOre, CoalOre, OakLeaves, - OakLog + OakLog, + Tallgrass } } From a8ef0c78914ba6dc1f6e974da55bb6ab93674d7d Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Fri, 7 Mar 2025 22:13:15 +0100 Subject: [PATCH 02/45] Refactor shared blocks (break serialization) --- src/shared/blocks.rs | 66 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/shared/blocks.rs b/src/shared/blocks.rs index 0619cc5..35602f1 100644 --- a/src/shared/blocks.rs +++ b/src/shared/blocks.rs @@ -1,40 +1,34 @@ -use serde::{Deserialize, Serialize}; - -macro_rules! enum_from_u8 { - ($name:ident { $( $variant:ident ),* $(,)? }) => { - #[repr(u8)] - #[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)] - pub enum $name { - $( $variant ),* - } - - impl $name { - pub fn from_u8(value: u8) -> Option<$name> { - match value { - $(x if x == $name::$variant as u8 => Some($name::$variant),)* - _ => None, - } - } - - pub fn to_u8(&self) -> u8 { - self.clone() as u8 - } - } - }; +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum BlockId { + Air = 0, + Grass = 1, + Dirt = 2, + Stone = 3, + CobbleStone = 4, + Bedrock = 5, + IronOre = 6, + CoalOre = 7, + OakLeaves = 8, + OakLog = 9, + Tallgrass = 10 } -enum_from_u8! { - BlockId { - Air, - Grass, - Dirt, - Stone, - CobbleStone, - Bedrock, - IronOre, - CoalOre, - OakLeaves, - OakLog, - Tallgrass +impl BlockId { + pub fn from_u8(index: u8) -> BlockId { + use BlockId::*; + match index { + 0 => Air, + 1 => Grass, + 2 => Dirt, + 3 => Stone, + 4 => CobbleStone, + 5 => Bedrock, + 6 => IronOre, + 7 => CoalOre, + 8 => OakLeaves, + 9 => OakLog, + 10 => Tallgrass, + _ => panic!("Invalid block id") + } } } From 2d9fbfb7b403a4ae19a96559daa53cc30733b5e8 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Fri, 7 Mar 2025 22:13:22 +0100 Subject: [PATCH 03/45] Update grass spawn rate --- src/server/terrain/resources.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/terrain/resources.rs b/src/server/terrain/resources.rs index bb125ca..118f25d 100644 --- a/src/server/terrain/resources.rs +++ b/src/server/terrain/resources.rs @@ -148,7 +148,7 @@ impl Default for TerrainGeneratorParams { max_bush_radius: 5, }, grass: GrassParams { - spawn_attempts_per_chunk: 500 + spawn_attempts_per_chunk: 1200 } } } From 49784b80e6b81dcebfc5c402abca0c0751c306fa Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Fri, 7 Mar 2025 22:13:36 +0100 Subject: [PATCH 04/45] Update client side block data representation --- src/client/terrain/util/blocks.rs | 91 ++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/src/client/terrain/util/blocks.rs b/src/client/terrain/util/blocks.rs index 09b2b6b..1a2a429 100644 --- a/src/client/terrain/util/blocks.rs +++ b/src/client/terrain/util/blocks.rs @@ -18,6 +18,53 @@ pub enum TextureName { Tallgrass } +pub enum MeshRepresentation { + None, + Cube([TextureName; 6]), + Cross([TextureName; 2]) +} + +pub struct BlockProperties { + has_collider: bool, + mesh_representation: MeshRepresentation +} + +impl BlockProperties { + pub fn new(has_collider: bool, mesh_representation: MeshRepresentation) { + BlockProperties {has_collider, mesh_representation} + } +} + +impl BlockId { + use MeshRepresentation::*; + + pub fn block_properties(&self) -> BlockProperties { + let touple = match self { + BlockId::Air => (true, None()), + BlockId::Grass => (true, Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide])), + BlockId::Dirt => (true, Cube([Dirt; 6])), + BlockId::Stone => (true, Cube([Stone; 6])), + BlockId::CobbleStone => (true, Cube([CobbleStone; 6])), + BlockId::Bedrock => (true, Cube([Bedrock; 6])), + BlockId::IronOre => (true, Cube([IronOre; 6])), + BlockId::CoalOre => (true, Cube([CoalOre; 6])), + BlockId::OakLeaves => (true, Cube([OakLeaves; 6])), + BlockId::OakLog => (true, Cube([OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide])), + BlockId::Tallgrass => (true, Cross([Tallgrass, Tallgrass])), + }; + + BlockProperties { + has_collider: touple.0, + mesh_representation: touple.1 + } + } + + pub fn collect_all_texture_names() { + // TODO: implement + } +} + +use bevy_inspector_egui::egui::panel::TopBottomSide; use TextureName::*; #[derive(Resource)] @@ -95,39 +142,17 @@ macro_rules! add_block { } pub static BLOCKS: [Block; 11] = [ - add_block!(BlockId::Air, [TextureName::Air; 6], false), - add_block!( - BlockId::Grass, - [ - TextureName::GrassTop, - TextureName::Dirt, - TextureName::GrassSide, - TextureName::GrassSide, - TextureName::GrassSide, - TextureName::GrassSide, - ], - true - ), - add_block!(BlockId::Dirt, [TextureName::Dirt; 6], true), - add_block!(BlockId::Stone, [TextureName::Stone; 6], true), - add_block!(BlockId::CobbleStone, [TextureName::CobbleStone; 6], true), - add_block!(BlockId::Bedrock, [TextureName::Bedrock; 6], true), - add_block!(BlockId::IronOre, [TextureName::IronOre; 6], true), - add_block!(BlockId::CoalOre, [TextureName::CoalOre; 6], true), - add_block!(BlockId::OakLeaves, [TextureName::OakLeaves; 6], true), - add_block!( - BlockId::OakLog, - [ - TextureName::OakLogTop, - TextureName::OakLogTop, - TextureName::OakLogSide, - TextureName::OakLogSide, - TextureName::OakLogSide, - TextureName::OakLogSide, - ], - true - ), - add_block!(BlockId::Tallgrass, [TextureName::Tallgrass; 6], true) + (Air, [Air; 6], false), + (Grass, [GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide], true), + (Dirt, [Dirt; 6], true), + (Stone, [Stone; 6], true), + (CobbleStone, [CobbleStone; 6], true), + (Bedrock, [Bedrock; 6], true), + (IronOre, [IronOre; 6], true), + (CoalOre, [CoalOre; 6], true), + (OakLeaves, [OakLeaves; 6], true), + (OakLog, [OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide], true), + (Tallgrass, [Tallgrass; 6], true) ]; type TextureUV = [f32; 2]; From 677ba20aaeff18e8c108f2a7a7614f0774d6ed9a Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 13:06:12 +0100 Subject: [PATCH 05/45] Update client block represenation --- src/client/terrain/util/blocks.rs | 122 ++++++++++++++++-------------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/src/client/terrain/util/blocks.rs b/src/client/terrain/util/blocks.rs index 1a2a429..957884d 100644 --- a/src/client/terrain/util/blocks.rs +++ b/src/client/terrain/util/blocks.rs @@ -18,39 +18,45 @@ pub enum TextureName { Tallgrass } -pub enum MeshRepresentation { - None, - Cube([TextureName; 6]), - Cross([TextureName; 2]) -} +mod client_block { + use rsmc::BlockId; -pub struct BlockProperties { - has_collider: bool, - mesh_representation: MeshRepresentation -} + use super::TextureName; + use super::TextureName::*; + use BlockId::*; -impl BlockProperties { - pub fn new(has_collider: bool, mesh_representation: MeshRepresentation) { - BlockProperties {has_collider, mesh_representation} - } -} + pub enum MeshRepresentation { + None, + Cube([TextureName; 6]), + Cross([TextureName; 2]) + } -impl BlockId { use MeshRepresentation::*; - pub fn block_properties(&self) -> BlockProperties { + pub struct BlockProperties { + pub has_collider: bool, + pub mesh_representation: MeshRepresentation + } + + impl BlockProperties { + pub fn new(has_collider: bool, mesh_representation: MeshRepresentation) { + BlockProperties {has_collider, mesh_representation} + } + } + + pub fn block_properties(block_id: BlockId) -> BlockProperties { let touple = match self { - BlockId::Air => (true, None()), - BlockId::Grass => (true, Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide])), - BlockId::Dirt => (true, Cube([Dirt; 6])), - BlockId::Stone => (true, Cube([Stone; 6])), - BlockId::CobbleStone => (true, Cube([CobbleStone; 6])), - BlockId::Bedrock => (true, Cube([Bedrock; 6])), - BlockId::IronOre => (true, Cube([IronOre; 6])), - BlockId::CoalOre => (true, Cube([CoalOre; 6])), - BlockId::OakLeaves => (true, Cube([OakLeaves; 6])), - BlockId::OakLog => (true, Cube([OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide])), - BlockId::Tallgrass => (true, Cross([Tallgrass, Tallgrass])), + Air => (true, None()), + Grass => (true, Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide])), + Dirt => (true, Cube([Dirt; 6])), + Stone => (true, Cube([Stone; 6])), + CobbleStone => (true, Cube([CobbleStone; 6])), + Bedrock => (true, Cube([Bedrock; 6])), + IronOre => (true, Cube([IronOre; 6])), + CoalOre => (true, Cube([CoalOre; 6])), + OakLeaves => (true, Cube([OakLeaves; 6])), + OakLog => (true, Cube([OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide])), + Tallgrass => (true, Cross([Tallgrass, Tallgrass])), }; BlockProperties { @@ -59,12 +65,22 @@ impl BlockId { } } - pub fn collect_all_texture_names() { - // TODO: implement + pub fn collect_all_texture_names() -> Vec { + BlockId::values().iter().map(|block_id| { + let properties = block_properties(block_id); + let mesh: MeshRepresentation = properties.mesh_representation; + + match mesh { + MeshRepresentation::None => vec![], + MeshRepresentation::Cube(textures) => Vec::from(textures), + MeshRepresentation::Cross(textures) => Vec::from(textures), + } + }).flatten() } } use bevy_inspector_egui::egui::panel::TopBottomSide; +use client_block::block_properties; use TextureName::*; #[derive(Resource)] @@ -106,18 +122,18 @@ impl TextureManager { [Tallgrass, Air, Air, Air], ]; - let mut texture_positions = Vec::new(); + let mut texture_positions = Vec::new(); - for x in 0..ATLAS_WIDTH { - for y in 0..ATLAS_HEIGHT { - texture_positions.push(( - *textures.get(y).unwrap().get(x).unwrap(), - (1.0 / 4.0 * (x as f32), 1.0 / 4.0 * (y as f32)), - )) + for x in 0..ATLAS_WIDTH { + for y in 0..ATLAS_HEIGHT { + texture_positions.push(( + *textures.get(y).unwrap().get(x).unwrap(), + (1.0 / 4.0 * (x as f32), 1.0 / 4.0 * (y as f32)), + )) + } } - } - texture_positions + texture_positions } pub fn get_texture_uv(&self, name: TextureName) -> Option<&TextureUV> { @@ -141,20 +157,6 @@ macro_rules! add_block { }; } -pub static BLOCKS: [Block; 11] = [ - (Air, [Air; 6], false), - (Grass, [GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide], true), - (Dirt, [Dirt; 6], true), - (Stone, [Stone; 6], true), - (CobbleStone, [CobbleStone; 6], true), - (Bedrock, [Bedrock; 6], true), - (IronOre, [IronOre; 6], true), - (CoalOre, [CoalOre; 6], true), - (OakLeaves, [OakLeaves; 6], true), - (OakLog, [OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide], true), - (Tallgrass, [Tallgrass; 6], true) -]; - type TextureUV = [f32; 2]; impl Block { @@ -163,8 +165,18 @@ impl Block { face: CubeFace, texture_manager: &TextureManager, ) -> Option<[f32; 2]> { - let block = &BLOCKS[block_id as usize]; - let texture_name = block.texture_names[face as usize]; - texture_manager.get_texture_uv(texture_name).copied() + let properties = block_properties(block_id); + let mesh = properties.mesh_representation; + + let texture_option: Option = match mesh { + client_block::MeshRepresentation::None => None(), + client_block::MeshRepresentation::Cube(textures) => Some(textures[face as usize]), + client_block::MeshRepresentation::Cross(textures) => Some(textures[face as usize]) + }; + + match texture_option { + Some(texture_name) => Some(texture_manager.get_texture_uv(texture_name).copied()), + None() => None() + } } } From fb45b6f867138c9251197ff92a355a878dd00a51 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 13:25:35 +0100 Subject: [PATCH 06/45] Make code compile again --- src/client/terrain/util/blocks.rs | 56 ++++++++-------------- src/server/terrain/resources.rs | 4 +- src/server/terrain/util/generator.rs | 4 +- src/shared/blocks.rs | 72 +++++++++++++++++++++------- src/shared/chunk_serializer.rs | 8 +++- 5 files changed, 84 insertions(+), 60 deletions(-) diff --git a/src/client/terrain/util/blocks.rs b/src/client/terrain/util/blocks.rs index 957884d..fc06f35 100644 --- a/src/client/terrain/util/blocks.rs +++ b/src/client/terrain/util/blocks.rs @@ -20,10 +20,7 @@ pub enum TextureName { mod client_block { use rsmc::BlockId; - use super::TextureName; - use super::TextureName::*; - use BlockId::*; pub enum MeshRepresentation { None, @@ -38,25 +35,21 @@ mod client_block { pub mesh_representation: MeshRepresentation } - impl BlockProperties { - pub fn new(has_collider: bool, mesh_representation: MeshRepresentation) { - BlockProperties {has_collider, mesh_representation} - } - } - pub fn block_properties(block_id: BlockId) -> BlockProperties { - let touple = match self { - Air => (true, None()), - Grass => (true, Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide])), - Dirt => (true, Cube([Dirt; 6])), - Stone => (true, Cube([Stone; 6])), - CobbleStone => (true, Cube([CobbleStone; 6])), - Bedrock => (true, Cube([Bedrock; 6])), - IronOre => (true, Cube([IronOre; 6])), - CoalOre => (true, Cube([CoalOre; 6])), - OakLeaves => (true, Cube([OakLeaves; 6])), - OakLog => (true, Cube([OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide])), - Tallgrass => (true, Cross([Tallgrass, Tallgrass])), + use TextureName::*; + + let touple = match block_id { + BlockId::Air => (true, None), + BlockId::Grass => (true, Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide])), + BlockId::Dirt => (true, Cube([Dirt; 6])), + BlockId::Stone => (true, Cube([Stone; 6])), + BlockId::CobbleStone => (true, Cube([CobbleStone; 6])), + BlockId::Bedrock => (true, Cube([Bedrock; 6])), + BlockId::IronOre => (true, Cube([IronOre; 6])), + BlockId::CoalOre => (true, Cube([CoalOre; 6])), + BlockId::OakLeaves => (true, Cube([OakLeaves; 6])), + BlockId::OakLog => (true, Cube([OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide])), + BlockId::Tallgrass => (true, Cross([Tallgrass, Tallgrass])), }; BlockProperties { @@ -67,7 +60,7 @@ mod client_block { pub fn collect_all_texture_names() -> Vec { BlockId::values().iter().map(|block_id| { - let properties = block_properties(block_id); + let properties = block_properties(*block_id); let mesh: MeshRepresentation = properties.mesh_representation; match mesh { @@ -75,11 +68,10 @@ mod client_block { MeshRepresentation::Cube(textures) => Vec::from(textures), MeshRepresentation::Cross(textures) => Vec::from(textures), } - }).flatten() + }).flatten().collect() } } -use bevy_inspector_egui::egui::panel::TopBottomSide; use client_block::block_properties; use TextureName::*; @@ -147,16 +139,6 @@ pub struct Block { pub is_solid: bool, } -macro_rules! add_block { - ($block_id:expr, $texture_names:expr, $is_solid:expr) => { - Block { - id: $block_id, - texture_names: $texture_names, - is_solid: $is_solid, - } - }; -} - type TextureUV = [f32; 2]; impl Block { @@ -169,14 +151,14 @@ impl Block { let mesh = properties.mesh_representation; let texture_option: Option = match mesh { - client_block::MeshRepresentation::None => None(), + client_block::MeshRepresentation::None => None, client_block::MeshRepresentation::Cube(textures) => Some(textures[face as usize]), client_block::MeshRepresentation::Cross(textures) => Some(textures[face as usize]) }; match texture_option { - Some(texture_name) => Some(texture_manager.get_texture_uv(texture_name).copied()), - None() => None() + Some(texture_name) => texture_manager.get_texture_uv(texture_name).copied(), + None => None } } } diff --git a/src/server/terrain/resources.rs b/src/server/terrain/resources.rs index 118f25d..8350e4e 100644 --- a/src/server/terrain/resources.rs +++ b/src/server/terrain/resources.rs @@ -148,8 +148,8 @@ impl Default for TerrainGeneratorParams { max_bush_radius: 5, }, grass: GrassParams { - spawn_attempts_per_chunk: 1200 - } + spawn_attempts_per_chunk: 1200, + }, } } } diff --git a/src/server/terrain/util/generator.rs b/src/server/terrain/util/generator.rs index 45ea605..3472f4e 100644 --- a/src/server/terrain/util/generator.rs +++ b/src/server/terrain/util/generator.rs @@ -79,8 +79,8 @@ impl Generator { let y = rand::random_range(0..(CHUNK_SIZE - 1)); let z = rand::random_range(chunk_range.clone()); - if chunk.get(x,y,z) == BlockId::Grass { - chunk.set(x,y + 1, z, BlockId::Tallgrass); + if chunk.get(x, y, z) == BlockId::Grass { + chunk.set(x, y + 1, z, BlockId::Tallgrass); } } diff --git a/src/shared/blocks.rs b/src/shared/blocks.rs index 35602f1..63fb97f 100644 --- a/src/shared/blocks.rs +++ b/src/shared/blocks.rs @@ -1,22 +1,24 @@ -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] pub enum BlockId { - Air = 0, - Grass = 1, - Dirt = 2, - Stone = 3, - CobbleStone = 4, - Bedrock = 5, - IronOre = 6, - CoalOre = 7, - OakLeaves = 8, - OakLog = 9, - Tallgrass = 10 + Air, + Grass, + Dirt, + Stone, + CobbleStone, + Bedrock, + IronOre, + CoalOre, + OakLeaves, + OakLog, + Tallgrass, } -impl BlockId { - pub fn from_u8(index: u8) -> BlockId { - use BlockId::*; - match index { +use serde::{Deserialize, Serialize}; +use BlockId::*; + +impl From for BlockId { + fn from(value: u8) -> Self { + match value { 0 => Air, 1 => Grass, 2 => Dirt, @@ -28,7 +30,43 @@ impl BlockId { 8 => OakLeaves, 9 => OakLog, 10 => Tallgrass, - _ => panic!("Invalid block id") + _ => panic!("Invalid block id"), + } + } +} + +impl Into for BlockId { + fn into(self) -> u8 { + match self { + Air => 0, + Grass => 1, + Dirt => 2, + Stone => 3, + CobbleStone => 4, + Bedrock => 5, + IronOre => 6, + CoalOre => 7, + OakLeaves => 8, + OakLog => 9, + Tallgrass => 10, } } } + +impl BlockId { + pub fn values() -> [BlockId; 11] { + [ + Air, + Grass, + Dirt, + Stone, + CobbleStone, + Bedrock, + IronOre, + CoalOre, + OakLeaves, + OakLog, + Tallgrass, + ] + } +} diff --git a/src/shared/chunk_serializer.rs b/src/shared/chunk_serializer.rs index ade60df..6a6ea79 100644 --- a/src/shared/chunk_serializer.rs +++ b/src/shared/chunk_serializer.rs @@ -12,7 +12,11 @@ impl Serialize for Chunk { where S: serde::Serializer, { - let data_as_u8: Vec = self.data.iter().map(|block_id| block_id.to_u8()).collect(); + // let data_as_u8: Vec = self.data.iter().map(|block_id| *block_id.into()).collect(); + let data_as_u8: Vec = self.data.iter().map(|block_id| { + let block_byte: u8 = (*block_id).into(); + block_byte + }).collect(); let serialized_data = serialize_buffer(data_as_u8); let mut state = serializer.serialize_struct("Chunk", 2)?; state.serialize_field("data", &serialized_data)?; @@ -50,7 +54,7 @@ impl<'de> Deserialize<'de> for Chunk { let deserialized_data = deserialize_buffer(bytes_slice); let data_as_block_id: [BlockId; CHUNK_LENGTH] = deserialized_data .into_iter() - .map(|i| BlockId::from_u8(i).unwrap()) + .map(|i| BlockId::from(i)) .collect::>() .try_into() .map_err(|_| serde::de::Error::custom("Failed to convert data to BlockId array"))?; From e4ac51f5cec860fc5d63ae11b5a94a25c004be68 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 13:25:45 +0100 Subject: [PATCH 07/45] Fix linter --- src/client/terrain/util/blocks.rs | 67 ++++++++++++++++------------ src/server/terrain/util/generator.rs | 1 - src/shared/blocks.rs | 6 +-- src/shared/chunk_serializer.rs | 14 +++--- 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/client/terrain/util/blocks.rs b/src/client/terrain/util/blocks.rs index fc06f35..9cbe24d 100644 --- a/src/client/terrain/util/blocks.rs +++ b/src/client/terrain/util/blocks.rs @@ -15,24 +15,24 @@ pub enum TextureName { OakLeaves, OakLogTop, OakLogSide, - Tallgrass + Tallgrass, } mod client_block { - use rsmc::BlockId; use super::TextureName; + use rsmc::BlockId; pub enum MeshRepresentation { None, Cube([TextureName; 6]), - Cross([TextureName; 2]) + Cross([TextureName; 2]), } use MeshRepresentation::*; pub struct BlockProperties { pub has_collider: bool, - pub mesh_representation: MeshRepresentation + pub mesh_representation: MeshRepresentation, } pub fn block_properties(block_id: BlockId) -> BlockProperties { @@ -40,7 +40,10 @@ mod client_block { let touple = match block_id { BlockId::Air => (true, None), - BlockId::Grass => (true, Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide])), + BlockId::Grass => ( + true, + Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide]), + ), BlockId::Dirt => (true, Cube([Dirt; 6])), BlockId::Stone => (true, Cube([Stone; 6])), BlockId::CobbleStone => (true, Cube([CobbleStone; 6])), @@ -48,27 +51,35 @@ mod client_block { BlockId::IronOre => (true, Cube([IronOre; 6])), BlockId::CoalOre => (true, Cube([CoalOre; 6])), BlockId::OakLeaves => (true, Cube([OakLeaves; 6])), - BlockId::OakLog => (true, Cube([OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide])), + BlockId::OakLog => ( + true, + Cube([ + OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide, + ]), + ), BlockId::Tallgrass => (true, Cross([Tallgrass, Tallgrass])), }; BlockProperties { has_collider: touple.0, - mesh_representation: touple.1 + mesh_representation: touple.1, } } pub fn collect_all_texture_names() -> Vec { - BlockId::values().iter().map(|block_id| { - let properties = block_properties(*block_id); - let mesh: MeshRepresentation = properties.mesh_representation; - - match mesh { - MeshRepresentation::None => vec![], - MeshRepresentation::Cube(textures) => Vec::from(textures), - MeshRepresentation::Cross(textures) => Vec::from(textures), - } - }).flatten().collect() + BlockId::values() + .iter() + .flat_map(|block_id| { + let properties = block_properties(*block_id); + let mesh: MeshRepresentation = properties.mesh_representation; + + match mesh { + MeshRepresentation::None => vec![], + MeshRepresentation::Cube(textures) => Vec::from(textures), + MeshRepresentation::Cross(textures) => Vec::from(textures), + } + }) + .collect() } } @@ -114,18 +125,18 @@ impl TextureManager { [Tallgrass, Air, Air, Air], ]; - let mut texture_positions = Vec::new(); + let mut texture_positions = Vec::new(); - for x in 0..ATLAS_WIDTH { - for y in 0..ATLAS_HEIGHT { - texture_positions.push(( - *textures.get(y).unwrap().get(x).unwrap(), - (1.0 / 4.0 * (x as f32), 1.0 / 4.0 * (y as f32)), - )) - } + for x in 0..ATLAS_WIDTH { + for y in 0..ATLAS_HEIGHT { + texture_positions.push(( + *textures.get(y).unwrap().get(x).unwrap(), + (1.0 / 4.0 * (x as f32), 1.0 / 4.0 * (y as f32)), + )) } + } - texture_positions + texture_positions } pub fn get_texture_uv(&self, name: TextureName) -> Option<&TextureUV> { @@ -153,12 +164,12 @@ impl Block { let texture_option: Option = match mesh { client_block::MeshRepresentation::None => None, client_block::MeshRepresentation::Cube(textures) => Some(textures[face as usize]), - client_block::MeshRepresentation::Cross(textures) => Some(textures[face as usize]) + client_block::MeshRepresentation::Cross(textures) => Some(textures[face as usize]), }; match texture_option { Some(texture_name) => texture_manager.get_texture_uv(texture_name).copied(), - None => None + None => None, } } } diff --git a/src/server/terrain/util/generator.rs b/src/server/terrain/util/generator.rs index 3472f4e..eb01856 100644 --- a/src/server/terrain/util/generator.rs +++ b/src/server/terrain/util/generator.rs @@ -1,5 +1,4 @@ use terrain_resources::{Generator, NoiseFunctionParams, TerrainGeneratorParams}; -use terrain_util::Block; use crate::prelude::*; diff --git a/src/shared/blocks.rs b/src/shared/blocks.rs index 63fb97f..9eb1ed5 100644 --- a/src/shared/blocks.rs +++ b/src/shared/blocks.rs @@ -35,9 +35,9 @@ impl From for BlockId { } } -impl Into for BlockId { - fn into(self) -> u8 { - match self { +impl From for u8 { + fn from(val: BlockId) -> Self { + match val { Air => 0, Grass => 1, Dirt => 2, diff --git a/src/shared/chunk_serializer.rs b/src/shared/chunk_serializer.rs index 6a6ea79..440e493 100644 --- a/src/shared/chunk_serializer.rs +++ b/src/shared/chunk_serializer.rs @@ -13,10 +13,14 @@ impl Serialize for Chunk { S: serde::Serializer, { // let data_as_u8: Vec = self.data.iter().map(|block_id| *block_id.into()).collect(); - let data_as_u8: Vec = self.data.iter().map(|block_id| { - let block_byte: u8 = (*block_id).into(); - block_byte - }).collect(); + let data_as_u8: Vec = self + .data + .iter() + .map(|block_id| { + let block_byte: u8 = (*block_id).into(); + block_byte + }) + .collect(); let serialized_data = serialize_buffer(data_as_u8); let mut state = serializer.serialize_struct("Chunk", 2)?; state.serialize_field("data", &serialized_data)?; @@ -54,7 +58,7 @@ impl<'de> Deserialize<'de> for Chunk { let deserialized_data = deserialize_buffer(bytes_slice); let data_as_block_id: [BlockId; CHUNK_LENGTH] = deserialized_data .into_iter() - .map(|i| BlockId::from(i)) + .map(BlockId::from) .collect::>() .try_into() .map_err(|_| serde::de::Error::custom("Failed to convert data to BlockId array"))?; From 414350ee865f6f496daf3e8c6afe951380c2976e Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 13:55:37 +0100 Subject: [PATCH 08/45] Fix panics, udpate colliders --- src/client/collider/systems.rs | 4 +++- src/client/terrain/util/blocks.rs | 6 +++--- src/client/terrain/util/mesher.rs | 8 +++++--- src/server/terrain/util/generator.rs | 1 + 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/client/collider/systems.rs b/src/client/collider/systems.rs index 8b7c2d0..e504066 100644 --- a/src/client/collider/systems.rs +++ b/src/client/collider/systems.rs @@ -1,3 +1,5 @@ +use terrain_util::client_block::block_properties; + use crate::prelude::*; static COLLIDER_GRID_SIZE: u32 = 4; @@ -56,7 +58,7 @@ pub fn handle_collider_update_events_system( match block { Some(block) => { - if block != BlockId::Air { + if block_properties(block).has_collider { transform.translation = collider_position + COLLIDER_CUBOID_WIDTH / 2.0; } else { transform.translation = COLLIDER_RESTING_POSITION; diff --git a/src/client/terrain/util/blocks.rs b/src/client/terrain/util/blocks.rs index 9cbe24d..dd49b07 100644 --- a/src/client/terrain/util/blocks.rs +++ b/src/client/terrain/util/blocks.rs @@ -18,7 +18,7 @@ pub enum TextureName { Tallgrass, } -mod client_block { +pub mod client_block { use super::TextureName; use rsmc::BlockId; @@ -39,7 +39,7 @@ mod client_block { use TextureName::*; let touple = match block_id { - BlockId::Air => (true, None), + BlockId::Air => (false, None), BlockId::Grass => ( true, Cube([GrassTop, Dirt, GrassSide, GrassSide, GrassSide, GrassSide]), @@ -57,7 +57,7 @@ mod client_block { OakLogTop, OakLogTop, OakLogSide, OakLogSide, OakLogSide, OakLogSide, ]), ), - BlockId::Tallgrass => (true, Cross([Tallgrass, Tallgrass])), + BlockId::Tallgrass => (false, Cross([Tallgrass, Tallgrass])), }; BlockProperties { diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 6e76df6..07cf8ee 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -1,4 +1,4 @@ -use terrain_util::TextureManager; +use terrain_util::{client_block::{block_properties, MeshRepresentation}, TextureManager}; use crate::prelude::*; @@ -91,8 +91,10 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt for z in 1..CHUNK_SIZE + 1 { let block_id = chunk.get_unpadded(x, y, z); - if block_id == BlockId::Air { - continue; + match block_properties(block_id).mesh_representation { + MeshRepresentation::None => continue, + MeshRepresentation::Cube(_) => {} + MeshRepresentation::Cross(_) => continue, } fn update_mask( diff --git a/src/server/terrain/util/generator.rs b/src/server/terrain/util/generator.rs index eb01856..353642a 100644 --- a/src/server/terrain/util/generator.rs +++ b/src/server/terrain/util/generator.rs @@ -202,6 +202,7 @@ impl Generator { let z = position.z as usize; let block = chunk.get_unpadded(x, y, z); + if block == BlockId::Air { return block; } From 9fd69846c896c73ac7ef6819986f28d22601b3f8 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 14:02:51 +0100 Subject: [PATCH 09/45] Fix meshing --- src/client/terrain/util/mesher.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 07cf8ee..9c5c802 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -105,7 +105,8 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt y: usize, z: usize, ) { - if chunk.get_unpadded(x, y, z) == BlockId::Air { + if let MeshRepresentation::Cube(_) = block_properties(chunk.get_unpadded(x, y, z)).mesh_representation { + } else { *mask |= value; } } From 45fe0c8fd2ed557f9d1564f3227dde4ed42b168b Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 14:02:58 +0100 Subject: [PATCH 10/45] Fix linter --- src/client/terrain/util/mesher.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 9c5c802..c301998 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -1,4 +1,7 @@ -use terrain_util::{client_block::{block_properties, MeshRepresentation}, TextureManager}; +use terrain_util::{ + client_block::{block_properties, MeshRepresentation}, + TextureManager, +}; use crate::prelude::*; @@ -105,7 +108,9 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt y: usize, z: usize, ) { - if let MeshRepresentation::Cube(_) = block_properties(chunk.get_unpadded(x, y, z)).mesh_representation { + if let MeshRepresentation::Cube(_) = + block_properties(chunk.get_unpadded(x, y, z)).mesh_representation + { } else { *mask |= value; } From f3af9fb03fc1bcc2ea1e1ff4564ff6848507f892 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 14:07:15 +0100 Subject: [PATCH 11/45] Refactor mesher --- src/client/terrain/util/mesher.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index c301998..f721885 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -95,9 +95,8 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt let block_id = chunk.get_unpadded(x, y, z); match block_properties(block_id).mesh_representation { - MeshRepresentation::None => continue, MeshRepresentation::Cube(_) => {} - MeshRepresentation::Cross(_) => continue, + _ => continue, } fn update_mask( @@ -108,11 +107,9 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt y: usize, z: usize, ) { - if let MeshRepresentation::Cube(_) = - block_properties(chunk.get_unpadded(x, y, z)).mesh_representation - { - } else { - *mask |= value; + match block_properties(chunk.get_unpadded(x, y, z)).mesh_representation { + MeshRepresentation::Cube(_) => {} + _ => *mask |= value, } } From 6b9c9f62ca01cbbfbc0bfa1c19d3e36c58485890 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 14:24:55 +0100 Subject: [PATCH 12/45] Add skip_terrain feature flag --- Cargo.toml | 1 + src/client/player/systems/controller.rs | 8 ++++++-- src/client/terrain/mod.rs | 18 +++++++++++++----- src/client/terrain/systems.rs | 14 ++++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4dc1e03..bd618c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,4 +61,5 @@ ortho_camera = [] lock_player = [] physics_debug = [] raycast_debug = [] +skip_terrain = [] visual_debug = ["wireframe", "physics_debug", "raycast_debug"] diff --git a/src/client/player/systems/controller.rs b/src/client/player/systems/controller.rs index e0a34bf..a37b94e 100644 --- a/src/client/player/systems/controller.rs +++ b/src/client/player/systems/controller.rs @@ -1,8 +1,12 @@ use crate::prelude::*; -#[cfg(not(feature = "lock_player"))] +#[cfg(feature = "skip_terrain")] +const SPAWN_POINT: Vec3 = Vec3::new(0.0, 1.0, 0.0); + +#[cfg(all(not(feature = "skip_terrain"), not(feature = "lock_player")))] const SPAWN_POINT: Vec3 = Vec3::new(0.0, 64.0, 0.0); -#[cfg(feature = "lock_player")] + +#[cfg(all(not(feature = "skip_terrain"), feature = "lock_player"))] const SPAWN_POINT: Vec3 = Vec3::new(128.0, 96.0, -128.0); pub fn setup_player_camera(mut commands: Commands) { diff --git a/src/client/terrain/mod.rs b/src/client/terrain/mod.rs index 04588a0..58c4bfa 100644 --- a/src/client/terrain/mod.rs +++ b/src/client/terrain/mod.rs @@ -12,14 +12,22 @@ impl Plugin for TerrainPlugin { fn build(&self, app: &mut App) { info!("Building TerrainPlugin"); app.insert_resource(ChunkManager::new()); - app.insert_resource(terrain_resources::SpawnAreaLoaded(false)); app.insert_resource(util::TextureManager::new()); app.add_event::(); app.add_event::(); app.add_event::(); - app.add_systems(Startup, terrain_systems::prepare_spawn_area_system); - app.add_systems(Startup, terrain_systems::generate_world_system); - app.add_systems(Update, terrain_systems::handle_chunk_mesh_update_events); - app.add_systems(Update, terrain_systems::handle_terrain_regeneration_events); + #[cfg(feature = "skip_terrain")] + { + app.insert_resource(terrain_resources::SpawnAreaLoaded(true)); + app.add_systems(Startup, terrain_systems::generate_simple_ground_system); + } + #[cfg(not(feature = "skip_terrain"))] + { + app.insert_resource(terrain_resources::SpawnAreaLoaded(false)); + app.add_systems(Startup, terrain_systems::prepare_spawn_area_system); + app.add_systems(Startup, terrain_systems::generate_world_system); + app.add_systems(Update, terrain_systems::handle_chunk_mesh_update_events); + app.add_systems(Update, terrain_systems::handle_terrain_regeneration_events); + } } } diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 0c94f99..846c5e9 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -1,5 +1,19 @@ use crate::prelude::*; +pub fn generate_simple_ground_system( + mut meshes: ResMut>, + mut materials: ResMut>, + mut commands: Commands, +) { + let mesh = Cuboid::new(64.0, 1.0, 64.0); + + commands.spawn(( + Mesh3d(meshes.add(mesh)), + MeshMaterial3d(materials.add(Color::srgba(1.0, 0.0, 1.0, 1.0))), + Name::new("Simple Ground Plane"), + )); +} + pub fn prepare_spawn_area_system(mut client: ResMut) { info!("Sending chunk requests for spawn area"); From 2b99c2cdd8a88045bc137a6757c6cebec58f9184 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 14:55:29 +0100 Subject: [PATCH 13/45] Impl get block positions --- src/client/terrain/util/mesher.rs | 23 +++++++++++++++++++++++ src/shared/blocks.rs | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index f721885..12d4730 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -5,6 +5,29 @@ use terrain_util::{ use crate::prelude::*; +pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { + let mut map: HashMap> = HashMap::new(); + + for x in 0..CHUNK_SIZE { + for y in 0..CHUNK_SIZE { + for z in 0..CHUNK_SIZE { + let block_id = chunk.get(x,y,z); + let pos = Vec3::new(x as f32, y as f32, z as f32); + if let MeshRepresentation::Cross(_) = block_properties(block_id).mesh_representation { + match map.get_mut(&block_id) { + Some(positions) => positions.push(pos), + None => { + map.insert(block_id, vec![pos]); + } + }; + } + } + } + } + + map +} + pub fn create_cube_mesh_from_data(geometry_data: GeometryData) -> Option { let GeometryData { position, diff --git a/src/shared/blocks.rs b/src/shared/blocks.rs index 9eb1ed5..e180ff3 100644 --- a/src/shared/blocks.rs +++ b/src/shared/blocks.rs @@ -1,4 +1,4 @@ -#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash)] pub enum BlockId { Air, Grass, From 1af5ffe084ac2092203db7e0d7b3640e12988a48 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 15:57:41 +0100 Subject: [PATCH 14/45] Add handles for cross meshes, boilerplate for mesher --- src/client/terrain/mod.rs | 2 + src/client/terrain/resources.rs | 15 +++++++ src/client/terrain/systems.rs | 21 ++++++++++ src/client/terrain/util/blocks.rs | 1 + src/client/terrain/util/mesher.rs | 68 ++++++++++++++++++++++++++++++- 5 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/client/terrain/mod.rs b/src/client/terrain/mod.rs index 58c4bfa..fc465a4 100644 --- a/src/client/terrain/mod.rs +++ b/src/client/terrain/mod.rs @@ -13,9 +13,11 @@ impl Plugin for TerrainPlugin { info!("Building TerrainPlugin"); app.insert_resource(ChunkManager::new()); app.insert_resource(util::TextureManager::new()); + app.insert_resource(resources::Mesher::new()); app.add_event::(); app.add_event::(); app.add_event::(); + app.add_systems(Startup, terrain_systems::populate_mesher_meshes); #[cfg(feature = "skip_terrain")] { app.insert_resource(terrain_resources::SpawnAreaLoaded(true)); diff --git a/src/client/terrain/resources.rs b/src/client/terrain/resources.rs index 4efbe70..d9e9cce 100644 --- a/src/client/terrain/resources.rs +++ b/src/client/terrain/resources.rs @@ -1,3 +1,5 @@ +use terrain_util::client_block::MeshRepresentation; + use crate::prelude::*; #[derive(Resource)] @@ -8,3 +10,16 @@ impl SpawnAreaLoaded { resource.0 } } + +#[derive(Resource)] +pub struct Mesher { + pub mesh_handles: HashMap> +} + +impl Mesher { + pub fn new() -> Mesher { + Mesher { + mesh_handles: HashMap::new() + } + } +} diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 846c5e9..7da0d1b 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -1,5 +1,26 @@ +use terrain_resources::Mesher; +use terrain_util::{client_block::block_properties, instance_mesh_for_repr}; + use crate::prelude::*; +pub fn populate_mesher_meshes( + mut mesher: ResMut, + mut meshes: ResMut>, + texture_manager: ResMut +) { + BlockId::values().iter().for_each(|block_id| { + let mesh_repr = block_properties(*block_id).mesh_representation; + let mesh = instance_mesh_for_repr(mesh_repr.clone(), &texture_manager); + match mesh { + Some(mesh) => { + let handle = meshes.add(mesh); + mesher.mesh_handles.insert(mesh_repr, handle); + }, + None => {} + } + }); +} + pub fn generate_simple_ground_system( mut meshes: ResMut>, mut materials: ResMut>, diff --git a/src/client/terrain/util/blocks.rs b/src/client/terrain/util/blocks.rs index dd49b07..2c5b574 100644 --- a/src/client/terrain/util/blocks.rs +++ b/src/client/terrain/util/blocks.rs @@ -22,6 +22,7 @@ pub mod client_block { use super::TextureName; use rsmc::BlockId; + #[derive(Eq, Hash, PartialEq, Clone)] pub enum MeshRepresentation { None, Cube([TextureName; 6]), diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 12d4730..cc404a1 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -1,10 +1,76 @@ use terrain_util::{ client_block::{block_properties, MeshRepresentation}, - TextureManager, + TextureManager, TextureName, }; use crate::prelude::*; +pub fn instance_mesh_for_repr(rep: MeshRepresentation, texture_manager: &TextureManager) -> Option { + match rep { + MeshRepresentation::None => None, + MeshRepresentation::Cube(_) => None, + MeshRepresentation::Cross(textures) => { + let geometry_data = create_cross_geometry(textures, texture_manager); + // TODO: refactor rename to create_mesh_from_data because it is not cube representation specific + create_cube_mesh_from_data(geometry_data) + } + } +} + +fn create_cross_geometry(textures: [TextureName; 2], + texture_manager: &TextureManager, + ) -> GeometryData { + let position = vec!(); + let uv = vec!(); + let normal = vec!(); + let indices = vec!(); + + + // prepare face 1 + + { + let uv = texture_manager.get_texture_uv(textures[0]).expect("Texture is not present in manager"); + // let face_uv = Block::get_block_face_uvs(block_id, face, texture_manager) + } + + + + // prepare face 2 + + // if faces & (1 << i) == 0 { + // return; + // } + // + // let face_vertices = face_vertices(*face); + // for vertex in face_vertices.iter() { + // position.push([ + // vertex.position[0] * 0.5 + x + 0.5, + // vertex.position[1] * 0.5 + y + 0.5, + // vertex.position[2] * 0.5 + z + 0.5, + // ]); + // + // let block_uvs = Block::get_block_face_uvs(block_id, *face, texture_manager).unwrap(); + // uv.push([ + // block_uvs[0] + vertex.uv[0] * 0.25 - 0.001, + // block_uvs[1] + (1.0 - vertex.uv[1]) * 0.25, + // ]); + // normal.push(vertex.normal); + // } + // + // let offsets = [0, 1, 2, 2, 1, 3]; + // offsets.iter().for_each(|offset| { + // indices.push(index_offset + offset); + // }); + // index_offset += 4; + + GeometryData { + position, + uv, + normal, + indices, + } +} + pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { let mut map: HashMap> = HashMap::new(); From 671bb70b048e51b58b23137d4e5a788b02617f51 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 15:57:51 +0100 Subject: [PATCH 15/45] Fix linter --- src/client/terrain/resources.rs | 10 +++- src/client/terrain/systems.rs | 11 ++--- src/client/terrain/util/mesher.rs | 80 ++++++++++++++++--------------- 3 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/client/terrain/resources.rs b/src/client/terrain/resources.rs index d9e9cce..75818fa 100644 --- a/src/client/terrain/resources.rs +++ b/src/client/terrain/resources.rs @@ -13,13 +13,19 @@ impl SpawnAreaLoaded { #[derive(Resource)] pub struct Mesher { - pub mesh_handles: HashMap> + pub mesh_handles: HashMap>, +} + +impl Default for Mesher { + fn default() -> Self { + Self::new() + } } impl Mesher { pub fn new() -> Mesher { Mesher { - mesh_handles: HashMap::new() + mesh_handles: HashMap::new(), } } } diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 7da0d1b..287f605 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -6,17 +6,14 @@ use crate::prelude::*; pub fn populate_mesher_meshes( mut mesher: ResMut, mut meshes: ResMut>, - texture_manager: ResMut + texture_manager: ResMut, ) { BlockId::values().iter().for_each(|block_id| { let mesh_repr = block_properties(*block_id).mesh_representation; let mesh = instance_mesh_for_repr(mesh_repr.clone(), &texture_manager); - match mesh { - Some(mesh) => { - let handle = meshes.add(mesh); - mesher.mesh_handles.insert(mesh_repr, handle); - }, - None => {} + if let Some(mesh) = mesh { + let handle = meshes.add(mesh); + mesher.mesh_handles.insert(mesh_repr, handle); } }); } diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index cc404a1..a381ad0 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -5,7 +5,10 @@ use terrain_util::{ use crate::prelude::*; -pub fn instance_mesh_for_repr(rep: MeshRepresentation, texture_manager: &TextureManager) -> Option { +pub fn instance_mesh_for_repr( + rep: MeshRepresentation, + texture_manager: &TextureManager, +) -> Option { match rep { MeshRepresentation::None => None, MeshRepresentation::Cube(_) => None, @@ -17,51 +20,51 @@ pub fn instance_mesh_for_repr(rep: MeshRepresentation, texture_manager: &Texture } } -fn create_cross_geometry(textures: [TextureName; 2], +fn create_cross_geometry( + textures: [TextureName; 2], texture_manager: &TextureManager, - ) -> GeometryData { - let position = vec!(); - let uv = vec!(); - let normal = vec!(); - let indices = vec!(); - +) -> GeometryData { + let position = vec![]; + let uv = vec![]; + let normal = vec![]; + let indices = vec![]; // prepare face 1 { - let uv = texture_manager.get_texture_uv(textures[0]).expect("Texture is not present in manager"); + let uv = texture_manager + .get_texture_uv(textures[0]) + .expect("Texture is not present in manager"); // let face_uv = Block::get_block_face_uvs(block_id, face, texture_manager) } - - // prepare face 2 - // if faces & (1 << i) == 0 { - // return; - // } - // - // let face_vertices = face_vertices(*face); - // for vertex in face_vertices.iter() { - // position.push([ - // vertex.position[0] * 0.5 + x + 0.5, - // vertex.position[1] * 0.5 + y + 0.5, - // vertex.position[2] * 0.5 + z + 0.5, - // ]); - // - // let block_uvs = Block::get_block_face_uvs(block_id, *face, texture_manager).unwrap(); - // uv.push([ - // block_uvs[0] + vertex.uv[0] * 0.25 - 0.001, - // block_uvs[1] + (1.0 - vertex.uv[1]) * 0.25, - // ]); - // normal.push(vertex.normal); - // } - // - // let offsets = [0, 1, 2, 2, 1, 3]; - // offsets.iter().for_each(|offset| { - // indices.push(index_offset + offset); - // }); - // index_offset += 4; + // if faces & (1 << i) == 0 { + // return; + // } + // + // let face_vertices = face_vertices(*face); + // for vertex in face_vertices.iter() { + // position.push([ + // vertex.position[0] * 0.5 + x + 0.5, + // vertex.position[1] * 0.5 + y + 0.5, + // vertex.position[2] * 0.5 + z + 0.5, + // ]); + // + // let block_uvs = Block::get_block_face_uvs(block_id, *face, texture_manager).unwrap(); + // uv.push([ + // block_uvs[0] + vertex.uv[0] * 0.25 - 0.001, + // block_uvs[1] + (1.0 - vertex.uv[1]) * 0.25, + // ]); + // normal.push(vertex.normal); + // } + // + // let offsets = [0, 1, 2, 2, 1, 3]; + // offsets.iter().for_each(|offset| { + // indices.push(index_offset + offset); + // }); + // index_offset += 4; GeometryData { position, @@ -77,9 +80,10 @@ pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { for x in 0..CHUNK_SIZE { for y in 0..CHUNK_SIZE { for z in 0..CHUNK_SIZE { - let block_id = chunk.get(x,y,z); + let block_id = chunk.get(x, y, z); let pos = Vec3::new(x as f32, y as f32, z as f32); - if let MeshRepresentation::Cross(_) = block_properties(block_id).mesh_representation { + if let MeshRepresentation::Cross(_) = block_properties(block_id).mesh_representation + { match map.get_mut(&block_id) { Some(positions) => positions.push(pos), None => { From 522ac40ae70f28f3813cefdd79190634b9e26b45 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 17:06:11 +0100 Subject: [PATCH 16/45] Update grass meshes --- src/client/terrain/mod.rs | 1 + src/client/terrain/resources.rs | 2 + src/client/terrain/systems.rs | 53 +++++++++++++- src/client/terrain/util/mesher.rs | 117 ++++++++++++++++++------------ 4 files changed, 124 insertions(+), 49 deletions(-) diff --git a/src/client/terrain/mod.rs b/src/client/terrain/mod.rs index fc465a4..824e1f9 100644 --- a/src/client/terrain/mod.rs +++ b/src/client/terrain/mod.rs @@ -18,6 +18,7 @@ impl Plugin for TerrainPlugin { app.add_event::(); app.add_event::(); app.add_systems(Startup, terrain_systems::populate_mesher_meshes); + app.add_systems(Startup, terrain_systems::prepare_mesher_materials); #[cfg(feature = "skip_terrain")] { app.insert_resource(terrain_resources::SpawnAreaLoaded(true)); diff --git a/src/client/terrain/resources.rs b/src/client/terrain/resources.rs index 75818fa..c2fa113 100644 --- a/src/client/terrain/resources.rs +++ b/src/client/terrain/resources.rs @@ -14,6 +14,7 @@ impl SpawnAreaLoaded { #[derive(Resource)] pub struct Mesher { pub mesh_handles: HashMap>, + pub transparent_material_handle: Option> } impl Default for Mesher { @@ -26,6 +27,7 @@ impl Mesher { pub fn new() -> Mesher { Mesher { mesh_handles: HashMap::new(), + transparent_material_handle: None, } } } diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 287f605..a27f17b 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -1,5 +1,6 @@ +use bevy::asset; use terrain_resources::Mesher; -use terrain_util::{client_block::block_properties, instance_mesh_for_repr}; +use terrain_util::{client_block::block_properties, get_cross_block_positions, instance_mesh_for_repr}; use crate::prelude::*; @@ -18,6 +19,16 @@ pub fn populate_mesher_meshes( }); } +pub fn prepare_mesher_materials( + mut mesher: ResMut, + materials: ResMut>, + asset_server: Res, +) { + let texture_handle = obtain_texture_handle(&asset_server).clone(); + let material_handle = create_transparent_material(texture_handle, materials); + mesher.transparent_material_handle = Some(material_handle); +} + pub fn generate_simple_ground_system( mut meshes: ResMut>, mut materials: ResMut>, @@ -80,6 +91,7 @@ pub fn handle_chunk_mesh_update_events( mut chunk_mesh_update_events: EventReader, mut mesh_query: Query<(Entity, &terrain_components::ChunkMesh)>, texture_manager: ResMut, + mesher: Res ) { for event in chunk_mesh_update_events.read() { info!( @@ -102,6 +114,7 @@ pub fn handle_chunk_mesh_update_events( chunk, &texture_manager, ); + add_cross_objects(&mut commands, &chunk, &mesher); } None => { println!("No chunk found"); @@ -131,6 +144,30 @@ fn add_chunk_objects( } } +fn add_cross_objects( + commands: &mut Commands, + chunk: &Chunk, + mesher: &Mesher, +) { + let values = get_cross_block_positions(chunk); + for (mesh_repr, positions) in values { + let mesh_handle = mesher.mesh_handles.get(&mesh_repr).expect("Handle is not yet populated"); + let material_handle = mesher.transparent_material_handle.clone().expect("Material has not yet been set"); + + for position in positions { + commands.spawn(( + Mesh3d(mesh_handle.clone()), + MeshMaterial3d(material_handle.clone()), + Transform::from_xyz( + chunk.position.x * CHUNK_SIZE as f32 + position.x, + chunk.position.y * CHUNK_SIZE as f32 + position.y, + chunk.position.z * CHUNK_SIZE as f32 + position.z + ) + )); + } + } +} + fn create_chunk_mesh( chunk: &Chunk, texture_manager: &terrain_util::TextureManager, @@ -138,6 +175,20 @@ fn create_chunk_mesh( terrain_util::create_chunk_mesh(chunk, texture_manager) } +fn create_transparent_material( + texture_handle: Handle, + mut materials: ResMut>, +) -> Handle { + materials.add(StandardMaterial { + perceptual_roughness: 0.5, + reflectance: 0.0, + unlit: false, + specular_transmission: 0.0, + base_color_texture: Some(texture_handle), + ..default() + }) +} + #[cfg(not(feature = "wireframe"))] fn create_chunk_material( texture_handle: Handle, diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index a381ad0..9384e2e 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -24,47 +24,40 @@ fn create_cross_geometry( textures: [TextureName; 2], texture_manager: &TextureManager, ) -> GeometryData { - let position = vec![]; - let uv = vec![]; - let normal = vec![]; - let indices = vec![]; + let mut position = vec![]; + let mut uv = vec![]; + let mut normal = vec![]; + let mut indices = vec![]; - // prepare face 1 + let index_offset = 0; { - let uv = texture_manager + let face_verticies = cross_face_vertices(CrossFace::Face1); + + let face_uv = texture_manager .get_texture_uv(textures[0]) .expect("Texture is not present in manager"); - // let face_uv = Block::get_block_face_uvs(block_id, face, texture_manager) - } - // prepare face 2 - - // if faces & (1 << i) == 0 { - // return; - // } - // - // let face_vertices = face_vertices(*face); - // for vertex in face_vertices.iter() { - // position.push([ - // vertex.position[0] * 0.5 + x + 0.5, - // vertex.position[1] * 0.5 + y + 0.5, - // vertex.position[2] * 0.5 + z + 0.5, - // ]); - // - // let block_uvs = Block::get_block_face_uvs(block_id, *face, texture_manager).unwrap(); - // uv.push([ - // block_uvs[0] + vertex.uv[0] * 0.25 - 0.001, - // block_uvs[1] + (1.0 - vertex.uv[1]) * 0.25, - // ]); - // normal.push(vertex.normal); - // } - // - // let offsets = [0, 1, 2, 2, 1, 3]; - // offsets.iter().for_each(|offset| { - // indices.push(index_offset + offset); - // }); - // index_offset += 4; + + for vertex in face_verticies { + position.push([ + vertex.position[0] * 0.5 + 0.5, + vertex.position[1] * 0.5 + 0.5, + vertex.position[2] * 0.5 + 0.5, + ]); + + uv.push([ + face_uv[0] + vertex.uv[0] * 0.25, + face_uv[1] + (1.0 - vertex.uv[1]) * 0.25, + ]); + normal.push(vertex.normal); + } + + let offsets = [0, 1, 2, 2, 1, 3]; + offsets.iter().for_each(|offset| { + indices.push(index_offset + offset); + }); + } GeometryData { position, @@ -74,22 +67,26 @@ fn create_cross_geometry( } } -pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { - let mut map: HashMap> = HashMap::new(); +pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { + let mut map: HashMap> = HashMap::new(); for x in 0..CHUNK_SIZE { for y in 0..CHUNK_SIZE { for z in 0..CHUNK_SIZE { let block_id = chunk.get(x, y, z); let pos = Vec3::new(x as f32, y as f32, z as f32); - if let MeshRepresentation::Cross(_) = block_properties(block_id).mesh_representation - { - match map.get_mut(&block_id) { - Some(positions) => positions.push(pos), - None => { - map.insert(block_id, vec![pos]); - } - }; + let mesh_repr = block_properties(block_id).mesh_representation; + + match mesh_repr { + MeshRepresentation::Cross(_) => { + match map.get_mut(&mesh_repr) { + Some(positions) => positions.push(pos), + None => { + map.insert(mesh_repr, vec![pos]); + } + }; + } + _ => {} } } } @@ -228,9 +225,9 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt geometry_data.indices.extend( cube_data - .indices - .iter() - .map(|i| i + geometry_data.position.len() as u32), + .indices + .iter() + .map(|i| i + geometry_data.position.len() as u32), ); geometry_data.position.extend(cube_data.position); geometry_data.uv.extend(cube_data.uv); @@ -252,6 +249,12 @@ pub enum CubeFace { Forward, } +#[derive(Debug, Clone, Copy)] +pub enum CrossFace { + Face1, + Face2 +} + const CUBE_FACES: [CubeFace; 6] = [ CubeFace::Top, CubeFace::Bottom, @@ -316,6 +319,24 @@ fn face_vertices(face_index: CubeFace) -> [Vertex; 4] { } } +#[rustfmt::skip] +fn cross_face_vertices(face: CrossFace) -> [Vertex; 4] { + match face { + CrossFace::Face1 => [ + Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 1.0] }, + ], + CrossFace::Face2 => [ + Vertex{ position: [-1.0, 1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 1.0] }, + ], + } +} + #[cfg(test)] mod tests { use super::*; From 3350b0a51092a40262984708c61c70245e3b868d Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 17:10:34 +0100 Subject: [PATCH 17/45] Fix linter --- src/client/terrain/resources.rs | 2 +- src/client/terrain/systems.rs | 29 ++++++++++++++++------------- src/client/terrain/util/mesher.rs | 26 +++++++++++--------------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/client/terrain/resources.rs b/src/client/terrain/resources.rs index c2fa113..6bdfee4 100644 --- a/src/client/terrain/resources.rs +++ b/src/client/terrain/resources.rs @@ -14,7 +14,7 @@ impl SpawnAreaLoaded { #[derive(Resource)] pub struct Mesher { pub mesh_handles: HashMap>, - pub transparent_material_handle: Option> + pub transparent_material_handle: Option>, } impl Default for Mesher { diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index a27f17b..3afb502 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -1,6 +1,7 @@ -use bevy::asset; use terrain_resources::Mesher; -use terrain_util::{client_block::block_properties, get_cross_block_positions, instance_mesh_for_repr}; +use terrain_util::{ + client_block::block_properties, get_cross_block_positions, instance_mesh_for_repr, +}; use crate::prelude::*; @@ -91,7 +92,7 @@ pub fn handle_chunk_mesh_update_events( mut chunk_mesh_update_events: EventReader, mut mesh_query: Query<(Entity, &terrain_components::ChunkMesh)>, texture_manager: ResMut, - mesher: Res + mesher: Res, ) { for event in chunk_mesh_update_events.read() { info!( @@ -114,7 +115,7 @@ pub fn handle_chunk_mesh_update_events( chunk, &texture_manager, ); - add_cross_objects(&mut commands, &chunk, &mesher); + add_cross_objects(&mut commands, chunk, &mesher); } None => { println!("No chunk found"); @@ -144,15 +145,17 @@ fn add_chunk_objects( } } -fn add_cross_objects( - commands: &mut Commands, - chunk: &Chunk, - mesher: &Mesher, -) { +fn add_cross_objects(commands: &mut Commands, chunk: &Chunk, mesher: &Mesher) { let values = get_cross_block_positions(chunk); for (mesh_repr, positions) in values { - let mesh_handle = mesher.mesh_handles.get(&mesh_repr).expect("Handle is not yet populated"); - let material_handle = mesher.transparent_material_handle.clone().expect("Material has not yet been set"); + let mesh_handle = mesher + .mesh_handles + .get(&mesh_repr) + .expect("Handle is not yet populated"); + let material_handle = mesher + .transparent_material_handle + .clone() + .expect("Material has not yet been set"); for position in positions { commands.spawn(( @@ -161,8 +164,8 @@ fn add_cross_objects( Transform::from_xyz( chunk.position.x * CHUNK_SIZE as f32 + position.x, chunk.position.y * CHUNK_SIZE as f32 + position.y, - chunk.position.z * CHUNK_SIZE as f32 + position.z - ) + chunk.position.z * CHUNK_SIZE as f32 + position.z, + ), )); } } diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 9384e2e..d186fd1 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -38,7 +38,6 @@ fn create_cross_geometry( .get_texture_uv(textures[0]) .expect("Texture is not present in manager"); - for vertex in face_verticies { position.push([ vertex.position[0] * 0.5 + 0.5, @@ -77,16 +76,13 @@ pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap { - match map.get_mut(&mesh_repr) { - Some(positions) => positions.push(pos), - None => { - map.insert(mesh_repr, vec![pos]); - } - }; - } - _ => {} + if let MeshRepresentation::Cross(_) = mesh_repr { + match map.get_mut(&mesh_repr) { + Some(positions) => positions.push(pos), + None => { + map.insert(mesh_repr, vec![pos]); + } + }; } } } @@ -225,9 +221,9 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt geometry_data.indices.extend( cube_data - .indices - .iter() - .map(|i| i + geometry_data.position.len() as u32), + .indices + .iter() + .map(|i| i + geometry_data.position.len() as u32), ); geometry_data.position.extend(cube_data.position); geometry_data.uv.extend(cube_data.uv); @@ -252,7 +248,7 @@ pub enum CubeFace { #[derive(Debug, Clone, Copy)] pub enum CrossFace { Face1, - Face2 + Face2, } const CUBE_FACES: [CubeFace; 6] = [ From d9a21d9c23ed6b7ddf82cee40ac04eb7a085ea80 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 17:24:53 +0100 Subject: [PATCH 18/45] Attempt add 2nd face --- assets/textures/texture_atlas.png | Bin 7007 -> 7255 bytes src/client/terrain/systems.rs | 4 +++- src/client/terrain/util/mesher.rs | 20 ++++++++++++-------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/assets/textures/texture_atlas.png b/assets/textures/texture_atlas.png index b63670ad30779c0c1a80c03377dbf8ab245ee2a4..0cd48675b5159d37208e7dc35062d6e492318f07 100644 GIT binary patch delta 7161 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 MuA-$}s%RDbAKH~4G5`Po delta 6911 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, mut chunk_manager: ResMut, ) { - let render_distance = Vec3::new(4.0, 4.0, 4.0); + let render_distance = Vec3::new(1.0, 1.0, 1.0); info!("Sending chunk requests for chunks"); @@ -184,9 +184,11 @@ fn create_transparent_material( ) -> Handle { materials.add(StandardMaterial { perceptual_roughness: 0.5, + double_sided: true, reflectance: 0.0, unlit: false, specular_transmission: 0.0, + alpha_mode: AlphaMode::Mask(1.0), base_color_texture: Some(texture_handle), ..default() }) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index d186fd1..5016a0a 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -29,10 +29,12 @@ fn create_cross_geometry( let mut normal = vec![]; let mut indices = vec![]; - let index_offset = 0; + let mut index_offset = 0; - { - let face_verticies = cross_face_vertices(CrossFace::Face1); + let cross_faces = [CrossFace::Face1, CrossFace::Face2]; + + cross_faces.iter().for_each(|cross_face| { + let face_verticies = cross_face_vertices(*cross_face); let face_uv = texture_manager .get_texture_uv(textures[0]) @@ -47,7 +49,7 @@ fn create_cross_geometry( uv.push([ face_uv[0] + vertex.uv[0] * 0.25, - face_uv[1] + (1.0 - vertex.uv[1]) * 0.25, + face_uv[1] + vertex.uv[1] * 0.25, ]); normal.push(vertex.normal); } @@ -56,7 +58,9 @@ fn create_cross_geometry( offsets.iter().for_each(|offset| { indices.push(index_offset + offset); }); - } + + index_offset += 4; + }); GeometryData { position, @@ -221,9 +225,9 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt geometry_data.indices.extend( cube_data - .indices - .iter() - .map(|i| i + geometry_data.position.len() as u32), + .indices + .iter() + .map(|i| i + geometry_data.position.len() as u32), ); geometry_data.position.extend(cube_data.position); geometry_data.uv.extend(cube_data.uv); From 0e426fa63734b3e59432629b3a31f0a8293f4922 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 17:35:31 +0100 Subject: [PATCH 19/45] Fix face1 --- src/client/terrain/systems.rs | 1 + src/client/terrain/util/mesher.rs | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 286be4b..0c50e59 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -185,6 +185,7 @@ fn create_transparent_material( materials.add(StandardMaterial { perceptual_roughness: 0.5, double_sided: true, + cull_mode: None, reflectance: 0.0, unlit: false, specular_transmission: 0.0, diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 5016a0a..f8368fe 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -31,7 +31,8 @@ fn create_cross_geometry( let mut index_offset = 0; - let cross_faces = [CrossFace::Face1, CrossFace::Face2]; + // let cross_faces = [CrossFace::Face1, CrossFace::Face2]; + let cross_faces = [CrossFace::Face1]; cross_faces.iter().for_each(|cross_face| { let face_verticies = cross_face_vertices(*cross_face); @@ -54,7 +55,7 @@ fn create_cross_geometry( normal.push(vertex.normal); } - let offsets = [0, 1, 2, 2, 1, 3]; + let offsets = [0, 1, 3, 1, 2, 3]; offsets.iter().for_each(|offset| { indices.push(index_offset + offset); }); @@ -326,12 +327,12 @@ fn cross_face_vertices(face: CrossFace) -> [Vertex; 4] { Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 0.0] }, Vertex{ position: [ 1.0, 1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 0.0] }, Vertex{ position: [ 1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 1.0] }, - Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 1.0] }, ], CrossFace::Face2 => [ Vertex{ position: [-1.0, 1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 0.0] }, Vertex{ position: [ 1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 0.0] }, - Vertex{ position: [ 1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 1.0] }, + Vertex{ position: [ 1.0, -1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 1.0] }, Vertex{ position: [-1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 1.0] }, ], } From 9fa2d7a3a998f9e0102f276378833c15b1fe43b1 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 17:37:49 +0100 Subject: [PATCH 20/45] Fix Face2, linter --- src/client/terrain/util/mesher.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index f8368fe..7e56fef 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -31,8 +31,7 @@ fn create_cross_geometry( let mut index_offset = 0; - // let cross_faces = [CrossFace::Face1, CrossFace::Face2]; - let cross_faces = [CrossFace::Face1]; + let cross_faces = [CrossFace::Face1, CrossFace::Face2]; cross_faces.iter().for_each(|cross_face| { let face_verticies = cross_face_vertices(*cross_face); @@ -226,9 +225,9 @@ pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Opt geometry_data.indices.extend( cube_data - .indices - .iter() - .map(|i| i + geometry_data.position.len() as u32), + .indices + .iter() + .map(|i| i + geometry_data.position.len() as u32), ); geometry_data.position.extend(cube_data.position); geometry_data.uv.extend(cube_data.uv); From 7697f16274697b64cc11d2a17540781fe3888352 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 18:11:41 +0100 Subject: [PATCH 21/45] Update normals --- src/client/terrain/util/mesher.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 7e56fef..eba93b6 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -323,16 +323,16 @@ fn face_vertices(face_index: CubeFace) -> [Vertex; 4] { fn cross_face_vertices(face: CrossFace) -> [Vertex; 4] { match face { CrossFace::Face1 => [ - Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 0.0] }, - Vertex{ position: [ 1.0, 1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 0.0] }, - Vertex{ position: [ 1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 1.0] }, - Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.7071, 0.0, -0.7071], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, 1.0], normal: [0.7071, 0.0, -0.7071], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, 1.0], normal: [0.7071, 0.0, -0.7071], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.7071, 0.0, -0.7071], uv: [0.0, 1.0] }, ], CrossFace::Face2 => [ - Vertex{ position: [-1.0, 1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 0.0] }, - Vertex{ position: [ 1.0, 1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 0.0] }, - Vertex{ position: [ 1.0, -1.0, -1.0], normal: [0.0, 0.0, 0.0], uv: [1.0, 1.0] }, - Vertex{ position: [-1.0, -1.0, 1.0], normal: [0.0, 0.0, 0.0], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, 1.0], normal: [-0.7071, 0.0, -0.7071], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, -1.0], normal: [-0.7071, 0.0, -0.7071], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, -1.0], normal: [-0.7071, 0.0, -0.7071], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, 1.0], normal: [-0.7071, 0.0, -0.7071], uv: [0.0, 1.0] }, ], } } From dddb991fd0c30d8371d6407b68e99cc6b950e1dd Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 18:11:53 +0100 Subject: [PATCH 22/45] Update grass material properties --- src/client/terrain/systems.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 0c50e59..d1f7339 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -183,7 +183,7 @@ fn create_transparent_material( mut materials: ResMut>, ) -> Handle { materials.add(StandardMaterial { - perceptual_roughness: 0.5, + perceptual_roughness: 1.0, double_sided: true, cull_mode: None, reflectance: 0.0, From c540744093f046b7f03dfda34a31bea68363c789 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 18:15:30 +0100 Subject: [PATCH 23/45] Add grass slider --- src/server/terrain/systems.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/terrain/systems.rs b/src/server/terrain/systems.rs index b4d78b0..1c64baf 100644 --- a/src/server/terrain/systems.rs +++ b/src/server/terrain/systems.rs @@ -308,12 +308,14 @@ mod visualizer { ui.group(|ui| { ui.vertical(|ui| { ui.label("Trees"); - add_slider_const!(ui, &mut generator.params.tree.spawn_attempts_per_chunk, 0..=1000, "spawn attempts"); add_slider_const!(ui, &mut generator.params.tree.min_stump_height, 0..=20, "min_stump_height"); add_slider_const!(ui, &mut generator.params.tree.max_stump_height, 0..=20, "max_stump_height"); add_slider_const!(ui, &mut generator.params.tree.min_bush_radius, 0..=10, "min_bush_radius"); add_slider_const!(ui, &mut generator.params.tree.max_bush_radius, 0..=10, "max_bush_radius"); + + ui.label("Grass"); + add_slider_const!(ui, &mut generator.params.grass.spawn_attempts_per_chunk, 0..=3000, "spawn attempts"); }); }); From 60e28b0cbbc07e9807cf86566f6344b1df7b0c49 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 18:18:17 +0100 Subject: [PATCH 24/45] Make mesher more readable --- src/client/terrain/util/mesher.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index eba93b6..0e68542 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -323,16 +323,16 @@ fn face_vertices(face_index: CubeFace) -> [Vertex; 4] { fn cross_face_vertices(face: CrossFace) -> [Vertex; 4] { match face { CrossFace::Face1 => [ - Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.7071, 0.0, -0.7071], uv: [0.0, 0.0] }, - Vertex{ position: [ 1.0, 1.0, 1.0], normal: [0.7071, 0.0, -0.7071], uv: [1.0, 0.0] }, - Vertex{ position: [ 1.0, -1.0, 1.0], normal: [0.7071, 0.0, -0.7071], uv: [1.0, 1.0] }, - Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.7071, 0.0, -0.7071], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, -1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, 1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, 1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, -1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 1.0] }, ], CrossFace::Face2 => [ - Vertex{ position: [-1.0, 1.0, 1.0], normal: [-0.7071, 0.0, -0.7071], uv: [0.0, 0.0] }, - Vertex{ position: [ 1.0, 1.0, -1.0], normal: [-0.7071, 0.0, -0.7071], uv: [1.0, 0.0] }, - Vertex{ position: [ 1.0, -1.0, -1.0], normal: [-0.7071, 0.0, -0.7071], uv: [1.0, 1.0] }, - Vertex{ position: [-1.0, -1.0, 1.0], normal: [-0.7071, 0.0, -0.7071], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, 1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, -1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, -1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, 1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 1.0] }, ], } } From 1a66ca7eb1e0bd06a7d2169115e1a1f344458829 Mon Sep 17 00:00:00 2001 From: Daniel Bengl <53896675+CuddlyBunion341@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:22:16 +0100 Subject: [PATCH 25/45] Update src/shared/chunk_serializer.rs --- src/shared/chunk_serializer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shared/chunk_serializer.rs b/src/shared/chunk_serializer.rs index 440e493..18294f2 100644 --- a/src/shared/chunk_serializer.rs +++ b/src/shared/chunk_serializer.rs @@ -12,7 +12,6 @@ impl Serialize for Chunk { where S: serde::Serializer, { - // let data_as_u8: Vec = self.data.iter().map(|block_id| *block_id.into()).collect(); let data_as_u8: Vec = self .data .iter() From 1b3dfe1d95a32e88a70de1c12a89a8f6f85833f6 Mon Sep 17 00:00:00 2001 From: Daniel Bengl <53896675+CuddlyBunion341@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:22:45 +0100 Subject: [PATCH 26/45] Update src/client/terrain/systems.rs --- src/client/terrain/systems.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index d1f7339..9583437 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -59,7 +59,7 @@ pub fn generate_world_system( mut client: ResMut, mut chunk_manager: ResMut, ) { - let render_distance = Vec3::new(1.0, 1.0, 1.0); + let render_distance = Vec3::new(4.0, 4.0, 4.0); info!("Sending chunk requests for chunks"); From 494671a9003924b800f067fbb02e26d6fd2aa324 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 18:54:18 +0100 Subject: [PATCH 27/45] Refactor mesher --- src/client/terrain/util/cross_mesher.rs | 119 ++++++++ src/client/terrain/util/cube_mesher.rs | 220 +++++++++++++++ src/client/terrain/util/mesher.rs | 354 +----------------------- src/client/terrain/util/mod.rs | 4 + 4 files changed, 347 insertions(+), 350 deletions(-) create mode 100644 src/client/terrain/util/cross_mesher.rs create mode 100644 src/client/terrain/util/cube_mesher.rs diff --git a/src/client/terrain/util/cross_mesher.rs b/src/client/terrain/util/cross_mesher.rs new file mode 100644 index 0000000..3df30eb --- /dev/null +++ b/src/client/terrain/util/cross_mesher.rs @@ -0,0 +1,119 @@ +use terrain_util::{ + client_block::{block_properties, MeshRepresentation}, create_cube_mesh_from_data, GeometryData, TextureManager, TextureName, Vertex +}; + +use crate::prelude::*; + +pub fn instance_mesh_for_repr( + rep: MeshRepresentation, + texture_manager: &TextureManager, +) -> Option { + match rep { + MeshRepresentation::None => None, + MeshRepresentation::Cube(_) => None, + MeshRepresentation::Cross(textures) => { + let geometry_data = create_cross_geometry(textures, texture_manager); + // TODO: refactor rename to create_mesh_from_data because it is not cube representation specific + create_cube_mesh_from_data(geometry_data) + } + } +} + +fn create_cross_geometry( + textures: [TextureName; 2], + texture_manager: &TextureManager, +) -> GeometryData { + let mut position = vec![]; + let mut uv = vec![]; + let mut normal = vec![]; + let mut indices = vec![]; + + let mut index_offset = 0; + + let cross_faces = [CrossFace::Face1, CrossFace::Face2]; + + cross_faces.iter().for_each(|cross_face| { + let face_verticies = cross_face_vertices(*cross_face); + + let face_uv = texture_manager + .get_texture_uv(textures[0]) + .expect("Texture is not present in manager"); + + for vertex in face_verticies { + position.push([ + vertex.position[0] * 0.5 + 0.5, + vertex.position[1] * 0.5 + 0.5, + vertex.position[2] * 0.5 + 0.5, + ]); + + uv.push([ + face_uv[0] + vertex.uv[0] * 0.25, + face_uv[1] + vertex.uv[1] * 0.25, + ]); + normal.push(vertex.normal); + } + + let offsets = [0, 1, 3, 1, 2, 3]; + offsets.iter().for_each(|offset| { + indices.push(index_offset + offset); + }); + + index_offset += 4; + }); + + GeometryData { + position, + uv, + normal, + indices, + } +} + +pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { + let mut map: HashMap> = HashMap::new(); + + for x in 0..CHUNK_SIZE { + for y in 0..CHUNK_SIZE { + for z in 0..CHUNK_SIZE { + let block_id = chunk.get(x, y, z); + let pos = Vec3::new(x as f32, y as f32, z as f32); + let mesh_repr = block_properties(block_id).mesh_representation; + + if let MeshRepresentation::Cross(_) = mesh_repr { + match map.get_mut(&mesh_repr) { + Some(positions) => positions.push(pos), + None => { + map.insert(mesh_repr, vec![pos]); + } + }; + } + } + } + } + + map +} + +#[derive(Debug, Clone, Copy)] +pub enum CrossFace { + Face1, + Face2, +} + +#[rustfmt::skip] +fn cross_face_vertices(face: CrossFace) -> [Vertex; 4] { + match face { + CrossFace::Face1 => [ + Vertex{ position: [-1.0, 1.0, -1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, 1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, 1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, -1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 1.0] }, + ], + CrossFace::Face2 => [ + Vertex{ position: [-1.0, 1.0, 1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 0.0] }, + Vertex{ position: [ 1.0, 1.0, -1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 0.0] }, + Vertex{ position: [ 1.0, -1.0, -1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, 1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 1.0] }, + ], + } +} diff --git a/src/client/terrain/util/cube_mesher.rs b/src/client/terrain/util/cube_mesher.rs new file mode 100644 index 0000000..19a3254 --- /dev/null +++ b/src/client/terrain/util/cube_mesher.rs @@ -0,0 +1,220 @@ +use terrain_util::{ + client_block::{block_properties, MeshRepresentation}, create_cube_mesh_from_data, GeometryData, TextureManager, TextureName, Vertex +}; + +use crate::prelude::*; + +pub fn create_cube_geometry_data( + x: f32, + y: f32, + z: f32, + faces: u8, + block_id: BlockId, + texture_manager: &TextureManager, +) -> GeometryData { + let mut position = Vec::new(); + let mut uv = Vec::new(); + let mut normal = Vec::new(); + let mut indices = Vec::new(); + let mut index_offset = 0; + + CUBE_FACES.iter().enumerate().for_each(|(i, face)| { + if faces & (1 << i) == 0 { + return; + } + + let face_vertices = face_vertices(*face); + for vertex in face_vertices.iter() { + position.push([ + vertex.position[0] * 0.5 + x + 0.5, + vertex.position[1] * 0.5 + y + 0.5, + vertex.position[2] * 0.5 + z + 0.5, + ]); + + let block_uvs = Block::get_block_face_uvs(block_id, *face, texture_manager).unwrap(); + uv.push([ + block_uvs[0] + vertex.uv[0] * 0.25 - 0.001, + block_uvs[1] + (1.0 - vertex.uv[1]) * 0.25, + ]); + normal.push(vertex.normal); + } + + let offsets = [0, 1, 2, 2, 1, 3]; + offsets.iter().for_each(|offset| { + indices.push(index_offset + offset); + }); + index_offset += 4; + }); + + GeometryData { + position, + uv, + normal, + indices, + } +} + +pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Option { + let mut geometry_data = GeometryData { + position: Vec::new(), + uv: Vec::new(), + normal: Vec::new(), + indices: Vec::new(), + }; + + for x in 1..CHUNK_SIZE + 1 { + for y in 1..CHUNK_SIZE + 1 { + for z in 1..CHUNK_SIZE + 1 { + let block_id = chunk.get_unpadded(x, y, z); + + match block_properties(block_id).mesh_representation { + MeshRepresentation::Cube(_) => {} + _ => continue, + } + + fn update_mask( + chunk: &Chunk, + mask: &mut u8, + value: u8, + x: usize, + y: usize, + z: usize, + ) { + match block_properties(chunk.get_unpadded(x, y, z)).mesh_representation { + MeshRepresentation::Cube(_) => {} + _ => *mask |= value, + } + } + + let mut mask = 0b000000; + + update_mask(chunk, &mut mask, 0b000001, x, y + 1, z); + update_mask(chunk, &mut mask, 0b000010, x, y - 1, z); + + update_mask(chunk, &mut mask, 0b000100, x + 1, y, z); + update_mask(chunk, &mut mask, 0b001000, x - 1, y, z); + + update_mask(chunk, &mut mask, 0b010000, x, y, z - 1); + update_mask(chunk, &mut mask, 0b100000, x, y, z + 1); + + let cube_data = create_cube_geometry_data( + (x - 1) as f32, + (y - 1) as f32, + (z - 1) as f32, + mask, + block_id, + texture_manager, + ); + + geometry_data.indices.extend( + cube_data + .indices + .iter() + .map(|i| i + geometry_data.position.len() as u32), + ); + geometry_data.position.extend(cube_data.position); + geometry_data.uv.extend(cube_data.uv); + geometry_data.normal.extend(cube_data.normal); + } + } + } + + create_cube_mesh_from_data(geometry_data) +} + +#[derive(Debug, Clone, Copy)] +pub enum CubeFace { + Top, + Bottom, + Right, + Left, + Back, + Forward, +} + +const CUBE_FACES: [CubeFace; 6] = [ + CubeFace::Top, + CubeFace::Bottom, + CubeFace::Right, + CubeFace::Left, + CubeFace::Back, + CubeFace::Forward, +]; + +#[rustfmt::skip] +fn face_vertices(face_index: CubeFace) -> [Vertex; 4] { + match face_index { + CubeFace::Left => [ + Vertex{ position: [-1.0, -1.0, -1.0], normal: [-1.0, 0.0, 0.0], uv: [0.0, 0.0] }, + Vertex{ position: [-1.0, -1.0, 1.0], normal: [-1.0, 0.0, 0.0], uv: [1.0, 0.0] }, + Vertex{ position: [-1.0, 1.0, -1.0], normal: [-1.0, 0.0, 0.0], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, 1.0], normal: [-1.0, 0.0, 0.0], uv: [1.0, 1.0] }, + ], + CubeFace::Right => [ + Vertex{ position: [1.0, -1.0, 1.0], normal: [1.0, 0.0, 0.0], uv: [0.0, 0.0] }, + Vertex{ position: [1.0, -1.0, -1.0], normal: [1.0, 0.0, 0.0], uv: [1.0, 0.0] }, + Vertex{ position: [1.0, 1.0, 1.0], normal: [1.0, 0.0, 0.0], uv: [0.0, 1.0] }, + Vertex{ position: [1.0, 1.0, -1.0], normal: [1.0, 0.0, 0.0], uv: [1.0, 1.0] }, + ], + CubeFace::Bottom => [ + Vertex{ position: [1.0, -1.0, 1.0], normal: [0.0, -1.0, 0.0], uv: [0.0, 0.0] }, + Vertex{ position: [-1.0, -1.0, 1.0], normal: [0.0, -1.0, 0.0], uv: [1.0, 0.0] }, + Vertex{ position: [1.0, -1.0, -1.0], normal: [0.0, -1.0, 0.0], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.0, -1.0, 0.0], uv: [1.0, 1.0] }, + ], + CubeFace::Top => [ + Vertex{ position: [1.0, 1.0, -1.0], normal: [0.0, 1.0, 0.0], uv: [0.0, 0.0] }, + Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 1.0, 0.0], uv: [1.0, 0.0] }, + Vertex{ position: [1.0, 1.0, 1.0], normal: [0.0, 1.0, 0.0], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, 1.0], normal: [0.0, 1.0, 0.0], uv: [1.0, 1.0] }, + ], + CubeFace::Back => [ + Vertex{ position: [1.0, -1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [0.0, 0.0] }, + Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [1.0, 0.0] }, + Vertex{ position: [1.0, 1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [0.0, 1.0] }, + Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [1.0, 1.0] }, + ], + CubeFace::Forward => [ + Vertex{ position: [-1.0, -1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [0.0, 0.0] }, + Vertex{ position: [1.0, -1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [1.0, 0.0] }, + Vertex{ position: [-1.0, 1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [0.0, 1.0] }, + Vertex{ position: [1.0, 1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [1.0, 1.0] } + ], + } +} + +#[cfg(test)] +mod tests { + use super::*; + use terrain_util::TextureManager; + + #[test] + fn test_create_cube_mesh_from_data() { + let geometry_data = GeometryData { + position: vec![ + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [1.0, 1.0, 0.0], + [0.0, 1.0, 0.0], + ], + uv: vec![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], + normal: vec![[0.0, 0.0, 1.0]; 4], + indices: vec![0, 1, 2, 2, 3, 0], + }; + + let mesh = create_cube_mesh_from_data(geometry_data); + assert!(mesh.is_some()); + } + + #[test] + fn test_create_cube_geometry_data() { + let texture_manager = TextureManager::new(); + let geometry_data = + create_cube_geometry_data(0.0, 0.0, 0.0, 0b111111, BlockId::Stone, &texture_manager); + + assert_eq!(geometry_data.position.len(), 6 * 4); + assert_eq!(geometry_data.uv.len(), 6 * 4); + assert_eq!(geometry_data.normal.len(), 6 * 4); + assert_eq!(geometry_data.indices.len(), 6 * 6); + } +} diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index 0e68542..e35aa94 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -5,96 +5,6 @@ use terrain_util::{ use crate::prelude::*; -pub fn instance_mesh_for_repr( - rep: MeshRepresentation, - texture_manager: &TextureManager, -) -> Option { - match rep { - MeshRepresentation::None => None, - MeshRepresentation::Cube(_) => None, - MeshRepresentation::Cross(textures) => { - let geometry_data = create_cross_geometry(textures, texture_manager); - // TODO: refactor rename to create_mesh_from_data because it is not cube representation specific - create_cube_mesh_from_data(geometry_data) - } - } -} - -fn create_cross_geometry( - textures: [TextureName; 2], - texture_manager: &TextureManager, -) -> GeometryData { - let mut position = vec![]; - let mut uv = vec![]; - let mut normal = vec![]; - let mut indices = vec![]; - - let mut index_offset = 0; - - let cross_faces = [CrossFace::Face1, CrossFace::Face2]; - - cross_faces.iter().for_each(|cross_face| { - let face_verticies = cross_face_vertices(*cross_face); - - let face_uv = texture_manager - .get_texture_uv(textures[0]) - .expect("Texture is not present in manager"); - - for vertex in face_verticies { - position.push([ - vertex.position[0] * 0.5 + 0.5, - vertex.position[1] * 0.5 + 0.5, - vertex.position[2] * 0.5 + 0.5, - ]); - - uv.push([ - face_uv[0] + vertex.uv[0] * 0.25, - face_uv[1] + vertex.uv[1] * 0.25, - ]); - normal.push(vertex.normal); - } - - let offsets = [0, 1, 3, 1, 2, 3]; - offsets.iter().for_each(|offset| { - indices.push(index_offset + offset); - }); - - index_offset += 4; - }); - - GeometryData { - position, - uv, - normal, - indices, - } -} - -pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { - let mut map: HashMap> = HashMap::new(); - - for x in 0..CHUNK_SIZE { - for y in 0..CHUNK_SIZE { - for z in 0..CHUNK_SIZE { - let block_id = chunk.get(x, y, z); - let pos = Vec3::new(x as f32, y as f32, z as f32); - let mesh_repr = block_properties(block_id).mesh_representation; - - if let MeshRepresentation::Cross(_) = mesh_repr { - match map.get_mut(&mesh_repr) { - Some(positions) => positions.push(pos), - None => { - map.insert(mesh_repr, vec![pos]); - } - }; - } - } - } - } - - map -} - pub fn create_cube_mesh_from_data(geometry_data: GeometryData) -> Option { let GeometryData { position, @@ -121,153 +31,10 @@ pub fn create_cube_mesh_from_data(geometry_data: GeometryData) -> Option { ) } -pub fn create_cube_geometry_data( - x: f32, - y: f32, - z: f32, - faces: u8, - block_id: BlockId, - texture_manager: &TextureManager, -) -> GeometryData { - let mut position = Vec::new(); - let mut uv = Vec::new(); - let mut normal = Vec::new(); - let mut indices = Vec::new(); - let mut index_offset = 0; - - CUBE_FACES.iter().enumerate().for_each(|(i, face)| { - if faces & (1 << i) == 0 { - return; - } - - let face_vertices = face_vertices(*face); - for vertex in face_vertices.iter() { - position.push([ - vertex.position[0] * 0.5 + x + 0.5, - vertex.position[1] * 0.5 + y + 0.5, - vertex.position[2] * 0.5 + z + 0.5, - ]); - - let block_uvs = Block::get_block_face_uvs(block_id, *face, texture_manager).unwrap(); - uv.push([ - block_uvs[0] + vertex.uv[0] * 0.25 - 0.001, - block_uvs[1] + (1.0 - vertex.uv[1]) * 0.25, - ]); - normal.push(vertex.normal); - } - - let offsets = [0, 1, 2, 2, 1, 3]; - offsets.iter().for_each(|offset| { - indices.push(index_offset + offset); - }); - index_offset += 4; - }); - - GeometryData { - position, - uv, - normal, - indices, - } -} - -pub fn create_chunk_mesh(chunk: &Chunk, texture_manager: &TextureManager) -> Option { - let mut geometry_data = GeometryData { - position: Vec::new(), - uv: Vec::new(), - normal: Vec::new(), - indices: Vec::new(), - }; - - for x in 1..CHUNK_SIZE + 1 { - for y in 1..CHUNK_SIZE + 1 { - for z in 1..CHUNK_SIZE + 1 { - let block_id = chunk.get_unpadded(x, y, z); - - match block_properties(block_id).mesh_representation { - MeshRepresentation::Cube(_) => {} - _ => continue, - } - - fn update_mask( - chunk: &Chunk, - mask: &mut u8, - value: u8, - x: usize, - y: usize, - z: usize, - ) { - match block_properties(chunk.get_unpadded(x, y, z)).mesh_representation { - MeshRepresentation::Cube(_) => {} - _ => *mask |= value, - } - } - - let mut mask = 0b000000; - - update_mask(chunk, &mut mask, 0b000001, x, y + 1, z); - update_mask(chunk, &mut mask, 0b000010, x, y - 1, z); - - update_mask(chunk, &mut mask, 0b000100, x + 1, y, z); - update_mask(chunk, &mut mask, 0b001000, x - 1, y, z); - - update_mask(chunk, &mut mask, 0b010000, x, y, z - 1); - update_mask(chunk, &mut mask, 0b100000, x, y, z + 1); - - let cube_data = create_cube_geometry_data( - (x - 1) as f32, - (y - 1) as f32, - (z - 1) as f32, - mask, - block_id, - texture_manager, - ); - - geometry_data.indices.extend( - cube_data - .indices - .iter() - .map(|i| i + geometry_data.position.len() as u32), - ); - geometry_data.position.extend(cube_data.position); - geometry_data.uv.extend(cube_data.uv); - geometry_data.normal.extend(cube_data.normal); - } - } - } - - create_cube_mesh_from_data(geometry_data) -} - -#[derive(Debug, Clone, Copy)] -pub enum CubeFace { - Top, - Bottom, - Right, - Left, - Back, - Forward, -} - -#[derive(Debug, Clone, Copy)] -pub enum CrossFace { - Face1, - Face2, -} - -const CUBE_FACES: [CubeFace; 6] = [ - CubeFace::Top, - CubeFace::Bottom, - CubeFace::Right, - CubeFace::Left, - CubeFace::Back, - CubeFace::Forward, -]; - -struct Vertex { - position: [f32; 3], - uv: [f32; 2], - normal: [f32; 3], +pub struct Vertex { + pub position: [f32; 3], + pub uv: [f32; 2], + pub normal: [f32; 3], } pub struct GeometryData { @@ -276,116 +43,3 @@ pub struct GeometryData { pub normal: Vec<[f32; 3]>, pub indices: Vec, } - -#[rustfmt::skip] -fn face_vertices(face_index: CubeFace) -> [Vertex; 4] { - match face_index { - CubeFace::Left => [ - Vertex{ position: [-1.0, -1.0, -1.0], normal: [-1.0, 0.0, 0.0], uv: [0.0, 0.0] }, - Vertex{ position: [-1.0, -1.0, 1.0], normal: [-1.0, 0.0, 0.0], uv: [1.0, 0.0] }, - Vertex{ position: [-1.0, 1.0, -1.0], normal: [-1.0, 0.0, 0.0], uv: [0.0, 1.0] }, - Vertex{ position: [-1.0, 1.0, 1.0], normal: [-1.0, 0.0, 0.0], uv: [1.0, 1.0] }, - ], - CubeFace::Right => [ - Vertex{ position: [1.0, -1.0, 1.0], normal: [1.0, 0.0, 0.0], uv: [0.0, 0.0] }, - Vertex{ position: [1.0, -1.0, -1.0], normal: [1.0, 0.0, 0.0], uv: [1.0, 0.0] }, - Vertex{ position: [1.0, 1.0, 1.0], normal: [1.0, 0.0, 0.0], uv: [0.0, 1.0] }, - Vertex{ position: [1.0, 1.0, -1.0], normal: [1.0, 0.0, 0.0], uv: [1.0, 1.0] }, - ], - CubeFace::Bottom => [ - Vertex{ position: [1.0, -1.0, 1.0], normal: [0.0, -1.0, 0.0], uv: [0.0, 0.0] }, - Vertex{ position: [-1.0, -1.0, 1.0], normal: [0.0, -1.0, 0.0], uv: [1.0, 0.0] }, - Vertex{ position: [1.0, -1.0, -1.0], normal: [0.0, -1.0, 0.0], uv: [0.0, 1.0] }, - Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.0, -1.0, 0.0], uv: [1.0, 1.0] }, - ], - CubeFace::Top => [ - Vertex{ position: [1.0, 1.0, -1.0], normal: [0.0, 1.0, 0.0], uv: [0.0, 0.0] }, - Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 1.0, 0.0], uv: [1.0, 0.0] }, - Vertex{ position: [1.0, 1.0, 1.0], normal: [0.0, 1.0, 0.0], uv: [0.0, 1.0] }, - Vertex{ position: [-1.0, 1.0, 1.0], normal: [0.0, 1.0, 0.0], uv: [1.0, 1.0] }, - ], - CubeFace::Back => [ - Vertex{ position: [1.0, -1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [0.0, 0.0] }, - Vertex{ position: [-1.0, -1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [1.0, 0.0] }, - Vertex{ position: [1.0, 1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [0.0, 1.0] }, - Vertex{ position: [-1.0, 1.0, -1.0], normal: [0.0, 0.0, -1.0], uv: [1.0, 1.0] }, - ], - CubeFace::Forward => [ - Vertex{ position: [-1.0, -1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [0.0, 0.0] }, - Vertex{ position: [1.0, -1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [1.0, 0.0] }, - Vertex{ position: [-1.0, 1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [0.0, 1.0] }, - Vertex{ position: [1.0, 1.0, 1.0], normal: [0.0, 0.0, 1.0], uv: [1.0, 1.0] } - ], - } -} - -#[rustfmt::skip] -fn cross_face_vertices(face: CrossFace) -> [Vertex; 4] { - match face { - CrossFace::Face1 => [ - Vertex{ position: [-1.0, 1.0, -1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 0.0] }, - Vertex{ position: [ 1.0, 1.0, 1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 0.0] }, - Vertex{ position: [ 1.0, -1.0, 1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 1.0] }, - Vertex{ position: [-1.0, -1.0, -1.0], normal: [FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 1.0] }, - ], - CrossFace::Face2 => [ - Vertex{ position: [-1.0, 1.0, 1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 0.0] }, - Vertex{ position: [ 1.0, 1.0, -1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 0.0] }, - Vertex{ position: [ 1.0, -1.0, -1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [1.0, 1.0] }, - Vertex{ position: [-1.0, -1.0, 1.0], normal: [-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2], uv: [0.0, 1.0] }, - ], - } -} - -#[cfg(test)] -mod tests { - use super::*; - use terrain_util::TextureManager; - - #[test] - fn test_create_cube_mesh_from_data() { - let geometry_data = GeometryData { - position: vec![ - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [1.0, 1.0, 0.0], - [0.0, 1.0, 0.0], - ], - uv: vec![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], - normal: vec![[0.0, 0.0, 1.0]; 4], - indices: vec![0, 1, 2, 2, 3, 0], - }; - - let mesh = create_cube_mesh_from_data(geometry_data); - assert!(mesh.is_some()); - } - - #[test] - fn test_create_cube_geometry_data() { - let texture_manager = TextureManager::new(); - let geometry_data = - create_cube_geometry_data(0.0, 0.0, 0.0, 0b111111, BlockId::Stone, &texture_manager); - - assert_eq!(geometry_data.position.len(), 6 * 4); - assert_eq!(geometry_data.uv.len(), 6 * 4); - assert_eq!(geometry_data.normal.len(), 6 * 4); - assert_eq!(geometry_data.indices.len(), 6 * 6); - } - - #[test] - fn test_create_chunk_mesh() { - let texture_manager = TextureManager::new(); - let mut chunk = Chunk::new(Vec3::new(0.0, 0.0, 0.0)); - - for x in 1..CHUNK_SIZE + 1 { - for y in 1..CHUNK_SIZE + 1 { - for z in 1..CHUNK_SIZE + 1 { - chunk.set_unpadded(x, y, z, BlockId::Stone); - } - } - } - - let mesh = create_chunk_mesh(&chunk, &texture_manager); - assert!(mesh.is_some()); - } -} diff --git a/src/client/terrain/util/mod.rs b/src/client/terrain/util/mod.rs index 664bacd..eeed485 100644 --- a/src/client/terrain/util/mod.rs +++ b/src/client/terrain/util/mod.rs @@ -1,5 +1,9 @@ pub mod blocks; pub mod mesher; +pub mod cube_mesher; +pub mod cross_mesher; pub use blocks::*; pub use mesher::*; +pub use cube_mesher::*; +pub use cross_mesher::*; From 53fc448197daf41a51ed76f44a33666871c4d0c3 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 18:54:32 +0100 Subject: [PATCH 28/45] Fix linter --- src/client/terrain/util/cross_mesher.rs | 3 ++- src/client/terrain/util/cube_mesher.rs | 3 ++- src/client/terrain/util/mesher.rs | 5 ----- src/client/terrain/util/mod.rs | 8 ++++---- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/client/terrain/util/cross_mesher.rs b/src/client/terrain/util/cross_mesher.rs index 3df30eb..2d48413 100644 --- a/src/client/terrain/util/cross_mesher.rs +++ b/src/client/terrain/util/cross_mesher.rs @@ -1,5 +1,6 @@ use terrain_util::{ - client_block::{block_properties, MeshRepresentation}, create_cube_mesh_from_data, GeometryData, TextureManager, TextureName, Vertex + client_block::{block_properties, MeshRepresentation}, + create_cube_mesh_from_data, GeometryData, TextureManager, TextureName, Vertex, }; use crate::prelude::*; diff --git a/src/client/terrain/util/cube_mesher.rs b/src/client/terrain/util/cube_mesher.rs index 19a3254..af14b83 100644 --- a/src/client/terrain/util/cube_mesher.rs +++ b/src/client/terrain/util/cube_mesher.rs @@ -1,5 +1,6 @@ use terrain_util::{ - client_block::{block_properties, MeshRepresentation}, create_cube_mesh_from_data, GeometryData, TextureManager, TextureName, Vertex + client_block::{block_properties, MeshRepresentation}, + create_cube_mesh_from_data, GeometryData, TextureManager, Vertex, }; use crate::prelude::*; diff --git a/src/client/terrain/util/mesher.rs b/src/client/terrain/util/mesher.rs index e35aa94..a15198f 100644 --- a/src/client/terrain/util/mesher.rs +++ b/src/client/terrain/util/mesher.rs @@ -1,8 +1,3 @@ -use terrain_util::{ - client_block::{block_properties, MeshRepresentation}, - TextureManager, TextureName, -}; - use crate::prelude::*; pub fn create_cube_mesh_from_data(geometry_data: GeometryData) -> Option { diff --git a/src/client/terrain/util/mod.rs b/src/client/terrain/util/mod.rs index eeed485..c40fafe 100644 --- a/src/client/terrain/util/mod.rs +++ b/src/client/terrain/util/mod.rs @@ -1,9 +1,9 @@ pub mod blocks; -pub mod mesher; -pub mod cube_mesher; pub mod cross_mesher; +pub mod cube_mesher; +pub mod mesher; pub use blocks::*; -pub use mesher::*; -pub use cube_mesher::*; pub use cross_mesher::*; +pub use cube_mesher::*; +pub use mesher::*; From 1822949a9b2f4a7fd31b0032989bc36aefb37408 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 18:58:18 +0100 Subject: [PATCH 29/45] Fix grass cleanup --- src/client/terrain/systems.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 9583437..7e690ef 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -166,6 +166,13 @@ fn add_cross_objects(commands: &mut Commands, chunk: &Chunk, mesher: &Mesher) { chunk.position.y * CHUNK_SIZE as f32 + position.y, chunk.position.z * CHUNK_SIZE as f32 + position.z, ), + terrain_components::ChunkMesh { + key: [ + chunk.position.x as i32, + chunk.position.y as i32, + chunk.position.z as i32, + ], + }, )); } } From 3e530ac7c4a1824a73a53e6a581ec0f019f2463e Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sat, 8 Mar 2025 19:16:56 +0100 Subject: [PATCH 30/45] Fix floating grass --- src/client/collider/systems.rs | 2 +- src/client/player/systems/terrain.rs | 2 +- src/shared/blocks.rs | 4 ++++ src/shared/terrain.rs | 19 ++++++++++++++++--- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/client/collider/systems.rs b/src/client/collider/systems.rs index e504066..528d17b 100644 --- a/src/client/collider/systems.rs +++ b/src/client/collider/systems.rs @@ -135,7 +135,7 @@ mod tests { Vec3::new(1.0, 1.0, 1.0), ); resource.insert_chunks(chunks); - resource.set_block( + resource.update_block( Vec3 { x: 6.0, y: 7.0, diff --git a/src/client/player/systems/terrain.rs b/src/client/player/systems/terrain.rs index 398c87e..33346b1 100644 --- a/src/client/player/systems/terrain.rs +++ b/src/client/player/systems/terrain.rs @@ -8,7 +8,7 @@ pub fn handle_block_update_events( mut client: ResMut, ) { for event in block_update_events.read() { - chunk_manager.set_block(event.position, event.block); + chunk_manager.update_block(event.position, event.block); info!("Block update message: {:?}", event.position); chunk_mesh_update_events.send(terrain_events::ChunkMeshUpdateEvent { diff --git a/src/shared/blocks.rs b/src/shared/blocks.rs index e180ff3..300c8b9 100644 --- a/src/shared/blocks.rs +++ b/src/shared/blocks.rs @@ -69,4 +69,8 @@ impl BlockId { Tallgrass, ] } + + pub fn supports_grass(&self) -> bool { + *self == Grass || *self == Dirt + } } diff --git a/src/shared/terrain.rs b/src/shared/terrain.rs index 5f532c5..cdfd5d1 100644 --- a/src/shared/terrain.rs +++ b/src/shared/terrain.rs @@ -44,6 +44,19 @@ impl Chunk { self.set_unpadded(x + 1, y + 1, z + 1, value); } + pub fn update(&mut self, x: usize, y: usize, z: usize, value: BlockId) { + if Self::valid_padded(x, y, z) { + self.set(x, y, z, value); + } + + if !value.supports_grass() + && Self::valid_padded(x, y + 1, z) + && self.get(x, y + 1, z) == BlockId::Tallgrass + { + self.set(x, y + 1, z, BlockId::Air); + } + } + pub fn set_unpadded(&mut self, x: usize, y: usize, z: usize, value: BlockId) { self.data[Self::index(x, y, z)] = value; } @@ -155,7 +168,7 @@ impl ChunkManager { self.chunks.get_mut(&[x as i32, y as i32, z as i32]) } - pub fn set_block(&mut self, position: Vec3, block: BlockId) { + pub fn update_block(&mut self, position: Vec3, block: BlockId) { match self.chunk_from_selection(position) { Some(chunk) => { let chunk_position = Vec3::new( @@ -164,7 +177,7 @@ impl ChunkManager { chunk.position[2] * CHUNK_SIZE as f32, ); let local_position = (position - chunk_position).floor(); - chunk.set( + chunk.update( local_position.x as usize, local_position.y as usize, local_position.z as usize, @@ -280,7 +293,7 @@ mod tests { let block_position = Vec3::new(1.0, 1.0, 1.0); let block_id = BlockId::Stone; - chunk_manager.set_block(block_position, block_id); + chunk_manager.update_block(block_position, block_id); let retrieved_block = chunk_manager.get_block(block_position).unwrap(); assert_eq!(retrieved_block, block_id); } From b2dc45826a94fa7924e9abc2df494d92e801a559 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:00:07 +0100 Subject: [PATCH 31/45] Drastically improve performance by not instancing --- src/client/terrain/mod.rs | 1 - src/client/terrain/systems.rs | 85 ++++++++---------- src/client/terrain/util/cross_mesher.rs | 111 ++++++++++-------------- src/client/terrain/util/cube_mesher.rs | 2 +- 4 files changed, 85 insertions(+), 114 deletions(-) diff --git a/src/client/terrain/mod.rs b/src/client/terrain/mod.rs index 824e1f9..3a76c78 100644 --- a/src/client/terrain/mod.rs +++ b/src/client/terrain/mod.rs @@ -17,7 +17,6 @@ impl Plugin for TerrainPlugin { app.add_event::(); app.add_event::(); app.add_event::(); - app.add_systems(Startup, terrain_systems::populate_mesher_meshes); app.add_systems(Startup, terrain_systems::prepare_mesher_materials); #[cfg(feature = "skip_terrain")] { diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 7e690ef..c78daa5 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -1,25 +1,8 @@ use terrain_resources::Mesher; -use terrain_util::{ - client_block::block_properties, get_cross_block_positions, instance_mesh_for_repr, -}; +use terrain_util::create_cross_mesh_for_chunk; use crate::prelude::*; -pub fn populate_mesher_meshes( - mut mesher: ResMut, - mut meshes: ResMut>, - texture_manager: ResMut, -) { - BlockId::values().iter().for_each(|block_id| { - let mesh_repr = block_properties(*block_id).mesh_representation; - let mesh = instance_mesh_for_repr(mesh_repr.clone(), &texture_manager); - if let Some(mesh) = mesh { - let handle = meshes.add(mesh); - mesher.mesh_handles.insert(mesh_repr, handle); - } - }); -} - pub fn prepare_mesher_materials( mut mesher: ResMut, materials: ResMut>, @@ -115,7 +98,7 @@ pub fn handle_chunk_mesh_update_events( chunk, &texture_manager, ); - add_cross_objects(&mut commands, chunk, &mesher); + add_cross_objects(&mut commands, chunk, &mesher, &texture_manager, &mut meshes); } None => { println!("No chunk found"); @@ -145,37 +128,41 @@ fn add_chunk_objects( } } -fn add_cross_objects(commands: &mut Commands, chunk: &Chunk, mesher: &Mesher) { - let values = get_cross_block_positions(chunk); - for (mesh_repr, positions) in values { - let mesh_handle = mesher - .mesh_handles - .get(&mesh_repr) - .expect("Handle is not yet populated"); - let material_handle = mesher - .transparent_material_handle - .clone() - .expect("Material has not yet been set"); - - for position in positions { - commands.spawn(( - Mesh3d(mesh_handle.clone()), - MeshMaterial3d(material_handle.clone()), - Transform::from_xyz( - chunk.position.x * CHUNK_SIZE as f32 + position.x, - chunk.position.y * CHUNK_SIZE as f32 + position.y, - chunk.position.z * CHUNK_SIZE as f32 + position.z, - ), - terrain_components::ChunkMesh { - key: [ - chunk.position.x as i32, - chunk.position.y as i32, - chunk.position.z as i32, - ], - }, - )); - } +fn add_cross_objects( + commands: &mut Commands, + chunk: &Chunk, + mesher: &Mesher, + texture_manager: &terrain_util::TextureManager, + meshes: &mut ResMut>, +) { + let mesh = create_cross_mesh_for_chunk(chunk, texture_manager); + if mesh.is_none() { + return; } + let mesh = mesh.unwrap(); + let mesh_handle = meshes.add(mesh); + + commands.spawn(( + Mesh3d(mesh_handle), + MeshMaterial3d( + mesher + .transparent_material_handle + .clone() + .expect("Material exsits"), + ), + Transform::from_xyz( + chunk.position.x * CHUNK_SIZE as f32, + chunk.position.y * CHUNK_SIZE as f32, + chunk.position.z * CHUNK_SIZE as f32, + ), + terrain_components::ChunkMesh { + key: [ + chunk.position.x as i32, + chunk.position.y as i32, + chunk.position.z as i32, + ], + }, + )); } fn create_chunk_mesh( diff --git a/src/client/terrain/util/cross_mesher.rs b/src/client/terrain/util/cross_mesher.rs index 2d48413..b395466 100644 --- a/src/client/terrain/util/cross_mesher.rs +++ b/src/client/terrain/util/cross_mesher.rs @@ -1,27 +1,21 @@ use terrain_util::{ client_block::{block_properties, MeshRepresentation}, - create_cube_mesh_from_data, GeometryData, TextureManager, TextureName, Vertex, + create_cube_mesh_from_data, GeometryData, TextureManager, Vertex, }; use crate::prelude::*; -pub fn instance_mesh_for_repr( - rep: MeshRepresentation, +pub fn create_cross_mesh_for_chunk( + chunk: &Chunk, texture_manager: &TextureManager, ) -> Option { - match rep { - MeshRepresentation::None => None, - MeshRepresentation::Cube(_) => None, - MeshRepresentation::Cross(textures) => { - let geometry_data = create_cross_geometry(textures, texture_manager); - // TODO: refactor rename to create_mesh_from_data because it is not cube representation specific - create_cube_mesh_from_data(geometry_data) - } - } + let geometry_data = create_cross_geometry_for_chunk(chunk, texture_manager); + + create_cube_mesh_from_data(geometry_data) } -fn create_cross_geometry( - textures: [TextureName; 2], +fn create_cross_geometry_for_chunk( + chunk: &Chunk, texture_manager: &TextureManager, ) -> GeometryData { let mut position = vec![]; @@ -31,48 +25,6 @@ fn create_cross_geometry( let mut index_offset = 0; - let cross_faces = [CrossFace::Face1, CrossFace::Face2]; - - cross_faces.iter().for_each(|cross_face| { - let face_verticies = cross_face_vertices(*cross_face); - - let face_uv = texture_manager - .get_texture_uv(textures[0]) - .expect("Texture is not present in manager"); - - for vertex in face_verticies { - position.push([ - vertex.position[0] * 0.5 + 0.5, - vertex.position[1] * 0.5 + 0.5, - vertex.position[2] * 0.5 + 0.5, - ]); - - uv.push([ - face_uv[0] + vertex.uv[0] * 0.25, - face_uv[1] + vertex.uv[1] * 0.25, - ]); - normal.push(vertex.normal); - } - - let offsets = [0, 1, 3, 1, 2, 3]; - offsets.iter().for_each(|offset| { - indices.push(index_offset + offset); - }); - - index_offset += 4; - }); - - GeometryData { - position, - uv, - normal, - indices, - } -} - -pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap> { - let mut map: HashMap> = HashMap::new(); - for x in 0..CHUNK_SIZE { for y in 0..CHUNK_SIZE { for z in 0..CHUNK_SIZE { @@ -80,19 +32,46 @@ pub fn get_cross_block_positions(chunk: &Chunk) -> HashMap positions.push(pos), - None => { - map.insert(mesh_repr, vec![pos]); + if let MeshRepresentation::Cross(textures) = mesh_repr { + CrossFace::values().iter().for_each(|cross_face| { + let face_verticies = cross_face_vertices(*cross_face); + + let face_uv = texture_manager + .get_texture_uv(textures[0]) + .expect("Texture is not present in manager"); + + for vertex in face_verticies { + position.push([ + pos.x + vertex.position[0] * 0.5 + 0.5, + pos.y + vertex.position[1] * 0.5 + 0.5, + pos.z + vertex.position[2] * 0.5 + 0.5, + ]); + + uv.push([ + face_uv[0] + vertex.uv[0] * 0.25, + face_uv[1] + vertex.uv[1] * 0.25, + ]); + normal.push(vertex.normal); } - }; + + let offsets = [0, 1, 3, 1, 2, 3]; + offsets.iter().for_each(|offset| { + indices.push(index_offset + offset); + }); + + index_offset += 4; + }); } } } } - map + GeometryData { + position, + uv, + normal, + indices, + } } #[derive(Debug, Clone, Copy)] @@ -101,6 +80,12 @@ pub enum CrossFace { Face2, } +impl CrossFace { + pub fn values() -> [CrossFace; 2] { + [CrossFace::Face1, CrossFace::Face2] + } +} + #[rustfmt::skip] fn cross_face_vertices(face: CrossFace) -> [Vertex; 4] { match face { diff --git a/src/client/terrain/util/cube_mesher.rs b/src/client/terrain/util/cube_mesher.rs index af14b83..45462e5 100644 --- a/src/client/terrain/util/cube_mesher.rs +++ b/src/client/terrain/util/cube_mesher.rs @@ -34,7 +34,7 @@ pub fn create_cube_geometry_data( let block_uvs = Block::get_block_face_uvs(block_id, *face, texture_manager).unwrap(); uv.push([ - block_uvs[0] + vertex.uv[0] * 0.25 - 0.001, + block_uvs[0] + vertex.uv[0] * 0.25, block_uvs[1] + (1.0 - vertex.uv[1]) * 0.25, ]); normal.push(vertex.normal); From 28396701a4d6dd626532d8303d621c1685cb6b31 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:09:31 +0100 Subject: [PATCH 32/45] Refactor materials :3 --- src/client/terrain/mod.rs | 2 +- src/client/terrain/resources.rs | 2 ++ src/client/terrain/systems.rs | 53 +++++++++++++++------------------ 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/client/terrain/mod.rs b/src/client/terrain/mod.rs index 3a76c78..b70266c 100644 --- a/src/client/terrain/mod.rs +++ b/src/client/terrain/mod.rs @@ -17,7 +17,7 @@ impl Plugin for TerrainPlugin { app.add_event::(); app.add_event::(); app.add_event::(); - app.add_systems(Startup, terrain_systems::prepare_mesher_materials); + app.add_systems(Startup, terrain_systems::prepare_mesher_materials_system); #[cfg(feature = "skip_terrain")] { app.insert_resource(terrain_resources::SpawnAreaLoaded(true)); diff --git a/src/client/terrain/resources.rs b/src/client/terrain/resources.rs index 6bdfee4..213de09 100644 --- a/src/client/terrain/resources.rs +++ b/src/client/terrain/resources.rs @@ -15,6 +15,7 @@ impl SpawnAreaLoaded { pub struct Mesher { pub mesh_handles: HashMap>, pub transparent_material_handle: Option>, + pub chunk_material_handle: Option>, } impl Default for Mesher { @@ -28,6 +29,7 @@ impl Mesher { Mesher { mesh_handles: HashMap::new(), transparent_material_handle: None, + chunk_material_handle: None, } } } diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index c78daa5..2a0e2fa 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -3,14 +3,18 @@ use terrain_util::create_cross_mesh_for_chunk; use crate::prelude::*; -pub fn prepare_mesher_materials( +pub fn prepare_mesher_materials_system( mut mesher: ResMut, - materials: ResMut>, + mut materials: ResMut>, asset_server: Res, ) { - let texture_handle = obtain_texture_handle(&asset_server).clone(); - let material_handle = create_transparent_material(texture_handle, materials); - mesher.transparent_material_handle = Some(material_handle); + let texture_handle = obtain_texture_handle(&asset_server); + + let material = create_transparent_material(texture_handle.clone()); + mesher.transparent_material_handle = Some(materials.add(material)); + + let material = create_chunk_material(texture_handle); + mesher.chunk_material_handle = Some(materials.add(material)); } pub fn generate_simple_ground_system( @@ -92,11 +96,10 @@ pub fn handle_chunk_mesh_update_events( } add_chunk_objects( &mut commands, - &asset_server, &mut meshes, - &mut materials, chunk, &texture_manager, + &mesher, ); add_cross_objects(&mut commands, chunk, &mesher, &texture_manager, &mut meshes); } @@ -109,15 +112,16 @@ pub fn handle_chunk_mesh_update_events( fn add_chunk_objects( commands: &mut Commands, - asset_server: &Res, meshes: &mut ResMut>, - materials: &mut ResMut>, chunk: &Chunk, texture_manager: &terrain_util::TextureManager, + mesher: &Mesher, ) { if let Some(mesh) = create_chunk_mesh(chunk, texture_manager) { - let texture_handle = obtain_texture_handle(asset_server).clone(); - let material = create_chunk_material(texture_handle, &mut ResMut::reborrow(materials)); + let material = mesher + .chunk_material_handle + .clone() + .expect("Chunk material not loaded"); spawn_chunk( commands, &mut ResMut::reborrow(meshes), @@ -172,11 +176,8 @@ fn create_chunk_mesh( terrain_util::create_chunk_mesh(chunk, texture_manager) } -fn create_transparent_material( - texture_handle: Handle, - mut materials: ResMut>, -) -> Handle { - materials.add(StandardMaterial { +fn create_transparent_material(texture_handle: Handle) -> StandardMaterial { + StandardMaterial { perceptual_roughness: 1.0, double_sided: true, cull_mode: None, @@ -186,34 +187,28 @@ fn create_transparent_material( alpha_mode: AlphaMode::Mask(1.0), base_color_texture: Some(texture_handle), ..default() - }) + } } #[cfg(not(feature = "wireframe"))] -fn create_chunk_material( - texture_handle: Handle, - materials: &mut Mut>, -) -> Handle { - materials.add(StandardMaterial { +fn create_chunk_material(texture_handle: Handle) -> StandardMaterial { + StandardMaterial { perceptual_roughness: 0.5, reflectance: 0.0, unlit: false, specular_transmission: 0.0, base_color_texture: Some(texture_handle), ..default() - }) + } } #[cfg(feature = "wireframe")] -fn create_chunk_material( - _texture_handle: Handle, - materials: &mut Mut>, -) -> Handle { - materials.add(StandardMaterial { +fn create_chunk_material(_texture_handle: Handle) -> StandardMaterial { + StandardMaterial { base_color: Color::srgba(0.0, 0.0, 0.0, 0.0), alpha_mode: AlphaMode::Mask(0.5), ..default() - }) + } } fn obtain_texture_handle(asset_server: &Res) -> Handle { From 7c4495cd587095fd16adaf3f8e0622a37e47e60c Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:12:20 +0100 Subject: [PATCH 33/45] Refactor inline --- src/client/terrain/resources.rs | 4 -- src/client/terrain/systems.rs | 72 ++++++++++----------------------- 2 files changed, 21 insertions(+), 55 deletions(-) diff --git a/src/client/terrain/resources.rs b/src/client/terrain/resources.rs index 213de09..38c688a 100644 --- a/src/client/terrain/resources.rs +++ b/src/client/terrain/resources.rs @@ -1,5 +1,3 @@ -use terrain_util::client_block::MeshRepresentation; - use crate::prelude::*; #[derive(Resource)] @@ -13,7 +11,6 @@ impl SpawnAreaLoaded { #[derive(Resource)] pub struct Mesher { - pub mesh_handles: HashMap>, pub transparent_material_handle: Option>, pub chunk_material_handle: Option>, } @@ -27,7 +24,6 @@ impl Default for Mesher { impl Mesher { pub fn new() -> Mesher { Mesher { - mesh_handles: HashMap::new(), transparent_material_handle: None, chunk_material_handle: None, } diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 2a0e2fa..6cbb221 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -69,12 +69,9 @@ pub fn generate_world_system( }); } -#[allow(clippy::too_many_arguments)] pub fn handle_chunk_mesh_update_events( mut commands: Commands, - asset_server: Res, mut meshes: ResMut>, - mut materials: ResMut>, chunk_manager: ResMut, mut chunk_mesh_update_events: EventReader, mut mesh_query: Query<(Entity, &terrain_components::ChunkMesh)>, @@ -94,13 +91,7 @@ pub fn handle_chunk_mesh_update_events( commands.entity(entity).despawn(); } } - add_chunk_objects( - &mut commands, - &mut meshes, - chunk, - &texture_manager, - &mesher, - ); + add_chunk_objects(&mut commands, &mut meshes, chunk, &texture_manager, &mesher); add_cross_objects(&mut commands, chunk, &mesher, &texture_manager, &mut meshes); } None => { @@ -117,18 +108,30 @@ fn add_chunk_objects( texture_manager: &terrain_util::TextureManager, mesher: &Mesher, ) { - if let Some(mesh) = create_chunk_mesh(chunk, texture_manager) { + if let Some(mesh) = terrain_util::create_chunk_mesh(chunk, texture_manager) { let material = mesher .chunk_material_handle .clone() .expect("Chunk material not loaded"); - spawn_chunk( - commands, - &mut ResMut::reborrow(meshes), - material, - mesh, - chunk, - ); + + let meshes: &mut Mut> = &mut ResMut::reborrow(meshes); + commands.spawn(( + Mesh3d(meshes.add(mesh)), + Transform::from_xyz( + chunk.position.x * CHUNK_SIZE as f32, + chunk.position.y * CHUNK_SIZE as f32, + chunk.position.z * CHUNK_SIZE as f32, + ), + MeshMaterial3d(material), + player_components::Raycastable, + terrain_components::ChunkMesh { + key: [ + chunk.position.x as i32, + chunk.position.y as i32, + chunk.position.z as i32, + ], + }, + )); } } @@ -169,13 +172,6 @@ fn add_cross_objects( )); } -fn create_chunk_mesh( - chunk: &Chunk, - texture_manager: &terrain_util::TextureManager, -) -> Option { - terrain_util::create_chunk_mesh(chunk, texture_manager) -} - fn create_transparent_material(texture_handle: Handle) -> StandardMaterial { StandardMaterial { perceptual_roughness: 1.0, @@ -215,32 +211,6 @@ fn obtain_texture_handle(asset_server: &Res) -> Handle { asset_server.load("textures/texture_atlas.png") } -fn spawn_chunk( - commands: &mut Commands, - meshes: &mut Mut>, - material: Handle, - mesh: Mesh, - chunk: &Chunk, -) { - commands.spawn(( - Mesh3d(meshes.add(mesh)), - Transform::from_xyz( - chunk.position.x * CHUNK_SIZE as f32, - chunk.position.y * CHUNK_SIZE as f32, - chunk.position.z * CHUNK_SIZE as f32, - ), - MeshMaterial3d(material), - player_components::Raycastable, - terrain_components::ChunkMesh { - key: [ - chunk.position.x as i32, - chunk.position.y as i32, - chunk.position.z as i32, - ], - }, - )); -} - pub fn handle_terrain_regeneration_events( mut client: ResMut, mut world_regenerate_events: EventReader, From b37d3f804ae9f6e5c4f41f915b911994b93fef96 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:14:11 +0100 Subject: [PATCH 34/45] Refactor rename --- src/client/terrain/mod.rs | 10 ++++++++-- src/client/terrain/systems.rs | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/client/terrain/mod.rs b/src/client/terrain/mod.rs index b70266c..08a1083 100644 --- a/src/client/terrain/mod.rs +++ b/src/client/terrain/mod.rs @@ -28,8 +28,14 @@ impl Plugin for TerrainPlugin { app.insert_resource(terrain_resources::SpawnAreaLoaded(false)); app.add_systems(Startup, terrain_systems::prepare_spawn_area_system); app.add_systems(Startup, terrain_systems::generate_world_system); - app.add_systems(Update, terrain_systems::handle_chunk_mesh_update_events); - app.add_systems(Update, terrain_systems::handle_terrain_regeneration_events); + app.add_systems( + Update, + terrain_systems::handle_chunk_mesh_update_events_system, + ); + app.add_systems( + Update, + terrain_systems::handle_terrain_regeneration_events_system, + ); } } } diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 6cbb221..9b8e3ff 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -69,7 +69,7 @@ pub fn generate_world_system( }); } -pub fn handle_chunk_mesh_update_events( +pub fn handle_chunk_mesh_update_events_system( mut commands: Commands, mut meshes: ResMut>, chunk_manager: ResMut, @@ -211,7 +211,7 @@ fn obtain_texture_handle(asset_server: &Res) -> Handle { asset_server.load("textures/texture_atlas.png") } -pub fn handle_terrain_regeneration_events( +pub fn handle_terrain_regeneration_events_system( mut client: ResMut, mut world_regenerate_events: EventReader, chunk_manager: ResMut, From 74c2988441a4491e4a8fe784fd2f7d95b100b2eb Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:19:27 +0100 Subject: [PATCH 35/45] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e4666..0a579c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Increase world size - Fix issue where chunks were only serialized on the client - Add feature flags for debug rendering +- Add grass ## 0.1.1 From c8573bb38f8c39f22175f2f0bd0e45a6296329d6 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:26:16 +0100 Subject: [PATCH 36/45] Fix block placing at chunk borders --- src/shared/terrain.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/shared/terrain.rs b/src/shared/terrain.rs index cdfd5d1..3461934 100644 --- a/src/shared/terrain.rs +++ b/src/shared/terrain.rs @@ -11,7 +11,6 @@ pub const CHUNK_LENGTH: usize = PADDED_CHUNK_SIZE * PADDED_CHUNK_SIZE * PADDED_C #[derive(Debug, Clone, Copy)] pub struct Chunk { - // #[serde(with = "BigArray")] pub data: [BlockId; CHUNK_LENGTH], pub position: Vec3, } @@ -45,9 +44,7 @@ impl Chunk { } pub fn update(&mut self, x: usize, y: usize, z: usize, value: BlockId) { - if Self::valid_padded(x, y, z) { - self.set(x, y, z, value); - } + self.set(x, y, z, value); if !value.supports_grass() && Self::valid_padded(x, y + 1, z) From 92e34cf82de0437037ca4f018f9013d213bb62ce Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:31:32 +0100 Subject: [PATCH 37/45] Refactor rename mesher --- src/client/terrain/mod.rs | 2 +- src/client/terrain/resources.rs | 18 ++++++++-------- src/client/terrain/systems.rs | 38 ++++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/client/terrain/mod.rs b/src/client/terrain/mod.rs index 08a1083..44db79a 100644 --- a/src/client/terrain/mod.rs +++ b/src/client/terrain/mod.rs @@ -13,7 +13,7 @@ impl Plugin for TerrainPlugin { info!("Building TerrainPlugin"); app.insert_resource(ChunkManager::new()); app.insert_resource(util::TextureManager::new()); - app.insert_resource(resources::Mesher::new()); + app.insert_resource(resources::RenderMaterials::new()); app.add_event::(); app.add_event::(); app.add_event::(); diff --git a/src/client/terrain/resources.rs b/src/client/terrain/resources.rs index 38c688a..6b78ba7 100644 --- a/src/client/terrain/resources.rs +++ b/src/client/terrain/resources.rs @@ -10,22 +10,22 @@ impl SpawnAreaLoaded { } #[derive(Resource)] -pub struct Mesher { - pub transparent_material_handle: Option>, - pub chunk_material_handle: Option>, +pub struct RenderMaterials { + pub transparent_material: Option>, + pub chunk_material: Option>, } -impl Default for Mesher { +impl Default for RenderMaterials { fn default() -> Self { Self::new() } } -impl Mesher { - pub fn new() -> Mesher { - Mesher { - transparent_material_handle: None, - chunk_material_handle: None, +impl RenderMaterials { + pub fn new() -> RenderMaterials { + RenderMaterials { + transparent_material: None, + chunk_material: None, } } } diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 9b8e3ff..471fe45 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -1,20 +1,20 @@ -use terrain_resources::Mesher; +use terrain_resources::RenderMaterials; use terrain_util::create_cross_mesh_for_chunk; use crate::prelude::*; pub fn prepare_mesher_materials_system( - mut mesher: ResMut, + mut render_materials: ResMut, mut materials: ResMut>, asset_server: Res, ) { let texture_handle = obtain_texture_handle(&asset_server); let material = create_transparent_material(texture_handle.clone()); - mesher.transparent_material_handle = Some(materials.add(material)); + render_materials.transparent_material = Some(materials.add(material)); let material = create_chunk_material(texture_handle); - mesher.chunk_material_handle = Some(materials.add(material)); + render_materials.chunk_material = Some(materials.add(material)); } pub fn generate_simple_ground_system( @@ -76,7 +76,7 @@ pub fn handle_chunk_mesh_update_events_system( mut chunk_mesh_update_events: EventReader, mut mesh_query: Query<(Entity, &terrain_components::ChunkMesh)>, texture_manager: ResMut, - mesher: Res, + materials: Res, ) { for event in chunk_mesh_update_events.read() { info!( @@ -91,8 +91,20 @@ pub fn handle_chunk_mesh_update_events_system( commands.entity(entity).despawn(); } } - add_chunk_objects(&mut commands, &mut meshes, chunk, &texture_manager, &mesher); - add_cross_objects(&mut commands, chunk, &mesher, &texture_manager, &mut meshes); + add_chunk_objects( + &mut commands, + &mut meshes, + chunk, + &texture_manager, + &materials, + ); + add_cross_objects( + &mut commands, + chunk, + &materials, + &texture_manager, + &mut meshes, + ); } None => { println!("No chunk found"); @@ -106,11 +118,11 @@ fn add_chunk_objects( meshes: &mut ResMut>, chunk: &Chunk, texture_manager: &terrain_util::TextureManager, - mesher: &Mesher, + materials: &RenderMaterials, ) { if let Some(mesh) = terrain_util::create_chunk_mesh(chunk, texture_manager) { - let material = mesher - .chunk_material_handle + let material = materials + .chunk_material .clone() .expect("Chunk material not loaded"); @@ -138,7 +150,7 @@ fn add_chunk_objects( fn add_cross_objects( commands: &mut Commands, chunk: &Chunk, - mesher: &Mesher, + materials: &RenderMaterials, texture_manager: &terrain_util::TextureManager, meshes: &mut ResMut>, ) { @@ -152,8 +164,8 @@ fn add_cross_objects( commands.spawn(( Mesh3d(mesh_handle), MeshMaterial3d( - mesher - .transparent_material_handle + materials + .transparent_material .clone() .expect("Material exsits"), ), From 60737df77a9186b16b399edaf80f5365c9e80900 Mon Sep 17 00:00:00 2001 From: Daniel Bengl <53896675+CuddlyBunion341@users.noreply.github.com> Date: Sun, 9 Mar 2025 10:40:52 +0100 Subject: [PATCH 38/45] Apply suggestions from code review --- src/client/terrain/systems.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 471fe45..a52e121 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -124,7 +124,7 @@ fn add_chunk_objects( let material = materials .chunk_material .clone() - .expect("Chunk material not loaded"); + .expect("Chunk material is loaded") let meshes: &mut Mut> = &mut ResMut::reborrow(meshes); commands.spawn(( @@ -143,6 +143,7 @@ fn add_chunk_objects( chunk.position.z as i32, ], }, + Name::from("Transparent Chunk Mesh") )); } } @@ -167,7 +168,7 @@ fn add_cross_objects( materials .transparent_material .clone() - .expect("Material exsits"), + .expect("Transparent material exists"), ), Transform::from_xyz( chunk.position.x * CHUNK_SIZE as f32, From 043808f1cb44b9e7b6eddd8ab10621e75353435a Mon Sep 17 00:00:00 2001 From: Daniel Bengl <53896675+CuddlyBunion341@users.noreply.github.com> Date: Sun, 9 Mar 2025 10:40:52 +0100 Subject: [PATCH 39/45] Apply suggestions from code review --- src/client/terrain/systems.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index a52e121..9298428 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -124,7 +124,7 @@ fn add_chunk_objects( let material = materials .chunk_material .clone() - .expect("Chunk material is loaded") + .expect("Chunk material is loaded"); let meshes: &mut Mut> = &mut ResMut::reborrow(meshes); commands.spawn(( @@ -143,7 +143,7 @@ fn add_chunk_objects( chunk.position.z as i32, ], }, - Name::from("Transparent Chunk Mesh") + Name::from("Transparent Chunk Mesh"), )); } } From 80315778a2ed14e4e321154e368b4434fe3215ff Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:57:48 +0100 Subject: [PATCH 40/45] Add test, fix padded check --- src/shared/terrain.rs | 44 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/shared/terrain.rs b/src/shared/terrain.rs index 3461934..eecc9a6 100644 --- a/src/shared/terrain.rs +++ b/src/shared/terrain.rs @@ -24,7 +24,7 @@ impl Chunk { } pub fn valid_padded(x: usize, y: usize, z: usize) -> bool { - (1..CHUNK_SIZE).contains(&x) && (1..CHUNK_SIZE).contains(&y) && (1..CHUNK_SIZE).contains(&z) + (0..CHUNK_SIZE).contains(&x) && (0..CHUNK_SIZE).contains(&y) && (0..CHUNK_SIZE).contains(&z) } pub fn valid_unpadded(x: usize, y: usize, z: usize) -> bool { @@ -305,4 +305,46 @@ mod tests { let retrieved_chunk_positions = chunk_manager.get_all_chunk_positions(); assert_eq!(retrieved_chunk_positions.len(), 3); } + + #[test] + fn test_tallgrass_update() { + let mut chunk_manager = ChunkManager::new(); + let chunk_position = Vec3::new(0.0, 0.0, 0.0); + let chunk = Chunk::new(chunk_position); + chunk_manager.set_chunk(chunk_position, chunk); + + let grass_position = Vec3::new(0.0, 0.0, 0.0); + let tallgrass_position = Vec3::new(0.0, 1.0, 0.0); + + chunk_manager.update_block(grass_position, BlockId::Grass); + assert_eq!( + chunk_manager.get_block(grass_position).unwrap(), + BlockId::Grass + ); + chunk_manager.update_block(tallgrass_position, BlockId::Tallgrass); + assert_eq!( + chunk_manager.get_block(tallgrass_position).unwrap(), + BlockId::Tallgrass + ); + + chunk_manager.update_block(grass_position, BlockId::Dirt); + assert_eq!( + chunk_manager.get_block(grass_position).unwrap(), + BlockId::Dirt + ); + assert_eq!( + chunk_manager.get_block(tallgrass_position).unwrap(), + BlockId::Tallgrass + ); + + chunk_manager.update_block(grass_position, BlockId::Air); + assert_eq!( + chunk_manager.get_block(grass_position).unwrap(), + BlockId::Air + ); + assert_eq!( + chunk_manager.get_block(tallgrass_position).unwrap(), + BlockId::Air + ); + } } From 595fab8a2fe69975a4437b58c6bd39ddcc90f3e2 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 10:59:49 +0100 Subject: [PATCH 41/45] Reformat --- src/shared/terrain.rs | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/shared/terrain.rs b/src/shared/terrain.rs index eecc9a6..ff3acd1 100644 --- a/src/shared/terrain.rs +++ b/src/shared/terrain.rs @@ -307,6 +307,7 @@ mod tests { } #[test] + #[rustfmt::skip] fn test_tallgrass_update() { let mut chunk_manager = ChunkManager::new(); let chunk_position = Vec3::new(0.0, 0.0, 0.0); @@ -317,34 +318,16 @@ mod tests { let tallgrass_position = Vec3::new(0.0, 1.0, 0.0); chunk_manager.update_block(grass_position, BlockId::Grass); - assert_eq!( - chunk_manager.get_block(grass_position).unwrap(), - BlockId::Grass - ); + assert_eq!(chunk_manager.get_block(grass_position).unwrap(), BlockId::Grass); chunk_manager.update_block(tallgrass_position, BlockId::Tallgrass); - assert_eq!( - chunk_manager.get_block(tallgrass_position).unwrap(), - BlockId::Tallgrass - ); + assert_eq!(chunk_manager.get_block(tallgrass_position).unwrap(), BlockId::Tallgrass); chunk_manager.update_block(grass_position, BlockId::Dirt); - assert_eq!( - chunk_manager.get_block(grass_position).unwrap(), - BlockId::Dirt - ); - assert_eq!( - chunk_manager.get_block(tallgrass_position).unwrap(), - BlockId::Tallgrass - ); + assert_eq!(chunk_manager.get_block(grass_position).unwrap(), BlockId::Dirt); + assert_eq!(chunk_manager.get_block(tallgrass_position).unwrap(), BlockId::Tallgrass); chunk_manager.update_block(grass_position, BlockId::Air); - assert_eq!( - chunk_manager.get_block(grass_position).unwrap(), - BlockId::Air - ); - assert_eq!( - chunk_manager.get_block(tallgrass_position).unwrap(), - BlockId::Air - ); + assert_eq!(chunk_manager.get_block(grass_position).unwrap(), BlockId::Air); + assert_eq!(chunk_manager.get_block(tallgrass_position).unwrap(), BlockId::Air); } } From 673425763717f0bd8c90880c645c2e2ae2b6b88a Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 11:23:11 +0100 Subject: [PATCH 42/45] Optimize grass generation --- src/server/terrain/resources.rs | 2 +- src/server/terrain/systems.rs | 2 +- src/server/terrain/util/generator.rs | 38 +++++++++++++--------------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/server/terrain/resources.rs b/src/server/terrain/resources.rs index 8350e4e..a322d5f 100644 --- a/src/server/terrain/resources.rs +++ b/src/server/terrain/resources.rs @@ -148,7 +148,7 @@ impl Default for TerrainGeneratorParams { max_bush_radius: 5, }, grass: GrassParams { - spawn_attempts_per_chunk: 1200, + spawn_attempts_per_chunk: 10, }, } } diff --git a/src/server/terrain/systems.rs b/src/server/terrain/systems.rs index 1c64baf..0c5de38 100644 --- a/src/server/terrain/systems.rs +++ b/src/server/terrain/systems.rs @@ -315,7 +315,7 @@ mod visualizer { add_slider_const!(ui, &mut generator.params.tree.max_bush_radius, 0..=10, "max_bush_radius"); ui.label("Grass"); - add_slider_const!(ui, &mut generator.params.grass.spawn_attempts_per_chunk, 0..=3000, "spawn attempts"); + add_slider_const!(ui, &mut generator.params.grass.spawn_attempts_per_chunk, 0..=100, "spawn attempts"); }); }); diff --git a/src/server/terrain/util/generator.rs b/src/server/terrain/util/generator.rs index 353642a..a1d7ce7 100644 --- a/src/server/terrain/util/generator.rs +++ b/src/server/terrain/util/generator.rs @@ -59,28 +59,12 @@ impl Generator { z: z as f32, }; - let block = self.decorate_block(chunk, pos); - chunk.set_unpadded(x, y, z, block); + self.decorate_block(chunk, pos); }); for _ in 0..self.params.tree.spawn_attempts_per_chunk { self.attempt_spawn_tree(chunk); } - - for _ in 0..self.params.grass.spawn_attempts_per_chunk { - self.attempt_spawn_grass(chunk); - } - } - - fn attempt_spawn_grass(&self, chunk: &mut Chunk) { - let chunk_range = 0..CHUNK_SIZE; - let x = rand::random_range(chunk_range.clone()); - let y = rand::random_range(0..(CHUNK_SIZE - 1)); - let z = rand::random_range(chunk_range.clone()); - - if chunk.get(x, y, z) == BlockId::Grass { - chunk.set(x, y + 1, z, BlockId::Tallgrass); - } } fn attempt_spawn_tree(&self, chunk: &mut Chunk) { @@ -196,7 +180,7 @@ impl Generator { blocks } - fn decorate_block(&self, chunk: &Chunk, position: Vec3) -> BlockId { + fn decorate_block(&self, chunk: &mut Chunk, position: Vec3) { let x = position.x as usize; let y = position.y as usize; let z = position.z as usize; @@ -204,7 +188,17 @@ impl Generator { let block = chunk.get_unpadded(x, y, z); if block == BlockId::Air { - return block; + if y > 0 + && Chunk::valid_unpadded(x, y - 1, z) + && chunk.get_unpadded(x, y - 1, z) == BlockId::Grass + { + let random_number = + rand::random_range(0..=self.params.grass.spawn_attempts_per_chunk); + if random_number == 0 { + chunk.set_unpadded(x, y, z, BlockId::Tallgrass); + } + } + return; } let mut depth_below_nearest_air = 0; @@ -224,11 +218,13 @@ impl Generator { depth_below_nearest_air += 1; } - match depth_below_nearest_air { + let block = match depth_below_nearest_air { 0_i32..=1_i32 => BlockId::Grass, 2..3 => BlockId::Dirt, _ => BlockId::Stone, - } + }; + + chunk.set_unpadded(x, y, z, block); } fn generate_block(&self, position: Vec3) -> BlockId { From 818d0d4c2a33fe5dacaa187d06435b5c0ce0f032 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 11:24:48 +0100 Subject: [PATCH 43/45] Refactor rename frequency --- src/server/terrain/resources.rs | 4 ++-- src/server/terrain/systems.rs | 2 +- src/server/terrain/util/generator.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/terrain/resources.rs b/src/server/terrain/resources.rs index a322d5f..0f79d3a 100644 --- a/src/server/terrain/resources.rs +++ b/src/server/terrain/resources.rs @@ -50,7 +50,7 @@ pub struct HeightAdjustParams { } pub struct GrassParams { - pub spawn_attempts_per_chunk: u32, + pub frequency: u32, } #[derive(Debug)] @@ -148,7 +148,7 @@ impl Default for TerrainGeneratorParams { max_bush_radius: 5, }, grass: GrassParams { - spawn_attempts_per_chunk: 10, + frequency: 10, }, } } diff --git a/src/server/terrain/systems.rs b/src/server/terrain/systems.rs index 0c5de38..27f015c 100644 --- a/src/server/terrain/systems.rs +++ b/src/server/terrain/systems.rs @@ -315,7 +315,7 @@ mod visualizer { add_slider_const!(ui, &mut generator.params.tree.max_bush_radius, 0..=10, "max_bush_radius"); ui.label("Grass"); - add_slider_const!(ui, &mut generator.params.grass.spawn_attempts_per_chunk, 0..=100, "spawn attempts"); + add_slider_const!(ui, &mut generator.params.grass.frequency, 0..=100, "frequnecy"); }); }); diff --git a/src/server/terrain/util/generator.rs b/src/server/terrain/util/generator.rs index a1d7ce7..8549572 100644 --- a/src/server/terrain/util/generator.rs +++ b/src/server/terrain/util/generator.rs @@ -193,7 +193,7 @@ impl Generator { && chunk.get_unpadded(x, y - 1, z) == BlockId::Grass { let random_number = - rand::random_range(0..=self.params.grass.spawn_attempts_per_chunk); + rand::random_range(0..=self.params.grass.frequency); if random_number == 0 { chunk.set_unpadded(x, y, z, BlockId::Tallgrass); } From 6db4279bac74ff0fe9d124bc54f8f07348358a45 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 11:25:21 +0100 Subject: [PATCH 44/45] Fix linter --- src/server/terrain/resources.rs | 4 +--- src/server/terrain/util/generator.rs | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/server/terrain/resources.rs b/src/server/terrain/resources.rs index 0f79d3a..c74f188 100644 --- a/src/server/terrain/resources.rs +++ b/src/server/terrain/resources.rs @@ -147,9 +147,7 @@ impl Default for TerrainGeneratorParams { min_bush_radius: 3, max_bush_radius: 5, }, - grass: GrassParams { - frequency: 10, - }, + grass: GrassParams { frequency: 10 }, } } } diff --git a/src/server/terrain/util/generator.rs b/src/server/terrain/util/generator.rs index 8549572..65b714a 100644 --- a/src/server/terrain/util/generator.rs +++ b/src/server/terrain/util/generator.rs @@ -192,8 +192,7 @@ impl Generator { && Chunk::valid_unpadded(x, y - 1, z) && chunk.get_unpadded(x, y - 1, z) == BlockId::Grass { - let random_number = - rand::random_range(0..=self.params.grass.frequency); + let random_number = rand::random_range(0..=self.params.grass.frequency); if random_number == 0 { chunk.set_unpadded(x, y, z, BlockId::Tallgrass); } From 4dad87e7f7d9f119a2bd4ad3c09524056de3dd56 Mon Sep 17 00:00:00 2001 From: CuddlyBunion341 Date: Sun, 9 Mar 2025 11:27:33 +0100 Subject: [PATCH 45/45] Refactor --- src/client/terrain/systems.rs | 51 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/client/terrain/systems.rs b/src/client/terrain/systems.rs index 9298428..fbcd04c 100644 --- a/src/client/terrain/systems.rs +++ b/src/client/terrain/systems.rs @@ -155,34 +155,31 @@ fn add_cross_objects( texture_manager: &terrain_util::TextureManager, meshes: &mut ResMut>, ) { - let mesh = create_cross_mesh_for_chunk(chunk, texture_manager); - if mesh.is_none() { - return; - } - let mesh = mesh.unwrap(); - let mesh_handle = meshes.add(mesh); + if let Some(mesh) = create_cross_mesh_for_chunk(chunk, texture_manager) { + let mesh_handle = meshes.add(mesh); - commands.spawn(( - Mesh3d(mesh_handle), - MeshMaterial3d( - materials - .transparent_material - .clone() - .expect("Transparent material exists"), - ), - Transform::from_xyz( - chunk.position.x * CHUNK_SIZE as f32, - chunk.position.y * CHUNK_SIZE as f32, - chunk.position.z * CHUNK_SIZE as f32, - ), - terrain_components::ChunkMesh { - key: [ - chunk.position.x as i32, - chunk.position.y as i32, - chunk.position.z as i32, - ], - }, - )); + commands.spawn(( + Mesh3d(mesh_handle), + MeshMaterial3d( + materials + .transparent_material + .clone() + .expect("Transparent material exists"), + ), + Transform::from_xyz( + chunk.position.x * CHUNK_SIZE as f32, + chunk.position.y * CHUNK_SIZE as f32, + chunk.position.z * CHUNK_SIZE as f32, + ), + terrain_components::ChunkMesh { + key: [ + chunk.position.x as i32, + chunk.position.y as i32, + chunk.position.z as i32, + ], + }, + )); + } } fn create_transparent_material(texture_handle: Handle) -> StandardMaterial {