diff --git a/Cargo.lock b/Cargo.lock index 8e4e7d6..2636f58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2287,12 +2287,14 @@ dependencies = [ "futures", "glob", "image", + "linked-hash-map", "noise", "rand 0.8.3", "shaderc", "wgpu", "wgpu_glyph", "winit", + "yaml-rust", ] [[package]] @@ -2426,3 +2428,12 @@ name = "xml-rs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml index fc5a24c..ea21456 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ rand = "0.8" noise = "0.7" dirs = "3.0" flate2 = "1.0" +yaml-rust = "0.4" +linked-hash-map = "0.5" [build-dependencies] anyhow = "1.0" diff --git a/src/app/mod.rs b/src/app/mod.rs index 4af6e5c..53e0b22 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -28,23 +28,26 @@ impl App { }; - // let timer = Instant::now(); - // for x in 0..16 { - // for z in 0..16 { - // let pos = &Point3{x,y:0,z}; - // let block = app.world.gen_block(pos); - // content.write(pos, block); - // } - // } - // println!("Generated in {}", timer.elapsed().as_secs_f32()); - - // let timer = Instant::now(); - // app.world.save(); - // println!("Saved in {}", timer.elapsed().as_secs_f32()); - let timer = Instant::now(); - app.world.load(); - println!("Loaded in {}", timer.elapsed().as_secs_f32()); + for x in 0..16 { + for z in 0..16 { + let pos = &Point3{x,y:0,z}; + let block = app.world.gen_block(pos); + content.write(pos, block); + } + } + println!("Generated in {}", timer.elapsed().as_secs_f32()); + + let timer = Instant::now(); + app.world.save(); + println!("Saved in {}", timer.elapsed().as_secs_f32()); + + // let timer = Instant::now(); + // app.world.load(); + // for (pos, block) in app.world.all_blocks() { + // content.write(&pos, block); + // } + // println!("Loaded in {}", timer.elapsed().as_secs_f32()); // let pos = &Point3{x:1,y:0,z:0}; // let block = app.world.gen_block(pos); diff --git a/src/app/world/chunk.rs b/src/app/world/chunk.rs index a527064..54ff49c 100644 --- a/src/app/world/chunk.rs +++ b/src/app/world/chunk.rs @@ -6,10 +6,12 @@ use byteorder::ByteOrder; use cgmath::Point3; use std::io::Write; use std::convert::TryInto; +use std::io::Read; use crate::app::world::{WorldBlock, block}; -pub const SIZE: u32 = 32; -const SIZE_QB: u32 = SIZE*SIZE*SIZE; +pub const SIZE: usize = 32; +const SIZE_SQ: usize = SIZE*SIZE; +const SIZE_QB: usize = SIZE*SIZE*SIZE; pub struct WorldChunk { nodes: Box<[u32]>, @@ -24,9 +26,28 @@ impl WorldChunk { } } + pub fn all_blocks(&self) -> Vec<(Point3, &WorldBlock)> { + self.nodes.iter().enumerate() + .filter(|(_, n)| **n != 0) + .map(|(index, node)| { + + let mut idx = index; + let z = idx / SIZE_SQ; + idx -= z * SIZE_SQ; + let y = idx / SIZE; + let x = idx % SIZE; + + let pos = Point3{x: x as u32, y: y as u32, z: z as u32}; + + let block = &self.blocks[(*node-1) as usize]; + (pos, block) + }) + .collect() + } + pub fn get_block(&mut self, block_pos: &Point3) -> &mut WorldBlock { // Get block index - let index = (block_pos.x + SIZE * (block_pos.y + SIZE * block_pos.z)) as usize; + let index = (block_pos.x + SIZE as u32 * (block_pos.y + SIZE as u32 * block_pos.z)) as usize; let mut value = self.nodes[index]; // Allocate new block if value == 0 { @@ -47,15 +68,11 @@ impl WorldChunk { bytes.extend(node_bytes.iter()); // Blocks - // let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast()); - // for block in self.blocks.iter() { - // encoder.write_all(&block.materials).unwrap(); - // } - // bytes.extend(encoder.finish().unwrap()); - + let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast()); for block in self.blocks.iter() { - bytes.extend(&block.materials); + encoder.write_all(&block.materials).unwrap(); } + bytes.extend(encoder.finish().unwrap()); bytes } @@ -65,14 +82,19 @@ impl WorldChunk { let (node_bytes, block_bytes) = bytes.split_at((SIZE_QB*4) as usize); // Nodes + let mut block_num = 0; for i in 0..SIZE_QB { let idx = (i*4) as usize; - instance.nodes[i as usize] = u32::from_le_bytes([node_bytes[idx], node_bytes[idx+1], node_bytes[idx+2], node_bytes[idx+3]]); + let val = u32::from_le_bytes([node_bytes[idx], node_bytes[idx+1], node_bytes[idx+2], node_bytes[idx+3]]); + instance.nodes[i as usize] = val; + if val > 0 { block_num+=1; } } // Blocks - let block_num = block_bytes.len() / block::SIZE_QB; - println!("block_num {}", block_num); + let mut decoder = DeflateDecoder::new(block_bytes); + let mut block_bytes = Vec::new(); + decoder.read_to_end(&mut block_bytes).expect("Failed to decode"); + for i in 0..block_num { let mut block = WorldBlock::new(); let idx = i * block::SIZE_QB; diff --git a/src/app/world/mod.rs b/src/app/world/mod.rs index 5220b57..db81c3a 100644 --- a/src/app/world/mod.rs +++ b/src/app/world/mod.rs @@ -1,20 +1,23 @@ mod generator; pub mod chunk; pub mod block; +use linked_hash_map::LinkedHashMap; use std::path::PathBuf; use std::path::Path; use std::io::Write; use std::io::Read; use std::fs; -use cgmath::Point3; +use cgmath::{Point3, InnerSpace}; use std::collections::HashMap; use generator::WorldGen; use chunk::WorldChunk; use block::WorldBlock; +use yaml_rust::{Yaml, YamlLoader, YamlEmitter}; + pub struct World { name: String, - chunks: HashMap, WorldChunk> + chunks: HashMap, WorldChunk>, } impl World { @@ -22,10 +25,23 @@ impl World { pub fn new(name: String) -> Self { Self { name, - chunks: HashMap::new() + chunks: HashMap::new(), } } + pub fn all_blocks(&self) -> Vec<(Point3, &WorldBlock)> { + self.chunks.iter().map(|(chunk_pos, chunk)| { + let off = chunk_pos * chunk::SIZE as u32; + let result: Vec<(Point3, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| { + ( + Point3{x: off.x+pos.x, y: off.y+pos.y, z: off.z+pos.z}, + *block + ) + }).collect(); + result + }).flatten().collect() + } + pub fn save(&self) { // Get directory let mut dir = dirs::data_local_dir().unwrap(); @@ -41,6 +57,18 @@ impl World { let mut file = fs::File::create(path).expect("Failed to create file"); file.write(&bytes).unwrap(); } + + // Save metadata + let mut data: LinkedHashMap = LinkedHashMap::new(); + data.insert(Yaml::String("name".to_string()), Yaml::String(self.name.clone())); + + let mut data_str = String::new(); + let mut emit = YamlEmitter::new(&mut data_str); + emit.dump(&Yaml::Hash(data)).expect("Failed to encode yaml"); + + let data_path: PathBuf = [dir.to_str().unwrap(), "world.yml"].iter().collect(); + let mut data_file = fs::File::create(data_path).unwrap(); + data_file.write_all(data_str.as_bytes()).expect("Failed to write"); } pub fn load(&mut self) { @@ -74,12 +102,15 @@ impl World { println!("loaded {:?}", pos); self.chunks.insert(pos, chunk); } + + // Load metadata + } pub fn gen_block(&mut self, block_pos: &Point3) -> &WorldBlock { // Split world space to chunk and block space - let ch_pos = block_pos / chunk::SIZE; - let bl_pos = block_pos % chunk::SIZE; + let ch_pos = block_pos / chunk::SIZE as u32; + let bl_pos = block_pos % chunk::SIZE as u32; // Create chunk if does not exist if let None = self.chunks.get(&ch_pos) { self.chunks.insert(block_pos.clone(), WorldChunk::new()); @@ -94,8 +125,8 @@ impl World { #[allow(unused)] pub fn get_block(&mut self, block_pos: &Point3) -> Option<&WorldBlock> { // Split world space to chunk and block space - let ch_pos = block_pos / chunk::SIZE; - let bl_pos = block_pos % chunk::SIZE; + let ch_pos = block_pos / chunk::SIZE as u32; + let bl_pos = block_pos % chunk::SIZE as u32; // Return block if exists if let Some(chunk) = self.chunks.get_mut(&ch_pos) { return Some(chunk.get_block(&bl_pos));