Merge branch 'main' into return_of_the_crab
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
commit
9e52359112
18 changed files with 664 additions and 610 deletions
9
.drone.yml
Normal file
9
.drone.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: frontend
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: dependencies
|
||||||
|
image: rust:slim
|
||||||
|
commands:
|
||||||
|
- cargo doc
|
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
101
src/chunk.rs
101
src/chunk.rs
|
@ -1,9 +1,10 @@
|
||||||
use std::{collections::VecDeque, convert::TryInto, usize};
|
use std::{collections::VecDeque, usize};
|
||||||
|
|
||||||
use crate::{cube, quad::Quad, vertex::Vertex};
|
use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex};
|
||||||
use ahash::{AHashMap, AHashSet};
|
use 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);
|
self.layer_to_quads(y, offset, culled, &mut queue, highlighted)
|
||||||
let mut layer_quads = 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)
|
||||||
}
|
}
|
||||||
|
|
132
src/cube.rs
132
src/cube.rs
|
@ -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
75
src/geometry.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
40
src/npc.rs
40
src/npc.rs
|
@ -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);
|
||||||
|
@ -25,34 +22,35 @@ impl Npc {
|
||||||
let rotation: Vector3<f32> = Vector3::new(0.0, 0.0, 0.0);
|
let rotation: Vector3<f32> = Vector3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Self {
|
return Self {
|
||||||
position,
|
position,
|
||||||
|
@ -61,7 +59,7 @@ impl Npc {
|
||||||
indices,
|
indices,
|
||||||
vertices,
|
vertices,
|
||||||
vertex_buffer: None,
|
vertex_buffer: None,
|
||||||
index_buffer: None
|
index_buffer: None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
170
src/quad.rs
170
src/quad.rs
|
@ -1,13 +1,171 @@
|
||||||
|
use cgmath::{Vector3, Zero};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
chunk::{
|
||||||
|
BlockType, FaceFlags, FACE_ALL, FACE_BACK, FACE_BOTTOM, FACE_FRONT, FACE_LEFT, FACE_RIGHT,
|
||||||
|
FACE_TOP,
|
||||||
|
},
|
||||||
|
geometry::Geometry,
|
||||||
|
vertex::BlockVertex,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
];
|
];
|
||||||
|
|
|
@ -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,12 +132,23 @@ impl State {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_keyboard(&mut self, key_code: &VirtualKeyCode, state: &ElementState) {
|
fn input_keyboard(&mut self, key_code: VirtualKeyCode, state: ElementState) {
|
||||||
match key_code {
|
if state == ElementState::Pressed {
|
||||||
VirtualKeyCode::F1 if state == &ElementState::Pressed => {
|
match key_code {
|
||||||
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),
|
||||||
}
|
}
|
||||||
_ => self.world_state.input_keyboard(key_code, state),
|
} else {
|
||||||
|
self.world_state.input_keyboard(key_code, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,15 +160,9 @@ impl State {
|
||||||
|
|
||||||
pub fn window_event(&mut self, event: &WindowEvent) {
|
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)
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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: &view_bind_group_layout,
|
||||||
layout: &uniform_bind_group_layout,
|
entries: &[wgpu::BindGroupEntry {
|
||||||
entries: &[wgpu::BindGroupEntry {
|
binding: 0,
|
||||||
binding: 0,
|
resource: view_buffer.as_entire_binding(),
|
||||||
resource: uniform_buffer.as_entire_binding(),
|
}],
|
||||||
}],
|
label: Some("view_bind_group"),
|
||||||
label: Some("uniform_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 {
|
||||||
) {
|
label: None,
|
||||||
self.world.npc.vertex_buffer = Some(render_context
|
contents: &bytemuck::cast_slice(&self.world.npc.vertices),
|
||||||
.device
|
usage: wgpu::BufferUsage::VERTEX,
|
||||||
.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
|
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(
|
||||||
|
|
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
104
src/vertex.rs
104
src/vertex.rs
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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],
|
21
src/world.rs
21
src/world.rs
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue