diff --git a/src/game/mod.rs b/src/game/mod.rs index ab9e7c4..2f3791c 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -27,7 +27,7 @@ impl Game { Self { state: GameState::Paused, player: Player::new(), - world: World::new("default".to_string()), + world: World::init("hello".to_string()), prev_pos: None } diff --git a/src/game/world/chunk.rs b/src/game/world/chunk.rs index 22e280c..d5ff836 100644 --- a/src/game/world/chunk.rs +++ b/src/game/world/chunk.rs @@ -29,7 +29,6 @@ impl WorldChunk { } } - #[allow(unused)] pub fn from_bytes(bytes: Vec) -> Self { let mut instance = Self::new(); @@ -61,7 +60,6 @@ impl WorldChunk { instance } - #[allow(unused)] pub fn to_bytes(&self) -> Vec { // Header diff --git a/src/game/world/data.rs b/src/game/world/data.rs new file mode 100644 index 0000000..785a082 --- /dev/null +++ b/src/game/world/data.rs @@ -0,0 +1,16 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct WorldData { + pub name: String, + pub display_name: String +} + +impl Default for WorldData { + fn default() -> Self { + Self { + name: String::from("default"), + display_name: String::from("Default") + } + } +} \ No newline at end of file diff --git a/src/game/world/mod.rs b/src/game/world/mod.rs index 85c7d62..00d8519 100644 --- a/src/game/world/mod.rs +++ b/src/game/world/mod.rs @@ -1,4 +1,5 @@ mod generator; +mod data; pub mod chunk; pub mod block; use std::time::Instant; @@ -6,17 +7,12 @@ use std::path::PathBuf; use std::io::Write; use std::io::Read; use std::fs; -use cgmath::{Point3, Vector3}; +use cgmath::Vector3; use std::collections::HashMap; use generator::WorldGen; use chunk::WorldChunk; use block::WorldBlock; - -#[derive(serde::Serialize, serde::Deserialize, Debug)] -struct WorldData { - #[serde(default)] - pub name: String -} +use data::WorldData; pub struct World { data: WorldData, @@ -25,43 +21,56 @@ pub struct World { impl World { - pub fn new(name: String) -> Self { - Self { - data: WorldData { name }, + pub fn init(name: String) -> Self { + let mut instance = Self { + data: WorldData { name, ..Default::default() }, chunks: HashMap::new(), + }; + instance.load_metadata(); + instance.save_all(); + instance + } + + /* + * Loads metadata + */ + fn load_metadata(&mut self) -> bool { + let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").join(self.data.name.clone()).join("world.dat"); + if let Ok(mut file) = fs::File::open(filepath) { + let mut buf = Vec::new(); + file.read_to_end(&mut buf).unwrap(); + if let Ok(data) = toml::from_slice::(&buf[..]) { + self.data = data; + return true; + } } + return false; } - pub fn all_blocks(&self) -> Vec<(Vector3, &WorldBlock)> { - self.chunks.iter().map(|(chunk_pos, chunk)| { - let off = chunk_pos * chunk::SIZE as i32; - let result: Vec<(Vector3, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| { - ( - Vector3{x: off.x+pos.x as i32, y: off.y+pos.y as i32, z: off.z+pos.z as i32}, - *block - ) - }).collect(); - result - }).flatten().collect() - } - - pub fn save(&self) { + /* + * Saves metadata and all loaded chunks + */ + pub fn save_all(&self) { // Get directory - let dir = dirs::data_local_dir().unwrap().join("Voxelgame"); - fs::create_dir_all(&dir).expect("Failed to create directory"); + let dirpath = dirs::data_local_dir().unwrap().join("Voxelgame").join("saves").join(&self.data.name); + fs::create_dir_all(&dirpath).expect("Failed to create world directory"); // Save all chunks for (pos, chunk) in self.chunks.iter() { - let path = dir.with_file_name(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z)); - let mut file = fs::File::create(path).expect("Failed to create file"); - file.write(&chunk.to_bytes()).unwrap(); + let filepath = dirpath.join(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z)); + let mut file = fs::File::create(filepath).expect("Failed to create chunk file"); + file.write(&chunk.to_bytes()).expect("Failed to write chunk file"); } // Save metadata - let data_str = toml::to_string(&self.data).unwrap(); - let data_path: PathBuf = [dir.to_str().unwrap(), "world.dat"].iter().collect(); - let mut data_file = fs::File::create(data_path).unwrap(); - data_file.write_all(data_str.as_bytes()).expect("Failed to write world metadata"); + let datastr = toml::to_string(&self.data).unwrap(); + let datapath: PathBuf = dirpath.join("world.dat"); + let mut datafile = fs::File::create(datapath).expect("Failed to create world metadata file"); + datafile.write_all(datastr.as_bytes()).expect("Failed to write world metadata"); + } + + pub fn get_chunk(&mut self, chunk_wpos: &Vector3) -> Option<&mut WorldChunk> { + self.chunks.get_mut(chunk_wpos) } /* @@ -76,12 +85,13 @@ impl World { // Try loading chunk from file let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z); - let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name(filename); + let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").join("saves").join(&self.data.name).join(filename); + if filepath.is_file() { let timer = Instant::now(); let mut buff = Vec::new(); - let mut file = fs::File::open(filepath).expect("Failed to open file"); - file.read_to_end(&mut buff).expect("Failed to read file"); + let mut file = fs::File::open(filepath).expect("Failed to open chunk file"); + file.read_to_end(&mut buff).expect("Failed to read chunk file"); let chunk = WorldChunk::from_bytes(buff); self.chunks.insert(pos, chunk); println!("Chunk at {:?} loaded ({} ms)", pos, timer.elapsed().as_millis()); @@ -99,13 +109,11 @@ impl World { } } - // Save + // Save generated chunk to file let timer1 = Instant::now(); - let dir = dirs::data_local_dir().unwrap().join("Voxelgame"); - fs::create_dir_all(&dir).expect("Failed to create directory"); - let path = dir.with_file_name(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z)); - let mut file = fs::File::create(path).expect("Failed to create file"); - file.write(&chunk.to_bytes()).unwrap(); + fs::create_dir_all(&filepath.parent().unwrap()).expect("Failed to create world directory"); + let mut file = fs::File::create(filepath).expect("Failed to create chunk file"); + file.write(&chunk.to_bytes()).expect("Failed to write chunk file"); // Add to list self.chunks.insert(pos, chunk); @@ -117,33 +125,14 @@ impl World { true } - pub fn load(&mut self) -> bool { - // Load metadata - let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name("world.dat"); - if let Ok(mut file) = fs::File::open(filepath) { - let mut buf = Vec::new(); - file.read_to_end(&mut buf).unwrap(); - if let Ok(data) = toml::from_slice::(&buf[..]) { - self.data = data; - return true; - } - } - return false; - } - - #[allow(unused)] - pub fn get_block(&mut self, block_wpos: &Vector3) -> Option<&WorldBlock> { - // Split world space to chunk and block space - let ch_pos = block_wpos / chunk::SIZE as i32; - let bl_pos = block_wpos % chunk::SIZE as i32; - // Return block if exists - if let Some(chunk) = self.chunks.get_mut(&ch_pos) { - return Some(chunk.get_block(&bl_pos)); - } - None - } - - pub fn get_chunk(&mut self, chunk_wpos: &Vector3) -> Option<&mut WorldChunk> { - self.chunks.get_mut(chunk_wpos) - } + // pub fn get_block(&mut self, block_wpos: &Vector3) -> Option<&WorldBlock> { + // // Split world space to chunk and block space + // let ch_pos = block_wpos / chunk::SIZE as i32; + // let bl_pos = block_wpos % chunk::SIZE as i32; + // // Return block if exists + // if let Some(chunk) = self.chunks.get_mut(&ch_pos) { + // return Some(chunk.get_block(&bl_pos)); + // } + // None + // } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 65eba93..6fde57f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use cgmath::Vector3; +use crate::renderer::renderer_view::RendererView; use winit::{ event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, MouseButton}, event_loop::{ControlFlow, EventLoop}, @@ -43,13 +45,28 @@ fn main() { match event { // Window closed (ALT+F4,) WindowEvent::CloseRequested => { *c = ControlFlow::Exit; } - // Escape key - WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. }, .. } => { - // Request pause - if let GameState::Paused = game.update_state(GameState::Paused) { - window.set_cursor_grab(false).unwrap(); - window.set_cursor_visible(true); + // Key pressed + WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, virtual_keycode, .. }, .. } => { + if let Some(key) = virtual_keycode { + match key { + // Escape pressed + VirtualKeyCode::Escape => { + if let GameState::Paused = game.update_state(GameState::Paused) { + window.set_cursor_grab(false).unwrap(); + window.set_cursor_visible(true); + } + } + // Test key + VirtualKeyCode::T => { + let t = &mut renderer as &mut dyn RendererView; + t.shift(&Vector3::{ x:32, y:0, z:0 }); + println!("shifted"); + } + _ => () + } + } + } // Window clicked WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left, .. } => { diff --git a/src/renderer/buffers/content.rs b/src/renderer/buffers/content.rs index 45f359a..80859f7 100644 --- a/src/renderer/buffers/content.rs +++ b/src/renderer/buffers/content.rs @@ -101,7 +101,6 @@ impl Content { ContentStats { node_tex_size, block_tex_size, block_tex_used, block_count, block_used } } - #[allow(unused)] pub fn write(&mut self, queue: &wgpu::Queue, block_pos: &Vector3, block: &WorldBlock) { // Get node @@ -139,6 +138,37 @@ impl Content { ); } + pub fn shift(&mut self, offset: &Vector3) { + let timer = Instant::now(); + let mut result = vec![0_u32; self.node_buffer.len()].into_boxed_slice(); + let ox = offset.x as usize; + let oy = offset.y as usize; + let oz = offset.z as usize; + + for x0 in 0..NODE_TEX_SIZE { + let x1 = x0+ox; + if x1 >= NODE_TEX_SIZE { continue; } + + for y0 in 0..NODE_TEX_SIZE { + let y1 = y0+oy; + if y1 >= NODE_TEX_SIZE { continue; } + + for z0 in 0..NODE_TEX_SIZE { + let z1 = z0+oz; + if z1 >= NODE_TEX_SIZE { continue; } + + let idx0 = x0 + NODE_TEX_SIZE * (y0 + NODE_TEX_SIZE * z0); + let idx1 = x1 + NODE_TEX_SIZE * (y1 + NODE_TEX_SIZE * z1); + result[idx1] = self.node_buffer[idx0]; + } + } + } + + self.node_buffer = result; + self.node_dirty = true; + println!("Shifted in {} ms", timer.elapsed().as_millis()); + } + fn write_nodes(&mut self, queue: &wgpu::Queue, z: usize) { // Convert u32 node buffer to u8 let node_origin = wgpu::Origin3d{ x:0, y: 0, z: z as u32 }; diff --git a/src/renderer/renderer_view.rs b/src/renderer/renderer_view.rs index a26148f..d955dd5 100644 --- a/src/renderer/renderer_view.rs +++ b/src/renderer/renderer_view.rs @@ -6,6 +6,7 @@ use cgmath::Vector3; pub trait RendererView { fn write(&mut self, block_pos: &Vector3, block: &WorldBlock); + fn shift(&mut self, offset: &Vector3); fn get_camera_transform(&mut self) -> &mut dyn CameraTransform; fn get_ui(&mut self) -> &mut UserInterface; } @@ -16,6 +17,10 @@ impl RendererView for Renderer { self.buffers.content.write(&self.queue, block_pos, block) } + fn shift(&mut self, offset: &Vector3) { + self.buffers.content.shift(offset); + } + fn get_camera_transform(&mut self) -> &mut dyn CameraTransform { &mut self.camera }