Allow for chunks with negative coordinates
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Sijmen 2021-06-02 19:45:29 +02:00
parent 023d010c84
commit 15247d12d4
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
6 changed files with 100 additions and 109 deletions

10
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,6 +1124,7 @@ dependencies = [
"futures",
"gltf",
"image",
"itertools",
"log",
"noise",
"rayon",

View file

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

View file

@ -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<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
@ -217,10 +218,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 +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<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();

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

@ -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<Vector3<usize>, GeometryBuffers>,
pub chunk_buffers: AHashMap<Point3<isize>, GeometryBuffers>,
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 +256,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 =
@ -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);
}
}
}

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();
.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(offset, h.as_ref());
chunk_geometry.push((Vector3::new(x, y, z), geometry));
}
}
chunk_geometry
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