From 15247d12d4db7fcc73ea8581691e3c3fd9b91127 Mon Sep 17 00:00:00 2001 From: Vijfhoek Date: Wed, 2 Jun 2021 19:45:29 +0200 Subject: [PATCH] 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