From 15247d12d4db7fcc73ea8581691e3c3fd9b91127 Mon Sep 17 00:00:00 2001 From: Vijfhoek Date: Wed, 2 Jun 2021 19:45:29 +0200 Subject: [PATCH 1/2] Allow for chunks with negative coordinates --- Cargo.lock | 10 +++ Cargo.toml | 1 + src/chunk.rs | 23 +++---- src/quad.rs | 10 +-- src/state/world_state.rs | 32 +++++----- src/world.rs | 133 +++++++++++++++++---------------------- 6 files changed, 100 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbfde82..601e532 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -970,6 +970,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.7" @@ -1115,6 +1124,7 @@ dependencies = [ "futures", "gltf", "image", + "itertools", "log", "noise", "rayon", diff --git a/Cargo.toml b/Cargo.toml index f4f9d21..02c9931 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ noise = "0.7.0" rayon = "1.5.1" wgpu = "0.8.1" winit = { version = "0.25.0", default_features = false, features = ["x11", "web-sys"] } +itertools = "0.10.0" [profile.release] debug = true diff --git a/src/chunk.rs b/src/chunk.rs index 73a9b2d..7772795 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -2,7 +2,7 @@ use std::{collections::VecDeque, usize}; use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex}; use ahash::{AHashMap, AHashSet}; -use cgmath::Vector3; +use cgmath::{Point3, Vector3}; use noise::utils::{NoiseMapBuilder, PlaneMapBuilder}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -55,7 +55,8 @@ pub struct Block { pub block_type: BlockType, } -pub const CHUNK_SIZE: usize = 64; +pub const CHUNK_SIZE: usize = 32; +pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize; type ChunkBlocks = [[[Option; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]; @@ -217,10 +218,10 @@ impl Chunk { fn layer_to_quads( &self, y: usize, - offset: Vector3, + offset: Point3, culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>, queue: &mut VecDeque<(usize, usize)>, - highlighted: Option<&(Vector3, Vector3)>, + highlighted: Option<&(Point3, Vector3)>, ) -> Vec { let mut quads: Vec = Vec::new(); let mut visited = AHashSet::new(); @@ -236,7 +237,7 @@ impl Chunk { if let Some(&(block_type, visible_faces)) = &culled.get(&(x, z)) { let mut quad_faces = visible_faces; - if hl == Some(Vector3::new(x, y, z)) { + if hl == Some(Point3::new(x, y, z)) { let mut quad = Quad::new(position, 1, 1); quad.highlighted_normal = highlighted.unwrap().1; quad.visible_faces = quad_faces; @@ -258,7 +259,7 @@ impl Chunk { for x_ in x..CHUNK_SIZE { xmax = x_ + 1; - if visited.contains(&(xmax, z)) || hl == Some(Vector3::new(xmax, y, z)) { + if visited.contains(&(xmax, z)) || hl == Some(Point3::new(xmax, y, z)) { break; } @@ -280,7 +281,7 @@ impl Chunk { zmax = z_ + 1; for x_ in x..xmax { - if visited.contains(&(x_, zmax)) || hl == Some(Vector3::new(x_, y, zmax)) { + if visited.contains(&(x_, zmax)) || hl == Some(Point3::new(x_, y, zmax)) { break 'z; } @@ -299,7 +300,7 @@ impl Chunk { } } - let mut quad = Quad::new(position, (xmax - x) as i32, (zmax - z) as i32); + let mut quad = Quad::new(position, (xmax - x) as isize, (zmax - z) as isize); quad.visible_faces = quad_faces; quad.block_type = Some(block_type); quads.push(quad); @@ -319,14 +320,14 @@ impl Chunk { pub fn to_geometry( &self, - offset: Vector3, - highlighted: Option<&(Vector3, Vector3)>, + position: Point3, + highlighted: Option<&(Point3, Vector3)>, ) -> Geometry { let quads: Vec = (0..CHUNK_SIZE) .into_par_iter() .flat_map(|y| { let (culled, mut queue) = self.cull_layer(y); - self.layer_to_quads(y, offset, culled, &mut queue, highlighted) + self.layer_to_quads(y, position, culled, &mut queue, highlighted) }) .collect(); diff --git a/src/quad.rs b/src/quad.rs index 55da2f5..0bbd25b 100644 --- a/src/quad.rs +++ b/src/quad.rs @@ -1,4 +1,4 @@ -use cgmath::{Vector3, Zero}; +use cgmath::{Point3, Vector3, Zero}; use crate::{ chunk::{ @@ -11,9 +11,9 @@ use crate::{ #[derive(Debug)] pub struct Quad { - pub position: Vector3, - pub dx: i32, - pub dz: i32, + pub position: Point3, + pub dx: isize, + pub dz: isize, pub highlighted_normal: Vector3, pub visible_faces: FaceFlags, @@ -21,7 +21,7 @@ pub struct Quad { } impl Quad { - pub fn new(position: Vector3, dx: i32, dz: i32) -> Self { + pub fn new(position: Point3, dx: isize, dz: isize) -> Self { Quad { position, dx, diff --git a/src/state/world_state.rs b/src/state/world_state.rs index cbb0e55..5d763e1 100644 --- a/src/state/world_state.rs +++ b/src/state/world_state.rs @@ -13,7 +13,7 @@ use winit::{ use crate::{ camera::{Camera, Projection}, - chunk::{Block, BlockType, CHUNK_SIZE}, + chunk::{Block, BlockType, CHUNK_ISIZE}, geometry::GeometryBuffers, render_context::RenderContext, texture::{Texture, TextureManager}, @@ -35,13 +35,13 @@ pub struct WorldState { pub time_bind_group: wgpu::BindGroup, pub world: World, - pub chunk_buffers: AHashMap, GeometryBuffers>, + pub chunk_buffers: AHashMap, GeometryBuffers>, time: Time, time_buffer: wgpu::Buffer, wireframe: bool, shader: wgpu::ShaderModule, render_pipeline_layout: wgpu::PipelineLayout, - pub highlighted: Option<(Vector3, Vector3)>, + pub highlighted: Option<(Point3, Vector3)>, pub forward_pressed: bool, pub backward_pressed: bool, @@ -256,13 +256,13 @@ impl WorldState { pub fn update_chunk_geometry( &mut self, render_context: &RenderContext, - chunk_position: Vector3, + chunk_position: Point3, ) { - let chunk = &mut self.world.chunks[chunk_position.y][chunk_position.z][chunk_position.x]; - let offset = chunk_position.map(|f| (f * CHUNK_SIZE) as i32); + let chunk = &mut self.world.chunks.get(&chunk_position).unwrap(); + let offset = chunk_position * CHUNK_ISIZE; let geometry = chunk.to_geometry( offset, - World::highlighted_for_chunk(self.highlighted, chunk_position).as_ref(), + World::highlighted_for_chunk(self.highlighted, &chunk_position).as_ref(), ); let buffers = @@ -393,7 +393,7 @@ impl WorldState { let camera_pos = Vector2::new(camera_pos.x, camera_pos.z); for (position, buffers) in &self.chunk_buffers { - let pos = (position * CHUNK_SIZE).cast().unwrap(); + let pos = (position * CHUNK_ISIZE).cast().unwrap(); let pos = Vector2::new(pos.x, pos.z); if (pos - camera_pos).magnitude() > 300.0 { continue; @@ -433,12 +433,10 @@ impl WorldState { let camera = &self.camera; let old = self.highlighted; - let new = self - .world - .raycast(camera.position.to_vec(), camera.direction()); + let new = self.world.raycast(camera.position, camera.direction()); - let old_chunk = old.map(|h| h.0 / CHUNK_SIZE); - let new_chunk = new.map(|h| h.0 / CHUNK_SIZE); + let old_chunk = old.map(|(pos, _)| pos.map(|n| n.div_euclid(CHUNK_ISIZE))); + let new_chunk = new.map(|(pos, _)| pos.map(|n| n.div_euclid(CHUNK_ISIZE))); if old != new { self.highlighted = new; @@ -460,12 +458,10 @@ impl WorldState { let camera = &self.camera; let world = &mut self.world; - if let Some((pos, face_normal)) = - world.raycast(camera.position.to_vec(), camera.direction()) - { + if let Some((pos, face_normal)) = world.raycast(camera.position, camera.direction()) { if button == &MouseButton::Left { world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None); - self.update_chunk_geometry(render_context, pos / CHUNK_SIZE); + self.update_chunk_geometry(render_context, pos / CHUNK_ISIZE); } else if button == &MouseButton::Right { let new_pos = pos.cast().unwrap() + face_normal; @@ -478,7 +474,7 @@ impl WorldState { }), ); - self.update_chunk_geometry(render_context, pos / CHUNK_SIZE); + self.update_chunk_geometry(render_context, pos / CHUNK_ISIZE); } } } diff --git a/src/world.rs b/src/world.rs index 483a59e..608c52d 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,62 +1,62 @@ +use std::collections::HashMap; + use crate::{ - chunk::{Block, Chunk, CHUNK_SIZE}, + chunk::{Block, Chunk, CHUNK_ISIZE, CHUNK_SIZE}, geometry::Geometry, npc::Npc, vertex::BlockVertex, }; -use cgmath::{InnerSpace, Vector3}; +use cgmath::{EuclideanSpace, InnerSpace, Point3, Vector3}; use rayon::prelude::*; pub struct World { - pub chunks: Vec>>, + pub chunks: HashMap, Chunk>, pub npc: Npc, } const WORLD_SIZE: Vector3 = Vector3::new( - 32 * 16 / CHUNK_SIZE, + 8 * 16 / CHUNK_SIZE, 16 * 16 / CHUNK_SIZE, - 32 * 16 / CHUNK_SIZE, + 8 * 16 / CHUNK_SIZE, ); impl World { pub fn generate() -> Self { - let mut chunks = Vec::new(); - let npc = Npc::load(); + let half: Vector3 = WORLD_SIZE.cast().unwrap() / 2; - (0..WORLD_SIZE.y) - .into_par_iter() - .map(|y| { - let mut chunks_z = Vec::new(); - for z in 0..WORLD_SIZE.z { - let mut chunks_x = Vec::new(); - for x in 0..WORLD_SIZE.x { - let chunk = Chunk::generate(x as i32, y as i32, z as i32); - chunks_x.push(chunk); - } - chunks_z.push(chunks_x); - } - chunks_z + let coords: Vec<_> = + itertools::iproduct!(-half.x..half.x, 0..WORLD_SIZE.y as isize, -half.z..half.z) + .collect(); + + let chunks: HashMap<_, _> = coords + .par_iter() + .map(|&(x, y, z)| { + ( + Point3::new(x, y, z), + Chunk::generate(x as i32, y as i32, z as i32), + ) }) - .collect_into_vec(&mut chunks); + .collect(); Self { chunks, npc } } pub fn highlighted_for_chunk( - highlighted: Option<(Vector3, Vector3)>, - chunk_position: Vector3, - ) -> Option<(Vector3, Vector3)> { - let position = chunk_position * CHUNK_SIZE; + highlighted: Option<(Point3, Vector3)>, + chunk_position: &Point3, + ) -> Option<(Point3, Vector3)> { + let position = chunk_position * CHUNK_ISIZE; if let Some((pos, face)) = highlighted { if pos.x >= position.x - && pos.x < position.x + CHUNK_SIZE + && pos.x < position.x + CHUNK_ISIZE && pos.y >= position.y - && pos.y < position.y + CHUNK_SIZE + && pos.y < position.y + CHUNK_ISIZE && pos.z >= position.z - && pos.z < position.z + CHUNK_SIZE + && pos.z < position.z + CHUNK_ISIZE { - Some((pos - position, face)) + let point: Point3 = EuclideanSpace::from_vec(pos - position); + Some((point.cast().unwrap(), face)) } else { None } @@ -67,26 +67,18 @@ impl World { pub fn to_geometry( &self, - highlighted: Option<(Vector3, Vector3)>, - ) -> Vec<(Vector3, Geometry)> { + highlighted: Option<(Point3, Vector3)>, + ) -> Vec<(Point3, Geometry)> { let instant = std::time::Instant::now(); let chunks = &self.chunks; let geometry = chunks .par_iter() - .enumerate() - .flat_map(|(y, chunks_y)| { - let mut chunk_geometry = Vec::new(); - for (z, chunks_z) in chunks_y.iter().enumerate() { - for (x, chunk) in chunks_z.iter().enumerate() { - let chunk_position = Vector3::new(x as usize, y as usize, z as usize); - let offset = (chunk_position * CHUNK_SIZE).cast().unwrap(); - let h = Self::highlighted_for_chunk(highlighted, chunk_position); - let geometry = chunk.to_geometry(offset, h.as_ref()); - chunk_geometry.push((Vector3::new(x, y, z), geometry)); - } - } - chunk_geometry + .map(|(chunk_position, chunk)| { + let position = (chunk_position * CHUNK_ISIZE).cast().unwrap(); + let h = Self::highlighted_for_chunk(highlighted, chunk_position); + let geometry = chunk.to_geometry(position, h.as_ref()); + (*chunk_position, geometry) }) .collect(); @@ -97,41 +89,32 @@ impl World { } pub fn get_block(&self, x: isize, y: isize, z: isize) -> Option<&Block> { - if x < 0 || y < 0 || z < 0 { - return None; - } - - let chunk = match self - .chunks - .get(y as usize / CHUNK_SIZE) - .and_then(|chunk_layer| chunk_layer.get(z as usize / CHUNK_SIZE)) - .and_then(|chunk_row| chunk_row.get(x as usize / CHUNK_SIZE)) - { - Some(v) => v, + let chunk = match self.chunks.get(&Point3::new( + x.div_euclid(CHUNK_ISIZE), + y.div_euclid(CHUNK_ISIZE), + z.div_euclid(CHUNK_ISIZE), + )) { + Some(chunk) => chunk, None => return None, }; - chunk.blocks[y as usize % CHUNK_SIZE][z as usize % CHUNK_SIZE][x as usize % CHUNK_SIZE] - .as_ref() + let bx = x.rem_euclid(CHUNK_ISIZE) as usize; + let by = y.rem_euclid(CHUNK_ISIZE) as usize; + let bz = z.rem_euclid(CHUNK_ISIZE) as usize; + chunk.blocks[by][bz][bx].as_ref() } pub fn set_block(&mut self, x: isize, y: isize, z: isize, block: Option) { - if x < 0 || y < 0 || z < 0 { - return; + if let Some(chunk) = self.chunks.get_mut(&Point3::new( + x.div_euclid(CHUNK_ISIZE), + y.div_euclid(CHUNK_ISIZE), + z.div_euclid(CHUNK_ISIZE), + )) { + let bx = x.rem_euclid(CHUNK_ISIZE) as usize; + let by = y.rem_euclid(CHUNK_ISIZE) as usize; + let bz = z.rem_euclid(CHUNK_ISIZE) as usize; + chunk.blocks[by][bz][bx] = block; } - - let chunk = match self - .chunks - .get_mut(y as usize / CHUNK_SIZE) - .and_then(|chunk_layer| chunk_layer.get_mut(z as usize / CHUNK_SIZE)) - .and_then(|chunk_row| chunk_row.get_mut(x as usize / CHUNK_SIZE)) - { - Some(v) => v, - None => return, - }; - - chunk.blocks[y as usize % CHUNK_SIZE][z as usize % CHUNK_SIZE][x as usize % CHUNK_SIZE] = - block; } fn calc_scale(vector: Vector3, scalar: f32) -> f32 { @@ -145,9 +128,9 @@ impl World { #[allow(dead_code)] pub fn raycast( &self, - origin: Vector3, + origin: Point3, direction: Vector3, - ) -> Option<(Vector3, Vector3)> { + ) -> Option<(Point3, Vector3)> { let direction = direction.normalize(); let scale = Vector3::new( Self::calc_scale(direction, direction.x), @@ -155,7 +138,7 @@ impl World { Self::calc_scale(direction, direction.z), ); - let mut position: Vector3 = origin.cast().unwrap(); + let mut position: Point3 = origin.map(|x| x.floor() as i32); let step = direction.map(|x| x.signum() as i32); // Truncate the origin From 11b6abb0d4808689346dea6822dc716a33d024bb Mon Sep 17 00:00:00 2001 From: Vijfhoek Date: Thu, 3 Jun 2021 00:08:49 +0200 Subject: [PATCH 2/2] Implement chunk loading and saving --- .gitignore | 4 ++ Cargo.lock | 68 ++++++++++++++++++++++++ Cargo.toml | 4 ++ src/chunk.rs | 111 +++++++++++++++++++++++++++++++++++---- src/state/world_state.rs | 40 +++++++++++--- 5 files changed, 209 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 8a177c1..b2b5659 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ /generated/ /flamegraph.svg /perf.data* +/chunks/ + +profile.txt +callgrind.out.* diff --git a/Cargo.lock b/Cargo.lock index 601e532..734004d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1128,8 +1128,12 @@ dependencies = [ "log", "noise", "rayon", + "rmp-serde", + "serde", + "serde_repr", "wgpu", "winit", + "zstd", ] [[package]] @@ -1603,6 +1607,27 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "rmp" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6" +dependencies = [ + "byteorder", + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839395ef53057db96b84c9238ab29e1a13f2e5c8ec9f66bef853ab4197303924" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1626,6 +1651,9 @@ name = "serde" version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1649,6 +1677,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "slab" version = "0.4.3" @@ -2023,3 +2062,32 @@ dependencies = [ "maybe-uninit", "pkg-config", ] + +[[package]] +name = "zstd" +version = "0.8.2+zstd.1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83508bcbbdc9c3abcf77e8e56773d3ffcd2479e0933caab2e7d6b5a9e183aae" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.0+zstd.1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d30375f78e185ca4c91930f42ea2c0162f9aa29737032501f93b79266d985ae7" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.0+zstd.1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2141bed8922b427761470e6bbfeff255da94fa20b0bbeab0d9297fcaf71e3aa7" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index 02c9931..6c47552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,11 @@ noise = "0.7.0" rayon = "1.5.1" wgpu = "0.8.1" winit = { version = "0.25.0", default_features = false, features = ["x11", "web-sys"] } +serde_repr = "0.1.7" +rmp-serde = "0.15.4" itertools = "0.10.0" +serde = { version = "1.0.126", features = ["derive"] } +zstd = "0.8.2" [profile.release] debug = true diff --git a/src/chunk.rs b/src/chunk.rs index 7772795..8c2832b 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,4 +1,8 @@ -use std::{collections::VecDeque, usize}; +use std::{ + collections::VecDeque, + io::{Read, Write}, + usize, +}; use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex}; use ahash::{AHashMap, AHashSet}; @@ -6,17 +10,26 @@ use cgmath::{Point3, Vector3}; use noise::utils::{NoiseMapBuilder, PlaneMapBuilder}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Serialize; +use serde::{ + de::{SeqAccess, Visitor}, + ser::{SerializeSeq, Serializer}, + Deserialize, +}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + #[allow(dead_code)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u8)] pub enum BlockType { - Cobblestone, - Dirt, - Stone, - Grass, - Bedrock, - Sand, - Gravel, - Water, + Cobblestone = 1, + Dirt = 2, + Stone = 3, + Grass = 4, + Bedrock = 5, + Sand = 6, + Gravel = 7, + Water = 8, } impl BlockType { @@ -50,7 +63,7 @@ pub const FACE_FRONT: FaceFlags = 32; pub const FACE_ALL: FaceFlags = FACE_LEFT | FACE_RIGHT | FACE_BOTTOM | FACE_TOP | FACE_BACK | FACE_FRONT; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct Block { pub block_type: BlockType, } @@ -60,10 +73,63 @@ pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize; type ChunkBlocks = [[[Option; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]; +#[derive(Clone, Default)] pub struct Chunk { pub blocks: ChunkBlocks, } +impl Serialize for Chunk { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(CHUNK_SIZE.pow(3)))?; + for layer in self.blocks.iter() { + for row in layer { + for block in row { + seq.serialize_element(block)?; + } + } + } + seq.end() + } +} + +struct ChunkVisitor; + +impl<'de> Visitor<'de> for ChunkVisitor { + type Value = Chunk; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a chunk") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut chunk = Chunk::default(); + for layer in chunk.blocks.iter_mut() { + for row in layer { + for block in row { + *block = seq.next_element()?.unwrap(); + } + } + } + + Ok(chunk) + } +} + +impl<'de> Deserialize<'de> for Chunk { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(ChunkVisitor) + } +} + impl Chunk { pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self { let fbm = noise::Fbm::new(); @@ -333,4 +399,27 @@ impl Chunk { Self::quads_to_geometry(quads) } + + pub fn save(&self, position: Point3) -> anyhow::Result<()> { + let data = rmp_serde::encode::to_vec_named(self)?; + let compressed = zstd::block::compress(&data, 0)?; + + let path = format!("chunks/{}_{}_{}.bin", position.x, position.y, position.z); + let mut file = std::fs::File::create(&path)?; + file.write(&compressed)?; + + Ok(()) + } + + pub fn load(&mut self, position: Point3) -> anyhow::Result<()> { + let path = format!("chunks/{}_{}_{}.bin", position.x, position.y, position.z); + let mut file = std::fs::File::open(&path)?; + + let mut compressed = Vec::new(); + file.read_to_end(&mut compressed)?; + let data = zstd::block::decompress(&compressed, 1024 * 1024)?; + + *self = rmp_serde::decode::from_slice(&data)?; + Ok(()) + } } diff --git a/src/state/world_state.rs b/src/state/world_state.rs index 5d763e1..ecc2050 100644 --- a/src/state/world_state.rs +++ b/src/state/world_state.rs @@ -1,4 +1,7 @@ -use std::time::{Duration, Instant}; +use std::{ + collections::VecDeque, + time::{Duration, Instant}, +}; use ahash::AHashMap; use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3}; @@ -36,6 +39,8 @@ pub struct WorldState { pub world: World, pub chunk_buffers: AHashMap, GeometryBuffers>, + pub chunk_save_queue: VecDeque>, + pub chunk_load_queue: VecDeque>, time: Time, time_buffer: wgpu::Buffer, wireframe: bool, @@ -336,6 +341,8 @@ impl WorldState { world, chunk_buffers: AHashMap::new(), + chunk_load_queue: VecDeque::new(), + chunk_save_queue: VecDeque::new(), wireframe: false, highlighted: None, @@ -487,21 +494,23 @@ impl WorldState { VirtualKeyCode::A => self.left_pressed = pressed, VirtualKeyCode::D => self.right_pressed = pressed, VirtualKeyCode::F2 if pressed => self.creative = !self.creative, + VirtualKeyCode::F3 if pressed => self.chunk_save_queue.extend(self.world.chunks.keys()), + VirtualKeyCode::F4 if pressed => self.chunk_load_queue.extend(self.world.chunks.keys()), VirtualKeyCode::Space => { - self.up_speed = if self.creative { - if pressed { + self.up_speed = if pressed { + if self.creative { 1.0 } else { - 0.0 + 0.6 } } else { - 0.6 + 0.0 } } VirtualKeyCode::LShift if self.creative => { self.up_speed = if pressed { -1.0 } else { 0.0 } } - VirtualKeyCode::LControl => self.sprinting = state == ElementState::Pressed, + VirtualKeyCode::LControl => self.sprinting = pressed, _ => (), } } @@ -510,7 +519,7 @@ impl WorldState { self.world .get_block( position.x as isize, - (position.y - 1.62) as isize, + (position.y - 1.8) as isize, position.z as isize, ) .is_some() @@ -556,6 +565,23 @@ impl WorldState { } pub fn update(&mut self, dt: Duration, render_context: &RenderContext) { + if let Some(position) = self.chunk_load_queue.pop_front() { + let chunk = self.world.chunks.entry(position).or_default(); + if let Err(err) = chunk.load(position) { + eprintln!("Failed to load chunk {:?}: {:?}", position, err); + } else { + self.update_chunk_geometry(render_context, position); + println!("Loaded chunk {:?}", position); + } + } else if let Some(position) = self.chunk_save_queue.pop_front() { + let chunk = self.world.chunks.get(&position).unwrap(); + if let Err(err) = chunk.save(position) { + eprintln!("Failed to save chunk {:?}: {:?}", position, err); + } else { + println!("Saved chunk {:?}", position); + } + } + self.update_position(dt); self.update_aim(render_context);