Cull invisible block faces, reintroduce removing/adding blocks

This commit is contained in:
Sijmen 2021-06-01 10:48:30 +02:00
parent ff20d5ad29
commit 65f0a706d3
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
9 changed files with 406 additions and 190 deletions

View file

@ -1,4 +1,4 @@
use std::{collections::VecDeque, usize};
use std::{collections::VecDeque, convert::TryInto, usize};
use crate::{cube, quad::Quad, vertex::Vertex};
use ahash::{AHashMap, AHashSet};
@ -19,9 +19,9 @@ pub enum BlockType {
}
impl BlockType {
#[rustfmt::skip]
pub const fn texture_indices(self) -> (usize, usize, usize, usize, usize, usize) {
#[rustfmt::skip]
let indices = match self {
match self {
BlockType::Cobblestone => ( 0, 0, 0, 0, 0, 0),
BlockType::Dirt => ( 1, 1, 1, 1, 1, 1),
BlockType::Stone => ( 2, 2, 2, 2, 2, 2),
@ -30,11 +30,23 @@ impl BlockType {
BlockType::Sand => ( 6, 6, 6, 6, 6, 6),
BlockType::Gravel => ( 7, 7, 7, 7, 7, 7),
BlockType::Water => ( 8, 8, 8, 8, 8, 8), // up to 71
};
indices
}
}
pub const fn is_transparent(self) -> bool {
matches!(self, BlockType::Water)
}
}
pub type FaceFlags = usize;
pub const FACE_NONE: FaceFlags = 0;
pub const FACE_LEFT: FaceFlags = 1;
pub const FACE_RIGHT: FaceFlags = 2;
pub const FACE_BOTTOM: FaceFlags = 4;
pub const FACE_TOP: FaceFlags = 8;
pub const FACE_BACK: FaceFlags = 16;
pub const FACE_FRONT: FaceFlags = 32;
#[derive(Debug, Clone, Copy)]
pub struct Block {
pub block_type: BlockType,
@ -116,9 +128,9 @@ impl Chunk {
});
}
if chunk_y < 128 / CHUNK_SIZE as i32 {
for y in 0..CHUNK_SIZE {
if blocks[y][z][x].is_none() {
blocks[y][z][x] = Some(Block {
for layer in blocks.iter_mut() {
if layer[z][x].is_none() {
layer[z][x] = Some(Block {
block_type: BlockType::Water,
});
}
@ -133,25 +145,55 @@ impl Chunk {
}
}
fn check_visible(&self, x: usize, y: usize, z: usize) -> bool {
self.get_block(x - 1, y - 1, z - 1).is_some()
&& self.get_block(x + 1, y - 1, z - 1).is_some()
&& self.get_block(x - 1, y + 1, z - 1).is_some()
&& self.get_block(x + 1, y + 1, z - 1).is_some()
&& self.get_block(x - 1, y - 1, z + 1).is_some()
&& self.get_block(x + 1, y - 1, z + 1).is_some()
&& self.get_block(x - 1, y + 1, z + 1).is_some()
&& self.get_block(x + 1, y + 1, z + 1).is_some()
#[rustfmt::skip]
fn check_visible_faces(&self, x: usize, y: usize, z: usize) -> FaceFlags {
let mut visible_faces = FACE_NONE;
let transparent = self.blocks[y][z][x].unwrap().block_type.is_transparent();
if x == 0 || self.blocks[y][z][x - 1].is_none()
|| transparent != self.blocks[y][z][x - 1].unwrap().block_type.is_transparent()
{
visible_faces |= FACE_LEFT;
}
if x == CHUNK_SIZE - 1 || self.blocks[y][z][x + 1].is_none()
|| transparent != self.blocks[y][z][x + 1].unwrap().block_type.is_transparent()
{
visible_faces |= FACE_RIGHT;
}
if y == 0 || self.blocks[y - 1][z][x].is_none()
|| transparent != self.blocks[y - 1][z][x].unwrap().block_type.is_transparent()
{
visible_faces |= FACE_BOTTOM;
}
if y == CHUNK_SIZE - 1 || self.blocks[y + 1][z][x].is_none()
|| transparent != self.blocks[y + 1][z][x].unwrap().block_type.is_transparent()
{
visible_faces |= FACE_TOP;
}
if z == 0 || self.blocks[y][z - 1][x].is_none()
|| transparent != self.blocks[y][z - 1][x].unwrap().block_type.is_transparent()
{
visible_faces |= FACE_BACK;
}
if z == CHUNK_SIZE - 1 || self.blocks[y][z + 1][x].is_none()
|| transparent != self.blocks[y][z + 1][x].unwrap().block_type.is_transparent()
{
visible_faces |= FACE_FRONT;
}
visible_faces
}
fn cull_layer(
&self,
y: usize,
) -> (
AHashMap<(usize, usize), BlockType>,
AHashMap<(usize, usize), (BlockType, FaceFlags)>,
VecDeque<(usize, usize)>,
) {
let mut output = AHashMap::new();
let mut culled = AHashMap::new();
let mut queue = VecDeque::new();
let y_blocks = &self.blocks[y];
@ -159,26 +201,28 @@ impl Chunk {
for (x, block) in z_blocks.iter().enumerate() {
if let Some(block) = block {
// Don't add the block if it's not visible
if self.check_visible(x, y, z) {
let visible_faces = self.check_visible_faces(x, y, z);
if visible_faces == FACE_NONE {
continue;
}
output.insert((x, z), block.block_type);
culled.insert((x, z), (block.block_type, visible_faces));
queue.push_back((x, z));
}
}
}
(output, queue)
(culled, queue)
}
fn layer_to_quads(
&self,
y: usize,
offset: Vector3<i32>,
culled: AHashMap<(usize, usize), BlockType>,
culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>,
queue: &mut VecDeque<(usize, usize)>,
) -> Vec<(BlockType, i32, Vector3<i32>, Quad)> {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad)> = Vec::new();
) -> Vec<(BlockType, i32, Vector3<i32>, Quad, bool, FaceFlags)> {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, bool, FaceFlags)> = Vec::new();
let mut visited = AHashSet::new();
while let Some((x, z)) = queue.pop_front() {
if visited.contains(&(x, z)) {
@ -186,43 +230,73 @@ impl Chunk {
}
visited.insert((x, z));
if let Some(&block_type) = &culled.get(&(x, z)) {
if block_type == BlockType::Water {
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)) {
let quad = Quad::new(x as i32, z as i32, 1, 1);
quads.push((block_type, y as i32, offset, quad));
quads.push((block_type, y as i32, offset, quad, true, quad_faces));
continue;
}
// Extend horizontally
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));
continue;
}
// Extend along the X axis
let mut xmax = x + 1;
for x_ in x..CHUNK_SIZE {
xmax = x_ + 1;
if culled.get(&(x_ + 1, z)) != Some(&block_type)
|| visited.contains(&(x_ + 1, z))
if visited.contains(&(xmax, z))
|| self.highlighted == Some(Vector3::new(xmax, y, z))
{
break;
}
visited.insert((x_ + 1, z));
if let Some(&(block_type_, visible_faces_)) = culled.get(&(xmax, z)) {
quad_faces |= visible_faces_;
if block_type != block_type_ {
break;
}
} else {
break;
}
visited.insert((xmax, z));
}
// Extend vertically
// Extend along the Z axis
let mut zmax = z + 1;
'z: for z_ in z..CHUNK_SIZE {
zmax = z_ + 1;
for x_ in x..xmax {
if culled.get(&(x_, z_ + 1)) != Some(&block_type)
|| visited.contains(&(x_, z_ + 1))
if visited.contains(&(x_, zmax))
|| self.highlighted == Some(Vector3::new(x_, y, zmax))
{
break 'z;
}
if let Some(&(block_type_, visible_faces_)) = culled.get(&(x_, zmax)) {
quad_faces |= visible_faces_;
if block_type != block_type_ {
break 'z;
}
} else {
break 'z;
}
}
for x_ in x..xmax {
visited.insert((x_, z_ + 1));
visited.insert((x_, zmax));
}
}
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));
quads.push((block_type, y as i32, offset, quad, false, quad_faces));
}
}
@ -230,28 +304,37 @@ impl Chunk {
}
fn quads_to_geometry(
quads: Vec<(BlockType, i32, Vector3<i32>, Quad)>,
quads: Vec<(BlockType, i32, Vector3<i32>, Quad, bool, FaceFlags)>,
) -> (Vec<Vertex>, Vec<u16>) {
let mut vertices = Vec::new();
let mut indices = Vec::new();
for (quad_index, (block_type, y, offset, quad)) in quads.iter().enumerate() {
for (block_type, y, offset, quad, highlighted, visible_faces) in quads {
let texture_indices = block_type.texture_indices();
vertices.extend(&cube::vertices(quad, *y, 1.0, *offset, texture_indices));
for index in cube::INDICES {
indices.push(index + quad_index as u16 * 24);
}
let (quad_vertices, quad_indices) = &cube::vertices(
&quad,
y,
1.0,
offset,
texture_indices,
highlighted,
visible_faces,
vertices.len().try_into().unwrap(),
);
vertices.extend(quad_vertices);
indices.extend(quad_indices);
}
(vertices, indices)
}
pub fn to_geometry(&self, offset: Vector3<i32>) -> (Vec<Vertex>, Vec<u16>) {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad)> = Vec::new();
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, bool, 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);
quads.append(&mut layer_quads);
}

View file

@ -1,15 +1,23 @@
use cgmath::Vector3;
use crate::{quad::Quad, vertex::Vertex};
use crate::{
chunk::{FaceFlags, FACE_BACK, FACE_BOTTOM, FACE_FRONT, FACE_LEFT, FACE_RIGHT, FACE_TOP},
quad::Quad,
vertex::Vertex,
};
#[allow(clippy::many_single_char_names)]
#[rustfmt::skip]
pub fn vertices(
quad: &Quad,
y: i32,
z_height: f32,
offset: Vector3<i32>,
texture_indices: (usize, usize, usize, usize, usize, usize),
) -> [Vertex; 24] {
highlighted: bool,
visible_faces: FaceFlags,
start_index: u16,
) -> (Vec<Vertex>, Vec<u16>) {
let w = quad.w as f32;
let h = quad.h as f32;
let zh = z_height;
@ -19,65 +27,100 @@ pub fn vertices(
let z = (quad.y + offset.z) as f32;
let t = texture_indices;
let highlighted = highlighted as i32;
#[rustfmt::skip]
let vertices = [
// Left
Vertex { position: [x, y, z ], texture_coordinates: [h, 1.0, t.0 as f32], normal: [-1.0, 0.0, 0.0] },
Vertex { position: [x, y, z + h ], texture_coordinates: [0.0, 1.0, t.0 as f32], normal: [-1.0, 0.0, 0.0] },
Vertex { position: [x, y + zh, z + h ], texture_coordinates: [0.0, 0.0, t.0 as f32], normal: [-1.0, 0.0, 0.0] },
Vertex { position: [x, y + zh, z ], texture_coordinates: [h, 0.0, t.0 as f32], normal: [-1.0, 0.0, 0.0] },
let mut current_index = start_index;
let mut vertices = Vec::new();
let mut indices = Vec::new();
// Right
Vertex { position: [x + w, y, z ], texture_coordinates: [0.0, 1.0, t.1 as f32], normal: [1.0, 0.0, 0.0] },
Vertex { position: [x + w, y, z + h ], texture_coordinates: [h, 1.0, t.1 as f32], normal: [1.0, 0.0, 0.0] },
Vertex { position: [x + w, y + zh, z + h ], texture_coordinates: [h, 0.0, t.1 as f32], normal: [1.0, 0.0, 0.0] },
Vertex { position: [x + w, y + zh, z ], texture_coordinates: [0.0, 0.0, t.1 as f32], normal: [1.0, 0.0, 0.0] },
if visible_faces & FACE_LEFT == FACE_LEFT {
let normal = [-1.0, 0.0, 0.0];
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 },
Vertex { position: [x, y + zh, z + h], texture_coordinates: [0.0, 0.0, t.0 as f32], normal, highlighted },
Vertex { position: [x, y + zh, z ], texture_coordinates: [h, 0.0, t.0 as f32], normal, highlighted },
]);
indices.extend(&[
2 + current_index, current_index, 1 + current_index,
3 + current_index, current_index, 2 + current_index,
]);
current_index += 4;
}
// Back
Vertex { position: [x, y, z ], texture_coordinates: [w, 1.0, t.2 as f32], normal: [0.0, 0.0, -1.0] },
Vertex { position: [x, y + zh, z ], texture_coordinates: [w, 0.0, t.2 as f32], normal: [0.0, 0.0, -1.0] },
Vertex { position: [x + w, y + zh, z ], texture_coordinates: [0.0, 0.0, t.2 as f32], normal: [0.0, 0.0, -1.0] },
Vertex { position: [x + w, y, z ], texture_coordinates: [0.0, 1.0, t.2 as f32], normal: [0.0, 0.0, -1.0] },
if visible_faces & FACE_RIGHT == FACE_RIGHT {
let normal = [1.0, 0.0, 0.0];
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 },
Vertex { position: [x + w, y + zh, z + h], texture_coordinates: [h, 0.0, t.1 as f32], normal, highlighted },
Vertex { position: [x + w, y + zh, z ], texture_coordinates: [0.0, 0.0, t.1 as f32], normal, highlighted },
]);
indices.extend(&[
1 + current_index, current_index, 2 + current_index,
2 + current_index, current_index, 3 + current_index,
]);
current_index += 4;
}
// Front
Vertex { position: [x, y, z + h ], texture_coordinates: [0.0, 1.0, t.3 as f32], normal: [0.0, 0.0, 1.0] },
Vertex { position: [x, y + zh, z + h ], texture_coordinates: [0.0, 0.0, t.3 as f32], normal: [0.0, 0.0, 1.0] },
Vertex { position: [x + w, y + zh, z + h ], texture_coordinates: [w, 0.0, t.3 as f32], normal: [0.0, 0.0, 1.0] },
Vertex { position: [x + w, y, z + h ], texture_coordinates: [w, 1.0, t.3 as f32], normal: [0.0, 0.0, 1.0] },
if visible_faces & FACE_BACK == FACE_BACK {
let normal = [0.0, 0.0, -1.0];
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 },
Vertex { position: [x + w, y + zh, z], texture_coordinates: [0.0, 0.0, t.2 as f32], normal, highlighted },
Vertex { position: [x + w, y, z], texture_coordinates: [0.0, 1.0, t.2 as f32], normal, highlighted },
]);
indices.extend(&[
2 + current_index, current_index, 1 + current_index,
3 + current_index, current_index, 2 + current_index,
]);
current_index += 4;
}
// Bottom
Vertex { position: [x, y, z + 0.0], texture_coordinates: [w, 0.0, t.4 as f32], normal: [0.0, -1.0, 0.0] },
Vertex { position: [x, y, z + h ], texture_coordinates: [w, h, t.4 as f32], normal: [0.0, -1.0, 0.0] },
Vertex { position: [x + w, y, z + h ], texture_coordinates: [0.0, h, t.4 as f32], normal: [0.0, -1.0, 0.0] },
Vertex { position: [x + w, y, z ], texture_coordinates: [0.0, 0.0, t.4 as f32], normal: [0.0, -1.0, 0.0] },
if visible_faces & FACE_FRONT == FACE_FRONT {
let normal = [0.0, 0.0, 1.0];
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 },
Vertex { position: [x + w, y + zh, z + h], texture_coordinates: [w, 0.0, t.3 as f32], normal, highlighted },
Vertex { position: [x + w, y, z + h], texture_coordinates: [w, 1.0, t.3 as f32], normal, highlighted },
]);
indices.extend(&[
1 + current_index, current_index, 2 + current_index,
2 + current_index, current_index, 3 + current_index,
]);
current_index += 4;
}
// Top
Vertex { position: [x, y + zh, z ], texture_coordinates: [0.0, 0.0, t.5 as f32], normal: [0.0, 1.0, 0.0] },
Vertex { position: [x, y + zh, z + h ], texture_coordinates: [0.0, h, t.5 as f32], normal: [0.0, 1.0, 0.0] },
Vertex { position: [x + w, y + zh, z + h ], texture_coordinates: [w, h, t.5 as f32], normal: [0.0, 1.0, 0.0] },
Vertex { position: [x + w, y + zh, z ], texture_coordinates: [w, 0.0, t.5 as f32], normal: [0.0, 1.0, 0.0] },
];
vertices
if visible_faces & FACE_BOTTOM == FACE_BOTTOM {
let normal = [0.0, -1.0, 0.0];
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 },
Vertex { position: [x + w, y, z + h], texture_coordinates: [0.0, h, t.4 as f32], normal, highlighted },
Vertex { position: [x + w, y, z ], texture_coordinates: [0.0, 0.0, t.4 as f32], normal, highlighted },
]);
indices.extend(&[
current_index, 2 + current_index, 1 + current_index,
current_index, 3 + current_index, 2 + current_index,
]);
current_index += 4;
}
if visible_faces & FACE_TOP == FACE_TOP {
let normal = [0.0, 1.0, 0.0];
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 },
Vertex { position: [x + w, y + zh, z + h], texture_coordinates: [w, h, t.5 as f32], normal, highlighted },
Vertex { position: [x + w, y + zh, z ], texture_coordinates: [w, 0.0, t.5 as f32], normal, highlighted },
]);
indices.extend(&[
current_index, 1 + current_index, 2 + current_index,
current_index, 2 + current_index, 3 + current_index,
]);
}
(vertices, indices)
}
#[rustfmt::skip]
pub const INDICES: &[u16] = &[
2, 0, 1,
3, 0, 2,
5, 4, 6,
6, 4, 7,
10, 8, 9,
11, 8, 10,
13, 12, 14,
14, 12, 15,
16, 18, 17,
16, 19, 18,
20, 21, 22,
20, 22, 23,
];

View file

@ -1,10 +1,10 @@
mod camera;
mod chunk;
mod cube;
mod time;
mod quad;
mod state;
mod texture;
mod time;
mod uniforms;
mod vertex;
mod world;
@ -100,8 +100,8 @@ fn main() {
let fps_min = 1_000_000 / frametime_max.as_micros();
println!(
"{} frames | frametime avg={:?} min={:?} max={:?} | fps avg={} min={} max={} | {} tris",
frames, frametime, frametime_min, frametime_max, fps, fps_min, fps_max, triangle_count,
"{:>4} frames | frametime avg={:>5.2}ms min={:>5.2}ms max={:>5.2}ms | fps avg={:>4} min={:>4} max={:>4} | {:>8} tris",
frames, frametime.as_secs_f32() * 1000.0, frametime_min.as_secs_f32() * 1000.0, frametime_max.as_secs_f32() * 1000.0, fps, fps_min, fps_max, triangle_count,
);
elapsed = Duration::from_secs(0);

View file

@ -19,6 +19,7 @@ struct VertexInput {
[[location(0)]] position: vec3<f32>;
[[location(1)]] texture_coordinates: vec3<f32>;
[[location(2)]] normal: vec3<f32>;
[[location(3)]] highlighted: i32;
};
struct VertexOutput {
@ -26,6 +27,7 @@ struct VertexOutput {
[[location(0)]] texture_coordinates: vec3<f32>;
[[location(1)]] world_normal: vec3<f32>;
[[location(2)]] world_position: vec3<f32>;
[[location(3)]] highlighted: i32;
};
[[stage(vertex)]]
@ -44,6 +46,7 @@ fn main(model: VertexInput) -> VertexOutput {
}
out.clip_position = uniforms.view_projection * vec4<f32>(out.world_position, 1.0);
out.highlighted = model.highlighted;
return out;
}
@ -59,7 +62,7 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
i32(in.texture_coordinates.z)
);
let light_position = vec3<f32>(256.0, 500.0, 200.0);
let light_position = vec3<f32>(-100.0, 500.0, -200.0);
let light_color = vec3<f32>(1.0, 1.0, 1.0);
let ambient_strength = 0.1;
@ -76,6 +79,9 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let specular_color = specular_strength * light_color;
var result: vec3<f32> = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
if (in.highlighted != 0) {
result = result + 0.3;
}
return vec4<f32>(result, object_color.a);
}

