Implement chunk loading and saving
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
This commit is contained in:
parent
15247d12d4
commit
11b6abb0d4
5 changed files with 209 additions and 18 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -2,3 +2,7 @@
|
||||||
/generated/
|
/generated/
|
||||||
/flamegraph.svg
|
/flamegraph.svg
|
||||||
/perf.data*
|
/perf.data*
|
||||||
|
/chunks/
|
||||||
|
|
||||||
|
profile.txt
|
||||||
|
callgrind.out.*
|
||||||
|
|
68
Cargo.lock
generated
68
Cargo.lock
generated
|
@ -1128,8 +1128,12 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"noise",
|
"noise",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"rmp-serde",
|
||||||
|
"serde",
|
||||||
|
"serde_repr",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
|
"zstd",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1603,6 +1607,27 @@ version = "0.6.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
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]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
@ -1626,6 +1651,9 @@ name = "serde"
|
||||||
version = "1.0.126"
|
version = "1.0.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
|
@ -1649,6 +1677,17 @@ dependencies = [
|
||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -2023,3 +2062,32 @@ dependencies = [
|
||||||
"maybe-uninit",
|
"maybe-uninit",
|
||||||
"pkg-config",
|
"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",
|
||||||
|
]
|
||||||
|
|
|
@ -19,7 +19,11 @@ noise = "0.7.0"
|
||||||
rayon = "1.5.1"
|
rayon = "1.5.1"
|
||||||
wgpu = "0.8.1"
|
wgpu = "0.8.1"
|
||||||
winit = { version = "0.25.0", default_features = false, features = ["x11", "web-sys"] }
|
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"
|
itertools = "0.10.0"
|
||||||
|
serde = { version = "1.0.126", features = ["derive"] }
|
||||||
|
zstd = "0.8.2"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
111
src/chunk.rs
111
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 crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex};
|
||||||
use ahash::{AHashMap, AHashSet};
|
use ahash::{AHashMap, AHashSet};
|
||||||
|
@ -6,17 +10,26 @@ use cgmath::{Point3, Vector3};
|
||||||
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
|
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
|
||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
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)]
|
#[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 {
|
pub enum BlockType {
|
||||||
Cobblestone,
|
Cobblestone = 1,
|
||||||
Dirt,
|
Dirt = 2,
|
||||||
Stone,
|
Stone = 3,
|
||||||
Grass,
|
Grass = 4,
|
||||||
Bedrock,
|
Bedrock = 5,
|
||||||
Sand,
|
Sand = 6,
|
||||||
Gravel,
|
Gravel = 7,
|
||||||
Water,
|
Water = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockType {
|
impl BlockType {
|
||||||
|
@ -50,7 +63,7 @@ pub const FACE_FRONT: FaceFlags = 32;
|
||||||
pub const FACE_ALL: FaceFlags =
|
pub const FACE_ALL: FaceFlags =
|
||||||
FACE_LEFT | FACE_RIGHT | FACE_BOTTOM | FACE_TOP | FACE_BACK | FACE_FRONT;
|
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 struct Block {
|
||||||
pub block_type: BlockType,
|
pub block_type: BlockType,
|
||||||
}
|
}
|
||||||
|
@ -60,10 +73,63 @@ pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize;
|
||||||
|
|
||||||
type ChunkBlocks = [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
|
type ChunkBlocks = [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
pub blocks: ChunkBlocks,
|
pub blocks: ChunkBlocks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for Chunk {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_seq(ChunkVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self {
|
pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self {
|
||||||
let fbm = noise::Fbm::new();
|
let fbm = noise::Fbm::new();
|
||||||
|
@ -333,4 +399,27 @@ impl Chunk {
|
||||||
|
|
||||||
Self::quads_to_geometry(quads)
|
Self::quads_to_geometry(quads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn save(&self, position: Point3<isize>) -> 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<isize>) -> 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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::time::{Duration, Instant};
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3};
|
use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3};
|
||||||
|
@ -36,6 +39,8 @@ pub struct WorldState {
|
||||||
pub world: World,
|
pub world: World,
|
||||||
|
|
||||||
pub chunk_buffers: AHashMap<Point3<isize>, GeometryBuffers>,
|
pub chunk_buffers: AHashMap<Point3<isize>, GeometryBuffers>,
|
||||||
|
pub chunk_save_queue: VecDeque<Point3<isize>>,
|
||||||
|
pub chunk_load_queue: VecDeque<Point3<isize>>,
|
||||||
time: Time,
|
time: Time,
|
||||||
time_buffer: wgpu::Buffer,
|
time_buffer: wgpu::Buffer,
|
||||||
wireframe: bool,
|
wireframe: bool,
|
||||||
|
@ -336,6 +341,8 @@ impl WorldState {
|
||||||
|
|
||||||
world,
|
world,
|
||||||
chunk_buffers: AHashMap::new(),
|
chunk_buffers: AHashMap::new(),
|
||||||
|
chunk_load_queue: VecDeque::new(),
|
||||||
|
chunk_save_queue: VecDeque::new(),
|
||||||
wireframe: false,
|
wireframe: false,
|
||||||
highlighted: None,
|
highlighted: None,
|
||||||
|
|
||||||
|
@ -487,21 +494,23 @@ impl WorldState {
|
||||||
VirtualKeyCode::A => self.left_pressed = pressed,
|
VirtualKeyCode::A => self.left_pressed = pressed,
|
||||||
VirtualKeyCode::D => self.right_pressed = pressed,
|
VirtualKeyCode::D => self.right_pressed = pressed,
|
||||||
VirtualKeyCode::F2 if pressed => self.creative = !self.creative,
|
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 => {
|
VirtualKeyCode::Space => {
|
||||||
self.up_speed = if self.creative {
|
self.up_speed = if pressed {
|
||||||
if pressed {
|
if self.creative {
|
||||||
1.0
|
1.0
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.6
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
0.6
|
0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::LShift if self.creative => {
|
VirtualKeyCode::LShift if self.creative => {
|
||||||
self.up_speed = if pressed { -1.0 } else { 0.0 }
|
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
|
self.world
|
||||||
.get_block(
|
.get_block(
|
||||||
position.x as isize,
|
position.x as isize,
|
||||||
(position.y - 1.62) as isize,
|
(position.y - 1.8) as isize,
|
||||||
position.z as isize,
|
position.z as isize,
|
||||||
)
|
)
|
||||||
.is_some()
|
.is_some()
|
||||||
|
@ -556,6 +565,23 @@ impl WorldState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, dt: Duration, render_context: &RenderContext) {
|
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_position(dt);
|
||||||
self.update_aim(render_context);
|
self.update_aim(render_context);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue