Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8228442
Add tallgrass texture/block definition
CuddlyBunion341 Mar 7, 2025
a8ef0c7
Refactor shared blocks (break serialization)
CuddlyBunion341 Mar 7, 2025
2d9fbfb
Update grass spawn rate
CuddlyBunion341 Mar 7, 2025
49784b8
Update client side block data representation
CuddlyBunion341 Mar 7, 2025
677ba20
Update client block represenation
CuddlyBunion341 Mar 8, 2025
fb45b6f
Make code compile again
CuddlyBunion341 Mar 8, 2025
e4ac51f
Fix linter
CuddlyBunion341 Mar 8, 2025
414350e
Fix panics, udpate colliders
CuddlyBunion341 Mar 8, 2025
9fd6984
Fix meshing
CuddlyBunion341 Mar 8, 2025
45fe0c8
Fix linter
CuddlyBunion341 Mar 8, 2025
f3af9fb
Refactor mesher
CuddlyBunion341 Mar 8, 2025
6b9c9f6
Add skip_terrain feature flag
CuddlyBunion341 Mar 8, 2025
2b99c2c
Impl get block positions
CuddlyBunion341 Mar 8, 2025
1af5ffe
Add handles for cross meshes, boilerplate for mesher
CuddlyBunion341 Mar 8, 2025
671bb70
Fix linter
CuddlyBunion341 Mar 8, 2025
522ac40
Update grass meshes
CuddlyBunion341 Mar 8, 2025
3350b0a
Fix linter
CuddlyBunion341 Mar 8, 2025
d9a21d9
Attempt add 2nd face
CuddlyBunion341 Mar 8, 2025
0e426fa
Fix face1
CuddlyBunion341 Mar 8, 2025
9fa2d7a
Fix Face2, linter
CuddlyBunion341 Mar 8, 2025
7697f16
Update normals
CuddlyBunion341 Mar 8, 2025
dddb991
Update grass material properties
CuddlyBunion341 Mar 8, 2025
c540744
Add grass slider
CuddlyBunion341 Mar 8, 2025
60e28b0
Make mesher more readable
CuddlyBunion341 Mar 8, 2025
1a66ca7
Update src/shared/chunk_serializer.rs
CuddlyBunion341 Mar 8, 2025
1b3dfe1
Update src/client/terrain/systems.rs
CuddlyBunion341 Mar 8, 2025
494671a
Refactor mesher
CuddlyBunion341 Mar 8, 2025
53fc448
Fix linter
CuddlyBunion341 Mar 8, 2025
1822949
Fix grass cleanup
CuddlyBunion341 Mar 8, 2025
3e530ac
Fix floating grass
CuddlyBunion341 Mar 8, 2025
b2dc458
Drastically improve performance by not instancing
CuddlyBunion341 Mar 9, 2025
2839670
Refactor materials :3
CuddlyBunion341 Mar 9, 2025
7c4495c
Refactor inline
CuddlyBunion341 Mar 9, 2025
b37d3f8
Refactor rename
CuddlyBunion341 Mar 9, 2025
74c2988
Update CHANGELOG.md
CuddlyBunion341 Mar 9, 2025
c8573bb
Fix block placing at chunk borders
CuddlyBunion341 Mar 9, 2025
92e34cf
Refactor rename mesher
CuddlyBunion341 Mar 9, 2025
60737df
Apply suggestions from code review
CuddlyBunion341 Mar 9, 2025
043808f
Apply suggestions from code review
CuddlyBunion341 Mar 9, 2025
8031577
Add test, fix padded check
CuddlyBunion341 Mar 9, 2025
595fab8
Reformat
CuddlyBunion341 Mar 9, 2025
6734257
Optimize grass generation
CuddlyBunion341 Mar 9, 2025
818d0d4
Refactor rename frequency
CuddlyBunion341 Mar 9, 2025
6db4279
Fix linter
CuddlyBunion341 Mar 9, 2025
4dad87e
Refactor
CuddlyBunion341 Mar 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,5 @@ ortho_camera = []
lock_player = []
physics_debug = []
raycast_debug = []
skip_terrain = []
visual_debug = ["wireframe", "physics_debug", "raycast_debug"]
Binary file modified assets/textures/texture_atlas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/client/collider/systems.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use terrain_util::client_block::block_properties;

use crate::prelude::*;

static COLLIDER_GRID_SIZE: u32 = 4;
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 6 additions & 2 deletions src/client/player/systems/controller.rs
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
21 changes: 16 additions & 5 deletions src/client/terrain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,25 @@ 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.insert_resource(resources::Mesher::new());
app.add_event::<terrain_events::BlockUpdateEvent>();
app.add_event::<terrain_events::ChunkMeshUpdateEvent>();
app.add_event::<terrain_events::WorldRegenerateEvent>();
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(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));
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);
}
}
}
23 changes: 23 additions & 0 deletions src/client/terrain/resources.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use terrain_util::client_block::MeshRepresentation;

