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", "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]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.3.7" version = "0.3.7"
@ -1574,25 +1593,6 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 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]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"

View file

@ -1,7 +1,6 @@
[package] [package]
name = "rustwgpu" name = "minecrab"
version = "0.1.0" version = "0.1.0"
authors = ["Vijfhoek <me@vijf.life>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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 ahash::{AHashMap, AHashSet};
use cgmath::{Vector3, Zero}; use cgmath::Vector3;
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder}; use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[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_TOP: FaceFlags = 8;
pub const FACE_BACK: FaceFlags = 16; pub const FACE_BACK: FaceFlags = 16;
pub const FACE_FRONT: FaceFlags = 32; 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)] #[derive(Debug, Clone, Copy)]
pub struct Block { pub struct Block {
@ -218,12 +221,13 @@ impl Chunk {
culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>, culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>,
queue: &mut VecDeque<(usize, usize)>, queue: &mut VecDeque<(usize, usize)>,
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>, highlighted: Option<&(Vector3<usize>, Vector3<i32>)>,
) -> Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)> { ) -> Vec<Quad> {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)> = let mut quads: Vec<Quad> = Vec::new();
Vec::new();
let mut visited = AHashSet::new(); let mut visited = AHashSet::new();
let hl = highlighted.map(|h| h.0); let hl = highlighted.map(|h| h.0);
while let Some((x, z)) = queue.pop_front() { while let Some((x, z)) = queue.pop_front() {
let position = offset + Vector3::new(x, y, z).cast().unwrap();
if visited.contains(&(x, z)) { if visited.contains(&(x, z)) {
continue; continue;
} }
@ -233,28 +237,19 @@ impl Chunk {
let mut quad_faces = visible_faces; let mut quad_faces = visible_faces;
if hl == Some(Vector3::new(x, y, z)) { if hl == Some(Vector3::new(x, y, z)) {
let quad = Quad::new(x as i32, z as i32, 1, 1); let mut quad = Quad::new(position, 1, 1);
quads.push(( quad.highlighted_normal = highlighted.unwrap().1;
block_type, quad.visible_faces = quad_faces;
y as i32, quad.block_type = Some(block_type);
offset, quads.push(quad);
quad,
highlighted.unwrap().1,
quad_faces,
));
continue; continue;
} }
if block_type == BlockType::Water { if block_type == BlockType::Water {
let quad = Quad::new(x as i32, z as i32, 1, 1); let mut quad = Quad::new(position, 1, 1);
quads.push(( quad.visible_faces = quad_faces;
block_type, quad.block_type = Some(block_type);
y as i32, quads.push(quad);
offset,
quad,
Vector3::zero(),
quad_faces,
));
continue; 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); let mut quad = Quad::new(position, (xmax - x) as i32, (zmax - z) as i32);
quads.push(( quad.visible_faces = quad_faces;
block_type, quad.block_type = Some(block_type);
y as i32, quads.push(quad);
offset,
quad,
Vector3::zero(),
quad_faces,
));
} }
} }
quads quads
} }
fn quads_to_geometry( fn quads_to_geometry(quads: Vec<Quad>) -> Geometry<BlockVertex> {
quads: Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)>, let mut geometry: Geometry<BlockVertex> = Default::default();
) -> (Vec<Vertex>, Vec<u16>) { for quad in quads {
let mut vertices = Vec::new(); geometry.append(&mut quad.to_geometry(geometry.vertices.len() as u16));
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);
} }
geometry
(vertices, indices)
} }
pub fn to_geometry( pub fn to_geometry(
&self, &self,
offset: Vector3<i32>, offset: Vector3<i32>,
highlighted: Option<&(Vector3<usize>, Vector3<i32>)>, highlighted: Option<&(Vector3<usize>, Vector3<i32>)>,
) -> (Vec<Vertex>, Vec<u16>) { ) -> Geometry<BlockVertex> {
let mut quads: Vec<(BlockType, i32, Vector3<i32>, Quad, Vector3<i32>, FaceFlags)> = let quads: Vec<Quad> = (0..CHUNK_SIZE)
Vec::new(); .into_par_iter()
.flat_map(|y| {
for y in 0..CHUNK_SIZE {
let (culled, mut queue) = self.cull_layer(y); let (culled, mut queue) = self.cull_layer(y);
let mut layer_quads = self.layer_to_quads(y, offset, culled, &mut queue, highlighted); self.layer_to_quads(y, offset, culled, &mut queue, highlighted)
quads.append(&mut layer_quads); })
} .collect();
Self::quads_to_geometry(quads) 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 aabb;
mod camera; mod camera;
mod chunk; mod chunk;
mod cube; mod geometry;
mod quad; mod quad;
mod render_context; mod render_context;
mod state; mod state;
mod text_renderer; mod text_renderer;
mod texture; mod texture;
mod time; mod time;
mod uniforms;
mod vertex; mod vertex;
mod view;
mod world; mod world;
mod npc; mod npc;
@ -106,7 +106,7 @@ fn main() {
let fps_min = 1_000_000 / frametime_max.as_micros(); let fps_min = 1_000_000 / frametime_max.as_micros();
println!( 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, 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 gltf;
extern crate wgpu; extern crate wgpu;
use cgmath::{Vector3}; use cgmath::Vector3;
use crate::{ use crate::vertex::BlockVertex;
vertex::Vertex,
};
pub struct Npc { pub struct Npc {
pub position: Vector3<f32>, pub position: Vector3<f32>,
pub scale: Vector3<f32>, pub scale: Vector3<f32>,
pub rotation: Vector3<f32>, pub rotation: Vector3<f32>,
pub vertices: Vec<Vertex>, pub vertices: Vec<BlockVertex>,
pub indices: Vec<u32>, pub indices: Vec<u32>,
pub vertex_buffer: Option<wgpu::Buffer>, pub vertex_buffer: Option<wgpu::Buffer>,
pub index_buffer: Option<wgpu::Buffer>, pub index_buffer: Option<wgpu::Buffer>,
} }
impl Npc { impl Npc {
pub fn load() -> Self { pub fn load() -> Self {
let position: Vector3<f32> = Vector3::new(0.0, 0.0, 0.0); 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 (model, buffers, _) = gltf::import("assets/models/minecrab.glb").unwrap();
let mut indices: Vec<u32> = Vec::new(); let mut indices = Vec::new();
let mut vertices: Vec<Vertex> = Vec::new(); let mut vertices = Vec::new();
for mesh in model.meshes() { for mesh in model.meshes() {
for primitive in mesh.primitives() { for primitive in mesh.primitives() {
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
indices = reader.read_indices().unwrap().into_u32().collect(); 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 pos_iter = reader.read_positions().unwrap();
let norm_iter = reader.read_normals().unwrap(); let norm_iter = reader.read_normals().unwrap();
let tex_iter = reader.read_tex_coords(0).unwrap().into_f32(); let tex_iter = reader.read_tex_coords(0).unwrap().into_f32();
for it in pos_iter.zip(norm_iter).zip(tex_iter) { for ((position, normal), texture_coordinates) in
let ((position, normal), [tex_x, tex_y]) = it; pos_iter.zip(norm_iter).zip(tex_iter)
{
let current_vert: Vertex = Vertex { let current_vert = BlockVertex {
position, position,
texture_coordinates: [tex_x, tex_y, 0.0], texture_coordinates,
normal, normal,
highlighted: 0 highlighted: 0,
texture_id: 0,
}; };
vertices.push(current_vert); vertices.push(current_vert);
@ -61,7 +59,7 @@ impl Npc {
indices, indices,
vertices, vertices,
vertex_buffer: None, 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)] #[derive(Debug)]
pub struct Quad { pub struct Quad {
pub x: i32, pub position: Vector3<i32>,
pub y: i32, pub dx: i32,
pub w: i32, pub dz: i32,
pub h: i32,
pub highlighted_normal: Vector3<i32>,
pub visible_faces: FaceFlags,
pub block_type: Option<BlockType>,
} }
impl Quad { impl Quad {
pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self { pub fn new(position: Vector3<i32>, dx: i32, dz: i32) -> Self {
Quad { x, y, w, h } 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 { struct VertexInput {
[[location(0)]] position: vec3<f32>; [[location(0)]] position: vec2<f32>;
[[location(1)]] texture_coordinates: vec2<f32>; [[location(1)]] texture_coordinates: vec2<f32>;
[[location(2)]] normal: vec3<f32>;
}; };
struct VertexOutput { struct VertexOutput {
@ -13,7 +12,7 @@ struct VertexOutput {
fn main(model: VertexInput) -> VertexOutput { fn main(model: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.texture_coordinates = model.texture_coordinates; 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; return out;
} }

View file

@ -1,7 +1,7 @@
[[block]] [[block]]
struct Uniforms { struct View {
view_position: vec4<f32>; position: vec4<f32>;
view_projection: mat4x4<f32>; projection: mat4x4<f32>;
}; };
[[block]] [[block]]
@ -10,24 +10,26 @@ struct Time {
}; };
[[group(1), binding(0)]] [[group(1), binding(0)]]
var<uniform> uniforms: Uniforms; var<uniform> view: View;
[[group(2), binding(0)]] [[group(2), binding(0)]]
var<uniform> time: Time; var<uniform> time: Time;
struct VertexInput { struct VertexInput {
[[location(0)]] position: vec3<f32>; [[location(0)]] position: vec3<f32>;
[[location(1)]] texture_coordinates: vec3<f32>; [[location(1)]] texture_coordinates: vec2<f32>;
[[location(2)]] normal: vec3<f32>; [[location(2)]] normal: vec3<f32>;
[[location(3)]] highlighted: i32; [[location(3)]] highlighted: i32;
[[location(4)]] texture_id: i32;
}; };
struct VertexOutput { struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>; [[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(1)]] world_normal: vec3<f32>;
[[location(2)]] world_position: vec3<f32>; [[location(2)]] world_position: vec3<f32>;
[[location(3)]] highlighted: i32; [[location(3)]] highlighted: i32;
[[location(4)]] texture_id: i32;
}; };
let pi: f32 = 3.14159265359; let pi: f32 = 3.14159265359;
@ -37,17 +39,19 @@ fn main(model: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.world_normal = model.normal; out.world_normal = model.normal;
if (model.texture_coordinates.z == 8.0) { if (model.texture_id == 8) {
// water // water
let offset = (sin(time.time * 0.5 + model.position.x) * cos(time.time * 0.9 + model.position.y) + 2.5) / 10.0; 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.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 { } else {
out.world_position = model.position; out.world_position = model.position;
out.texture_coordinates = model.texture_coordinates; 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; out.highlighted = model.highlighted;
return out; return out;
} }
@ -60,8 +64,8 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let object_color: vec4<f32> = textureSample( let object_color: vec4<f32> = textureSample(
texture_array, texture_array,
texture_sampler, texture_sampler,
in.texture_coordinates.xy, in.texture_coordinates,
i32(round(in.texture_coordinates.z)) in.texture_id
); );
let light_position = vec3<f32>(-100.0, 500.0, -200.0); 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 ambient_color = light_color * ambient_strength;
let light_direction = normalize(light_position - in.world_position); 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 half_direction = normalize(view_direction + light_direction);
let diffuse_strength = max(dot(in.world_normal, light_direction), 0.0); let diffuse_strength = max(dot(in.world_normal, light_direction), 0.0);

View file

@ -1,40 +1,36 @@
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use cgmath::Vector3; use cgmath::Vector3;
use wgpu::{ use wgpu::{BufferUsage, CommandEncoder, SwapChainTexture};
util::{BufferInitDescriptor, DeviceExt},
CommandEncoder, SwapChainTexture,
};
use crate::{ use crate::{
geometry::{Geometry, GeometryBuffers},
render_context::RenderContext, render_context::RenderContext,
state::PRIMITIVE_STATE,
text_renderer::{self, TextRenderer}, text_renderer::{self, TextRenderer},
texture::Texture, texture::Texture,
vertex::Vertex, vertex::{HudVertex, Vertex},
}; };
// TODO update aspect ratio when resizing
const UI_SCALE_X: f32 = 0.0045; const UI_SCALE_X: f32 = 0.0045;
const UI_SCALE_Y: f32 = 0.008; const UI_SCALE_Y: f32 = 0.008;
pub struct HudState { pub struct HudState {
texture_bind_group: wgpu::BindGroup, texture_bind_group: wgpu::BindGroup,
render_pipeline: wgpu::RenderPipeline, render_pipeline: wgpu::RenderPipeline,
crosshair_vertex_buffer: wgpu::Buffer, hud_geometry_buffers: GeometryBuffers,
crosshair_index_buffer: wgpu::Buffer,
text_renderer: TextRenderer, text_renderer: TextRenderer,
fps_vertex_buffer: wgpu::Buffer, fps_geometry_buffers: GeometryBuffers,
fps_index_buffer: wgpu::Buffer,
fps_index_count: usize,
fps_instant: Instant, fps_instant: Instant,
fps_frames: u32, fps_frames: u32,
fps_elapsed: Duration, fps_elapsed: Duration,
coordinates_vertex_buffer: wgpu::Buffer, coordinates_geometry_buffers: GeometryBuffers,
coordinates_index_buffer: wgpu::Buffer,
coordinates_index_count: usize,
coordinates_last: Vector3<f32>, coordinates_last: Vector3<f32>,
pub hotbar_cursor_position: i32,
} }
impl HudState { impl HudState {
@ -44,48 +40,37 @@ impl HudState {
let render_pipeline = let render_pipeline =
Self::create_render_pipeline(render_context, &[&texture_bind_group_layout]); Self::create_render_pipeline(render_context, &[&texture_bind_group_layout]);
let crosshair_vertex_buffer = // HUD buffers
render_context let hud_geometry = Geometry {
.device vertices: HUD_VERTICES.to_vec(),
.create_buffer_init(&BufferInitDescriptor { indices: HUD_INDICES.to_vec(),
label: Some("HUD crosshair vertex buffer"), };
contents: bytemuck::cast_slice(&CROSSHAIR_VERTICES), let hud_geometry_buffers =
usage: wgpu::BufferUsage::VERTEX, GeometryBuffers::from_geometry(render_context, &hud_geometry, BufferUsage::COPY_DST);
});
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,
});
// Text buffers
let text_renderer = TextRenderer::new(render_context).unwrap(); 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, ""); 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, ""); text_renderer.string_to_buffers(&render_context, -0.98, 0.97 - text_renderer::DY, "");
Self { Self {
texture_bind_group, texture_bind_group,
render_pipeline, render_pipeline,
crosshair_vertex_buffer,
crosshair_index_buffer,
text_renderer, text_renderer,
fps_vertex_buffer, hud_geometry_buffers,
fps_index_buffer,
fps_index_count, fps_geometry_buffers,
fps_instant: Instant::now(), fps_instant: Instant::now(),
fps_frames: 0, fps_frames: 0,
fps_elapsed: Duration::from_secs(0), fps_elapsed: Duration::from_secs(0),
coordinates_vertex_buffer, coordinates_geometry_buffers,
coordinates_index_buffer,
coordinates_index_count,
coordinates_last: Vector3::new(0.0, 0.0, 0.0), 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 fps = 1.0 / frametime.as_secs_f32();
let string = format!("{:<5.0} fps", fps); let string = format!("{:<5.0} fps", fps);
let (vertices, indices, index_count) = self.fps_geometry_buffers =
self.text_renderer self.text_renderer
.string_to_buffers(render_context, -0.98, 0.97, &string); .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_elapsed = Duration::from_secs(0);
self.fps_frames = 0; self.fps_frames = 0;
@ -113,15 +95,12 @@ impl HudState {
if position != &self.coordinates_last { if position != &self.coordinates_last {
let string = format!("({:.1},{:.1},{:.1})", position.x, position.y, position.z,); 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, render_context,
-0.98, -0.98,
0.97 - text_renderer::DY * 1.3, 0.97 - text_renderer::DY * 1.3,
&string, &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, render_encoder: &mut CommandEncoder,
) -> anyhow::Result<usize> { ) -> anyhow::Result<usize> {
let mut render_pass = render_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = render_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("render_pass"),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[wgpu::RenderPassColorAttachment {
view: &frame.view, view: &frame.view,
resolve_target: None, resolve_target: None,
@ -140,33 +118,58 @@ impl HudState {
store: true, store: true,
}, },
}], }],
depth_stencil_attachment: None, ..Default::default()
}); });
render_pass.set_pipeline(&self.render_pipeline); 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.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 the FPS text
render_pass.set_index_buffer(self.fps_index_buffer.slice(..), wgpu::IndexFormat::Uint16); self.fps_geometry_buffers.set_buffers(&mut render_pass);
render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); 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 the coordinates text
render_pass.set_index_buffer( self.coordinates_geometry_buffers
self.coordinates_index_buffer.slice(..), .set_buffers(&mut render_pass);
wgpu::IndexFormat::Uint16, 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) { fn create_textures(render_context: &RenderContext) -> (wgpu::BindGroupLayout, wgpu::BindGroup) {
@ -262,7 +265,7 @@ impl HudState {
vertex: wgpu::VertexState { vertex: wgpu::VertexState {
module, module,
entry_point: "main", entry_point: "main",
buffers: &[Vertex::desc()], buffers: &[HudVertex::descriptor()],
}, },
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module, module,
@ -273,83 +276,45 @@ impl HudState {
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}], }],
}), }),
primitive: wgpu::PrimitiveState { primitive: PRIMITIVE_STATE,
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,
},
depth_stencil: None, depth_stencil: None,
multisample: wgpu::MultisampleState { multisample: Default::default(),
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
}) })
} }
} }
pub const CROSSHAIR_VERTICES: &[Vertex] = &[ #[rustfmt::skip]
pub const HUD_VERTICES: [HudVertex; 12] = [
// Crosshair // Crosshair
Vertex { HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * 8.0], texture_coordinates: [240.0 / 256.0, 0.0 / 256.0] },
position: [-UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0, 0.0], HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0], texture_coordinates: [256.0 / 256.0, 0.0 / 256.0] },
texture_coordinates: [240.0 / 256.0, 0.0 / 256.0, 0.0], HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * -8.0], texture_coordinates: [256.0 / 256.0, 16.0 / 256.0] },
normal: [0.0, 0.0, 0.0], HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * -8.0], texture_coordinates: [240.0 / 256.0, 16.0 / 256.0] },
highlighted: 0,
},
Vertex {
position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [1.0, 0.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [UI_SCALE_X * 8.0, -UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [1.0, 16.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
Vertex {
position: [-UI_SCALE_X * 8.0, -UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [240.0 / 256.0, 16.0 / 256.0, 0.0],
normal: [0.0, 0.0, 0.0],
highlighted: 0,
},
// Hotbar // Hotbar
Vertex { HudVertex { position: [UI_SCALE_X * -91.0, -1.0 + UI_SCALE_Y * 22.0], texture_coordinates: [ 0.0 / 256.0, 0.0 / 256.0] },
position: [-UI_SCALE_X * 91.0, -1.0 + UI_SCALE_Y * 22.0, 0.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] },
texture_coordinates: [0.0 / 256.0, 0.0 / 256.0, 0.0], HudVertex { position: [UI_SCALE_X * 91.0, -1.0 ], texture_coordinates: [182.0 / 256.0, 22.0 / 256.0] },
normal: [0.0, 0.0, 0.0], HudVertex { position: [UI_SCALE_X * -91.0, -1.0 ], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0] },
highlighted: 0,
}, // Hotbar cursor
Vertex { HudVertex { position: [UI_SCALE_X * -92.0, -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0] },
position: [UI_SCALE_X * 91.0, -1.0 + UI_SCALE_Y * 22.0, 0.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] },
texture_coordinates: [182.0 / 256.0, 0.0 / 256.0, 0.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] },
normal: [0.0, 0.0, 0.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] },
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,
},
]; ];
#[rustfmt::skip] #[rustfmt::skip]
pub const CROSSHAIR_INDICES: &[u16] = &[ pub const HUD_INDICES: [u16; 18] = [
// Crosshair
1, 0, 3, 1, 0, 3,
1, 3, 2, 1, 3, 2,
// Hotbar
5, 4, 7, 5, 4, 7,
5, 7, 6, 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 cgmath::EuclideanSpace;
use winit::{ use winit::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{DeviceEvent, ElementState, KeyboardInput, VirtualKeyCode, WindowEvent}, event::{DeviceEvent, ElementState, MouseScrollDelta, VirtualKeyCode, WindowEvent},
window::Window, window::Window,
}; };
@ -15,6 +15,16 @@ use world_state::WorldState;
use crate::render_context::RenderContext; 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 struct State {
pub window_size: PhysicalSize<u32>, pub window_size: PhysicalSize<u32>,
render_context: RenderContext, 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 { match key_code {
VirtualKeyCode::F1 if state == &ElementState::Pressed => { VirtualKeyCode::F1 => self.world_state.toggle_wireframe(&self.render_context),
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)
}
} }
fn input_mouse(&mut self, dx: f64, dy: f64) { fn input_mouse(&mut self, dx: f64, dy: f64) {
@ -139,15 +160,9 @@ impl State {
pub fn window_event(&mut self, event: &WindowEvent) { pub fn window_event(&mut self, event: &WindowEvent) {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } if input.virtual_keycode.is_some() => {
input: self.input_keyboard(input.virtual_keycode.unwrap(), input.state)
KeyboardInput { }
virtual_keycode: Some(key),
state,
..
},
..
} => self.input_keyboard(key, state),
WindowEvent::MouseInput { WindowEvent::MouseInput {
button, button,
@ -157,14 +172,20 @@ impl State {
.world_state .world_state
.input_mouse_button(button, &self.render_context), .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) { pub fn device_event(&mut self, event: &DeviceEvent) {
match event { if let DeviceEvent::MouseMotion { delta } = event {
DeviceEvent::MouseMotion { delta: (dx, dy) } => self.input_mouse(*dx, *dy), 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 cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3};
use wgpu::{ use wgpu::{
util::{BufferInitDescriptor, DeviceExt}, util::{BufferInitDescriptor, DeviceExt},
CommandEncoder, SwapChainTexture, BufferUsage, CommandEncoder, SwapChainTexture,
}; };
use winit::{ use winit::{
dpi::PhysicalSize, dpi::PhysicalSize,
@ -14,19 +14,20 @@ use winit::{
use crate::{ use crate::{
camera::{Camera, Projection}, camera::{Camera, Projection},
chunk::{Block, BlockType, CHUNK_SIZE}, chunk::{Block, BlockType, CHUNK_SIZE},
geometry::GeometryBuffers,
render_context::RenderContext, render_context::RenderContext,
texture::{Texture, TextureManager}, texture::{Texture, TextureManager},
time::Time, time::Time,
uniforms::Uniforms, vertex::{BlockVertex, Vertex},
vertex::Vertex, view::View,
world::World, world::World,
}; };
pub struct WorldState { pub struct WorldState {
pub render_pipeline: wgpu::RenderPipeline, pub render_pipeline: wgpu::RenderPipeline,
pub uniforms: Uniforms, pub view: View,
pub uniform_buffer: wgpu::Buffer, pub view_buffer: wgpu::Buffer,
pub uniform_bind_group: wgpu::BindGroup, pub view_bind_group: wgpu::BindGroup,
pub texture_manager: TextureManager, pub texture_manager: TextureManager,
pub camera: Camera, pub camera: Camera,
pub projection: Projection, pub projection: Projection,
@ -34,7 +35,7 @@ pub struct WorldState {
pub time_bind_group: wgpu::BindGroup, pub time_bind_group: wgpu::BindGroup,
pub world: World, pub world: World,
pub chunk_buffers: AHashMap<Vector3<usize>, (wgpu::Buffer, wgpu::Buffer, usize)>, pub chunk_buffers: AHashMap<Vector3<usize>, GeometryBuffers>,
time: Time, time: Time,
time_buffer: wgpu::Buffer, time_buffer: wgpu::Buffer,
wireframe: bool, wireframe: bool,
@ -77,28 +78,23 @@ impl WorldState {
(camera, projection) (camera, projection)
} }
fn create_uniforms( fn create_view(
camera: &Camera, camera: &Camera,
projection: &Projection, projection: &Projection,
render_context: &RenderContext, render_context: &RenderContext,
) -> ( ) -> (View, wgpu::Buffer, wgpu::BindGroupLayout, wgpu::BindGroup) {
Uniforms, let mut view = View::new();
wgpu::Buffer, view.update_view_projection(camera, projection);
wgpu::BindGroupLayout,
wgpu::BindGroup,
) {
let mut uniforms = Uniforms::new();
uniforms.update_view_projection(camera, projection);
let uniform_buffer = render_context let view_buffer = render_context
.device .device
.create_buffer_init(&BufferInitDescriptor { .create_buffer_init(&BufferInitDescriptor {
label: Some("uniform_buffer"), label: Some("view_buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[view]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
}); });
let uniform_bind_group_layout = let view_bind_group_layout =
render_context render_context
.device .device
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -112,27 +108,21 @@ impl WorldState {
}, },
count: None, count: None,
}], }],
label: Some("uniform_bind_group_layout"), label: Some("view_bind_group_layout"),
}); });
let uniform_bind_group = let view_bind_group = render_context
render_context
.device .device
.create_bind_group(&wgpu::BindGroupDescriptor { .create_bind_group(&wgpu::BindGroupDescriptor {
layout: &uniform_bind_group_layout, layout: &view_bind_group_layout,
entries: &[wgpu::BindGroupEntry { entries: &[wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: uniform_buffer.as_entire_binding(), resource: view_buffer.as_entire_binding(),
}], }],
label: Some("uniform_bind_group"), label: Some("view_bind_group"),
}); });
( (view, view_buffer, view_bind_group_layout, view_bind_group)
uniforms,
uniform_buffer,
uniform_bind_group_layout,
uniform_bind_group,
)
} }
fn create_time( fn create_time(
@ -193,7 +183,7 @@ impl WorldState {
vertex: wgpu::VertexState { vertex: wgpu::VertexState {
module: &shader, module: &shader,
entry_point: "main", entry_point: "main",
buffers: &[Vertex::desc()], buffers: &[BlockVertex::descriptor()],
}, },
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &shader, module: &shader,
@ -232,52 +222,35 @@ impl WorldState {
let world_geometry = self.world.to_geometry(self.highlighted); let world_geometry = self.world.to_geometry(self.highlighted);
self.chunk_buffers.clear(); self.chunk_buffers.clear();
for (chunk_position, chunk_vertices, chunk_indices) in world_geometry { for (chunk_position, chunk_geometry) in world_geometry {
self.chunk_buffers.insert( let buffers = GeometryBuffers::from_geometry(
chunk_position, render_context,
( &chunk_geometry,
render_context BufferUsage::empty(),
.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(),
),
); );
self.chunk_buffers.insert(chunk_position, buffers);
} }
let elapsed = instant.elapsed(); let elapsed = instant.elapsed();
println!("World update took {:?}", elapsed); println!("World update took {:?}", elapsed);
} }
pub fn load_npc_geometry( pub fn load_npc_geometry(&mut self, render_context: &RenderContext) {
&mut self, self.world.npc.vertex_buffer = Some(render_context.device.create_buffer_init(
render_context: &RenderContext, &BufferInitDescriptor {
) {
self.world.npc.vertex_buffer = Some(render_context
.device
.create_buffer_init(&BufferInitDescriptor {
label: None, label: None,
contents: &bytemuck::cast_slice(&self.world.npc.vertices), contents: &bytemuck::cast_slice(&self.world.npc.vertices),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
})); },
));
self.world.npc.index_buffer = Some(render_context self.world.npc.index_buffer = Some(render_context.device.create_buffer_init(
.device &BufferInitDescriptor {
.create_buffer_init(&BufferInitDescriptor {
label: None, label: None,
contents: &bytemuck::cast_slice(&self.world.npc.indices), contents: &bytemuck::cast_slice(&self.world.npc.indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
})); },
));
} }
pub fn update_chunk_geometry( 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 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 offset = chunk_position.map(|f| (f * CHUNK_SIZE) as i32);
let (vertices, indices) = chunk.to_geometry( let geometry = chunk.to_geometry(
offset, offset,
World::highlighted_for_chunk(self.highlighted, chunk_position).as_ref(), World::highlighted_for_chunk(self.highlighted, chunk_position).as_ref(),
); );
self.chunk_buffers.insert( let buffers =
chunk_position, GeometryBuffers::from_geometry(render_context, &geometry, BufferUsage::empty());
( self.chunk_buffers.insert(chunk_position, buffers);
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(),
),
);
} }
pub fn toggle_wireframe(&mut self, render_context: &RenderContext) { pub fn toggle_wireframe(&mut self, render_context: &RenderContext) {
@ -331,8 +287,8 @@ impl WorldState {
let (camera, projection) = Self::create_camera(render_context); let (camera, projection) = Self::create_camera(render_context);
let (uniforms, uniform_buffer, world_uniform_layout, uniform_bind_group) = let (view, view_buffer, view_bind_group_layout, view_bind_group) =
Self::create_uniforms(&camera, &projection, render_context); Self::create_view(&camera, &projection, render_context);
let (time, time_buffer, time_layout, time_bind_group) = Self::create_time(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: &[], push_constant_ranges: &[],
bind_group_layouts: &[ bind_group_layouts: &[
&texture_manager.bind_group_layout, &texture_manager.bind_group_layout,
&world_uniform_layout, &view_bind_group_layout,
&time_layout, &time_layout,
], ],
}); });
@ -364,9 +320,9 @@ impl WorldState {
let mut world_state = Self { let mut world_state = Self {
render_pipeline, render_pipeline,
uniforms, view,
uniform_buffer, view_buffer,
uniform_bind_group, view_bind_group,
texture_manager, texture_manager,
camera, camera,
projection, projection,
@ -430,23 +386,22 @@ impl WorldState {
let tm = &self.texture_manager; let tm = &self.texture_manager;
render_pass.set_bind_group(0, tm.bind_group.as_ref().unwrap(), &[]); 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, &[]); render_pass.set_bind_group(2, &self.time_bind_group, &[]);
let camera_pos = self.camera.position.to_vec(); let camera_pos = self.camera.position.to_vec();
let camera_pos = Vector2::new(camera_pos.x, camera_pos.z); 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 = (position * CHUNK_SIZE).cast().unwrap();
let pos = Vector2::new(pos.x, pos.z); let pos = Vector2::new(pos.x, pos.z);
if (pos - camera_pos).magnitude() > 300.0 { if (pos - camera_pos).magnitude() > 300.0 {
continue; continue;
} }
render_pass.set_vertex_buffer(0, chunk_vertices.slice(..)); buffers.set_buffers(&mut render_pass);
render_pass.set_index_buffer(chunk_indices.slice(..), wgpu::IndexFormat::Uint16); buffers.draw_indexed(&mut render_pass);
render_pass.draw_indexed(0..*index_count as u32, 0, 0..1); triangle_count += buffers.index_count / 3;
triangle_count += index_count / 3;
} }
{ {
@ -454,8 +409,9 @@ impl WorldState {
let index_buffer = self.world.npc.index_buffer.as_ref(); let index_buffer = self.world.npc.index_buffer.as_ref();
render_pass.set_vertex_buffer(0, vertex_buffer.unwrap().slice(..)); render_pass.set_vertex_buffer(0, vertex_buffer.unwrap().slice(..));
render_pass.set_index_buffer(index_buffer.unwrap().slice(..), wgpu::IndexFormat::Uint32); render_pass
render_pass.draw_indexed(0..self.world.npc.indices.len() as u32 , 0, 0..1); .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 triangle_count
@ -504,12 +460,14 @@ impl WorldState {
let camera = &self.camera; let camera = &self.camera;
let world = &mut self.world; 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 { if button == &MouseButton::Left {
world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None); world.set_block(pos.x as isize, pos.y as isize, pos.z as isize, None);
self.update_chunk_geometry(render_context, pos / CHUNK_SIZE); self.update_chunk_geometry(render_context, pos / CHUNK_SIZE);
} else if button == &MouseButton::Right { } else if button == &MouseButton::Right {
let new_pos = pos.cast().unwrap() - axis; let new_pos = pos.cast().unwrap() + face_normal;
world.set_block( world.set_block(
new_pos.x as isize, new_pos.x as isize,
@ -525,8 +483,8 @@ impl WorldState {
} }
} }
pub fn input_keyboard(&mut self, key_code: &VirtualKeyCode, state: &ElementState) { pub fn input_keyboard(&mut self, key_code: VirtualKeyCode, state: ElementState) {
let pressed = state == &ElementState::Pressed; let pressed = state == ElementState::Pressed;
match key_code { match key_code {
VirtualKeyCode::W => self.forward_pressed = pressed, VirtualKeyCode::W => self.forward_pressed = pressed,
VirtualKeyCode::S => self.backward_pressed = pressed, VirtualKeyCode::S => self.backward_pressed = pressed,
@ -545,13 +503,9 @@ impl WorldState {
} }
} }
VirtualKeyCode::LShift if self.creative => { VirtualKeyCode::LShift if self.creative => {
self.up_speed = if pressed { self.up_speed = if pressed { -1.0 } else { 0.0 }
-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_position(dt);
self.update_aim(render_context); self.update_aim(render_context);
self.uniforms self.view
.update_view_projection(&self.camera, &self.projection); .update_view_projection(&self.camera, &self.projection);
render_context.queue.write_buffer( render_context
&self.uniform_buffer, .queue
0, .write_buffer(&self.view_buffer, 0, bytemuck::cast_slice(&[self.view]));
bytemuck::cast_slice(&[self.uniforms]),
);
self.time.time += dt.as_secs_f32(); self.time.time += dt.as_secs_f32();
render_context.queue.write_buffer( render_context.queue.write_buffer(

View file

@ -1,8 +1,11 @@
use std::convert::TryInto; use std::convert::TryInto;
use wgpu::util::{BufferInitDescriptor, DeviceExt}; use crate::{
geometry::{Geometry, GeometryBuffers},
use crate::{render_context::RenderContext, texture::Texture, vertex::Vertex}; render_context::RenderContext,
texture::Texture,
vertex::HudVertex,
};
pub const DX: f32 = 20.0 / 640.0; pub const DX: f32 = 20.0 / 640.0;
pub const DY: f32 = 20.0 / 360.0; pub const DY: f32 = 20.0 / 360.0;
@ -100,16 +103,16 @@ impl TextRenderer {
y: f32, y: f32,
c: u8, c: u8,
index_offset: u16, index_offset: u16,
) -> ([Vertex; 4], [u16; 6]) { ) -> ([HudVertex; 4], [u16; 6]) {
let (tx, ty) = Self::char_uv(c); let (tx, ty) = Self::char_uv(c);
let s = 1.0 / 16.0; let s = 1.0 / 16.0;
#[rustfmt::skip] #[rustfmt::skip]
let vertices = [ let vertices = [
Vertex { position: [x, y, 0.0], texture_coordinates: [tx, ty, 0.0], ..Default::default() }, HudVertex { position: [x, y ], texture_coordinates: [tx, ty ] },
Vertex { position: [x + DX, y, 0.0], texture_coordinates: [tx + s, ty, 0.0], ..Default::default() }, HudVertex { position: [x + DX, y ], texture_coordinates: [tx + s, ty ] },
Vertex { position: [x + DX, y - DY, 0.0], texture_coordinates: [tx + s, ty + s, 0.0], ..Default::default() }, HudVertex { position: [x + DX, y - DY], texture_coordinates: [tx + s, ty + s] },
Vertex { position: [x, y - DY, 0.0], texture_coordinates: [tx, ty + s, 0.0], ..Default::default() }, HudVertex { position: [x, y - DY], texture_coordinates: [tx, ty + s] },
]; ];
#[rustfmt::skip] #[rustfmt::skip]
@ -121,7 +124,7 @@ impl TextRenderer {
(vertices, indices) (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 vertices = Vec::new();
let mut indices = Vec::new(); let mut indices = Vec::new();
@ -141,7 +144,7 @@ impl TextRenderer {
} }
} }
(vertices, indices) Geometry::new(vertices, indices)
} }
pub fn string_to_buffers( pub fn string_to_buffers(
@ -150,25 +153,8 @@ impl TextRenderer {
x: f32, x: f32,
y: f32, y: f32,
string: &str, string: &str,
) -> (wgpu::Buffer, wgpu::Buffer, usize) { ) -> GeometryBuffers {
let (vertices, indices) = self.string_geometry(x, y, string); let geometry = self.string_geometry(x, y, string);
GeometryBuffers::from_geometry(render_context, &geometry, wgpu::BufferUsage::empty())
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())
} }
} }

View file

@ -1,41 +1,89 @@
use std::mem::size_of; use std::mem::size_of;
use wgpu::VertexAttribute;
pub trait Vertex {
fn descriptor() -> wgpu::VertexBufferLayout<'static>;
}
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex { pub struct PlainVertex {
pub position: [f32; 3], pub position: [f32; 3],
pub texture_coordinates: [f32; 3], pub texture_coordinates: [f32; 2],
pub normal: [f32; 3], pub normal: [f32; 3],
pub highlighted: i32,
} }
impl Vertex { const PLAIN_VERTEX_ATTRIBUTES: &[VertexAttribute] = &wgpu::vertex_attr_array![
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { 0 => Float32x3,
1 => Float32x2,
2 => Float32x3,
];
impl Vertex for PlainVertex {
fn descriptor() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout { wgpu::VertexBufferLayout {
array_stride: size_of::<Vertex>() as wgpu::BufferAddress, array_stride: size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex, step_mode: wgpu::InputStepMode::Vertex,
attributes: &[ attributes: PLAIN_VERTEX_ATTRIBUTES,
wgpu::VertexAttribute { }
offset: 0, }
shader_location: 0, }
format: wgpu::VertexFormat::Float32x3,
}, /// Vertex used to represent HUD vertices.
wgpu::VertexAttribute { ///
offset: 12, /// A vertex with a 2D position and no normal, for representing UI elements.
shader_location: 1, #[repr(C)]
format: wgpu::VertexFormat::Float32x3, #[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)]
}, pub struct HudVertex {
wgpu::VertexAttribute { pub position: [f32; 2],
offset: 24, pub texture_coordinates: [f32; 2],
shader_location: 2, }
format: wgpu::VertexFormat::Float32x3,
}, const HUD_VERTEX_ATTRIBUTES: &[VertexAttribute] = &wgpu::vertex_attr_array![
wgpu::VertexAttribute { 0 => Float32x2,
offset: 36, 1 => Float32x2,
shader_location: 3, ];
format: wgpu::VertexFormat::Sint32,
}, 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)] #[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Uniforms { pub struct View {
view_position: [f32; 4], view_position: [f32; 4],
view_projection: [[f32; 4]; 4], view_projection: [[f32; 4]; 4],
} }
impl Uniforms { impl View {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
view_position: [0.0; 4], view_position: [0.0; 4],

View file

@ -1,14 +1,15 @@
use crate::{ use crate::{
chunk::{Block, Chunk, CHUNK_SIZE}, chunk::{Block, Chunk, CHUNK_SIZE},
vertex::Vertex, geometry::Geometry,
npc::Npc, npc::Npc,
vertex::BlockVertex,
}; };
use cgmath::{InnerSpace, Vector3}; use cgmath::{InnerSpace, Vector3};
use rayon::prelude::*; use rayon::prelude::*;
pub struct World { pub struct World {
pub chunks: Vec<Vec<Vec<Chunk>>>, pub chunks: Vec<Vec<Vec<Chunk>>>,
pub npc: Npc pub npc: Npc,
} }
const WORLD_SIZE: Vector3<usize> = Vector3::new( const WORLD_SIZE: Vector3<usize> = Vector3::new(
@ -67,7 +68,7 @@ impl World {
pub fn to_geometry( pub fn to_geometry(
&self, &self,
highlighted: Option<(Vector3<usize>, Vector3<i32>)>, 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 instant = std::time::Instant::now();
let chunks = &self.chunks; let chunks = &self.chunks;
@ -75,17 +76,17 @@ impl World {
.par_iter() .par_iter()
.enumerate() .enumerate()
.flat_map(|(y, chunks_y)| { .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 (z, chunks_z) in chunks_y.iter().enumerate() {
for (x, chunk) in chunks_z.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 chunk_position = Vector3::new(x as usize, y as usize, z as usize);
let offset = (chunk_position * CHUNK_SIZE).cast().unwrap(); let offset = (chunk_position * CHUNK_SIZE).cast().unwrap();
let h = Self::highlighted_for_chunk(highlighted, chunk_position); let h = Self::highlighted_for_chunk(highlighted, chunk_position);
let (vertices, indices) = chunk.to_geometry(offset, h.as_ref()); let geometry = chunk.to_geometry(offset, h.as_ref());
geometry.push((Vector3::new(x, y, z), vertices, indices)); chunk_geometry.push((Vector3::new(x, y, z), geometry));
} }
} }
geometry chunk_geometry
}) })
.collect(); .collect();
@ -182,15 +183,15 @@ impl World {
if lengths.x < lengths.y && lengths.x < lengths.z { if lengths.x < lengths.y && lengths.x < lengths.z {
lengths.x += scale.x; lengths.x += scale.x;
position.x += step.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 { } else if lengths.y < lengths.x && lengths.y < lengths.z {
lengths.y += scale.y; lengths.y += scale.y;
position.y += step.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 { } else if lengths.z < lengths.x && lengths.z < lengths.y {
lengths.z += scale.z; lengths.z += scale.z;
position.z += step.z; position.z += step.z;
face = Vector3::unit_z() * step.z; face = Vector3::unit_z() * -step.z;
} else { } else {
return None; return None;
} }