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/
/flamegraph.svg
/perf.data*
/chunks/
profile.txt
callgrind.out.*

78
Cargo.lock generated
View file

@ -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",
]

View file

@ -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

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 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(())
}
}

View file

@ -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,

View file

@ -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);

View file

@ -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