View file

@ -203,42 +203,50 @@ pub const CROSSHAIR_VERTICES: &[Vertex] = &[
position: [-UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [240.0 / 256.0, 0.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [1.0, 0.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [UI_SCALE_X * 8.0, -UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [1.0, 16.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [-UI_SCALE_X * 8.0, -UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [240.0 / 256.0, 16.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
// Hotbar
Vertex {
position: [-UI_SCALE_X * 91.0, -1.0 + UI_SCALE_Y * 22.0, 0.0],
texture_coordinates: [0.0 / 256.0, 0.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [UI_SCALE_X * 91.0, -1.0 + UI_SCALE_Y * 22.0, 0.0],
texture_coordinates: [182.0 / 256.0, 0.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [UI_SCALE_X * 91.0, -1.0, 0.0],
texture_coordinates: [182.0 / 256.0, 22.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [-UI_SCALE_X * 91.0, -1.0, 0.0],
texture_coordinates: [0.0 / 256.0, 22.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
];

View file

@ -3,7 +3,7 @@ mod world;
use std::time::Duration;
use cgmath::{InnerSpace, Rad, Vector3};
use cgmath::{EuclideanSpace, InnerSpace, Rad, Vector3, Zero};
use winit::{
event::{DeviceEvent, ElementState, KeyboardInput, VirtualKeyCode},
window::Window,
@ -12,6 +12,8 @@ use winit::{
use hud::HudState;
use world::WorldState;
use crate::chunk::{Block, BlockType};
pub struct State {
pub window_size: winit::dpi::PhysicalSize<u32>,
render_surface: wgpu::Surface,
@ -141,6 +143,9 @@ impl State {
VirtualKeyCode::D => self.right_speed += amount,
VirtualKeyCode::LControl => self.up_speed -= amount,
VirtualKeyCode::Space => self.up_speed += amount,
VirtualKeyCode::F1 if state == &ElementState::Pressed => self
.world_state
.toggle_wireframe(&self.render_device, &self.swap_chain_descriptor),
_ => (),
}
}
@ -158,15 +163,19 @@ impl State {
}
fn update_aim(&mut self) {
// let camera = &self.world_state.camera;
// let chunk = &mut self.world_state.chunk;
// let position = chunk
// .raycast(camera.position.to_vec(), camera.direction())
// .map(|(position, _)| position);
// if position != chunk.highlighted {
// chunk.highlighted = position;
// self.world_state.update_chunk(&self.render_queue);
// }
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);
if position != chunk.highlighted {
chunk.highlighted = position;
println!("aiming at {:?}", position);
self.world_state
.update_chunk_geometry(&self.render_device, Vector3::zero());
}
}
fn input_mouse(&mut self, dx: f64, dy: f64) {
@ -183,32 +192,34 @@ impl State {
..
}) => self.input_keyboard(key, state),
// DeviceEvent::Button {
// button,
// state: ElementState::Pressed,
// } if self.mouse_grabbed => {
// let camera = &self.world_state.camera;
DeviceEvent::Button {
button,
state: ElementState::Pressed,
} if self.mouse_grabbed => {
let camera = &self.world_state.camera;
let chunk = &mut self.world_state.world.chunks[0][0][0];
// // if let Some((pos, axis)) = self
// // .world_state
// // .chunk
// // .raycast(camera.position.to_vec(), camera.direction())
// // {
// // if *button == 1 {
// // self.world_state.chunk.blocks[pos.y][pos.z][pos.x].take();
// // dbg!(&pos);
// // self.world_state.update_chunk(&self.render_queue);
// // } else if *button == 3 {
// // let new_pos = pos.map(|x| x as i32) - axis;
// // dbg!(&axis, &new_pos);
// // self.world_state.chunk.blocks[new_pos.y as usize][new_pos.z as usize]
// // [new_pos.x as usize] = Some(Block {
// // block_type: BlockType::Cobblestone,
// // });
// // self.world_state.update_chunk(&self.render_queue);
// // }
// // }
// }
if let Some((pos, axis)) =
chunk.raycast(camera.position.to_vec(), camera.direction())
{
if *button == 1 {
chunk.blocks[pos.y][pos.z][pos.x].take();
dbg!(&pos);
self.world_state
.update_chunk_geometry(&self.render_device, Vector3::zero());
} 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] =
Some(Block {
block_type: BlockType::Cobblestone,
});
self.world_state
.update_chunk_geometry(&self.render_device, Vector3::zero());
}
}
}
DeviceEvent::MouseMotion { delta: (dx, dy) } => self.input_mouse(*dx, *dy),
_ => (),
}
@ -220,13 +231,13 @@ 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 * 15.0 * dt_secs;
self.world_state.camera.position += forward * self.forward_speed * 30.0 * dt_secs;
let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
self.world_state.camera.position += right * self.right_speed * 15.0 * dt_secs;
self.world_state.camera.position += right * self.right_speed * 30.0 * dt_secs;
let up = Vector3::new(0.0, 1.0, 0.0).normalize();
self.world_state.camera.position += up * self.up_speed * 15.0 * dt_secs;
self.world_state.camera.position += up * self.up_speed * 30.0 * dt_secs;
self.world_state.update(dt, &self.render_queue);
@ -285,7 +296,9 @@ impl State {
render_pass.set_bind_group(1, &self.world_state.uniform_bind_group, &[]);
render_pass.set_bind_group(2, &self.world_state.time_bind_group, &[]);
for (chunk_vertices, chunk_indices, index_count) in &self.world_state.chunk_buffers {
for (chunk_vertices, chunk_indices, index_count) in
self.world_state.chunk_buffers.values()
{
render_pass.set_vertex_buffer(0, chunk_vertices.slice(..));
render_pass.set_index_buffer(chunk_indices.slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..*index_count as u32, 0, 0..1);

View file

@ -1,10 +1,13 @@
use std::time::{Duration, Instant};
use ahash::AHashMap;
use cgmath::Vector3;
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use winit::dpi::PhysicalSize;
use crate::{
camera::{Camera, Projection},
chunk::CHUNK_SIZE,
texture::{Texture, TextureManager},
time::Time,
uniforms::Uniforms,
@ -24,9 +27,12 @@ pub struct WorldState {
pub time_bind_group: wgpu::BindGroup,
pub world: World,
pub chunk_buffers: Vec<(wgpu::Buffer, wgpu::Buffer, usize)>,
pub chunk_buffers: AHashMap<Vector3<usize>, (wgpu::Buffer, wgpu::Buffer, usize)>,
time: Time,
time_buffer: wgpu::Buffer,
wireframe: bool,
shader: wgpu::ShaderModule,
render_pipeline_layout: wgpu::PipelineLayout,
}
impl WorldState {
@ -148,28 +154,13 @@ impl WorldState {
fn create_render_pipeline(
render_device: &wgpu::Device,
swap_chain_descriptor: &wgpu::SwapChainDescriptor,
bind_group_layouts: &[&wgpu::BindGroupLayout],
shader: &wgpu::ShaderModule,
pipeline_layout: &wgpu::PipelineLayout,
wireframe: bool,
) -> wgpu::RenderPipeline {
let shader = render_device.create_shader_module(
&(wgpu::ShaderModuleDescriptor {
label: Some("shader"),
flags: wgpu::ShaderFlags::all(),
source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/world.wgsl").into()),
}),
);
let render_pipeline_layout =
render_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("render_pipeline_layout"),
bind_group_layouts,
push_constant_ranges: &[],
});
let wireframe = false;
render_device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "main",
@ -207,31 +198,76 @@ impl WorldState {
})
}
pub fn update_chunk(&mut self, render_device: &wgpu::Device) {
pub fn update_world_geometry(&mut self, render_device: &wgpu::Device) {
let instant = Instant::now();
let world_geometry = self.world.to_geometry();
self.chunk_buffers.clear();
for (chunk_vertices, chunk_indices) in world_geometry {
self.chunk_buffers.push((
render_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: &bytemuck::cast_slice(&chunk_vertices),
usage: wgpu::BufferUsage::VERTEX,
}),
render_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: &bytemuck::cast_slice(&chunk_indices),
usage: wgpu::BufferUsage::INDEX,
}),
chunk_indices.len(),
));
for (chunk_position, chunk_vertices, chunk_indices) in world_geometry {
self.chunk_buffers.insert(
chunk_position,
(
render_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: &bytemuck::cast_slice(&chunk_vertices),
usage: wgpu::BufferUsage::VERTEX,
}),
render_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: &bytemuck::cast_slice(&chunk_indices),
usage: wgpu::BufferUsage::INDEX,
}),
chunk_indices.len(),
),
);
}
let elapsed = instant.elapsed();
println!("World update took {:?}", elapsed);
}
pub fn update_chunk_geometry(
&mut self,
render_device: &wgpu::Device,
chunk_position: Vector3<usize>,
) {
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);
self.chunk_buffers.insert(
chunk_position,
(
render_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: &bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsage::VERTEX,
}),
render_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: &bytemuck::cast_slice(&indices),
usage: wgpu::BufferUsage::INDEX,
}),
indices.len(),
),
);
}
pub fn toggle_wireframe(
&mut self,
render_device: &wgpu::Device,
swap_chain_descriptor: &wgpu::SwapChainDescriptor,
) {
self.wireframe = !self.wireframe;
self.render_pipeline = Self::create_render_pipeline(
render_device,
swap_chain_descriptor,
&self.shader,
&self.render_pipeline_layout,
self.wireframe,
)
}
pub fn new(
render_device: &wgpu::Device,
render_queue: &wgpu::Queue,
@ -248,14 +284,31 @@ impl WorldState {
let (time, time_buffer, time_layout, time_bind_group) = Self::create_time(&render_device);
let shader = render_device.create_shader_module(
&(wgpu::ShaderModuleDescriptor {
label: Some("shader"),
flags: wgpu::ShaderFlags::all(),
source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/world.wgsl").into()),
}),
);
let render_pipeline_layout =
render_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("render_pipeline_layout"),
push_constant_ranges: &[],
bind_group_layouts: &[
&texture_manager.bind_group_layout,
&world_uniform_layout,
&time_layout,
],
});
let render_pipeline = Self::create_render_pipeline(
&render_device,
&swap_chain_descriptor,
&[
&texture_manager.bind_group_layout,
&world_uniform_layout,
&time_layout,
],
&shader,
&render_pipeline_layout,
false,
);
let depth_texture =
@ -270,16 +323,19 @@ impl WorldState {
camera,
projection,
depth_texture,
shader,
render_pipeline_layout,
time,
time_buffer,
time_bind_group,
world,
chunk_buffers: Vec::new(),
chunk_buffers: AHashMap::new(),
wireframe: false,
};
world_state.update_chunk(render_device);
world_state.update_world_geometry(render_device);
world_state
}

View file

@ -6,6 +6,7 @@ pub struct Vertex {
pub position: [f32; 3],
pub texture_coordinates: [f32; 3],
pub normal: [f32; 3],
pub highlighted: i32,
}
impl Vertex {
@ -29,6 +30,11 @@ impl Vertex {
shader_location: 2,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: 36,
shader_location: 3,
format: wgpu::VertexFormat::Sint32,
},
],
}
}

View file

@ -2,7 +2,7 @@ use crate::{chunk::Chunk, vertex::Vertex};
use cgmath::Vector3;
pub struct World {
chunks: Vec<Vec<Vec<Chunk>>>,
pub chunks: Vec<Vec<Vec<Chunk>>>,
}
const WORLD_SIZE: usize = 16;
@ -26,7 +26,7 @@ impl World {
Self { chunks }
}
pub fn to_geometry(&self) -> Vec<(Vec<Vertex>, Vec<u16>)> {
pub fn to_geometry(&self) -> Vec<(Vector3<usize>, Vec<Vertex>, Vec<u16>)> {
let instant = std::time::Instant::now();
let mut geometry = Vec::new();
@ -34,7 +34,8 @@ impl World {
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);
geometry.push(chunk.to_geometry(offset));
let (vertices, indices) = chunk.to_geometry(offset);
geometry.push((Vector3::new(x, y, z), vertices, indices));
}
}
}