Merge branch 'main' into return_of_the_crab
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Sijmen 2021-06-02 16:37:56 +02:00
commit 9e52359112
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
18 changed files with 664 additions and 610 deletions

9
.drone.yml Normal file
View file

@ -0,0 +1,9 @@
kind: pipeline
type: docker
name: frontend
steps:
- name: dependencies
image: rust:slim
commands:
- cargo doc

38
Cargo.lock generated
View file

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

View file

@ -1,7 +1,6 @@
[package]
name = "rustwgpu"
name = "minecrab"
version = "0.1.0"
authors = ["Vijfhoek <me@vijf.life>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -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<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();
) -> Vec<Quad> {
let mut quads: Vec<Quad> = 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<i32>, Quad, Vector3<i32>, FaceFlags)>,
) -> (Vec<Vertex>, Vec<u16>) {
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<Quad>) -> Geometry<BlockVertex> {
let mut geometry: Geometry<BlockVertex> = 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<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 {
) -> Geometry<BlockVertex> {
let quads: Vec<Quad> = (0..CHUNK_SIZE)
.into_par_iter()
.flat_map(|y| {
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);
}
self.layer_to_quads(y, offset, culled, &mut queue, highlighted)
})
.collect();
Self::quads_to_geometry(quads)
}

View file

@ -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<i32>,
texture_indices: (usize, usize, usize, usize, usize, usize),
highlighted: Vector3<i32>,
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;
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)
}

75
src/geometry.rs Normal file
View file

@ -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<T: Vertex> {
pub vertices: Vec<T>,
pub indices: Vec<u16>,
}
impl<T: Vertex> Geometry<T> {
pub fn new(vertices: Vec<T>, indices: Vec<u16>) -> 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<T: Vertex + bytemuck::Pod>(
render_context: &RenderContext,
geometry: &Geometry<T>,
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);
}
}

View file

@ -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,
);

View file

@ -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<f32>,
pub scale: Vector3<f32>,
pub rotation: Vector3<f32>,
pub vertices: Vec<Vertex>,
pub vertices: Vec<BlockVertex>,
pub indices: Vec<u32>,
pub vertex_buffer: Option<wgpu::Buffer>,
pub index_buffer: Option<wgpu::Buffer>,
}
impl Npc {
pub fn load() -> Self {
let position: Vector3<f32> = Vector3::new(0.0, 0.0, 0.0);
@ -26,27 +23,28 @@ impl Npc {
let (model, buffers, _) = gltf::import("assets/models/minecrab.glb").unwrap();
let mut indices: Vec<u32> = Vec::new();
let mut vertices: Vec<Vertex> = 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 {
for ((position, normal), texture_coordinates) in
pos_iter.zip(norm_iter).zip(tex_iter)
{
let current_vert = BlockVertex {
position,
texture_coordinates: [tex_x, tex_y, 0.0],
texture_coordinates,
normal,
highlighted: 0
highlighted: 0,
texture_id: 0,
};
vertices.push(current_vert);
@ -61,7 +59,7 @@ impl Npc {
indices,
vertices,
vertex_buffer: None,
index_buffer: None
index_buffer: None,
};
}
}

View file

@ -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<i32>,
pub dx: i32,
pub dz: i32,
pub highlighted_normal: Vector3<i32>,
pub visible_faces: FaceFlags,
pub block_type: Option<BlockType>,
}
impl Quad {
pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self {
Quad { x, y, w, h }
pub fn new(position: Vector3<i32>, 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<BlockVertex> {
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)
}
}

View file

@ -1,7 +1,6 @@
struct VertexInput {
[[location(0)]] position: vec3<f32>;
[[location(0)]] position: vec2<f32>;
[[location(1)]] texture_coordinates: vec2<f32>;
[[location(2)]] normal: vec3<f32>;
};
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<f32>(model.position, 1.0);
out.clip_position = vec4<f32>(model.position, 0.0, 1.0);
return out;
}

View file

@ -1,7 +1,7 @@
[[block]]
struct Uniforms {
view_position: vec4<f32>;
view_projection: mat4x4<f32>;
struct View {
position: vec4<f32>;
projection: mat4x4<f32>;
};
[[block]]
@ -10,24 +10,26 @@ struct Time {
};
[[group(1), binding(0)]]
var<uniform> uniforms: Uniforms;
var<uniform> view: View;
[[group(2), binding(0)]]
var<uniform> time: Time;
struct VertexInput {
[[location(0)]] position: vec3<f32>;
[[location(1)]] texture_coordinates: vec3<f32>;
[[location(1)]] texture_coordinates: vec2<f32>;
[[location(2)]] normal: vec3<f32>;
[[location(3)]] highlighted: i32;
[[location(4)]] texture_id: i32;
};
struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] texture_coordinates: vec3<f32>;
[[location(0)]] texture_coordinates: vec2<f32>;
[[location(1)]] world_normal: vec3<f32>;
[[location(2)]] world_position: vec3<f32>;
[[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<f32>(model.position.x, model.position.y - offset, model.position.z);
out.texture_coordinates = vec3<f32>(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<f32>(out.world_position, 1.0);
out.clip_position = view.projection * vec4<f32>(out.world_position, 1.0);
out.highlighted = model.highlighted;
return out;
}
@ -60,8 +64,8 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let object_color: vec4<f32> = 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<f32>(-100.0, 500.0, -200.0);
@ -71,7 +75,7 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
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);

View file

@ -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<f32>,
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<usize> {
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,
];

View file

@ -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<u32>,
render_context: RenderContext,
@ -122,13 +132,24 @@ impl State {
);
}
fn input_keyboard(&mut self, key_code: &VirtualKeyCode, state: &ElementState) {
fn input_keyboard(&mut self, key_code: VirtualKeyCode, state: ElementState) {
if state == ElementState::Pressed {
match key_code {
VirtualKeyCode::F1 if state == &ElementState::Pressed => {
self.world_state.toggle_wireframe(&self.render_context)
}
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),
}
} else {
self.world_state.input_keyboard(key_code, state)
}
}
fn input_mouse(&mut self, dx: f64, dy: f64) {
@ -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)
}
}

View file

@ -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<Vector3<usize>, (wgpu::Buffer, wgpu::Buffer, usize)>,
pub chunk_buffers: AHashMap<Vector3<usize>, 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
let view_bind_group = render_context
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &uniform_bind_group_layout,
layout: &view_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
resource: view_buffer.as_entire_binding(),
}],
label: Some("uniform_bind_group"),
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 {
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 {
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(

View file

@ -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<Vertex>, Vec<u16>) {
pub fn string_geometry(&self, mut x: f32, mut y: f32, string: &str) -> Geometry<HudVertex> {
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())
}
}

View file

@ -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::<Vertex>() as wgpu::BufferAddress,
array_stride: size_of::<Self>() 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::<Self>() 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::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: BLOCK_VERTEX_ATTRIBUTES,
}
}
}

View file

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

View file

@ -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<Vec<Vec<Chunk>>>,
pub npc: Npc
pub npc: Npc,
}
const WORLD_SIZE: Vector3<usize> = Vector3::new(
@ -67,7 +68,7 @@ impl World {
pub fn to_geometry(
&self,
highlighted: Option<(Vector3<usize>, Vector3<i32>)>,
) -> Vec<(Vector3<usize>, Vec<Vertex>, Vec<u16>)> {
) -> Vec<(Vector3<usize>, Geometry<BlockVertex>)> {
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;
}