diff --git a/Cargo.toml b/Cargo.toml index daee6fd..52191c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,6 @@ bevy_flycam = "*" rand = "*" noise = "0.9" bevy_meshem = { git = "https://github.com/TheFelidae/Meshem.git" } -rayon = "1.5.1" \ No newline at end of file +rayon = "1.5.1" +bevy_egui = "0.31.1" +egui = "*" \ No newline at end of file diff --git a/src/client/mod.rs b/src/client/mod.rs index 5afdbe6..10ac5fa 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -6,6 +6,7 @@ use bevy::{ text::TextFont, DefaultPlugins, }; +use bevy_egui::EguiPlugin; use bevy_flycam::PlayerPlugin; use bevy_meshem::prelude::{generate_voxel_mesh, Face::Top}; @@ -42,8 +43,9 @@ impl Runtime { }; app.insert_resource(block_registry); - app.add_plugins(FpsOverlayPlugin::default()); + // app.add_plugins(FpsOverlayPlugin::default()); app.add_plugins(renderer::WorldRenderer::default()); + app.add_plugins(EguiPlugin); app.add_systems(Startup, systems::startup::setup_camera); app.add_systems(Startup, systems::test_scene::register); app.add_systems(Startup, systems::test_scene::setup); diff --git a/src/client/renderer/mod.rs b/src/client/renderer/mod.rs index b8d1b33..dda8751 100644 --- a/src/client/renderer/mod.rs +++ b/src/client/renderer/mod.rs @@ -1,3 +1,5 @@ +use std::sync::{Mutex, RwLock}; + use bevy::{ app::{App, Plugin, Startup, Update}, asset::{AssetServer, Assets, Handle}, @@ -5,8 +7,8 @@ use bevy::{ math::Vec3, pbr::{MeshMaterial3d, StandardMaterial}, prelude::{ - AlphaMode, Camera3d, Commands, Component, Entity, EventWriter, Mesh, Mesh3d, Query, Res, - ResMut, Transform, With, + AlphaMode, Camera3d, Commands, Component, Entity, EventReader, EventWriter, Mesh, Mesh3d, + Query, Res, ResMut, Transform, With, }, }; use bevy_meshem::{ @@ -17,12 +19,13 @@ use bevy_meshem::{ }, Dimensions, VoxelRegistry, }; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::{ data::world::{World, WorldChunk, WorldChunkStatus, WorldChunkStorage}, game::{ registry::BlockRegistry, - world_generator::{GameWorld, GenerateWorldSignal}, + world_generator::{ChunkDroppedEvent, ChunkGeneratedEvent, GameWorld, GenerateWorldSignal}, }, }; @@ -56,6 +59,7 @@ impl Plugin for WorldRenderer { fn build(&self, app: &mut App) { app.add_systems(Startup, sys_setup); app.add_systems(Update, sys_update); + app.add_systems(Update, sys_on_chunk_generated); } } @@ -101,7 +105,7 @@ fn sys_update( world: Query<&GameWorld>, mut request_world: EventWriter, ) { - let mut renderer = renderer.single_mut(); + /*let mut renderer = renderer.single_mut(); let world = world.single(); let block_registry = block_registry.into_inner(); let camera_translation = camera.single().1.translation; @@ -112,9 +116,9 @@ fn sys_update( // Identify chunks out of render distance (plus 2 for buffer), and remove them for mut chunk in render_chunks.iter_mut() { - if (chunk.1.position.0 - camera_grid_x).abs() > renderer.render_distance + 2 - || (chunk.1.position.1 - camera_grid_y).abs() > renderer.render_distance + 2 - || (chunk.1.position.2 - camera_grid_z).abs() > renderer.render_distance + 2 + if (chunk.1.position.0 - camera_grid_x).abs() > renderer.render_distance + 16 + || (chunk.1.position.1 - camera_grid_y).abs() > renderer.render_distance + 16 + || (chunk.1.position.2 - camera_grid_z).abs() > renderer.render_distance + 16 { commands.entity(chunk.0).despawn(); // Remove chunk from renderer @@ -122,19 +126,26 @@ fn sys_update( } } + let render_chunks = RwLock::new(render_chunks.iter_mut().collect::>()); + let meshes = RwLock::new(&mut meshes); + let commands = RwLock::new(&mut commands); // Identify chunks that should be loaded in by x y z - for x in camera_grid_x - renderer.render_distance..camera_grid_x + renderer.render_distance { - for y in camera_grid_y - renderer.render_distance..camera_grid_y + renderer.render_distance - { + let par_iter = (-renderer.render_distance..renderer.render_distance).into_par_iter(); + par_iter.for_each(|x| { + let par_iter = (-renderer.render_distance..renderer.render_distance).into_par_iter(); + par_iter.for_each(|y| { for z in camera_grid_z - renderer.render_distance..camera_grid_z + renderer.render_distance { let mut found = false; - // Check if chunk is already loaded (in ECS) - for chunk in render_chunks.iter_mut() { - if chunk.1.position == (x, y, z) { - found = true; - break; + { + let render_chunks = render_chunks.read().unwrap(); + // Check if chunk is already loaded (in ECS) + for chunk in render_chunks.iter() { + if chunk.1.position == (x, y, z) { + found = true; + break; + } } } @@ -159,13 +170,14 @@ fn sys_update( println!("Loaded chunk at {}, {}, {}", x, y, z); // Introduce adjacent chunks - + let mut meshes = meshes.write().unwrap(); let mesh = meshes.add(mesh); let new_chunk = WorldRendererChunk { position: (x, y, z), mesh: mesh.clone(), meta, }; + let mut commands = commands.write().unwrap(); commands.spawn(( Mesh3d(mesh), Transform::from_translation(Vec3::new( @@ -189,6 +201,159 @@ fn sys_update( } } } + }); + });*/ +} + +fn sys_on_chunk_generated( + mut commands: Commands, + mut ev_chunk_generated: EventReader, + data: ResMut>, + block_registry: Res, + world: Query<&GameWorld>, + renderer: Query<&WorldRenderer>, + chunks: Query<(Entity, &mut WorldRendererChunk, &mut Mesh3d)>, +) { + let world = world.single(); + let block_registry = block_registry.into_inner(); + let mesh_registry = Mutex::new(data); + let commands = Mutex::new(commands); + + let par_iter = ev_chunk_generated.par_read(); + let adj_faces = [Bottom, Top, Left, Right, Forward, Back]; + let adj_offsets = [ + (0, -1, 0), + (0, 1, 0), + (-1, 0, 0), + (1, 0, 0), + (0, 0, 1), + (0, 0, -1), + ]; + + par_iter.for_each(|event| { + let event = event; + let chunk = world.map.chunk_at(event.x, event.y, event.z); + let x = event.x; + let y = event.y; + let z = event.z; + match chunk { + WorldChunkStatus::Stored(stored) => { + let mut loaded = false; + { + let r = stored.read().unwrap(); + loaded = r.is_loaded(); + } + if loaded { + let r = stored.read().unwrap(); + let r_arc = r.unwrap().clone(); + let r_arc_3 = r_arc.read().unwrap(); + let data = r_arc_3.data(); + match mesh_grid( + (WorldChunk::SIZE, WorldChunk::SIZE, WorldChunk::SIZE), + &[], + data, + block_registry, + MeshingAlgorithm::Culling, + None, + ) { + Some((mesh, meta)) => { + let mut mesh = mesh; + let mut meta = meta; + + // Optimize mesh by introducing adjacent chunks + for i in 0..6 { + let offset = adj_offsets[i]; + let face = adj_faces[i]; + let adj_chunk = + world.map.chunk_at(x + offset.0, y + offset.1, z + offset.2); + match adj_chunk { + WorldChunkStatus::Stored(adj_stored) => { + let adj_r = adj_stored.read().unwrap(); + if adj_r.is_loaded() { + let adj_r_arc = adj_r.unwrap().clone(); + let adj_r_arc_3 = adj_r_arc.read().unwrap(); + let adj_data = adj_r_arc_3.data(); + introduce_adjacent_chunks( + block_registry, + &mut mesh, + &mut meta, + face, + adj_data, + ); + } + } + _ => {} + } + } + + // Optimize neighboring WorldRendererChunks by introducing this chunk + /* for i in 0..6 { + let offset = adj_offsets[i]; + let face = adj_faces[i]; + // Find WorldRendererChunk with the same position + for (entity, chunk, mesh) in chunks.iter() { + if chunk.position == (x + offset.0, y + offset.1, z + offset.2) { + // introduce_adjacent_chunks( + // block_registry, + // &mut mesh_registry.lock().unwrap().get_mut(mesh).unwrap(), + // &mut chunk.meta, + // face, + // data + // ); + } + } + }*/ + + println!("Meshed chunk at {}, {}, {}", x, y, z); + + let mesh = mesh_registry.lock().unwrap().add(mesh); + let mut commands = commands.lock().unwrap(); + commands.spawn(( + Mesh3d(mesh), + Transform::from_translation(Vec3::new( + x as f32 * WorldChunk::SIZE as f32, + y as f32 * WorldChunk::SIZE as f32, + z as f32 * WorldChunk::SIZE as f32, + )), + MeshMaterial3d(renderer.single().material.clone()), + WorldRendererChunk { + position: (x, y, z), + mesh: Handle::default(), + meta, + }, + )); + } + None => {} + } + } + } + WorldChunkStatus::Unloaded => {} + } + }); +} + +fn sys_on_chunk_dropped( + mut commands: Commands, + mut ev_chunk_dropped: EventReader, + world: Query<&GameWorld>, + renderer: Query<&WorldRenderer>, + chunks: Query<(Entity, &WorldRendererChunk)>, +) { + let world = world.single(); + let renderer = renderer.single(); + // find all WorldRendererChunks that are in the dropped chunks + for event in ev_chunk_dropped.par_read() { + let event = event.0; + let chunk = world.map.chunk_at(event.x, event.y, event.z); + let x = event.x; + let y = event.y; + let z = event.z; + + // Search for WorldRendererChunk with the same position + for (entity, chunk) in chunks.iter() { + if chunk.position == (x, y, z) { + commands.entity(entity).despawn(); + } } } } diff --git a/src/client/systems/debug.rs b/src/client/systems/debug.rs index dbbaf08..fd42358 100644 --- a/src/client/systems/debug.rs +++ b/src/client/systems/debug.rs @@ -1,12 +1,17 @@ use bevy::{ color::palettes::css::{GREEN_YELLOW, MAROON, WHEAT}, - prelude::{BuildChildren, ChildBuild, Commands, Component}, + prelude::{BuildChildren, ChildBuild, Commands, Component, Entity, Query, Text}, text::{Text2d, TextColor, TextFont, TextLayout, TextSpan}, + time::Time, ui::{ AlignItems, BackgroundColor, FlexDirection, JustifyContent, Node, PositionType, UiRect, Val, }, utils::default, }; +use bevy_egui::{EguiContext, EguiContexts}; +use egui::Color32; + +use crate::data::world; #[derive(Component)] pub struct DebugMenuComponent { @@ -15,15 +20,79 @@ pub struct DebugMenuComponent { pub fn setup_debug_menu(mut commands: Commands) { commands.spawn(( - TextSpan::new("Debug Menu"), - TextFont { ..default() }, + Text("Debug Menu".to_string()), Node { position_type: PositionType::Absolute, left: Val::Px(12.0), top: Val::Px(12.0), ..default() }, + DebugMenuComponent { show: true }, )); } -pub fn update_debug_menu() {} +pub fn update_debug_menu( + time: bevy::prelude::Res