diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..53ec8ed --- /dev/null +++ b/.drone.yml @@ -0,0 +1,9 @@ +kind: pipeline +type: docker +name: frontend + +steps: + - name: dependencies + image: rust:slim + commands: + - cargo doc diff --git a/Cargo.lock b/Cargo.lock index 51cc692..cbfde82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1103,6 +1103,25 @@ dependencies = [ "objc", ] +[[package]] +name = "minecrab" +version = "0.1.0" +dependencies = [ + "ahash 0.7.4", + "anyhow", + "bytemuck", + "cgmath", + "env_logger", + "futures", + "gltf", + "image", + "log", + "noise", + "rayon", + "wgpu", + "winit", +] + [[package]] name = "miniz_oxide" version = "0.3.7" @@ -1574,25 +1593,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "rustwgpu" -version = "0.1.0" -dependencies = [ - "ahash 0.7.4", - "anyhow", - "bytemuck", - "cgmath", - "env_logger", - "futures", - "gltf", - "image", - "log", - "noise", - "rayon", - "wgpu", - "winit", -] - [[package]] name = "ryu" version = "1.0.5" diff --git a/Cargo.toml b/Cargo.toml index 92bba24..f4f9d21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "rustwgpu" +name = "minecrab" version = "0.1.0" -authors = ["Vijfhoek "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/chunk.rs b/src/chunk.rs index 7f7aa73..73a9b2d 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,9 +1,10 @@ -use std::{collections::VecDeque, convert::TryInto, usize}; +use std::{collections::VecDeque, usize}; -use crate::{cube, quad::Quad, vertex::Vertex}; +use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex}; use ahash::{AHashMap, AHashSet}; -use cgmath::{Vector3, Zero}; +use cgmath::Vector3; use noise::utils::{NoiseMapBuilder, PlaneMapBuilder}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -46,6 +47,8 @@ pub const FACE_BOTTOM: FaceFlags = 4; pub const FACE_TOP: FaceFlags = 8; pub const FACE_BACK: FaceFlags = 16; 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)] pub struct Block { @@ -218,12 +221,13 @@ impl Chunk { culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>, queue: &mut VecDeque<(usize, usize)>, highlighted: Option<&(Vector3, Vector3)>, - ) -> Vec<(BlockType, i32, Vector3, Quad, Vector3, FaceFlags)> { - let mut quads: Vec<(BlockType, i32, Vector3, Quad, Vector3, FaceFlags)> = - Vec::new(); + ) -> Vec { + let mut quads: Vec = Vec::new(); let mut visited = AHashSet::new(); let hl = highlighted.map(|h| h.0); while let Some((x, z)) = queue.pop_front() { + let position = offset + Vector3::new(x, y, z).cast().unwrap(); + if visited.contains(&(x, z)) { continue; } @@ -233,28 +237,19 @@ impl Chunk { let mut quad_faces = visible_faces; 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, - highlighted.unwrap().1, - quad_faces, - )); + let mut quad = Quad::new(position, 1, 1); + quad.highlighted_normal = highlighted.unwrap().1; + quad.visible_faces = quad_faces; + quad.block_type = Some(block_type); + quads.push(quad); 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, - Vector3::zero(), - quad_faces, - )); + let mut quad = Quad::new(position, 1, 1); + quad.visible_faces = quad_faces; + quad.block_type = Some(block_type); + quads.push(quad); continue; } @@ -304,60 +299,36 @@ 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, - Vector3::zero(), - quad_faces, - )); + let mut quad = Quad::new(position, (xmax - x) as i32, (zmax - z) as i32); + quad.visible_faces = quad_faces; + quad.block_type = Some(block_type); + quads.push(quad); } } quads } - fn quads_to_geometry( - quads: Vec<(BlockType, i32, Vector3, Quad, Vector3, FaceFlags)>, - ) -> (Vec, Vec) { - let mut vertices = Vec::new(); - let mut indices = Vec::new(); - for (block_type, y, offset, quad, highlighted, visible_faces) in quads { - let texture_indices = block_type.texture_indices(); - - 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); + fn quads_to_geometry(quads: Vec) -> Geometry { + let mut geometry: Geometry = Default::default(); + for quad in quads { + geometry.append(&mut quad.to_geometry(geometry.vertices.len() as u16)); } - - (vertices, indices) + geometry } pub fn to_geometry( &self, offset: Vector3, highlighted: Option<&(Vector3, Vector3)>, - ) -> (Vec, Vec) { - let mut quads: Vec<(BlockType, i32, Vector3, Quad, Vector3, 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, highlighted); - quads.append(&mut layer_quads); - } + ) -> 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) + }) + .collect(); Self::quads_to_geometry(quads) } diff --git a/src/cube.rs b/src/cube.rs deleted file mode 100644 index a79402d..0000000 --- a/src/cube.rs +++ /dev/null @@ -1,132 +0,0 @@ -use cgmath::Vector3; - -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, - texture_indices: (usize, usize, usize, usize, usize, usize), - highlighted: Vector3, - visible_faces: FaceFlags, - start_index: u16, -) -> (Vec, Vec) { - let w = quad.w as f32; - let h = quad.h as f32; - let zh = z_height; - - let x = (quad.x + offset.x) as f32; - let y = (y + offset.y) as f32; - let z = (quad.y + offset.z) as f32; - - let t = texture_indices; - - let mut current_index = start_index; - let mut vertices = Vec::new(); - let mut indices = Vec::new(); - let highlighted: [f32; 3] = (-highlighted).cast().unwrap().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 }, - 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; - } - - 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 }, - 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; - } - - 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 }, - 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; - } - - 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 }, - 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; - } - - 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 }, - 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]; - 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 }, - 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) -} diff --git a/src/geometry.rs b/src/geometry.rs new file mode 100644 index 0000000..98127a8 --- /dev/null +++ b/src/geometry.rs @@ -0,0 +1,75 @@ +use wgpu::{ + util::{BufferInitDescriptor, DeviceExt}, + RenderPass, +}; + +use crate::{render_context::RenderContext, vertex::Vertex}; + +/// Represents a set of triangles by its vertices and indices. +#[derive(Default)] +pub struct Geometry { + pub vertices: Vec, + pub indices: Vec, +} + +impl Geometry { + pub fn new(vertices: Vec, indices: Vec) -> Self { + Self { vertices, indices } + } + + /// Moves all the vertices and indices of `other` into `Self`, leaving `other` empty. + pub fn append(&mut self, other: &mut Self) { + self.vertices.append(&mut other.vertices); + self.indices.append(&mut other.indices); + } + + /// Returns the number of indices in the vertex. + pub fn index_count(&self) -> usize { + self.indices.len() + } +} + +pub struct GeometryBuffers { + pub vertices: wgpu::Buffer, + pub indices: wgpu::Buffer, + pub index_count: usize, +} + +impl GeometryBuffers { + pub fn from_geometry( + render_context: &RenderContext, + geometry: &Geometry, + usage: wgpu::BufferUsage, + ) -> Self { + let vertices = render_context + .device + .create_buffer_init(&BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&geometry.vertices), + usage: wgpu::BufferUsage::VERTEX | usage, + }); + + let indices = render_context + .device + .create_buffer_init(&BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&geometry.indices), + usage: wgpu::BufferUsage::INDEX | usage, + }); + + Self { + vertices, + indices, + index_count: geometry.index_count(), + } + } + + pub fn set_buffers<'a>(&'a self, render_pass: &mut RenderPass<'a>) { + render_pass.set_vertex_buffer(0, self.vertices.slice(..)); + render_pass.set_index_buffer(self.indices.slice(..), wgpu::IndexFormat::Uint16); + } + + pub fn draw_indexed(&self, render_pass: &mut RenderPass) { + render_pass.draw_indexed(0..self.index_count as u32, 0, 0..1); + } +} diff --git a/src/main.rs b/src/main.rs index 9e7b52a..b8491c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,15 @@ mod aabb; mod camera; mod chunk; -mod cube; +mod geometry; mod quad; mod render_context; mod state; mod text_renderer; mod texture; mod time; -mod uniforms; mod vertex; +mod view; mod world; mod npc; @@ -106,7 +106,7 @@ fn main() { let fps_min = 1_000_000 / frametime_max.as_micros(); println!( - "{:>4} frames | frametime avg={:>5.2}ms min={:>5.2}ms max={:>5.2}ms | fps avg={:>4} min={:>4} max={:>4} | {:>8} tris", + "{:>4} frames | frametime avg={:>5.2}ms min={:>5.2}ms max={:>5.2}ms | fps avg={:>5} min={:>5} max={:>5} | {:>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, ); diff --git a/src/npc.rs b/src/npc.rs index c09d1ea..080b281 100644 --- a/src/npc.rs +++ b/src/npc.rs @@ -1,23 +1,20 @@ extern crate gltf; extern crate wgpu; -use cgmath::{Vector3}; +use cgmath::Vector3; -use crate::{ - vertex::Vertex, -}; +use crate::vertex::BlockVertex; pub struct Npc { pub position: Vector3, pub scale: Vector3, pub rotation: Vector3, - pub vertices: Vec, + pub vertices: Vec, pub indices: Vec, - pub vertex_buffer: Option, + pub vertex_buffer: Option, pub index_buffer: Option, } - impl Npc { pub fn load() -> Self { let position: Vector3 = Vector3::new(0.0, 0.0, 0.0); @@ -25,34 +22,35 @@ impl Npc { let rotation: Vector3 = Vector3::new(0.0, 0.0, 0.0); let (model, buffers, _) = gltf::import("assets/models/minecrab.glb").unwrap(); - - let mut indices: Vec = Vec::new(); - let mut vertices: Vec = Vec::new(); + + let mut indices = Vec::new(); + let mut vertices = Vec::new(); for mesh in model.meshes() { for primitive in mesh.primitives() { let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); indices = reader.read_indices().unwrap().into_u32().collect(); - // loop over all primitives and get the normals, position and color + // loop over all primitives and get the normals, position and color let pos_iter = reader.read_positions().unwrap(); let norm_iter = reader.read_normals().unwrap(); let tex_iter = reader.read_tex_coords(0).unwrap().into_f32(); - for it in pos_iter.zip(norm_iter).zip(tex_iter) { - let ((position, normal), [tex_x, tex_y]) = it; - - let current_vert: Vertex = Vertex { - position, - texture_coordinates: [tex_x, tex_y, 0.0], - normal, - highlighted: 0 + for ((position, normal), texture_coordinates) in + pos_iter.zip(norm_iter).zip(tex_iter) + { + let current_vert = BlockVertex { + position, + texture_coordinates, + normal, + highlighted: 0, + texture_id: 0, }; vertices.push(current_vert); } } - } + } return Self { position, @@ -61,7 +59,7 @@ impl Npc { indices, vertices, vertex_buffer: None, - index_buffer: None + index_buffer: None, }; } } diff --git a/src/quad.rs b/src/quad.rs index e0e6859..55da2f5 100644 --- a/src/quad.rs +++ b/src/quad.rs @@ -1,13 +1,171 @@ +use cgmath::{Vector3, Zero}; + +use crate::{ + chunk::{ + BlockType, FaceFlags, FACE_ALL, FACE_BACK, FACE_BOTTOM, FACE_FRONT, FACE_LEFT, FACE_RIGHT, + FACE_TOP, + }, + geometry::Geometry, + vertex::BlockVertex, +}; + #[derive(Debug)] pub struct Quad { - pub x: i32, - pub y: i32, - pub w: i32, - pub h: i32, + pub position: Vector3, + pub dx: i32, + pub dz: i32, + + pub highlighted_normal: Vector3, + pub visible_faces: FaceFlags, + pub block_type: Option, } impl Quad { - pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self { - Quad { x, y, w, h } + pub fn new(position: Vector3, dx: i32, dz: i32) -> Self { + Quad { + position, + dx, + dz, + + /// The normal of the face that was highlighted. + /// + /// Set to Vector3::zero if no faces are highlighted. + highlighted_normal: Vector3::zero(), + + /// Bitmap of the visible faces. + visible_faces: FACE_ALL, + + /// The `BlockType` of the blocks the quad describes. + /// + /// Used for determining which texture to map to it. When `None`, texture index 0 will be used. + block_type: None, + } + } + + /// Converts the quad to `Geometry` (i.e. a list of vertices and indices) to be rendered. + /// + /// # Arguments + /// + /// * `start_index` - Which geometry index to start at. + #[allow(clippy::many_single_char_names)] + #[rustfmt::skip] + pub fn to_geometry( + &self, + start_index: u16, + ) -> Geometry { + let dx = self.dx as f32; + let dz = self.dz as f32; + let dy = 1.0; + + let x = self.position.x as f32; + let y = self.position.y as f32; + let z = self.position.z as f32; + + let t = match self.block_type { + Some(block_type) => block_type.texture_indices(), + None => (0, 0, 0, 0, 0, 0), + }; + + let mut current_index = start_index; + let mut vertices = Vec::new(); + let mut indices = Vec::new(); + let highlighted: [f32; 3] = self.highlighted_normal.cast().unwrap().into(); + + if self.visible_faces & FACE_LEFT == FACE_LEFT { + let normal = [-1.0, 0.0, 0.0]; + let highlighted = (normal == highlighted) as i32; + vertices.extend(&[ + BlockVertex { position: [x, y, z ], texture_coordinates: [dz, 1.0], texture_id: t.0 as i32, normal, highlighted }, + BlockVertex { position: [x, y, z + dz], texture_coordinates: [0.0, 1.0], texture_id: t.0 as i32, normal, highlighted }, + BlockVertex { position: [x, y + dy, z + dz], texture_coordinates: [0.0, 0.0], texture_id: t.0 as i32, normal, highlighted }, + BlockVertex { position: [x, y + dy, z ], texture_coordinates: [dz, 0.0], texture_id: t.0 as i32, normal, highlighted }, + ]); + indices.extend(&[ + 2 + current_index, current_index, 1 + current_index, + 3 + current_index, current_index, 2 + current_index, + ]); + current_index += 4; + } + + if self.visible_faces & FACE_RIGHT == FACE_RIGHT { + let normal = [1.0, 0.0, 0.0]; + let highlighted = (normal == highlighted) as i32; + vertices.extend(&[ + BlockVertex { position: [x + dx, y, z ], texture_coordinates: [0.0, 1.0], texture_id: t.1 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y, z + dz], texture_coordinates: [dz, 1.0], texture_id: t.1 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y + dy, z + dz], texture_coordinates: [dz, 0.0], texture_id: t.1 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y + dy, z ], texture_coordinates: [0.0, 0.0], texture_id: t.1 as i32, normal, highlighted }, + ]); + indices.extend(&[ + 1 + current_index, current_index, 2 + current_index, + 2 + current_index, current_index, 3 + current_index, + ]); + current_index += 4; + } + + if self.visible_faces & FACE_BACK == FACE_BACK { + let normal = [0.0, 0.0, -1.0]; + let highlighted = (normal == highlighted) as i32; + vertices.extend(&[ + BlockVertex { position: [x, y, z], texture_coordinates: [dx, 1.0], texture_id: t.2 as i32, normal, highlighted }, + BlockVertex { position: [x, y + dy, z], texture_coordinates: [dx, 0.0], texture_id: t.2 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y + dy, z], texture_coordinates: [0.0, 0.0], texture_id: t.2 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y, z], texture_coordinates: [0.0, 1.0], texture_id: t.2 as i32, normal, highlighted }, + ]); + indices.extend(&[ + 2 + current_index, current_index, 1 + current_index, + 3 + current_index, current_index, 2 + current_index, + ]); + current_index += 4; + } + + if self.visible_faces & FACE_FRONT == FACE_FRONT { + let normal = [0.0, 0.0, 1.0]; + let highlighted = (normal == highlighted) as i32; + vertices.extend(&[ + BlockVertex { position: [x, y, z + dz], texture_coordinates: [0.0, 1.0], texture_id: t.3 as i32, normal, highlighted }, + BlockVertex { position: [x, y + dy, z + dz], texture_coordinates: [0.0, 0.0], texture_id: t.3 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y + dy, z + dz], texture_coordinates: [dx, 0.0], texture_id: t.3 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y, z + dz], texture_coordinates: [dx, 1.0], texture_id: t.3 as i32, normal, highlighted }, + ]); + indices.extend(&[ + 1 + current_index, current_index, 2 + current_index, + 2 + current_index, current_index, 3 + current_index, + ]); + current_index += 4; + } + + if self.visible_faces & FACE_BOTTOM == FACE_BOTTOM { + let normal = [0.0, -1.0, 0.0]; + let highlighted = (normal == highlighted) as i32; + vertices.extend(&[ + BlockVertex { position: [x, y, z ], texture_coordinates: [dx, 0.0], texture_id: t.4 as i32, normal, highlighted }, + BlockVertex { position: [x, y, z + dz], texture_coordinates: [dx, dz ], texture_id: t.4 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y, z + dz], texture_coordinates: [0.0, dz ], texture_id: t.4 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y, z ], texture_coordinates: [0.0, 0.0], texture_id: t.4 as i32, normal, highlighted }, + ]); + indices.extend(&[ + current_index, 2 + current_index, 1 + current_index, + current_index, 3 + current_index, 2 + current_index, + ]); + current_index += 4; + } + + if self.visible_faces & FACE_TOP == FACE_TOP { + let normal = [0.0, 1.0, 0.0]; + let highlighted = (normal == highlighted) as i32; + vertices.extend(&[ + BlockVertex { position: [x, y + dy, z ], texture_coordinates: [0.0, 0.0], texture_id: t.5 as i32, normal, highlighted }, + BlockVertex { position: [x, y + dy, z + dz], texture_coordinates: [0.0, dz ], texture_id: t.5 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y + dy, z + dz], texture_coordinates: [dx, dz ], texture_id: t.5 as i32, normal, highlighted }, + BlockVertex { position: [x + dx, y + dy, z ], texture_coordinates: [dx, 0.0], texture_id: t.5 as i32, normal, highlighted }, + ]); + indices.extend(&[ + current_index, 1 + current_index, 2 + current_index, + current_index, 2 + current_index, 3 + current_index, + ]); + } + + Geometry::new(vertices, indices) } } diff --git a/src/shaders/ui.wgsl b/src/shaders/ui.wgsl index 403e729..24acaf2 100644 --- a/src/shaders/ui.wgsl +++ b/src/shaders/ui.wgsl @@ -1,7 +1,6 @@ struct VertexInput { - [[location(0)]] position: vec3; + [[location(0)]] position: vec2; [[location(1)]] texture_coordinates: vec2; - [[location(2)]] normal: vec3; }; struct VertexOutput { @@ -13,7 +12,7 @@ struct VertexOutput { fn main(model: VertexInput) -> VertexOutput { var out: VertexOutput; out.texture_coordinates = model.texture_coordinates; - out.clip_position = vec4(model.position, 1.0); + out.clip_position = vec4(model.position, 0.0, 1.0); return out; } diff --git a/src/shaders/world.wgsl b/src/shaders/world.wgsl index 9cb550f..37b15f0 100644 --- a/src/shaders/world.wgsl +++ b/src/shaders/world.wgsl @@ -1,7 +1,7 @@ [[block]] -struct Uniforms { - view_position: vec4; - view_projection: mat4x4; +struct View { + position: vec4; + projection: mat4x4; }; [[block]] @@ -10,24 +10,26 @@ struct Time { }; [[group(1), binding(0)]] -var uniforms: Uniforms; +var view: View; [[group(2), binding(0)]] var time: Time; struct VertexInput { [[location(0)]] position: vec3; - [[location(1)]] texture_coordinates: vec3; + [[location(1)]] texture_coordinates: vec2; [[location(2)]] normal: vec3; [[location(3)]] highlighted: i32; + [[location(4)]] texture_id: i32; }; struct VertexOutput { [[builtin(position)]] clip_position: vec4; - [[location(0)]] texture_coordinates: vec3; + [[location(0)]] texture_coordinates: vec2; [[location(1)]] world_normal: vec3; [[location(2)]] world_position: vec3; [[location(3)]] highlighted: i32; + [[location(4)]] texture_id: i32; }; let pi: f32 = 3.14159265359; @@ -37,17 +39,19 @@ fn main(model: VertexInput) -> VertexOutput { var out: VertexOutput; out.world_normal = model.normal; - if (model.texture_coordinates.z == 8.0) { + if (model.texture_id == 8) { // water let offset = (sin(time.time * 0.5 + model.position.x) * cos(time.time * 0.9 + model.position.y) + 2.5) / 10.0; out.world_position = vec3(model.position.x, model.position.y - offset, model.position.z); - out.texture_coordinates = vec3(model.texture_coordinates.xy + (time.time / 10.0), 8.0 + (time.time * 10.0) % 32.0); + out.texture_coordinates = model.texture_coordinates + (time.time / 10.0); + out.texture_id = i32(8.0 + (time.time * 10.0) % 32.0); } else { out.world_position = model.position; out.texture_coordinates = model.texture_coordinates; + out.texture_id = model.texture_id; } - out.clip_position = uniforms.view_projection * vec4(out.world_position, 1.0); + out.clip_position = view.projection * vec4(out.world_position, 1.0); out.highlighted = model.highlighted; return out; } @@ -60,8 +64,8 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4 { let object_color: vec4 = textureSample( texture_array, texture_sampler, - in.texture_coordinates.xy, - i32(round(in.texture_coordinates.z)) + in.texture_coordinates, + in.texture_id ); let light_position = vec3(-100.0, 500.0, -200.0); @@ -71,7 +75,7 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4 { let ambient_color = light_color * ambient_strength; let light_direction = normalize(light_position - in.world_position); - let view_direction = normalize(uniforms.view_position.xyz - in.world_position); + let view_direction = normalize(view.position.xyz - in.world_position); let half_direction = normalize(view_direction + light_direction); let diffuse_strength = max(dot(in.world_normal, light_direction), 0.0); diff --git a/src/state/hud_state.rs b/src/state/hud_state.rs index c46d07d..b201cf9 100644 --- a/src/state/hud_state.rs +++ b/src/state/hud_state.rs @@ -1,40 +1,36 @@ use std::time::{Duration, Instant}; use cgmath::Vector3; -use wgpu::{ - util::{BufferInitDescriptor, DeviceExt}, - CommandEncoder, SwapChainTexture, -}; +use wgpu::{BufferUsage, CommandEncoder, SwapChainTexture}; use crate::{ + geometry::{Geometry, GeometryBuffers}, render_context::RenderContext, + state::PRIMITIVE_STATE, text_renderer::{self, TextRenderer}, texture::Texture, - vertex::Vertex, + vertex::{HudVertex, Vertex}, }; +// TODO update aspect ratio when resizing const UI_SCALE_X: f32 = 0.0045; const UI_SCALE_Y: f32 = 0.008; pub struct HudState { texture_bind_group: wgpu::BindGroup, render_pipeline: wgpu::RenderPipeline, - crosshair_vertex_buffer: wgpu::Buffer, - crosshair_index_buffer: wgpu::Buffer, + hud_geometry_buffers: GeometryBuffers, text_renderer: TextRenderer, - fps_vertex_buffer: wgpu::Buffer, - fps_index_buffer: wgpu::Buffer, - fps_index_count: usize, + fps_geometry_buffers: GeometryBuffers, fps_instant: Instant, fps_frames: u32, fps_elapsed: Duration, - coordinates_vertex_buffer: wgpu::Buffer, - coordinates_index_buffer: wgpu::Buffer, - coordinates_index_count: usize, + coordinates_geometry_buffers: GeometryBuffers, coordinates_last: Vector3, + pub hotbar_cursor_position: i32, } impl HudState { @@ -44,48 +40,37 @@ impl HudState { let render_pipeline = Self::create_render_pipeline(render_context, &[&texture_bind_group_layout]); - let crosshair_vertex_buffer = - render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: Some("HUD crosshair vertex buffer"), - contents: bytemuck::cast_slice(&CROSSHAIR_VERTICES), - usage: wgpu::BufferUsage::VERTEX, - }); - - let crosshair_index_buffer = - render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: Some("HUD crosshair index buffer"), - contents: bytemuck::cast_slice(CROSSHAIR_INDICES), - usage: wgpu::BufferUsage::INDEX, - }); + // HUD buffers + let hud_geometry = Geometry { + vertices: HUD_VERTICES.to_vec(), + indices: HUD_INDICES.to_vec(), + }; + let hud_geometry_buffers = + GeometryBuffers::from_geometry(render_context, &hud_geometry, BufferUsage::COPY_DST); + // Text buffers let text_renderer = TextRenderer::new(render_context).unwrap(); - let (fps_vertex_buffer, fps_index_buffer, fps_index_count) = + let fps_geometry_buffers = text_renderer.string_to_buffers(&render_context, -0.98, 0.97, ""); - let (coordinates_vertex_buffer, coordinates_index_buffer, coordinates_index_count) = + let coordinates_geometry_buffers = text_renderer.string_to_buffers(&render_context, -0.98, 0.97 - text_renderer::DY, ""); Self { texture_bind_group, render_pipeline, - crosshair_vertex_buffer, - crosshair_index_buffer, text_renderer, - fps_vertex_buffer, - fps_index_buffer, - fps_index_count, + hud_geometry_buffers, + + fps_geometry_buffers, fps_instant: Instant::now(), fps_frames: 0, fps_elapsed: Duration::from_secs(0), - coordinates_vertex_buffer, - coordinates_index_buffer, - coordinates_index_count, + coordinates_geometry_buffers, coordinates_last: Vector3::new(0.0, 0.0, 0.0), + + hotbar_cursor_position: 0, } } @@ -100,12 +85,9 @@ impl HudState { let fps = 1.0 / frametime.as_secs_f32(); let string = format!("{:<5.0} fps", fps); - let (vertices, indices, index_count) = + self.fps_geometry_buffers = self.text_renderer .string_to_buffers(render_context, -0.98, 0.97, &string); - self.fps_vertex_buffer = vertices; - self.fps_index_buffer = indices; - self.fps_index_count = index_count; self.fps_elapsed = Duration::from_secs(0); self.fps_frames = 0; @@ -113,15 +95,12 @@ impl HudState { if position != &self.coordinates_last { let string = format!("({:.1},{:.1},{:.1})", position.x, position.y, position.z,); - let (vertices, indices, index_count) = self.text_renderer.string_to_buffers( + self.coordinates_geometry_buffers = self.text_renderer.string_to_buffers( render_context, -0.98, 0.97 - text_renderer::DY * 1.3, &string, ); - self.coordinates_vertex_buffer = vertices; - self.coordinates_index_buffer = indices; - self.coordinates_index_count = index_count; } } @@ -131,7 +110,6 @@ impl HudState { render_encoder: &mut CommandEncoder, ) -> anyhow::Result { let mut render_pass = render_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("render_pass"), color_attachments: &[wgpu::RenderPassColorAttachment { view: &frame.view, resolve_target: None, @@ -140,33 +118,58 @@ impl HudState { store: true, }, }], - depth_stencil_attachment: None, + ..Default::default() }); render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_vertex_buffer(0, self.crosshair_vertex_buffer.slice(..)); - render_pass.set_index_buffer( - self.crosshair_index_buffer.slice(..), - wgpu::IndexFormat::Uint16, - ); + // Render the HUD elements + self.hud_geometry_buffers.set_buffers(&mut render_pass); render_pass.set_bind_group(0, &self.texture_bind_group, &[]); - render_pass.draw_indexed(0..CROSSHAIR_INDICES.len() as u32, 0, 0..1); + self.hud_geometry_buffers.draw_indexed(&mut render_pass); + render_pass.draw_indexed(0..self.hud_geometry_buffers.index_count as u32, 0, 0..1); - render_pass.set_vertex_buffer(0, self.fps_vertex_buffer.slice(..)); - render_pass.set_index_buffer(self.fps_index_buffer.slice(..), wgpu::IndexFormat::Uint16); + // Render the FPS text + self.fps_geometry_buffers.set_buffers(&mut render_pass); render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); - render_pass.draw_indexed(0..self.fps_index_count as u32, 0, 0..1); + self.fps_geometry_buffers.draw_indexed(&mut render_pass); - render_pass.set_vertex_buffer(0, self.coordinates_vertex_buffer.slice(..)); - render_pass.set_index_buffer( - self.coordinates_index_buffer.slice(..), - wgpu::IndexFormat::Uint16, + // Render the coordinates text + self.coordinates_geometry_buffers + .set_buffers(&mut render_pass); + render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); + self.coordinates_geometry_buffers + .draw_indexed(&mut render_pass); + + Ok(HUD_INDICES.len() / 3) + } + + pub fn redraw_hotbar_cursor(&self, render_context: &RenderContext) { + let x = (-92 + 20 * self.hotbar_cursor_position) as f32; + + #[rustfmt::skip] + let vertices = [ + HudVertex { position: [UI_SCALE_X * (x ), -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * (x + 24.0), -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 24.0 / 256.0, 22.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * (x + 24.0), -1.0 + UI_SCALE_Y * -1.0], texture_coordinates: [ 24.0 / 256.0, 46.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * (x ), -1.0 + UI_SCALE_Y * -1.0], texture_coordinates: [ 0.0 / 256.0, 46.0 / 256.0] }, + ]; + + render_context.queue.write_buffer( + &self.hud_geometry_buffers.vertices, + HudVertex::descriptor().array_stride * 8, + bytemuck::cast_slice(&vertices), ); - render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); - render_pass.draw_indexed(0..self.coordinates_index_count as u32, 0, 0..1); + } - Ok(CROSSHAIR_INDICES.len() / 3) + pub fn set_hotbar_cursor(&mut self, render_context: &RenderContext, i: i32) { + self.hotbar_cursor_position = i; + self.redraw_hotbar_cursor(render_context); + } + + pub fn move_hotbar_cursor(&mut self, render_context: &RenderContext, delta: i32) { + self.hotbar_cursor_position = (self.hotbar_cursor_position + delta).rem_euclid(9); + self.redraw_hotbar_cursor(render_context); } fn create_textures(render_context: &RenderContext) -> (wgpu::BindGroupLayout, wgpu::BindGroup) { @@ -262,7 +265,7 @@ impl HudState { vertex: wgpu::VertexState { module, entry_point: "main", - buffers: &[Vertex::desc()], + buffers: &[HudVertex::descriptor()], }, fragment: Some(wgpu::FragmentState { module, @@ -273,83 +276,45 @@ impl HudState { write_mask: wgpu::ColorWrite::ALL, }], }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - clamp_depth: false, - conservative: false, - }, + primitive: PRIMITIVE_STATE, depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, + multisample: Default::default(), }) } } -pub const CROSSHAIR_VERTICES: &[Vertex] = &[ +#[rustfmt::skip] +pub const HUD_VERTICES: [HudVertex; 12] = [ // Crosshair - 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, - }, + HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * 8.0], texture_coordinates: [240.0 / 256.0, 0.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0], texture_coordinates: [256.0 / 256.0, 0.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * -8.0], texture_coordinates: [256.0 / 256.0, 16.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * -8.0], texture_coordinates: [240.0 / 256.0, 16.0 / 256.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, - }, + HudVertex { position: [UI_SCALE_X * -91.0, -1.0 + UI_SCALE_Y * 22.0], texture_coordinates: [ 0.0 / 256.0, 0.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * 91.0, -1.0 + UI_SCALE_Y * 22.0], texture_coordinates: [182.0 / 256.0, 0.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * 91.0, -1.0 ], texture_coordinates: [182.0 / 256.0, 22.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * -91.0, -1.0 ], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0] }, + + // Hotbar cursor + HudVertex { position: [UI_SCALE_X * -92.0, -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * -68.0, -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 24.0 / 256.0, 22.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * -68.0, -1.0 + UI_SCALE_Y * -1.0], texture_coordinates: [ 24.0 / 256.0, 46.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * -92.0, -1.0 + UI_SCALE_Y * -1.0], texture_coordinates: [ 0.0 / 256.0, 46.0 / 256.0] }, ]; #[rustfmt::skip] -pub const CROSSHAIR_INDICES: &[u16] = &[ +pub const HUD_INDICES: [u16; 18] = [ + // Crosshair 1, 0, 3, 1, 3, 2, + // Hotbar 5, 4, 7, 5, 7, 6, + + // Hotbar cursor + 9, 8, 11, + 9, 11, 10, ]; diff --git a/src/state/mod.rs b/src/state/mod.rs index 374e731..936ef0f 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -6,7 +6,7 @@ use std::time::Duration; use cgmath::EuclideanSpace; use winit::{ dpi::PhysicalSize, - event::{DeviceEvent, ElementState, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{DeviceEvent, ElementState, MouseScrollDelta, VirtualKeyCode, WindowEvent}, window::Window, }; @@ -15,6 +15,16 @@ use world_state::WorldState; use crate::render_context::RenderContext; +pub const PRIMITIVE_STATE: wgpu::PrimitiveState = wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + clamp_depth: false, + conservative: false, +}; + pub struct State { pub window_size: PhysicalSize, render_context: RenderContext, @@ -122,12 +132,23 @@ impl State { ); } - fn input_keyboard(&mut self, key_code: &VirtualKeyCode, state: &ElementState) { - match key_code { - VirtualKeyCode::F1 if state == &ElementState::Pressed => { - self.world_state.toggle_wireframe(&self.render_context) + fn input_keyboard(&mut self, key_code: VirtualKeyCode, state: ElementState) { + if state == ElementState::Pressed { + match key_code { + VirtualKeyCode::F1 => self.world_state.toggle_wireframe(&self.render_context), + VirtualKeyCode::Key1 => self.hud_state.set_hotbar_cursor(&self.render_context, 0), + VirtualKeyCode::Key2 => self.hud_state.set_hotbar_cursor(&self.render_context, 1), + VirtualKeyCode::Key3 => self.hud_state.set_hotbar_cursor(&self.render_context, 2), + VirtualKeyCode::Key4 => self.hud_state.set_hotbar_cursor(&self.render_context, 3), + VirtualKeyCode::Key5 => self.hud_state.set_hotbar_cursor(&self.render_context, 4), + VirtualKeyCode::Key6 => self.hud_state.set_hotbar_cursor(&self.render_context, 5), + VirtualKeyCode::Key7 => self.hud_state.set_hotbar_cursor(&self.render_context, 6), + VirtualKeyCode::Key8 => self.hud_state.set_hotbar_cursor(&self.render_context, 7), + VirtualKeyCode::Key9 => self.hud_state.set_hotbar_cursor(&self.render_context, 8), + _ => self.world_state.input_keyboard(key_code, state), } - _ => self.world_state.input_keyboard(key_code, state), + } else { + self.world_state.input_keyboard(key_code, state) } } @@ -139,15 +160,9 @@ impl State { pub fn window_event(&mut self, event: &WindowEvent) { match event { - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(key), - state, - .. - }, - .. - } => self.input_keyboard(key, state), + WindowEvent::KeyboardInput { input, .. } if input.virtual_keycode.is_some() => { + self.input_keyboard(input.virtual_keycode.unwrap(), input.state) + } WindowEvent::MouseInput { button, @@ -157,14 +172,20 @@ impl State { .world_state .input_mouse_button(button, &self.render_context), + WindowEvent::MouseWheel { + delta: MouseScrollDelta::LineDelta(_, delta), + .. + } => self + .hud_state + .move_hotbar_cursor(&self.render_context, -*delta as i32), + _ => (), } } pub fn device_event(&mut self, event: &DeviceEvent) { - match event { - DeviceEvent::MouseMotion { delta: (dx, dy) } => self.input_mouse(*dx, *dy), - _ => (), + if let DeviceEvent::MouseMotion { delta } = event { + self.input_mouse(delta.0, delta.1) } } diff --git a/src/state/world_state.rs b/src/state/world_state.rs index ce3c6a8..cbb0e55 100644 --- a/src/state/world_state.rs +++ b/src/state/world_state.rs @@ -4,7 +4,7 @@ use ahash::AHashMap; use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3}; use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, - CommandEncoder, SwapChainTexture, + BufferUsage, CommandEncoder, SwapChainTexture, }; use winit::{ dpi::PhysicalSize, @@ -14,19 +14,20 @@ use winit::{ use crate::{ camera::{Camera, Projection}, chunk::{Block, BlockType, CHUNK_SIZE}, + geometry::GeometryBuffers, render_context::RenderContext, texture::{Texture, TextureManager}, time::Time, - uniforms::Uniforms, - vertex::Vertex, + vertex::{BlockVertex, Vertex}, + view::View, world::World, }; pub struct WorldState { pub render_pipeline: wgpu::RenderPipeline, - pub uniforms: Uniforms, - pub uniform_buffer: wgpu::Buffer, - pub uniform_bind_group: wgpu::BindGroup, + pub view: View, + pub view_buffer: wgpu::Buffer, + pub view_bind_group: wgpu::BindGroup, pub texture_manager: TextureManager, pub camera: Camera, pub projection: Projection, @@ -34,7 +35,7 @@ pub struct WorldState { pub time_bind_group: wgpu::BindGroup, pub world: World, - pub chunk_buffers: AHashMap, (wgpu::Buffer, wgpu::Buffer, usize)>, + pub chunk_buffers: AHashMap, GeometryBuffers>, time: Time, time_buffer: wgpu::Buffer, wireframe: bool, @@ -77,28 +78,23 @@ impl WorldState { (camera, projection) } - fn create_uniforms( + fn create_view( camera: &Camera, projection: &Projection, render_context: &RenderContext, - ) -> ( - Uniforms, - wgpu::Buffer, - wgpu::BindGroupLayout, - wgpu::BindGroup, - ) { - let mut uniforms = Uniforms::new(); - uniforms.update_view_projection(camera, projection); + ) -> (View, wgpu::Buffer, wgpu::BindGroupLayout, wgpu::BindGroup) { + let mut view = View::new(); + view.update_view_projection(camera, projection); - let uniform_buffer = render_context + let view_buffer = render_context .device .create_buffer_init(&BufferInitDescriptor { - label: Some("uniform_buffer"), - contents: bytemuck::cast_slice(&[uniforms]), + label: Some("view_buffer"), + contents: bytemuck::cast_slice(&[view]), usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, }); - let uniform_bind_group_layout = + let view_bind_group_layout = render_context .device .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -112,27 +108,21 @@ impl WorldState { }, count: None, }], - label: Some("uniform_bind_group_layout"), + label: Some("view_bind_group_layout"), }); - let uniform_bind_group = - render_context - .device - .create_bind_group(&wgpu::BindGroupDescriptor { - layout: &uniform_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: uniform_buffer.as_entire_binding(), - }], - label: Some("uniform_bind_group"), - }); + let view_bind_group = render_context + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: &view_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: view_buffer.as_entire_binding(), + }], + label: Some("view_bind_group"), + }); - ( - uniforms, - uniform_buffer, - uniform_bind_group_layout, - uniform_bind_group, - ) + (view, view_buffer, view_bind_group_layout, view_bind_group) } fn create_time( @@ -193,7 +183,7 @@ impl WorldState { vertex: wgpu::VertexState { module: &shader, entry_point: "main", - buffers: &[Vertex::desc()], + buffers: &[BlockVertex::descriptor()], }, fragment: Some(wgpu::FragmentState { module: &shader, @@ -232,52 +222,35 @@ impl WorldState { 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( - chunk_position, - ( - render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: None, - contents: &bytemuck::cast_slice(&chunk_vertices), - usage: wgpu::BufferUsage::VERTEX, - }), - render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: None, - contents: &bytemuck::cast_slice(&chunk_indices), - usage: wgpu::BufferUsage::INDEX, - }), - chunk_indices.len(), - ), + for (chunk_position, chunk_geometry) in world_geometry { + let buffers = GeometryBuffers::from_geometry( + render_context, + &chunk_geometry, + BufferUsage::empty(), ); + self.chunk_buffers.insert(chunk_position, buffers); } let elapsed = instant.elapsed(); println!("World update took {:?}", elapsed); } - pub fn load_npc_geometry( - &mut self, - render_context: &RenderContext, - ) { - self.world.npc.vertex_buffer = Some(render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: None, - contents: &bytemuck::cast_slice(&self.world.npc.vertices), - usage: wgpu::BufferUsage::VERTEX, - })); + pub fn load_npc_geometry(&mut self, render_context: &RenderContext) { + self.world.npc.vertex_buffer = Some(render_context.device.create_buffer_init( + &BufferInitDescriptor { + label: None, + contents: &bytemuck::cast_slice(&self.world.npc.vertices), + usage: wgpu::BufferUsage::VERTEX, + }, + )); - self.world.npc.index_buffer = Some(render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: None, - contents: &bytemuck::cast_slice(&self.world.npc.indices), - usage: wgpu::BufferUsage::INDEX, - })); + self.world.npc.index_buffer = Some(render_context.device.create_buffer_init( + &BufferInitDescriptor { + label: None, + contents: &bytemuck::cast_slice(&self.world.npc.indices), + usage: wgpu::BufferUsage::INDEX, + }, + )); } pub fn update_chunk_geometry( @@ -287,31 +260,14 @@ impl WorldState { ) { 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( + let geometry = chunk.to_geometry( offset, World::highlighted_for_chunk(self.highlighted, chunk_position).as_ref(), ); - self.chunk_buffers.insert( - chunk_position, - ( - render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: None, - contents: &bytemuck::cast_slice(&vertices), - usage: wgpu::BufferUsage::VERTEX, - }), - render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: None, - contents: &bytemuck::cast_slice(&indices), - usage: wgpu::BufferUsage::INDEX, - }), - indices.len(), - ), - ); + let buffers = + GeometryBuffers::from_geometry(render_context, &geometry, BufferUsage::empty()); + self.chunk_buffers.insert(chunk_position, buffers); } pub fn toggle_wireframe(&mut self, render_context: &RenderContext) { @@ -331,8 +287,8 @@ impl WorldState { let (camera, projection) = Self::create_camera(render_context); - let (uniforms, uniform_buffer, world_uniform_layout, uniform_bind_group) = - Self::create_uniforms(&camera, &projection, render_context); + let (view, view_buffer, view_bind_group_layout, view_bind_group) = + Self::create_view(&camera, &projection, render_context); let (time, time_buffer, time_layout, time_bind_group) = Self::create_time(render_context); @@ -352,7 +308,7 @@ impl WorldState { push_constant_ranges: &[], bind_group_layouts: &[ &texture_manager.bind_group_layout, - &world_uniform_layout, + &view_bind_group_layout, &time_layout, ], }); @@ -364,9 +320,9 @@ impl WorldState { let mut world_state = Self { render_pipeline, - uniforms, - uniform_buffer, - uniform_bind_group, + view, + view_buffer, + view_bind_group, texture_manager, camera, projection, @@ -430,23 +386,22 @@ impl WorldState { let tm = &self.texture_manager; render_pass.set_bind_group(0, tm.bind_group.as_ref().unwrap(), &[]); - render_pass.set_bind_group(1, &self.uniform_bind_group, &[]); + render_pass.set_bind_group(1, &self.view_bind_group, &[]); render_pass.set_bind_group(2, &self.time_bind_group, &[]); let camera_pos = self.camera.position.to_vec(); let camera_pos = Vector2::new(camera_pos.x, camera_pos.z); - for (position, (chunk_vertices, chunk_indices, index_count)) in &self.chunk_buffers { + for (position, buffers) in &self.chunk_buffers { let pos = (position * CHUNK_SIZE).cast().unwrap(); let pos = Vector2::new(pos.x, pos.z); if (pos - camera_pos).magnitude() > 300.0 { continue; } - 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); - triangle_count += index_count / 3; + buffers.set_buffers(&mut render_pass); + buffers.draw_indexed(&mut render_pass); + triangle_count += buffers.index_count / 3; } { @@ -454,8 +409,9 @@ impl WorldState { let index_buffer = self.world.npc.index_buffer.as_ref(); render_pass.set_vertex_buffer(0, vertex_buffer.unwrap().slice(..)); - render_pass.set_index_buffer(index_buffer.unwrap().slice(..), wgpu::IndexFormat::Uint32); - render_pass.draw_indexed(0..self.world.npc.indices.len() as u32 , 0, 0..1); + render_pass + .set_index_buffer(index_buffer.unwrap().slice(..), wgpu::IndexFormat::Uint32); + render_pass.draw_indexed(0..self.world.npc.indices.len() as u32, 0, 0..1); } triangle_count @@ -504,12 +460,14 @@ impl WorldState { let camera = &self.camera; let world = &mut self.world; - if let Some((pos, axis)) = world.raycast(camera.position.to_vec(), camera.direction()) { + if let Some((pos, face_normal)) = + world.raycast(camera.position.to_vec(), 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); } else if button == &MouseButton::Right { - let new_pos = pos.cast().unwrap() - axis; + let new_pos = pos.cast().unwrap() + face_normal; world.set_block( new_pos.x as isize, @@ -525,8 +483,8 @@ impl WorldState { } } - pub fn input_keyboard(&mut self, key_code: &VirtualKeyCode, state: &ElementState) { - let pressed = state == &ElementState::Pressed; + pub fn input_keyboard(&mut self, key_code: VirtualKeyCode, state: ElementState) { + let pressed = state == ElementState::Pressed; match key_code { VirtualKeyCode::W => self.forward_pressed = pressed, VirtualKeyCode::S => self.backward_pressed = pressed, @@ -545,13 +503,9 @@ impl WorldState { } } VirtualKeyCode::LShift if self.creative => { - self.up_speed = if pressed { - -1.0 - } else { - 0.0 - } + self.up_speed = if pressed { -1.0 } else { 0.0 } } - VirtualKeyCode::LControl => self.sprinting = state == &ElementState::Pressed, + VirtualKeyCode::LControl => self.sprinting = state == ElementState::Pressed, _ => (), } } @@ -609,13 +563,11 @@ impl WorldState { self.update_position(dt); self.update_aim(render_context); - self.uniforms + self.view .update_view_projection(&self.camera, &self.projection); - render_context.queue.write_buffer( - &self.uniform_buffer, - 0, - bytemuck::cast_slice(&[self.uniforms]), - ); + render_context + .queue + .write_buffer(&self.view_buffer, 0, bytemuck::cast_slice(&[self.view])); self.time.time += dt.as_secs_f32(); render_context.queue.write_buffer( diff --git a/src/text_renderer.rs b/src/text_renderer.rs index 8cb5a08..524fe5d 100644 --- a/src/text_renderer.rs +++ b/src/text_renderer.rs @@ -1,8 +1,11 @@ use std::convert::TryInto; -use wgpu::util::{BufferInitDescriptor, DeviceExt}; - -use crate::{render_context::RenderContext, texture::Texture, vertex::Vertex}; +use crate::{ + geometry::{Geometry, GeometryBuffers}, + render_context::RenderContext, + texture::Texture, + vertex::HudVertex, +}; pub const DX: f32 = 20.0 / 640.0; pub const DY: f32 = 20.0 / 360.0; @@ -100,16 +103,16 @@ impl TextRenderer { y: f32, c: u8, index_offset: u16, - ) -> ([Vertex; 4], [u16; 6]) { + ) -> ([HudVertex; 4], [u16; 6]) { let (tx, ty) = Self::char_uv(c); let s = 1.0 / 16.0; #[rustfmt::skip] let vertices = [ - Vertex { position: [x, y, 0.0], texture_coordinates: [tx, ty, 0.0], ..Default::default() }, - Vertex { position: [x + DX, y, 0.0], texture_coordinates: [tx + s, ty, 0.0], ..Default::default() }, - Vertex { position: [x + DX, y - DY, 0.0], texture_coordinates: [tx + s, ty + s, 0.0], ..Default::default() }, - Vertex { position: [x, y - DY, 0.0], texture_coordinates: [tx, ty + s, 0.0], ..Default::default() }, + HudVertex { position: [x, y ], texture_coordinates: [tx, ty ] }, + HudVertex { position: [x + DX, y ], texture_coordinates: [tx + s, ty ] }, + HudVertex { position: [x + DX, y - DY], texture_coordinates: [tx + s, ty + s] }, + HudVertex { position: [x, y - DY], texture_coordinates: [tx, ty + s] }, ]; #[rustfmt::skip] @@ -121,7 +124,7 @@ impl TextRenderer { (vertices, indices) } - pub fn string_geometry(&self, mut x: f32, mut y: f32, string: &str) -> (Vec, Vec) { + pub fn string_geometry(&self, mut x: f32, mut y: f32, string: &str) -> Geometry { let mut vertices = Vec::new(); let mut indices = Vec::new(); @@ -141,7 +144,7 @@ impl TextRenderer { } } - (vertices, indices) + Geometry::new(vertices, indices) } pub fn string_to_buffers( @@ -150,25 +153,8 @@ impl TextRenderer { x: f32, y: f32, string: &str, - ) -> (wgpu::Buffer, wgpu::Buffer, usize) { - let (vertices, indices) = self.string_geometry(x, y, string); - - let vertex_buffer = render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: Some("font renderer"), - contents: bytemuck::cast_slice(&vertices), - usage: wgpu::BufferUsage::VERTEX, - }); - - let index_buffer = render_context - .device - .create_buffer_init(&BufferInitDescriptor { - label: Some("font renderer"), - contents: bytemuck::cast_slice(&indices), - usage: wgpu::BufferUsage::INDEX, - }); - - (vertex_buffer, index_buffer, indices.len()) + ) -> GeometryBuffers { + let geometry = self.string_geometry(x, y, string); + GeometryBuffers::from_geometry(render_context, &geometry, wgpu::BufferUsage::empty()) } } diff --git a/src/vertex.rs b/src/vertex.rs index d8736be..622dc46 100644 --- a/src/vertex.rs +++ b/src/vertex.rs @@ -1,41 +1,89 @@ use std::mem::size_of; +use wgpu::VertexAttribute; + +pub trait Vertex { + fn descriptor() -> wgpu::VertexBufferLayout<'static>; +} + #[repr(C)] #[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Vertex { +pub struct PlainVertex { pub position: [f32; 3], - pub texture_coordinates: [f32; 3], + pub texture_coordinates: [f32; 2], pub normal: [f32; 3], - pub highlighted: i32, } -impl Vertex { - pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { +const PLAIN_VERTEX_ATTRIBUTES: &[VertexAttribute] = &wgpu::vertex_attr_array![ + 0 => Float32x3, + 1 => Float32x2, + 2 => Float32x3, +]; + +impl Vertex for PlainVertex { + fn descriptor() -> wgpu::VertexBufferLayout<'static> { wgpu::VertexBufferLayout { - array_stride: size_of::() as wgpu::BufferAddress, + array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: 12, - shader_location: 1, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: 24, - shader_location: 2, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: 36, - shader_location: 3, - format: wgpu::VertexFormat::Sint32, - }, - ], + attributes: PLAIN_VERTEX_ATTRIBUTES, + } + } +} + +/// Vertex used to represent HUD vertices. +/// +/// A vertex with a 2D position and no normal, for representing UI elements. +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] +pub struct HudVertex { + pub position: [f32; 2], + pub texture_coordinates: [f32; 2], +} + +const HUD_VERTEX_ATTRIBUTES: &[VertexAttribute] = &wgpu::vertex_attr_array![ + 0 => Float32x2, + 1 => Float32x2, +]; + +impl Vertex for HudVertex { + fn descriptor() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: HUD_VERTEX_ATTRIBUTES, + } + } +} + +/// Vertex used to represent block vertices. +/// +/// Aside from the usual vertex position, texture coordinates and normal, this "vertex" also +/// contains whether the block is highlighted (i.e. the player is pointing at the block) and its +/// texture index (to address the texture arrays) +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] +pub struct BlockVertex { + pub position: [f32; 3], + pub texture_coordinates: [f32; 2], + pub normal: [f32; 3], + pub highlighted: i32, + pub texture_id: i32, +} + +const BLOCK_VERTEX_ATTRIBUTES: &[VertexAttribute] = &wgpu::vertex_attr_array![ + 0 => Float32x3, + 1 => Float32x2, + 2 => Float32x3, + 3 => Sint32, + 4 => Sint32, +]; + +impl Vertex for BlockVertex { + fn descriptor() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: BLOCK_VERTEX_ATTRIBUTES, } } } diff --git a/src/uniforms.rs b/src/view.rs similarity index 94% rename from src/uniforms.rs rename to src/view.rs index 07cdcc5..1593dce 100644 --- a/src/uniforms.rs +++ b/src/view.rs @@ -4,12 +4,12 @@ use crate::camera::{Camera, Projection}; #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Uniforms { +pub struct View { view_position: [f32; 4], view_projection: [[f32; 4]; 4], } -impl Uniforms { +impl View { pub fn new() -> Self { Self { view_position: [0.0; 4], diff --git a/src/world.rs b/src/world.rs index d12b4ae..483a59e 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,14 +1,15 @@ use crate::{ chunk::{Block, Chunk, CHUNK_SIZE}, - vertex::Vertex, + geometry::Geometry, npc::Npc, + vertex::BlockVertex, }; use cgmath::{InnerSpace, Vector3}; use rayon::prelude::*; pub struct World { pub chunks: Vec>>, - pub npc: Npc + pub npc: Npc, } const WORLD_SIZE: Vector3 = Vector3::new( @@ -67,7 +68,7 @@ impl World { pub fn to_geometry( &self, highlighted: Option<(Vector3, Vector3)>, - ) -> Vec<(Vector3, Vec, Vec)> { + ) -> Vec<(Vector3, Geometry)> { let instant = std::time::Instant::now(); let chunks = &self.chunks; @@ -75,17 +76,17 @@ impl World { .par_iter() .enumerate() .flat_map(|(y, chunks_y)| { - let mut geometry = Vec::new(); + 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 (vertices, indices) = chunk.to_geometry(offset, h.as_ref()); - geometry.push((Vector3::new(x, y, z), vertices, indices)); + let geometry = chunk.to_geometry(offset, h.as_ref()); + chunk_geometry.push((Vector3::new(x, y, z), geometry)); } } - geometry + chunk_geometry }) .collect(); @@ -182,15 +183,15 @@ impl World { if lengths.x < lengths.y && lengths.x < lengths.z { lengths.x += scale.x; position.x += step.x; - face = Vector3::unit_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; + 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; + face = Vector3::unit_z() * -step.z; } else { return None; }