Merge branch 'serialize-chunks'
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Sijmen 2021-06-03 00:26:29 +02:00
commit a8936d9046
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
7 changed files with 309 additions and 127 deletions

4
.gitignore vendored
View file

@ -2,3 +2,7 @@
/generated/ /generated/
/flamegraph.svg /flamegraph.svg
/perf.data* /perf.data*
/chunks/
profile.txt
callgrind.out.*

78
Cargo.lock generated
View file

@ -970,6 +970,15 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "itertools"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.7" version = "0.4.7"
@ -1115,11 +1124,16 @@ dependencies = [
"futures", "futures",
"gltf", "gltf",
"image", "image",
"itertools",
"log", "log",
"noise", "noise",
"rayon", "rayon",
"rmp-serde",
"serde",
"serde_repr",
"wgpu", "wgpu",
"winit", "winit",
"zstd",
] ]
[[package]] [[package]]
@ -1593,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"
@ -1616,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"
@ -1639,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"
@ -2013,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",
]

View file

@ -19,6 +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"
serde = { version = "1.0.126", features = ["derive"] }
zstd = "0.8.2"
[profile.release] [profile.release]
debug = true debug = true

View file

@ -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 crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex};
use ahash::{AHashMap, AHashSet}; use ahash::{AHashMap, AHashSet};
use cgmath::Vector3; 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,19 +63,73 @@ 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,
} }
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]; 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();
@ -217,10 +284,10 @@ impl Chunk {
fn layer_to_quads( fn layer_to_quads(
&self, &self,
y: usize, y: usize,
offset: Vector3<i32>, offset: Point3<isize>,
culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>, culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>,
queue: &mut VecDeque<(usize, usize)>, queue: &mut VecDeque<(usize, usize)>,
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>, highlighted: Option<&(Point3<usize>, Vector3<i32>)>,
) -> Vec<Quad> { ) -> Vec<Quad> {
let mut quads: Vec<Quad> = Vec::new(); let mut quads: Vec<Quad> = Vec::new();
let mut visited = AHashSet::new(); let mut visited = AHashSet::new();
@ -236,7 +303,7 @@ impl Chunk {
if let Some(&(block_type, visible_faces)) = &culled.get(&(x, z)) { if let Some(&(block_type, visible_faces)) = &culled.get(&(x, z)) {
let mut quad_faces = visible_faces; 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); let mut quad = Quad::new(position, 1, 1);
quad.highlighted_normal = highlighted.unwrap().1; quad.highlighted_normal = highlighted.unwrap().1;
quad.visible_faces = quad_faces; quad.visible_faces = quad_faces;
@ -258,7 +325,7 @@ impl Chunk {
for x_ in x..CHUNK_SIZE { for x_ in x..CHUNK_SIZE {
xmax = x_ + 1; 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; break;
} }
@ -280,7 +347,7 @@ impl Chunk {
zmax = z_ + 1; zmax = z_ + 1;
for x_ in x..xmax { 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; 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.visible_faces = quad_faces;
quad.block_type = Some(block_type); quad.block_type = Some(block_type);
quads.push(quad); quads.push(quad);
@ -319,17 +386,40 @@ impl Chunk {
pub fn to_geometry( pub fn to_geometry(
&self, &self,
offset: Vector3<i32>, position: Point3<isize>,
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>, highlighted: Option<&(Point3<usize>, Vector3<i32>)>,
) -> Geometry<BlockVertex> { ) -> Geometry<BlockVertex> {
let quads: Vec<Quad> = (0..CHUNK_SIZE) let quads: Vec<Quad> = (0..CHUNK_SIZE)
.into_par_iter() .into_par_iter()
.flat_map(|y| { .flat_map(|y| {
let (culled, mut queue) = self.cull_layer(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(); .collect();
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(())
}
} }

View file

@ -1,4 +1,4 @@
use cgmath::{Vector3, Zero}; use cgmath::{Point3, Vector3, Zero};
use crate::{ use crate::{
chunk::{ chunk::{
@ -11,9 +11,9 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
pub struct Quad { pub struct Quad {
pub position: Vector3<i32>, pub position: Point3<isize>,
pub dx: i32, pub dx: isize,
pub dz: i32, pub dz: isize,
pub highlighted_normal: Vector3<i32>, pub highlighted_normal: Vector3<i32>,
pub visible_faces: FaceFlags, pub visible_faces: FaceFlags,
@ -21,7 +21,7 @@ pub struct Quad {
} }
impl 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 { Quad {
position, position,
dx, dx,

View file

@ -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};
@ -13,7 +16,7 @@ use winit::{
use crate::{ use crate::{
camera::{Camera, Projection}, camera::{Camera, Projection},
chunk::{Block, BlockType, CHUNK_SIZE}, chunk::{Block, BlockType, CHUNK_ISIZE},
geometry::GeometryBuffers, geometry::GeometryBuffers,
render_context::RenderContext, render_context::RenderContext,
texture::{Texture, TextureManager}, texture::{Texture, TextureManager},
@ -35,13 +38,15 @@ pub struct WorldState {
pub time_bind_group: wgpu::BindGroup, pub time_bind_group: wgpu::BindGroup,
pub world: World, 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: Time,
time_buffer: wgpu::Buffer, time_buffer: wgpu::Buffer,
wireframe: bool, wireframe: bool,
shader: wgpu::ShaderModule, shader: wgpu::ShaderModule,
render_pipeline_layout: wgpu::PipelineLayout, render_pipeline_layout: wgpu::PipelineLayout,
pub highlighted: Option<(Vector3<usize>, Vector3<i32>)>, pub highlighted: Option<(Point3<isize>, Vector3<i32>)>,
pub forward_pressed: bool, pub forward_pressed: bool,
pub backward_pressed: bool, pub backward_pressed: bool,
@ -256,13 +261,13 @@ impl WorldState {
pub fn update_chunk_geometry( pub fn update_chunk_geometry(
&mut self, &mut self,
render_context: &RenderContext, 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 chunk = &mut self.world.chunks.get(&chunk_position).unwrap();
let offset = chunk_position.map(|f| (f * CHUNK_SIZE) as i32); let offset = chunk_position * CHUNK_ISIZE;
let geometry = chunk.to_geometry( let geometry = chunk.to_geometry(
offset, offset,
World::highlighted_for_chunk(self.highlighted, chunk_position).as_ref(), World::highlighted_for_chunk(self.highlighted, &chunk_position).as_ref(),
); );
let buffers = let buffers =
@ -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,
@ -393,7 +400,7 @@ impl WorldState {
let camera_pos = Vector2::new(camera_pos.x, camera_pos.z); let camera_pos = Vector2::new(camera_pos.x, camera_pos.z);
for (position, buffers) in &self.chunk_buffers { 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); let pos = Vector2::new(pos.x, pos.z);
if (pos - camera_pos).magnitude() > 300.0 { if (pos - camera_pos).magnitude() > 300.0 {
continue; continue;
@ -433,12 +440,10 @@ impl WorldState {
let camera = &self.camera; let camera = &self.camera;
let old = self.highlighted; let old = self.highlighted;
let new = self let new = self.world.raycast(camera.position, camera.direction());
.world
.raycast(camera.position.to_vec(), camera.direction());
let old_chunk = old.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(|h| h.0 / CHUNK_SIZE); let new_chunk = new.map(|(pos, _)| pos.map(|n| n.div_euclid(CHUNK_ISIZE)));
if old != new { if old != new {
self.highlighted = new; self.highlighted = new;
@ -460,12 +465,10 @@ impl WorldState {
let camera = &self.camera; let camera = &self.camera;
let world = &mut self.world; let world = &mut self.world;
if let Some((pos, face_normal)) = if let Some((pos, face_normal)) = world.raycast(camera.position, camera.direction()) {
world.raycast(camera.position.to_vec(), camera.direction())
{
if button == &MouseButton::Left { if button == &MouseButton::Left {
world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None); 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 { } else if button == &MouseButton::Right {
let new_pos = pos.cast().unwrap() + face_normal; 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::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,
_ => (), _ => (),
} }
} }
@ -514,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()
@ -560,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);

View file

@ -1,62 +1,62 @@
use std::collections::HashMap;
use crate::{ use crate::{
chunk::{Block, Chunk, CHUNK_SIZE}, chunk::{Block, Chunk, CHUNK_ISIZE, CHUNK_SIZE},
geometry::Geometry, geometry::Geometry,
npc::Npc, npc::Npc,
vertex::BlockVertex, vertex::BlockVertex,
}; };
use cgmath::{InnerSpace, Vector3}; use cgmath::{EuclideanSpace, InnerSpace, Point3, Vector3};
use rayon::prelude::*; use rayon::prelude::*;
pub struct World { pub struct World {
pub chunks: Vec<Vec<Vec<Chunk>>>, pub chunks: HashMap<Point3<isize>, Chunk>,
pub npc: Npc, pub npc: Npc,
} }
const WORLD_SIZE: Vector3<usize> = Vector3::new( const WORLD_SIZE: Vector3<usize> = Vector3::new(
32 * 16 / CHUNK_SIZE, 8 * 16 / CHUNK_SIZE,
16 * 16 / CHUNK_SIZE, 16 * 16 / CHUNK_SIZE,
32 * 16 / CHUNK_SIZE, 8 * 16 / CHUNK_SIZE,
); );
impl World { impl World {
pub fn generate() -> Self { pub fn generate() -> Self {
let mut chunks = Vec::new();
let npc = Npc::load(); let npc = Npc::load();
let half: Vector3<isize> = WORLD_SIZE.cast().unwrap() / 2;
(0..WORLD_SIZE.y) let coords: Vec<_> =
.into_par_iter() itertools::iproduct!(-half.x..half.x, 0..WORLD_SIZE.y as isize, -half.z..half.z)
.map(|y| { .collect();
let mut chunks_z = Vec::new();
for z in 0..WORLD_SIZE.z { let chunks: HashMap<_, _> = coords
let mut chunks_x = Vec::new(); .par_iter()
for x in 0..WORLD_SIZE.x { .map(|&(x, y, z)| {
let chunk = Chunk::generate(x as i32, y as i32, z as i32); (
chunks_x.push(chunk); Point3::new(x, y, z),
} Chunk::generate(x as i32, y as i32, z as i32),
chunks_z.push(chunks_x); )
}
chunks_z
}) })
.collect_into_vec(&mut chunks); .collect();
Self { chunks, npc } Self { chunks, npc }
} }
pub fn highlighted_for_chunk( pub fn highlighted_for_chunk(
highlighted: Option<(Vector3<usize>, Vector3<i32>)>, highlighted: Option<(Point3<isize>, Vector3<i32>)>,
chunk_position: Vector3<usize>, chunk_position: &Point3<isize>,
) -> Option<(Vector3<usize>, Vector3<i32>)> { ) -> Option<(Point3<usize>, Vector3<i32>)> {
let position = chunk_position * CHUNK_SIZE; let position = chunk_position * CHUNK_ISIZE;
if let Some((pos, face)) = highlighted { if let Some((pos, face)) = highlighted {
if pos.x >= position.x 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
&& pos.y < position.y + CHUNK_SIZE && pos.y < position.y + CHUNK_ISIZE
&& pos.z >= position.z && 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 { } else {
None None
} }
@ -67,26 +67,18 @@ impl World {
pub fn to_geometry( pub fn to_geometry(
&self, &self,
highlighted: Option<(Vector3<usize>, Vector3<i32>)>, highlighted: Option<(Point3<isize>, Vector3<i32>)>,
) -> Vec<(Vector3<usize>, Geometry<BlockVertex>)> { ) -> Vec<(Point3<isize>, Geometry<BlockVertex>)> {
let instant = std::time::Instant::now(); let instant = std::time::Instant::now();
let chunks = &self.chunks; let chunks = &self.chunks;
let geometry = chunks let geometry = chunks
.par_iter() .par_iter()
.enumerate() .map(|(chunk_position, chunk)| {
.flat_map(|(y, chunks_y)| { let position = (chunk_position * CHUNK_ISIZE).cast().unwrap();
let mut chunk_geometry = Vec::new(); let h = Self::highlighted_for_chunk(highlighted, chunk_position);
for (z, chunks_z) in chunks_y.iter().enumerate() { let geometry = chunk.to_geometry(position, h.as_ref());
for (x, chunk) in chunks_z.iter().enumerate() { (*chunk_position, geometry)
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
}) })
.collect(); .collect();
@ -97,41 +89,32 @@ impl World {
} }
pub fn get_block(&self, x: isize, y: isize, z: isize) -> Option<&Block> { pub fn get_block(&self, x: isize, y: isize, z: isize) -> Option<&Block> {
if x < 0 || y < 0 || z < 0 { let chunk = match self.chunks.get(&Point3::new(
return None; x.div_euclid(CHUNK_ISIZE),
} y.div_euclid(CHUNK_ISIZE),
z.div_euclid(CHUNK_ISIZE),
let chunk = match self )) {
.chunks Some(chunk) => chunk,
.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,
None => return None, None => return None,
}; };
chunk.blocks[y as usize % CHUNK_SIZE][z as usize % CHUNK_SIZE][x as usize % CHUNK_SIZE] let bx = x.rem_euclid(CHUNK_ISIZE) as usize;
.as_ref() 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>) { pub fn set_block(&mut self, x: isize, y: isize, z: isize, block: Option<Block>) {
if x < 0 || y < 0 || z < 0 { if let Some(chunk) = self.chunks.get_mut(&Point3::new(
return; 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 { fn calc_scale(vector: Vector3<f32>, scalar: f32) -> f32 {
@ -145,9 +128,9 @@ impl World {
#[allow(dead_code)] #[allow(dead_code)]
pub fn raycast( pub fn raycast(
&self, &self,
origin: Vector3<f32>, origin: Point3<f32>,
direction: Vector3<f32>, direction: Vector3<f32>,
) -> Option<(Vector3<usize>, Vector3<i32>)> { ) -> Option<(Point3<isize>, Vector3<i32>)> {
let direction = direction.normalize(); let direction = direction.normalize();
let scale = Vector3::new( let scale = Vector3::new(
Self::calc_scale(direction, direction.x), Self::calc_scale(direction, direction.x),
@ -155,7 +138,7 @@ impl World {
Self::calc_scale(direction, direction.z), 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); let step = direction.map(|x| x.signum() as i32);
// Truncate the origin // Truncate the origin