Make adding/removing blocks work on the entire world

This commit is contained in:
Sijmen 2021-06-01 14:19:27 +02:00
parent 65f0a706d3
commit 6c6a321a0c
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
7 changed files with 274 additions and 65 deletions

View file

@ -2,7 +2,7 @@ use std::{collections::VecDeque, convert::TryInto, usize};
use crate::{cube, quad::Quad, vertex::Vertex};
use ahash::{AHashMap, AHashSet};
use cgmath::{InnerSpace, Vector3};
use cgmath::{InnerSpace, Vector3, Zero};
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
#[allow(dead_code)]
@ -58,7 +58,6 @@ type ChunkBlocks = [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
pub struct Chunk {
pub blocks: ChunkBlocks,
pub highlighted: Option<Vector3<usize>>,
}
impl Chunk {
@ -139,10 +138,7 @@ impl Chunk {
}
}
Self {
blocks,
highlighted: None,
}
Self { blocks }
}
#[rustfmt::skip]
@ -221,9 +217,12 @@ impl Chunk {
offset: Vector3<i32>,
culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>,
queue: &mut VecDeque<(usize, usize)>,
) -> Vec<(BlockType, i32, Vector3<i32>, Quad, bool, FaceFlags)> {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, bool, FaceFlags)> = Vec::new();
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>,
) -> Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)> {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)> =
Vec::new();
let mut visited = AHashSet::new();
let hl = highlighted.map(|h| h.0);
while let Some((x, z)) = queue.pop_front() {
if visited.contains(&(x, z)) {
continue;
@ -233,15 +232,29 @@ impl Chunk {
if let Some(&(block_type, visible_faces)) = &culled.get(&(x, z)) {
let mut quad_faces = visible_faces;
if self.highlighted == Some(Vector3::new(x, y, z)) {
if hl == Some(Vector3::new(x, y, z)) {
let quad = Quad::new(x as i32, z as i32, 1, 1);
quads.push((block_type, y as i32, offset, quad, true, quad_faces));
quads.push((
block_type,
y as i32,
offset,
quad,
highlighted.unwrap().1,
quad_faces,
));
continue;
}
if block_type == BlockType::Water {
let quad = Quad::new(x as i32, z as i32, 1, 1);
quads.push((block_type, y as i32, offset, quad, false, quad_faces));
quads.push((
block_type,
y as i32,
offset,
quad,
Vector3::zero(),
quad_faces,
));
continue;
}
@ -250,9 +263,7 @@ impl Chunk {
for x_ in x..CHUNK_SIZE {
xmax = x_ + 1;
if visited.contains(&(xmax, z))
|| self.highlighted == Some(Vector3::new(xmax, y, z))
{
if visited.contains(&(xmax, z)) || hl == Some(Vector3::new(xmax, y, z)) {
break;
}
@ -274,9 +285,7 @@ impl Chunk {
zmax = z_ + 1;
for x_ in x..xmax {
if visited.contains(&(x_, zmax))
|| self.highlighted == Some(Vector3::new(x_, y, zmax))
{
if visited.contains(&(x_, zmax)) || hl == Some(Vector3::new(x_, y, zmax)) {
break 'z;
}
@ -296,7 +305,14 @@ impl Chunk {
}
let quad = Quad::new(x as i32, z as i32, (xmax - x) as i32, (zmax - z) as i32);
quads.push((block_type, y as i32, offset, quad, false, quad_faces));
quads.push((
block_type,
y as i32,
offset,
quad,
Vector3::zero(),
quad_faces,
));
}
}
@ -304,7 +320,7 @@ impl Chunk {
}
fn quads_to_geometry(
quads: Vec<(BlockType, i32, Vector3<i32>, Quad, bool, FaceFlags)>,
quads: Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)>,
) -> (Vec<Vertex>, Vec<u16>) {
let mut vertices = Vec::new();
let mut indices = Vec::new();
@ -329,12 +345,17 @@ impl Chunk {
(vertices, indices)
}
pub fn to_geometry(&self, offset: Vector3<i32>) -> (Vec<Vertex>, Vec<u16>) {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, bool, FaceFlags)> = Vec::new();
pub fn to_geometry(
&self,
offset: Vector3<i32>,
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>,
) -> (Vec<Vertex>, Vec<u16>) {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)> =
Vec::new();
for y in 0..CHUNK_SIZE {
let (culled, mut queue) = self.cull_layer(y);
let mut layer_quads = self.layer_to_quads(y, offset, culled, &mut queue);
let mut layer_quads = self.layer_to_quads(y, offset, culled, &mut queue, highlighted);
quads.append(&mut layer_quads);
}
@ -342,11 +363,11 @@ impl Chunk {
}
pub fn get_block(&self, x: usize, y: usize, z: usize) -> Option<&Block> {
self.blocks
.get(y)
.and_then(|blocks| blocks.get(z))
.and_then(|blocks| blocks.get(x))
.and_then(|block| block.as_ref())
if x >= CHUNK_SIZE || y >= CHUNK_SIZE || z >= CHUNK_SIZE {
return None;
}
self.blocks[y][z][x].as_ref()
}
fn calc_scale(vector: Vector3<f32>, scalar: f32) -> f32 {
@ -360,9 +381,11 @@ impl Chunk {
#[allow(dead_code)]
pub fn raycast(
&self,
chunk_position: Vector3<usize>,
origin: Vector3<f32>,
direction: Vector3<f32>,
) -> Option<(Vector3<usize>, Vector3<i32>)> {
let origin = origin - chunk_position.map(|x| (x * 16) as f32);
let scale = Vector3::new(
Self::calc_scale(direction, direction.x),
Self::calc_scale(direction, direction.y),

View file

@ -14,7 +14,7 @@ pub fn vertices(
z_height: f32,
offset: Vector3<i32>,
texture_indices: (usize, usize, usize, usize, usize, usize),
highlighted: bool,
highlighted: Vector3<i32>,
visible_faces: FaceFlags,
start_index: u16,
) -> (Vec<Vertex>, Vec<u16>) {
@ -27,14 +27,15 @@ pub fn vertices(
let z = (quad.y + offset.z) as f32;
let t = texture_indices;
let highlighted = highlighted as i32;
let mut current_index = start_index;
let mut vertices = Vec::new();
let mut indices = Vec::new();
let highlighted: [f32; 3] = (-highlighted).map(|x| x as f32).into();
if visible_faces & FACE_LEFT == FACE_LEFT {
let normal = [-1.0, 0.0, 0.0];
let highlighted = (normal == highlighted) as i32;
vertices.extend(&[
Vertex { position: [x, y, z ], texture_coordinates: [h, 1.0, t.0 as f32], normal, highlighted },
Vertex { position: [x, y, z + h], texture_coordinates: [0.0, 1.0, t.0 as f32], normal, highlighted },
@ -50,6 +51,7 @@ pub fn vertices(
if visible_faces & FACE_RIGHT == FACE_RIGHT {
let normal = [1.0, 0.0, 0.0];
let highlighted = (normal == highlighted) as i32;
vertices.extend(&[
Vertex { position: [x + w, y, z ], texture_coordinates: [0.0, 1.0, t.1 as f32], normal, highlighted },
Vertex { position: [x + w, y, z + h], texture_coordinates: [h, 1.0, t.1 as f32], normal, highlighted },
@ -65,6 +67,7 @@ pub fn vertices(
if visible_faces & FACE_BACK == FACE_BACK {
let normal = [0.0, 0.0, -1.0];
let highlighted = (normal == highlighted) as i32;
vertices.extend(&[
Vertex { position: [x, y, z], texture_coordinates: [w, 1.0, t.2 as f32], normal, highlighted },
Vertex { position: [x, y + zh, z], texture_coordinates: [w, 0.0, t.2 as f32], normal, highlighted },
@ -80,6 +83,7 @@ pub fn vertices(
if visible_faces & FACE_FRONT == FACE_FRONT {
let normal = [0.0, 0.0, 1.0];
let highlighted = (normal == highlighted) as i32;
vertices.extend(&[
Vertex { position: [x, y, z + h], texture_coordinates: [0.0, 1.0, t.3 as f32], normal, highlighted },
Vertex { position: [x, y + zh, z + h], texture_coordinates: [0.0, 0.0, t.3 as f32], normal, highlighted },
@ -95,6 +99,7 @@ pub fn vertices(
if visible_faces & FACE_BOTTOM == FACE_BOTTOM {
let normal = [0.0, -1.0, 0.0];
let highlighted = (normal == highlighted) as i32;
vertices.extend(&[
Vertex { position: [x, y, z ], texture_coordinates: [w, 0.0, t.4 as f32], normal, highlighted },
Vertex { position: [x, y, z + h], texture_coordinates: [w, h, t.4 as f32], normal, highlighted },
@ -110,6 +115,7 @@ pub fn vertices(
if visible_faces & FACE_TOP == FACE_TOP {
let normal = [0.0, 1.0, 0.0];
let highlighted = (normal == highlighted) as i32;
vertices.extend(&[
Vertex { position: [x, y + zh, z ], texture_coordinates: [0.0, 0.0, t.5 as f32], normal, highlighted },
Vertex { position: [x, y + zh, z + h], texture_coordinates: [0.0, h, t.5 as f32], normal, highlighted },

View file

@ -30,6 +30,8 @@ struct VertexOutput {
[[location(3)]] highlighted: i32;
};
let pi: f32 = 3.14159265359;
[[stage(vertex)]]
fn main(model: VertexInput) -> VertexOutput {
var out: VertexOutput;
@ -80,7 +82,7 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
var result: vec3<f32> = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
if (in.highlighted != 0) {
result = result + 0.3;
result = result + 0.25 + sin(time.time * pi) * 0.07;
}
return vec4<f32>(result, object_color.a);

View file

@ -1,18 +1,18 @@
mod hud;
mod world;
pub mod hud_state;
pub mod world_state;
use std::time::Duration;
use cgmath::{EuclideanSpace, InnerSpace, Rad, Vector3, Zero};
use cgmath::{EuclideanSpace, InnerSpace, Rad, Vector3};
use winit::{
event::{DeviceEvent, ElementState, KeyboardInput, VirtualKeyCode},
window::Window,
};
use hud::HudState;
use world::WorldState;
use hud_state::HudState;
use world_state::WorldState;
use crate::chunk::{Block, BlockType};
use crate::chunk::{Block, BlockType, CHUNK_SIZE};
pub struct State {
pub window_size: winit::dpi::PhysicalSize<u32>,
@ -30,6 +30,7 @@ pub struct State {
forward_speed: f32,
up_speed: f32,
pub mouse_grabbed: bool,
sprinting: bool,
}
impl State {
@ -111,6 +112,7 @@ impl State {
right_speed: 0.0,
forward_speed: 0.0,
up_speed: 0.0,
sprinting: false,
mouse_grabbed: false,
}
}
@ -141,7 +143,8 @@ impl State {
VirtualKeyCode::S => self.forward_speed -= amount,
VirtualKeyCode::A => self.right_speed -= amount,
VirtualKeyCode::D => self.right_speed += amount,
VirtualKeyCode::LControl => self.up_speed -= amount,
VirtualKeyCode::LShift => self.up_speed -= amount,
VirtualKeyCode::LControl => self.sprinting = state == &ElementState::Pressed,
VirtualKeyCode::Space => self.up_speed += amount,
VirtualKeyCode::F1 if state == &ElementState::Pressed => self
.world_state
@ -164,17 +167,33 @@ impl State {
fn update_aim(&mut self) {
let camera = &self.world_state.camera;
let chunk = &mut self.world_state.world.chunks[0][0][0];
let position = chunk
.raycast(camera.position.to_vec(), camera.direction())
.map(|(position, _)| position);
let old = self.world_state.highlighted;
let new = self
.world_state
.world
.raycast(camera.position.to_vec(), camera.direction());
if position != chunk.highlighted {
chunk.highlighted = position;
println!("aiming at {:?}", position);
self.world_state
.update_chunk_geometry(&self.render_device, Vector3::zero());
let old_chunk = old.map(|h| h.0 / CHUNK_SIZE);
let new_chunk = new.map(|h| h.0 / CHUNK_SIZE);
if old != new {
self.world_state.highlighted = new;
if let Some(old_chunk_) = old_chunk {
self.world_state
.update_chunk_geometry(&self.render_device, old_chunk_);
}
if let Some(new_chunk_) = new_chunk {
// Don't update the same chunk twice
if old_chunk != new_chunk {
self.world_state
.update_chunk_geometry(&self.render_device, new_chunk_);
}
}
// println!("Aiming at {:?}", new);
}
}
@ -197,26 +216,30 @@ impl State {
state: ElementState::Pressed,
} if self.mouse_grabbed => {
let camera = &self.world_state.camera;
let chunk = &mut self.world_state.world.chunks[0][0][0];
let world = &mut self.world_state.world;
if let Some((pos, axis)) =
chunk.raycast(camera.position.to_vec(), camera.direction())
world.raycast(camera.position.to_vec(), camera.direction())
{
dbg!(&pos);
if *button == 1 {
chunk.blocks[pos.y][pos.z][pos.x].take();
dbg!(&pos);
world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None);
self.world_state
.update_chunk_geometry(&self.render_device, Vector3::zero());
.update_chunk_geometry(&self.render_device, pos / 16);
} else if *button == 3 {
let new_pos = pos.map(|x| x as i32) - axis;
dbg!(&axis, &new_pos);
chunk.blocks[new_pos.y as usize][new_pos.z as usize][new_pos.x as usize] =
world.set_block(
new_pos.x as isize,
new_pos.y as isize,
new_pos.z as isize,
Some(Block {
block_type: BlockType::Cobblestone,
});
}),
);
self.world_state
.update_chunk_geometry(&self.render_device, Vector3::zero());
.update_chunk_geometry(&self.render_device, pos / 16);
}
}
}
@ -231,13 +254,16 @@ impl State {
let (yaw_sin, yaw_cos) = self.world_state.camera.yaw.0.sin_cos();
let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
self.world_state.camera.position += forward * self.forward_speed * 30.0 * dt_secs;
self.world_state.camera.position +=
forward * self.forward_speed * 30.0 * (self.sprinting as i32 * 2 + 1) as f32 * dt_secs;
let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
self.world_state.camera.position += right * self.right_speed * 30.0 * dt_secs;
self.world_state.camera.position +=
right * self.right_speed * 30.0 * (self.sprinting as i32 * 2 + 1) as f32 * dt_secs;
let up = Vector3::new(0.0, 1.0, 0.0).normalize();
self.world_state.camera.position += up * self.up_speed * 30.0 * dt_secs;
self.world_state.camera.position +=
up * self.up_speed * 30.0 * (self.sprinting as i32 * 2 + 1) as f32 * dt_secs;
self.world_state.update(dt, &self.render_queue);

View file

@ -33,6 +33,7 @@ pub struct WorldState {
wireframe: bool,
shader: wgpu::ShaderModule,
render_pipeline_layout: wgpu::PipelineLayout,
pub highlighted: Option<(Vector3<usize>, Vector3<i32>)>,
}
impl WorldState {
@ -201,7 +202,7 @@ impl WorldState {
pub fn update_world_geometry(&mut self, render_device: &wgpu::Device) {
let instant = Instant::now();
let world_geometry = self.world.to_geometry();
let world_geometry = self.world.to_geometry(self.highlighted);
self.chunk_buffers.clear();
for (chunk_position, chunk_vertices, chunk_indices) in world_geometry {
self.chunk_buffers.insert(
@ -231,9 +232,13 @@ impl WorldState {
render_device: &wgpu::Device,
chunk_position: Vector3<usize>,
) {
// println!("Updating chunk {:?}", chunk_position);
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 (vertices, indices) = chunk.to_geometry(offset);
let (vertices, indices) = chunk.to_geometry(
offset,
World::highlighted_for_chunk(self.highlighted, chunk_position).as_ref(),
);
self.chunk_buffers.insert(
chunk_position,
@ -333,6 +338,7 @@ impl WorldState {
world,
chunk_buffers: AHashMap::new(),
wireframe: false,
highlighted: None,
};
world_state.update_world_geometry(render_device);

View file

@ -1,5 +1,8 @@
use crate::{chunk::Chunk, vertex::Vertex};
use cgmath::Vector3;
use crate::{
chunk::{Block, Chunk, CHUNK_SIZE},
vertex::Vertex,
};
use cgmath::{InnerSpace, Vector3};
pub struct World {
pub chunks: Vec<Vec<Vec<Chunk>>>,
@ -26,15 +29,42 @@ impl World {
Self { chunks }
}
pub fn to_geometry(&self) -> Vec<(Vector3<usize>, Vec<Vertex>, Vec<u16>)> {
pub fn highlighted_for_chunk(
highlighted: Option<(Vector3<usize>, Vector3<i32>)>,
chunk_position: Vector3<usize>,
) -> Option<(Vector3<usize>, Vector3<i32>)> {
let position = chunk_position * 16;
if let Some((pos, face)) = highlighted {
if pos.x >= position.x
&& pos.x < position.x + CHUNK_SIZE
&& pos.y >= position.y
&& pos.y < position.y + CHUNK_SIZE
&& pos.z >= position.z
&& pos.z < position.z + CHUNK_SIZE
{
Some((pos - position, face))
} else {
None
}
} else {
None
}
}
pub fn to_geometry(
&self,
highlighted: Option<(Vector3<usize>, Vector3<i32>)>,
) -> Vec<(Vector3<usize>, Vec<Vertex>, Vec<u16>)> {
let instant = std::time::Instant::now();
let mut geometry = Vec::new();
for (y, chunks_y) in self.chunks.iter().enumerate() {
for (z, chunks_z) in chunks_y.iter().enumerate() {
for (x, chunk) in chunks_z.iter().enumerate() {
let offset = Vector3::new(x as i32 * 16, y as i32 * 16, z as i32 * 16);
let (vertices, indices) = chunk.to_geometry(offset);
let chunk_position = Vector3::new(x as usize, y as usize, z as usize);
let offset = chunk_position.map(|x| x as i32 * 16);
let h = Self::highlighted_for_chunk(highlighted, chunk_position);
let (vertices, indices) = chunk.to_geometry(offset, h.as_ref());
geometry.push((Vector3::new(x, y, z), vertices, indices));
}
}
@ -45,4 +75,120 @@ impl World {
geometry
}
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,
None => return None,
};
chunk.blocks[y as usize % CHUNK_SIZE][z as usize % CHUNK_SIZE][x as usize % CHUNK_SIZE]
.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;
}
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 {
if scalar == 0.0 {
f32::INFINITY
} else {
(vector / scalar).magnitude()
}
}
#[allow(dead_code)]
pub fn raycast(
&self,
origin: Vector3<f32>,
direction: Vector3<f32>,
) -> Option<(Vector3<usize>, Vector3<i32>)> {
let direction = direction.normalize();
let scale = Vector3::new(
Self::calc_scale(direction, direction.x),
Self::calc_scale(direction, direction.y),
Self::calc_scale(direction, direction.z),
);
let mut position = origin.map(|x| x as i32);
let step = direction.map(|x| x.signum() as i32);
// Truncate the origin
let mut lengths = Vector3 {
x: if direction.x < 0.0 {
(origin.x - position.x as f32) * scale.x
} else {
(position.x as f32 + 1.0 - origin.x) * scale.x
},
y: if direction.y < 0.0 {
(origin.y - position.y as f32) * scale.y
} else {
(position.y as f32 + 1.0 - origin.y) * scale.y
},
z: if direction.z < 0.0 {
(origin.z - position.z as f32) * scale.z
} else {
(position.z as f32 + 1.0 - origin.z) * scale.z
},
};
let mut face;
while lengths.magnitude() < 100.0 {
if lengths.x < lengths.y && lengths.x < lengths.z {
lengths.x += scale.x;
position.x += step.x;
face = Vector3::unit_x() * step.x;
} else if lengths.y < lengths.x && lengths.y < lengths.z {
lengths.y += scale.y;
position.y += step.y;
face = Vector3::unit_y() * step.y;
} else if lengths.z < lengths.x && lengths.z < lengths.y {
lengths.z += scale.z;
position.z += step.z;
face = Vector3::unit_z() * step.z;
} else {
return None;
}
if self
.get_block(
position.x as isize,
position.y as isize,
position.z as isize,
)
.is_some()
{
// Intersection occurred
return Some((position.map(|x| x as usize), face));
}
}
None
}
}