use crate::prelude::*;

#[derive(Resource)]
Expand All @@ -8,3 +10,24 @@ impl SpawnAreaLoaded {
resource.0
}
}

#[derive(Resource)]
pub struct Mesher {
pub mesh_handles: HashMap<MeshRepresentation, Handle<Mesh>>,
pub transparent_material_handle: Option<Handle<StandardMaterial>>,
}

impl Default for Mesher {
fn default() -> Self {
Self::new()
}
}

impl Mesher {
pub fn new() -> Mesher {
Mesher {
mesh_handles: HashMap::new(),
transparent_material_handle: None,
}
}
}
91 changes: 90 additions & 1 deletion src/client/terrain/systems.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
use terrain_resources::Mesher;
use terrain_util::{
client_block::block_properties, get_cross_block_positions, instance_mesh_for_repr,
};

use crate::prelude::*;

pub fn populate_mesher_meshes(
mut mesher: ResMut<Mesher>,
mut meshes: ResMut<Assets<Mesh>>,
texture_manager: ResMut<terrain_util::TextureManager>,
) {
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<Mesher>,
materials: ResMut<Assets<StandardMaterial>>,
asset_server: Res<AssetServer>,
) {
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<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
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<RenetClient>) {
info!("Sending chunk requests for spawn area");

Expand All @@ -15,7 +59,7 @@ pub fn generate_world_system(
mut client: ResMut<RenetClient>,
mut chunk_manager: ResMut<ChunkManager>,
) {
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");

Expand Down Expand Up @@ -48,6 +92,7 @@ pub fn handle_chunk_mesh_update_events(
mut chunk_mesh_update_events: EventReader<terrain_events::ChunkMeshUpdateEvent>,
mut mesh_query: Query<(Entity, &terrain_components::ChunkMesh)>,
texture_manager: ResMut<terrain_util::TextureManager>,
mesher: Res<Mesher>,
) {
for event in chunk_mesh_update_events.read() {
info!(
Expand All @@ -70,6 +115,7 @@ pub fn handle_chunk_mesh_update_events(
chunk,
&texture_manager,
);
add_cross_objects(&mut commands, chunk, &mesher);
}
None => {
println!("No chunk found");
Expand Down Expand Up @@ -99,13 +145,56 @@ 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,
) -> Option<Mesh> {
terrain_util::create_chunk_mesh(chunk, texture_manager)
}

fn create_transparent_material(
texture_handle: Handle<Image>,
mut materials: ResMut<Assets<StandardMaterial>>,
) -> Handle<StandardMaterial> {
materials.add(StandardMaterial {
perceptual_roughness: 1.0,
double_sided: true,
cull_mode: None,
reflectance: 0.0,
unlit: false,
specular_transmission: 0.0,
alpha_mode: AlphaMode::Mask(1.0),
base_color_texture: Some(texture_handle),
..default()
})
}

#[cfg(not(feature = "wireframe"))]
fn create_chunk_material(
texture_handle: Handle<Image>,
Expand Down
131 changes: 82 additions & 49 deletions src/client/terrain/util/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,76 @@ pub enum TextureName {
OakLeaves,
OakLogTop,
OakLogSide,
Tallgrass,
}

pub mod client_block {
use super::TextureName;
use rsmc::BlockId;

#[derive(Eq, Hash, PartialEq, Clone)]
pub enum MeshRepresentation {
None,
Cube([TextureName; 6]),
Cross([TextureName; 2]),
}

use MeshRepresentation::*;

pub struct BlockProperties {
pub has_collider: bool,
pub mesh_representation: MeshRepresentation,
}

pub fn block_properties(block_id: BlockId) -> BlockProperties {
use TextureName::*;

let touple = match block_id {
BlockId::Air => (false, 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 => (false, Cross([Tallgrass, Tallgrass])),
};

BlockProperties {
has_collider: touple.0,
mesh_representation: touple.1,
}
}

pub fn collect_all_texture_names() -> Vec<TextureName> {
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()
}
}

use client_block::block_properties;
use TextureName::*;

#[derive(Resource)]
Expand Down Expand Up @@ -55,7 +123,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();
Expand Down Expand Up @@ -83,51 +151,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,
}
};
}

pub static BLOCKS: [Block; 10] = [
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
),
];

type TextureUV = [f32; 2];

impl Block {
Expand All @@ -136,8 +159,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<TextureName> = 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) => texture_manager.get_texture_uv(texture_name).copied(),
None => None,
}
}
}
Loading