diff --git a/src/main.rs b/src/main.rs index c4bec8d..e27b0ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ mod aabb; mod camera; -mod chunk; mod geometry; mod npc; -mod quad; mod render_context; mod renderable; mod state; diff --git a/src/state/hud_state.rs b/src/state/hud_state.rs index 33f5d1c..d3fe888 100644 --- a/src/state/hud_state.rs +++ b/src/state/hud_state.rs @@ -287,8 +287,8 @@ impl HudState { pub const HUD_VERTICES: [HudVertex; 12] = [ // Crosshair HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * 8.0], texture_coordinates: [240.0 / 256.0, 0.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0], texture_coordinates: [256.0 / 256.0, 0.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * -8.0], texture_coordinates: [256.0 / 256.0, 16.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0], texture_coordinates: [ 1.0, 0.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * -8.0], texture_coordinates: [ 1.0, 16.0 / 256.0] }, HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * -8.0], texture_coordinates: [240.0 / 256.0, 16.0 / 256.0] }, // Hotbar diff --git a/src/state/mod.rs b/src/state/mod.rs index 09f747d..486e4cc 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -20,8 +20,8 @@ pub const PRIMITIVE_STATE: wgpu::PrimitiveState = wgpu::PrimitiveState { strip_index_format: None, front_face: wgpu::FrontFace::Ccw, cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, clamp_depth: false, + polygon_mode: wgpu::PolygonMode::Fill, conservative: false, }; @@ -47,6 +47,7 @@ impl State { }) .await .unwrap(); + println!("Using {:?}", adapter.get_info().backend); let (render_device, queue) = adapter .request_device( diff --git a/src/world/block.rs b/src/world/block.rs new file mode 100644 index 0000000..ccd9aaf --- /dev/null +++ b/src/world/block.rs @@ -0,0 +1,41 @@ +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] +#[repr(u8)] +pub enum BlockType { + Cobblestone = 1, + Dirt = 2, + Stone = 3, + Grass = 4, + Bedrock = 5, + Sand = 6, + Gravel = 7, + Water = 8, +} + +impl BlockType { + #[rustfmt::skip] + pub const fn texture_indices(self) -> (usize, usize, usize, usize, usize, usize) { + match self { + BlockType::Cobblestone => ( 0, 0, 0, 0, 0, 0), + BlockType::Dirt => ( 1, 1, 1, 1, 1, 1), + BlockType::Stone => ( 2, 2, 2, 2, 2, 2), + BlockType::Grass => ( 4, 4, 4, 4, 2, 3), + BlockType::Bedrock => ( 5, 5, 5, 5, 5, 5), + BlockType::Sand => ( 6, 6, 6, 6, 6, 6), + BlockType::Gravel => ( 7, 7, 7, 7, 7, 7), + BlockType::Water => ( 8, 8, 8, 8, 8, 8), // up to 71 + } + } + + pub const fn is_transparent(self) -> bool { + matches!(self, BlockType::Water) + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct Block { + pub block_type: BlockType, +} diff --git a/src/chunk.rs b/src/world/chunk.rs similarity index 84% rename from src/chunk.rs rename to src/world/chunk.rs index f3c6f5a..02fbc81 100644 --- a/src/chunk.rs +++ b/src/world/chunk.rs @@ -1,81 +1,33 @@ -use std::{collections::VecDeque, usize}; +use std::collections::VecDeque; use crate::{ aabb::Aabb, geometry::{Geometry, GeometryBuffers}, - quad::Quad, + render_context::RenderContext, vertex::BlockVertex, view::View, + world::{ + block::{Block, BlockType}, + face_flags::*, + quad::Quad, + }, }; use ahash::{AHashMap, AHashSet}; use cgmath::{Point3, Vector3}; use noise::utils::{NoiseMapBuilder, PlaneMapBuilder}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; - use serde::{ de::{SeqAccess, Visitor}, - ser::{SerializeSeq, Serializer}, - Deserialize, Serialize, + ser::SerializeSeq, + Deserialize, Serialize, Serializer, }; -use serde_repr::{Deserialize_repr, Serialize_repr}; - -#[allow(dead_code)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] -#[repr(u8)] -pub enum BlockType { - Cobblestone = 1, - Dirt = 2, - Stone = 3, - Grass = 4, - Bedrock = 5, - Sand = 6, - Gravel = 7, - Water = 8, -} - -impl BlockType { - #[rustfmt::skip] - pub const fn texture_indices(self) -> (usize, usize, usize, usize, usize, usize) { - match self { - BlockType::Cobblestone => ( 0, 0, 0, 0, 0, 0), - BlockType::Dirt => ( 1, 1, 1, 1, 1, 1), - BlockType::Stone => ( 2, 2, 2, 2, 2, 2), - BlockType::Grass => ( 4, 4, 4, 4, 2, 3), - BlockType::Bedrock => ( 5, 5, 5, 5, 5, 5), - BlockType::Sand => ( 6, 6, 6, 6, 6, 6), - BlockType::Gravel => ( 7, 7, 7, 7, 7, 7), - BlockType::Water => ( 8, 8, 8, 8, 8, 8), // up to 71 - } - } - - pub const fn is_transparent(self) -> bool { - matches!(self, BlockType::Water) - } -} - -pub type FaceFlags = usize; -pub const FACE_NONE: FaceFlags = 0; -pub const FACE_LEFT: FaceFlags = 1; -pub const FACE_RIGHT: FaceFlags = 2; -pub const FACE_BOTTOM: FaceFlags = 4; -pub const FACE_TOP: FaceFlags = 8; -pub const FACE_BACK: FaceFlags = 16; -pub const FACE_FRONT: FaceFlags = 32; -pub const FACE_ALL: FaceFlags = - FACE_LEFT | FACE_RIGHT | FACE_BOTTOM | FACE_TOP | FACE_BACK | FACE_FRONT; - -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct Block { - pub block_type: BlockType, -} +use wgpu::BufferUsage; pub const CHUNK_SIZE: usize = 32; pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize; -type ChunkBlocks = [[[Option; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]; - pub struct Chunk { - pub blocks: ChunkBlocks, + pub blocks: [[[Option; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE], pub buffers: Option>, } @@ -88,23 +40,6 @@ impl Default for Chunk { } } -impl Serialize for Chunk { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(Some(CHUNK_SIZE.pow(3)))?; - for layer in self.blocks.iter() { - for row in layer { - for block in row { - seq.serialize_element(block)?; - } - } - } - seq.end() - } -} - struct ChunkVisitor; impl<'de> Visitor<'de> for ChunkVisitor { @@ -131,6 +66,23 @@ impl<'de> Visitor<'de> for ChunkVisitor { } } +impl Serialize for Chunk { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(CHUNK_SIZE.pow(3)))?; + for layer in self.blocks.iter() { + for row in layer { + for block in row { + seq.serialize_element(block)?; + } + } + } + seq.end() + } +} + impl<'de> Deserialize<'de> for Chunk { fn deserialize(deserializer: D) -> Result where @@ -218,6 +170,22 @@ impl Chunk { } } + pub fn block_coords_to_local( + chunk_coords: Point3, + block_coords: Point3, + ) -> Option> { + let chunk_position = chunk_coords * CHUNK_ISIZE; + let position = block_coords - chunk_position; + if (0..CHUNK_ISIZE).contains(&position.x) + && (0..CHUNK_ISIZE).contains(&position.y) + && (0..CHUNK_ISIZE).contains(&position.z) + { + Some(position.cast().unwrap()) + } else { + None + } + } + #[rustfmt::skip] fn check_visible_faces(&self, x: usize, y: usize, z: usize) -> FaceFlags { let mut visible_faces = FACE_NONE; @@ -294,7 +262,7 @@ impl Chunk { offset: Point3, culled: AHashMap<(usize, usize), (BlockType, FaceFlags)>, queue: &mut VecDeque<(usize, usize)>, - highlighted: Option<&(Point3, Vector3)>, + highlighted: Option<(Vector3, Vector3)>, ) -> Vec { let mut quads: Vec = Vec::new(); let mut visited = AHashSet::new(); @@ -310,7 +278,7 @@ impl Chunk { if let Some(&(block_type, visible_faces)) = &culled.get(&(x, z)) { let mut quad_faces = visible_faces; - if hl == Some(Point3::new(x, y, z)) { + if hl == Some(Vector3::new(x, y, z)) { let mut quad = Quad::new(position, 1, 1); quad.highlighted_normal = highlighted.unwrap().1; quad.visible_faces = quad_faces; @@ -332,7 +300,7 @@ impl Chunk { for x_ in x..CHUNK_SIZE { xmax = x_ + 1; - if visited.contains(&(xmax, z)) || hl == Some(Point3::new(xmax, y, z)) { + if visited.contains(&(xmax, z)) || hl == Some(Vector3::new(xmax, y, z)) { break; } @@ -354,7 +322,7 @@ impl Chunk { zmax = z_ + 1; for x_ in x..xmax { - if visited.contains(&(x_, zmax)) || hl == Some(Point3::new(x_, y, zmax)) { + if visited.contains(&(x_, zmax)) || hl == Some(Vector3::new(x_, y, zmax)) { break 'z; } @@ -391,20 +359,30 @@ impl Chunk { geometry } - pub fn to_geometry( - &self, - position: Point3, - highlighted: Option<&(Point3, Vector3)>, - ) -> Geometry { + pub fn update_geometry( + &mut self, + render_context: &RenderContext, + chunk_coords: Point3, + highlighted: Option<(Point3, Vector3)>, + ) { + let highlighted = highlighted.and_then(|(position, normal)| { + Self::block_coords_to_local(chunk_coords, position).map(|x| (x, normal)) + }); + + let offset = chunk_coords * CHUNK_ISIZE; let quads: Vec = (0..CHUNK_SIZE) .into_par_iter() .flat_map(|y| { let (culled, mut queue) = self.cull_layer(y); - self.layer_to_quads(y, position, culled, &mut queue, highlighted) + self.layer_to_quads(y, offset, culled, &mut queue, highlighted) }) .collect(); - Self::quads_to_geometry(quads) + self.buffers = Some(GeometryBuffers::from_geometry( + render_context, + &Self::quads_to_geometry(quads), + BufferUsage::empty(), + )); } pub fn save(&self, position: Point3, store: &sled::Db) -> anyhow::Result<()> { diff --git a/src/world/face_flags.rs b/src/world/face_flags.rs new file mode 100644 index 0000000..82a3845 --- /dev/null +++ b/src/world/face_flags.rs @@ -0,0 +1,10 @@ +pub type FaceFlags = usize; +pub const FACE_NONE: FaceFlags = 0; +pub const FACE_LEFT: FaceFlags = 1; +pub const FACE_RIGHT: FaceFlags = 2; +pub const FACE_BOTTOM: FaceFlags = 4; +pub const FACE_TOP: FaceFlags = 8; +pub const FACE_BACK: FaceFlags = 16; +pub const FACE_FRONT: FaceFlags = 32; +pub const FACE_ALL: FaceFlags = + FACE_LEFT | FACE_RIGHT | FACE_BOTTOM | FACE_TOP | FACE_BACK | FACE_FRONT; diff --git a/src/world.rs b/src/world/mod.rs similarity index 90% rename from src/world.rs rename to src/world/mod.rs index ef2c13e..7c46f06 100644 --- a/src/world.rs +++ b/src/world/mod.rs @@ -1,18 +1,28 @@ -use std::time::Instant; -use std::{collections::VecDeque, time::Duration}; +pub mod block; +pub mod chunk; +pub mod face_flags; +pub mod quad; + +use std::{ + collections::VecDeque, + time::{Duration, Instant}, +}; use crate::{ camera::Camera, - chunk::{self, Block, BlockType, Chunk, CHUNK_ISIZE}, geometry::GeometryBuffers, npc::Npc, render_context::RenderContext, renderable::Renderable, view::View, + world::{ + block::{Block, BlockType}, + chunk::{Chunk, CHUNK_ISIZE}, + }, }; use ahash::AHashMap; use cgmath::{EuclideanSpace, InnerSpace, Point3, Vector3}; -use wgpu::{BufferUsage, RenderPass}; +use wgpu::RenderPass; pub struct World { pub npc: Npc, @@ -203,17 +213,7 @@ impl World { chunk_position: Point3, ) { let chunk = self.chunks.get_mut(&chunk_position).unwrap(); - let offset = chunk_position * CHUNK_ISIZE; - let geometry = chunk.to_geometry( - offset, - World::highlighted_for_chunk(self.highlighted, &chunk_position).as_ref(), - ); - - chunk.buffers = Some(GeometryBuffers::from_geometry( - render_context, - &geometry, - BufferUsage::empty(), - )); + chunk.update_geometry(render_context, chunk_position, self.highlighted); } fn update_highlight(&mut self, render_context: &RenderContext, camera: &Camera) { @@ -263,29 +263,6 @@ impl World { } } - pub fn highlighted_for_chunk( - highlighted: Option<(Point3, Vector3)>, - chunk_position: &Point3, - ) -> Option<(Point3, Vector3)> { - let position = chunk_position * CHUNK_ISIZE; - if let Some((pos, face)) = highlighted { - if pos.x >= position.x - && pos.x < position.x + CHUNK_ISIZE - && pos.y >= position.y - && pos.y < position.y + CHUNK_ISIZE - && pos.z >= position.z - && pos.z < position.z + CHUNK_ISIZE - { - let point: Point3 = EuclideanSpace::from_vec(pos - position); - Some((point.cast().unwrap(), face)) - } else { - None - } - } else { - None - } - } - pub fn get_block(&self, x: isize, y: isize, z: isize) -> Option<&Block> { let chunk = match self.chunks.get(&Point3::new( x.div_euclid(CHUNK_ISIZE), diff --git a/src/quad.rs b/src/world/quad.rs similarity index 86% rename from src/quad.rs rename to src/world/quad.rs index 8803e5c..899450b 100644 --- a/src/quad.rs +++ b/src/world/quad.rs @@ -1,12 +1,9 @@ use cgmath::{Point3, 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, + world::{block::BlockType, face_flags::*}, }; #[derive(Debug)] @@ -69,11 +66,11 @@ impl Quad { 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; + let normal = Vector3::new(-1, 0, 0); + let highlighted = (self.highlighted_normal == normal) as i32; + let normal = normal.cast().unwrap().into(); 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 }, @@ -88,8 +85,9 @@ impl Quad { } if self.visible_faces & FACE_RIGHT == FACE_RIGHT { - let normal = [1.0, 0.0, 0.0]; - let highlighted = (normal == highlighted) as i32; + let normal = Vector3::new(1, 0, 0); + let highlighted = (self.highlighted_normal == normal) as i32; + let normal = normal.cast().unwrap().into(); 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 }, @@ -104,8 +102,9 @@ impl Quad { } if self.visible_faces & FACE_BACK == FACE_BACK { - let normal = [0.0, 0.0, -1.0]; - let highlighted = (normal == highlighted) as i32; + let normal = Vector3::new(0, 0, -1); + let highlighted = (self.highlighted_normal == normal) as i32; + let normal = normal.cast().unwrap().into(); 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 }, @@ -120,8 +119,9 @@ impl Quad { } if self.visible_faces & FACE_FRONT == FACE_FRONT { - let normal = [0.0, 0.0, 1.0]; - let highlighted = (normal == highlighted) as i32; + let normal = Vector3::new(0, 0, 1); + let highlighted = (self.highlighted_normal == normal) as i32; + let normal = normal.cast().unwrap().into(); 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 }, @@ -136,8 +136,9 @@ impl Quad { } if self.visible_faces & FACE_BOTTOM == FACE_BOTTOM { - let normal = [0.0, -1.0, 0.0]; - let highlighted = (normal == highlighted) as i32; + let normal = Vector3::new(0, -1, 0); + let highlighted = (self.highlighted_normal == normal) as i32; + let normal = normal.cast().unwrap().into(); 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 }, @@ -152,8 +153,9 @@ impl Quad { } if self.visible_faces & FACE_TOP == FACE_TOP { - let normal = [0.0, 1.0, 0.0]; - let highlighted = (normal == highlighted) as i32; + let normal = Vector3::new(0, 1, 0); + let highlighted = (self.highlighted_normal == normal) as i32; + let normal = normal.cast().unwrap().into(); 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 },