diff --git a/src/render_context.rs b/src/render_context.rs index d075fdd..5359064 100644 --- a/src/render_context.rs +++ b/src/render_context.rs @@ -1,7 +1,10 @@ +use crate::texture::TextureManager; + pub struct RenderContext { pub surface: wgpu::Surface, pub device: wgpu::Device, pub queue: wgpu::Queue, pub swap_chain_descriptor: wgpu::SwapChainDescriptor, pub swap_chain: wgpu::SwapChain, + pub texture_manager: Option, } diff --git a/src/shaders/ui.wgsl b/src/shaders/ui.wgsl index 24acaf2..40f2e9f 100644 --- a/src/shaders/ui.wgsl +++ b/src/shaders/ui.wgsl @@ -1,11 +1,13 @@ struct VertexInput { [[location(0)]] position: vec2; [[location(1)]] texture_coordinates: vec2; + [[location(2)]] texture_index: i32; }; struct VertexOutput { [[builtin(position)]] clip_position: vec4; [[location(0)]] texture_coordinates: vec2; + [[location(1)]] texture_index: i32; }; [[stage(vertex)]] @@ -13,13 +15,14 @@ fn main(model: VertexInput) -> VertexOutput { var out: VertexOutput; out.texture_coordinates = model.texture_coordinates; out.clip_position = vec4(model.position, 0.0, 1.0); + out.texture_index = model.texture_index; return out; } [[group(0), binding(0)]] var sampler: sampler; -[[group(0), binding(1)]] var texture: texture_2d; +[[group(0), binding(1)]] var texture: texture_2d_array; [[stage(fragment)]] fn main(in: VertexOutput) -> [[location(0)]] vec4 { - return textureSample(texture, sampler, in.texture_coordinates); + return textureSample(texture, sampler, in.texture_coordinates, in.texture_index); } diff --git a/src/state/hud_state.rs b/src/state/hud_state.rs index d3fe888..22f5ba3 100644 --- a/src/state/hud_state.rs +++ b/src/state/hud_state.rs @@ -1,4 +1,7 @@ -use std::time::{Duration, Instant}; +use std::{ + convert::TryInto, + time::{Duration, Instant}, +}; use cgmath::Vector3; use wgpu::{BufferUsage, CommandEncoder, SwapChainTexture}; @@ -10,6 +13,7 @@ use crate::{ text_renderer::{self, TextRenderer}, texture::Texture, vertex::{HudVertex, Vertex}, + world::block::BlockType, }; // TODO update aspect ratio when resizing @@ -30,7 +34,10 @@ pub struct HudState { coordinates_geometry_buffers: GeometryBuffers, coordinates_last: Vector3, - pub hotbar_cursor_position: i32, + + pub hotbar_cursor_position: usize, + hotbar_blocks: [Option; 9], + hotbar_block_buffers: Option>, } impl HudState { @@ -55,7 +62,19 @@ impl HudState { let coordinates_geometry_buffers = text_renderer.string_to_buffers(&render_context, -0.98, 0.97 - text_renderer::DY, ""); - Self { + let hotbar_blocks = [ + Some(BlockType::Dirt), + Some(BlockType::Stone), + Some(BlockType::Sand), + None, + Some(BlockType::Grass), + None, + None, + None, + None, + ]; + + let mut hud_state = Self { texture_bind_group, render_pipeline, text_renderer, @@ -71,7 +90,17 @@ impl HudState { coordinates_last: Vector3::new(0.0, 0.0, 0.0), hotbar_cursor_position: 0, - } + hotbar_blocks, + hotbar_block_buffers: None, + }; + + hud_state.hotbar_block_buffers = Some(GeometryBuffers::from_geometry( + render_context, + &hud_state.hotbar_block_vertices(), + wgpu::BufferUsage::empty(), + )); + + hud_state } pub fn update(&mut self, render_context: &RenderContext, position: &Vector3) { @@ -106,6 +135,7 @@ impl HudState { pub fn render( &self, + render_context: &RenderContext, frame: &SwapChainTexture, render_encoder: &mut CommandEncoder, ) -> anyhow::Result { @@ -141,18 +171,65 @@ impl HudState { self.coordinates_geometry_buffers .draw_indexed(&mut render_pass); + // Render the blocks on the hot bar + let texture_manager = render_context.texture_manager.as_ref().unwrap(); + render_pass.set_bind_group(0, texture_manager.bind_group.as_ref().unwrap(), &[]); + self.hotbar_block_buffers + .as_ref() + .unwrap() + .apply_buffers(&mut render_pass); + self.hotbar_block_buffers + .as_ref() + .unwrap() + .draw_indexed(&mut render_pass); + Ok(HUD_INDICES.len() / 3) } + pub fn selected_block_type(&self) -> Option { + self.hotbar_blocks[self.hotbar_cursor_position] + } + + fn hotbar_block_vertices(&self) -> Geometry { + let mut vertices = Vec::new(); + let mut indices = Vec::new(); + + let mut index = 0; + for cursor_index in 0..9 { + if let Some(block) = self.hotbar_blocks[cursor_index as usize] { + let x = (-92 + 20 * cursor_index as i32) as f32; + let texture_index = block.texture_indices().2.try_into().unwrap(); + + #[rustfmt::skip] + vertices.extend(&[ + HudVertex { position: [UI_SCALE_X * (x + 5.0), -1.0 + UI_SCALE_Y * 18.0], texture_coordinates: [0.0, 0.0], texture_index }, + HudVertex { position: [UI_SCALE_X * (x + 19.0), -1.0 + UI_SCALE_Y * 18.0], texture_coordinates: [1.0, 0.0], texture_index }, + HudVertex { position: [UI_SCALE_X * (x + 19.0), -1.0 + UI_SCALE_Y * 4.0], texture_coordinates: [1.0, 1.0], texture_index }, + HudVertex { position: [UI_SCALE_X * (x + 5.0), -1.0 + UI_SCALE_Y * 4.0], texture_coordinates: [0.0, 1.0], texture_index }, + ]); + + #[rustfmt::skip] + indices.extend(&[ + index, 2 + index, 1 + index, + index, 3 + index, 2 + index, + ]); + index += 4; + } + } + + Geometry::new(vertices, indices) + } + pub fn redraw_hotbar_cursor(&self, render_context: &RenderContext) { - let x = (-92 + 20 * self.hotbar_cursor_position) as f32; + let x = (-92 + 20 * self.hotbar_cursor_position as i32) as f32; + let texture_index = 0; #[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] }, + HudVertex { position: [UI_SCALE_X * (x ), -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0], texture_index }, + 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], texture_index }, + 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], texture_index }, + HudVertex { position: [UI_SCALE_X * (x ), -1.0 + UI_SCALE_Y * -1.0], texture_coordinates: [ 0.0 / 256.0, 46.0 / 256.0], texture_index }, ]; render_context.queue.write_buffer( @@ -162,13 +239,14 @@ impl HudState { ); } - pub fn set_hotbar_cursor(&mut self, render_context: &RenderContext, i: i32) { + pub fn set_hotbar_cursor(&mut self, render_context: &RenderContext, i: usize) { 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.hotbar_cursor_position = + (self.hotbar_cursor_position as i32 + delta).rem_euclid(9) as usize; self.redraw_hotbar_cursor(render_context); } @@ -208,7 +286,7 @@ impl HudState { visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Texture { sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, + view_dimension: wgpu::TextureViewDimension::D2Array, multisampled: false, }, count: None, @@ -286,22 +364,22 @@ impl HudState { #[rustfmt::skip] 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: [ 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] }, + HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * 8.0], texture_coordinates: [240.0 / 256.0, 0.0 / 256.0], texture_index: 0 }, + HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0], texture_coordinates: [ 1.0, 0.0 / 256.0], texture_index: 0 }, + HudVertex { position: [UI_SCALE_X * 8.0, UI_SCALE_Y * -8.0], texture_coordinates: [ 1.0, 16.0 / 256.0], texture_index: 0 }, + HudVertex { position: [UI_SCALE_X * -8.0, UI_SCALE_Y * -8.0], texture_coordinates: [240.0 / 256.0, 16.0 / 256.0], texture_index: 0 }, // Hotbar - HudVertex { position: [UI_SCALE_X * -91.0, -1.0 + UI_SCALE_Y * 22.0], texture_coordinates: [ 0.0 / 256.0, 0.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * 91.0, -1.0 + UI_SCALE_Y * 22.0], texture_coordinates: [182.0 / 256.0, 0.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * 91.0, -1.0 ], texture_coordinates: [182.0 / 256.0, 22.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * -91.0, -1.0 ], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * -91.0, -1.0 + UI_SCALE_Y * 22.0], texture_coordinates: [ 0.0 / 256.0, 0.0 / 256.0], texture_index: 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_index: 0 }, + HudVertex { position: [UI_SCALE_X * 91.0, -1.0 ], texture_coordinates: [182.0 / 256.0, 22.0 / 256.0], texture_index: 0 }, + HudVertex { position: [UI_SCALE_X * -91.0, -1.0 ], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0], texture_index: 0 }, // Hotbar cursor - HudVertex { position: [UI_SCALE_X * -92.0, -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * -68.0, -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 24.0 / 256.0, 22.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * -68.0, -1.0 + UI_SCALE_Y * -1.0], texture_coordinates: [ 24.0 / 256.0, 46.0 / 256.0] }, - HudVertex { position: [UI_SCALE_X * -92.0, -1.0 + UI_SCALE_Y * -1.0], texture_coordinates: [ 0.0 / 256.0, 46.0 / 256.0] }, + HudVertex { position: [UI_SCALE_X * -92.0, -1.0 + UI_SCALE_Y * 23.0], texture_coordinates: [ 0.0 / 256.0, 22.0 / 256.0], texture_index: 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_index: 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], texture_index: 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], texture_index: 0 }, ]; #[rustfmt::skip] diff --git a/src/state/mod.rs b/src/state/mod.rs index 486e4cc..0488ee5 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -13,7 +13,7 @@ use winit::{ use hud_state::HudState; use world_state::WorldState; -use crate::render_context::RenderContext; +use crate::{render_context::RenderContext, texture::TextureManager}; pub const PRIMITIVE_STATE: wgpu::PrimitiveState = wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, @@ -96,15 +96,20 @@ impl State { let (swap_chain_descriptor, swap_chain) = Self::create_swap_chain(window, &render_adapter, &render_device, &render_surface); - let render_context = RenderContext { + let mut render_context = RenderContext { surface: render_surface, device: render_device, queue: render_queue, swap_chain_descriptor, swap_chain, + texture_manager: None, }; + let mut texture_manager = TextureManager::new(&render_context); + texture_manager.load_all(&render_context).unwrap(); + render_context.texture_manager = Some(texture_manager); + let world_state = WorldState::new(&render_context); let hud_state = HudState::new(&render_context); @@ -169,9 +174,11 @@ impl State { button, state: ElementState::Pressed, .. - } if self.mouse_grabbed => self - .world_state - .input_mouse_button(button, &self.render_context), + } if self.mouse_grabbed => self.world_state.input_mouse_button( + button, + &self.render_context, + self.hud_state.selected_block_type(), + ), WindowEvent::MouseWheel { delta: MouseScrollDelta::LineDelta(_, delta), @@ -211,8 +218,12 @@ impl State { .create_command_encoder(&Default::default()); let mut triangle_count = 0; - triangle_count += self.world_state.render(&frame, &mut render_encoder); - triangle_count += self.hud_state.render(&frame, &mut render_encoder)?; + triangle_count += + self.world_state + .render(&self.render_context, &frame, &mut render_encoder); + triangle_count += + self.hud_state + .render(&self.render_context, &frame, &mut render_encoder)?; self.render_context .queue diff --git a/src/state/world_state.rs b/src/state/world_state.rs index 459e944..e6d3e27 100644 --- a/src/state/world_state.rs +++ b/src/state/world_state.rs @@ -15,12 +15,12 @@ use crate::{ camera::{Camera, Projection}, render_context::RenderContext, renderable::Renderable, - texture::{Texture, TextureManager}, + texture::Texture, time::Time, utils, vertex::{BlockVertex, Vertex}, view::View, - world::World, + world::{block::BlockType, World}, }; pub struct WorldState { @@ -28,7 +28,6 @@ pub struct WorldState { pub view: View, pub view_buffer: wgpu::Buffer, pub view_bind_group: wgpu::BindGroup, - pub texture_manager: TextureManager, pub camera: Camera, pub projection: Projection, pub depth_texture: Texture, @@ -52,12 +51,6 @@ pub struct WorldState { } impl WorldState { - fn create_textures(render_context: &RenderContext) -> TextureManager { - let mut texture_manager = TextureManager::new(&render_context); - texture_manager.load_all(render_context).unwrap(); - texture_manager - } - fn create_camera(render_context: &RenderContext) -> (Camera, Projection) { let camera = Camera::new( (10.0, 140.0, 10.0).into(), @@ -226,8 +219,6 @@ impl WorldState { } pub fn new(render_context: &RenderContext) -> WorldState { - let texture_manager = Self::create_textures(render_context); - let (camera, projection) = Self::create_camera(render_context); let (view, view_buffer, view_bind_group_layout, view_bind_group) = @@ -246,6 +237,7 @@ impl WorldState { }), ); + let texture_manager = render_context.texture_manager.as_ref().unwrap(); let render_pipeline_layout = render_context .device @@ -267,7 +259,6 @@ impl WorldState { view, view_buffer, view_bind_group, - texture_manager, camera, projection, depth_texture, @@ -292,7 +283,12 @@ impl WorldState { } } - pub fn render(&self, frame: &SwapChainTexture, render_encoder: &mut CommandEncoder) -> usize { + pub fn render( + &self, + render_context: &RenderContext, + frame: &SwapChainTexture, + render_encoder: &mut CommandEncoder, + ) -> usize { let mut triangle_count = 0; let mut render_pass = render_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -322,8 +318,8 @@ impl WorldState { render_pass.set_pipeline(&self.render_pipeline); - let tm = &self.texture_manager; - render_pass.set_bind_group(0, tm.bind_group.as_ref().unwrap(), &[]); + let texture_manager = render_context.texture_manager.as_ref().unwrap(); + render_pass.set_bind_group(0, texture_manager.bind_group.as_ref().unwrap(), &[]); render_pass.set_bind_group(1, &self.view_bind_group, &[]); render_pass.set_bind_group(2, &self.time_bind_group, &[]); @@ -344,11 +340,19 @@ impl WorldState { } } - pub fn input_mouse_button(&mut self, button: &MouseButton, render_context: &RenderContext) { + pub fn input_mouse_button( + &mut self, + button: &MouseButton, + render_context: &RenderContext, + selected: Option, + ) { if button == &MouseButton::Left { self.world.break_at_crosshair(render_context, &self.camera); } else if button == &MouseButton::Right { - self.world.place_at_crosshair(render_context, &self.camera); + if let Some(selected) = selected { + self.world + .place_at_crosshair(render_context, &self.camera, selected); + } } } diff --git a/src/text_renderer.rs b/src/text_renderer.rs index 8815d98..4cc90c5 100644 --- a/src/text_renderer.rs +++ b/src/text_renderer.rs @@ -60,7 +60,7 @@ impl TextRenderer { visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Texture { sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, + view_dimension: wgpu::TextureViewDimension::D2Array, multisampled: false, }, count: None, @@ -109,10 +109,10 @@ impl TextRenderer { #[rustfmt::skip] let vertices = [ - HudVertex { position: [x, y ], texture_coordinates: [tx, ty ] }, - HudVertex { position: [x + DX, y ], texture_coordinates: [tx + s, ty ] }, - HudVertex { position: [x + DX, y - DY], texture_coordinates: [tx + s, ty + s] }, - HudVertex { position: [x, y - DY], texture_coordinates: [tx, ty + s] }, + HudVertex { position: [x, y ], texture_coordinates: [tx, ty ], texture_index: 0 }, + HudVertex { position: [x + DX, y ], texture_coordinates: [tx + s, ty ], texture_index: 0 }, + HudVertex { position: [x + DX, y - DY], texture_coordinates: [tx + s, ty + s], texture_index: 0 }, + HudVertex { position: [x, y - DY], texture_coordinates: [tx, ty + s], texture_index: 0 }, ]; #[rustfmt::skip] diff --git a/src/texture.rs b/src/texture.rs index b926fd7..e8f5b25 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -102,7 +102,7 @@ impl Texture { let view = texture.create_view(&wgpu::TextureViewDescriptor { label: Some(&format!("texture_view_{}", label)), - dimension: Some(wgpu::TextureViewDimension::D2), + dimension: Some(wgpu::TextureViewDimension::D2Array), ..wgpu::TextureViewDescriptor::default() }); diff --git a/src/vertex.rs b/src/vertex.rs index 622dc46..5bb7992 100644 --- a/src/vertex.rs +++ b/src/vertex.rs @@ -38,11 +38,13 @@ impl Vertex for PlainVertex { pub struct HudVertex { pub position: [f32; 2], pub texture_coordinates: [f32; 2], + pub texture_index: i32, } const HUD_VERTEX_ATTRIBUTES: &[VertexAttribute] = &wgpu::vertex_attr_array![ 0 => Float32x2, 1 => Float32x2, + 2 => Sint32, ]; impl Vertex for HudVertex { diff --git a/src/world/mod.rs b/src/world/mod.rs index 7f18948..c0555a1 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -125,15 +125,15 @@ impl Renderable for World { eprintln!("Failed to save chunk {:?}: {:?}", position, err); } else { if unload { - self.chunks.remove(&position); + self.chunks.remove(&position); - if DEBUG_IO { - println!("Saved and unloaded chunk {:?}", position); - } + if DEBUG_IO { + println!("Saved and unloaded chunk {:?}", position); + } } else { if DEBUG_IO { - println!("Saved chunk {:?}", position); - } + println!("Saved chunk {:?}", position); + } } } } else { @@ -249,7 +249,12 @@ impl World { } } - pub fn place_at_crosshair(&mut self, render_context: &RenderContext, camera: &Camera) { + pub fn place_at_crosshair( + &mut self, + render_context: &RenderContext, + camera: &Camera, + block_type: BlockType, + ) { if let Some((pos, face_normal)) = self.raycast(camera.position, camera.direction()) { let new_pos = pos.cast().unwrap() + face_normal; @@ -257,9 +262,7 @@ impl World { new_pos.x as isize, new_pos.y as isize, new_pos.z as isize, - Some(Block { - block_type: BlockType::Cobblestone, - }), + Some(Block { block_type }), ); self.update_chunk_geometry(render_context, pos / CHUNK_ISIZE);