Merge branch 'serialize-chunks'
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
commit
a8936d9046
7 changed files with 309 additions and 127 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -2,3 +2,7 @@
|
|||
/generated/
|
||||
/flamegraph.svg
|
||||
/perf.data*
|
||||
/chunks/
|
||||
|
||||
profile.txt
|
||||
callgrind.out.*
|
||||
|
|
78
Cargo.lock
generated
78
Cargo.lock
generated
|
@ -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,11 +1124,16 @@ dependencies = [
|
|||
"futures",
|
||||
"gltf",
|
||||
"image",
|
||||
"itertools",
|
||||
"log",
|
||||
"noise",
|
||||
"rayon",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"wgpu",
|
||||
"winit",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1593,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"
|
||||
|
@ -1616,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"
|
||||
|
@ -1639,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"
|
||||
|
@ -2013,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",
|
||||
]
|
||||
|
|
|
@ -19,6 +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
|
||||
|
|
134
src/chunk.rs
134
src/chunk.rs
|
@ -1,22 +1,35 @@
|
|||
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};
|
||||
use cgmath::Vector3;
|
||||
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,19 +63,73 @@ 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,
|
||||
}
|
||||
|
||||
pub const CHUNK_SIZE: usize = 64;
|
||||
pub const CHUNK_SIZE: usize = 32;
|
||||
pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize;
|
||||
|
||||
type ChunkBlocks = [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Chunk {
|
||||
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 {
|
||||
pub fn generate(chunk_x: i32, chunk_y: i32, chunk_z: i32) -> Self {
|
||||
let fbm = noise::Fbm::new();
|
||||
|
@ -217,10 +284,10 @@ impl Chunk {
|
|||
fn layer_to_quads(
|
||||
&self,
|
||||
y: usize,
|
||||
offset: Vector3<i32>,
|
||||
offset: Point3<isize>,
|
||||
culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>,
|
||||
queue: &mut VecDeque<(usize, usize)>,
|
||||
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>,
|
||||
highlighted: Option<&(Point3<usize>, Vector3<i32>)>,
|
||||
) -> Vec<Quad> {
|
||||
let mut quads: Vec<Quad> = Vec::new();
|
||||
let mut visited = AHashSet::new();
|
||||
|
@ -236,7 +303,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 +325,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 +347,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 +366,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,17 +386,40 @@ impl Chunk {
|
|||
|
||||
pub fn to_geometry(
|
||||
&self,
|
||||
offset: Vector3<i32>,
|
||||
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>,
|
||||
position: Point3<isize>,
|
||||
highlighted: Option<&(Point3<usize>, Vector3<i32>)>,
|
||||
) -> Geometry<BlockVertex> {
|
||||
let quads: Vec<Quad> = (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();
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
10
src/quad.rs
10
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<i32>,
|
||||
pub dx: i32,
|
||||
pub dz: i32,
|
||||
pub position: Point3<isize>,
|
||||
pub dx: isize,
|
||||
pub dz: isize,
|
||||
|
||||
pub highlighted_normal: Vector3<i32>,
|
||||
pub visible_faces: FaceFlags,
|
||||
|
@ -21,7 +21,7 @@ pub struct Quad {
|
|||
}
|
||||
|
||||
impl Quad {
|
||||
pub fn new(position: Vector3<i32>, dx: i32, dz: i32) -> Self {
|
||||
pub fn new(position: Point3<isize>, dx: isize, dz: isize) -> Self {
|
||||
Quad {
|
||||
position,
|
||||
dx,
|
||||
|
|
|
@ -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};
|
||||
|
@ -13,7 +16,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 +38,15 @@ pub struct WorldState {
|
|||
pub time_bind_group: wgpu::BindGroup,
|
||||
pub world: World,
|
||||
|
||||
pub chunk_buffers: AHashMap<Vector3<usize>, 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_buffer: wgpu::Buffer,
|
||||
wireframe: bool,
|
||||
shader: wgpu::ShaderModule,
|
||||
render_pipeline_layout: wgpu::PipelineLayout,
|
||||
pub highlighted: Option<(Vector3<usize>, Vector3<i32>)>,
|
||||
pub highlighted: Option<(Point3<isize>, Vector3<i32>)>,
|
||||
|
||||
pub forward_pressed: bool,
|
||||
pub backward_pressed: bool,
|
||||
|
@ -256,13 +261,13 @@ impl WorldState {
|
|||
pub fn update_chunk_geometry(
|
||||
&mut self,
|
||||
render_context: &RenderContext,
|
||||
chunk_position: Vector3<usize>,
|
||||
chunk_position: Point3<isize>,
|
||||
) {
|
||||
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 =
|
||||
|
@ -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,
|
||||
|
||||
|
@ -393,7 +400,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 +440,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 +465,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 +481,7 @@ impl WorldState {
|
|||
}),
|
||||
);
|
||||
|
||||
self.update_chunk_geometry(render_context, pos / CHUNK_SIZE);
|
||||
self.update_chunk_geometry(render_context, pos / CHUNK_ISIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -491,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,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -514,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()
|
||||
|
@ -560,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);
|
||||
|
||||
|
|
133
src/world.rs
133
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<Vec<Vec<Chunk>>>,
|
||||
pub chunks: HashMap<Point3<isize>, Chunk>,
|
||||
pub npc: Npc,
|
||||
}
|
||||
|
||||
const WORLD_SIZE: Vector3<usize> = 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<isize> = 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<usize>, Vector3<i32>)>,
|
||||
chunk_position: Vector3<usize>,
|
||||
) -> Option<(Vector3<usize>, Vector3<i32>)> {
|
||||
let position = chunk_position * CHUNK_SIZE;
|
||||
highlighted: Option<(Point3<isize>, Vector3<i32>)>,
|
||||
chunk_position: &Point3<isize>,
|
||||
) -> Option<(Point3<usize>, Vector3<i32>)> {
|
||||
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<isize> = 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<usize>, Vector3<i32>)>,
|
||||
) -> Vec<(Vector3<usize>, Geometry<BlockVertex>)> {
|
||||
highlighted: Option<(Point3<isize>, Vector3<i32>)>,
|
||||
) -> Vec<(Point3<isize>, Geometry<BlockVertex>)> {
|
||||
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<Block>) {
|
||||
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<f32>, scalar: f32) -> f32 {
|
||||
|
@ -145,9 +128,9 @@ impl World {
|
|||
#[allow(dead_code)]
|
||||
pub fn raycast(
|
||||
&self,
|
||||
origin: Vector3<f32>,
|
||||
origin: Point3<f32>,
|
||||
direction: Vector3<f32>,
|
||||
) -> Option<(Vector3<usize>, Vector3<i32>)> {
|
||||
) -> Option<(Point3<isize>, Vector3<i32>)> {
|
||||
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<i32> = origin.cast().unwrap();
|
||||
let mut position: Point3<i32> = origin.map(|x| x.floor() as i32);
|
||||
let step = direction.map(|x| x.signum() as i32);
|
||||
|
||||
// Truncate the origin
|
||||
|
|
Loading…
Reference in a new issue