diff --git a/src/hud/debug_hud.rs b/src/hud/debug_hud.rs new file mode 100644 index 0000000..1d5b247 --- /dev/null +++ b/src/hud/debug_hud.rs @@ -0,0 +1,90 @@ +use std::time::{Duration, Instant}; + +use cgmath::Point3; +use wgpu::RenderPass; + +use crate::{ + geometry::GeometryBuffers, + render_context::RenderContext, + text_renderer::{self, TextRenderer}, +}; + +pub struct DebugHud { + text_renderer: TextRenderer, + + fps_instant: Instant, + fps_elapsed: Duration, + fps_frames: u32, + fps_geometry_buffers: GeometryBuffers, + + coordinates_last: Point3, + coordinates_geometry_buffers: GeometryBuffers, +} + +impl DebugHud { + pub fn new(render_context: &RenderContext) -> Self { + let text_renderer = TextRenderer::new(render_context).unwrap(); + let fps_geometry_buffers = + text_renderer.string_to_buffers(&render_context, -0.98, 0.97, ""); + let coordinates_geometry_buffers = + text_renderer.string_to_buffers(&render_context, -0.98, 0.97 - text_renderer::DY, ""); + + Self { + text_renderer, + + fps_instant: Instant::now(), + fps_elapsed: Duration::default(), + fps_frames: 0, + fps_geometry_buffers, + + coordinates_last: Point3::new(0.0, 0.0, 0.0), + coordinates_geometry_buffers, + } + } + + pub fn update(&mut self, render_context: &RenderContext, position: &Point3) { + let elapsed = self.fps_instant.elapsed(); + self.fps_instant = Instant::now(); + self.fps_elapsed += elapsed; + self.fps_frames += 1; + + if self.fps_elapsed.as_millis() >= 500 { + let frametime = self.fps_elapsed / self.fps_frames; + let fps = 1.0 / frametime.as_secs_f32(); + + let string = format!("{:<5.0} fps", fps); + self.fps_geometry_buffers = + self.text_renderer + .string_to_buffers(render_context, -0.98, 0.97, &string); + + self.fps_elapsed = Duration::from_secs(0); + self.fps_frames = 0; + } + + if position != &self.coordinates_last { + let string = format!("({:.1},{:.1},{:.1})", position.x, position.y, position.z,); + self.coordinates_geometry_buffers = self.text_renderer.string_to_buffers( + render_context, + -0.98, + 0.97 - text_renderer::DY * 1.3, + &string, + ); + } + } + + pub fn render<'a>(&'a self, render_pass: &mut RenderPass<'a>) -> usize { + let mut triangle_count = 0; + + // Render the FPS text + self.fps_geometry_buffers.apply_buffers(render_pass); + render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); + triangle_count += self.fps_geometry_buffers.draw_indexed(render_pass); + + // Render the coordinates text + self.coordinates_geometry_buffers.apply_buffers(render_pass); + render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); + triangle_count += self.coordinates_geometry_buffers.draw_indexed(render_pass); + + triangle_count + } +} diff --git a/src/hud/hotbar_hud.rs b/src/hud/hotbar_hud.rs new file mode 100644 index 0000000..172aedb --- /dev/null +++ b/src/hud/hotbar_hud.rs @@ -0,0 +1,97 @@ +use std::convert::TryInto; + +use wgpu::{BufferUsage, RenderPass}; + +use crate::{ + geometry::{Geometry, GeometryBuffers}, + hud::{UI_SCALE_X, UI_SCALE_Y}, + render_context::RenderContext, + vertex::HudVertex, + world::block::BlockType, +}; + +pub struct HotbarHud { + pub blocks: [Option; 9], + pub last_blocks: [Option; 9], + + pub geometry_buffers: GeometryBuffers, +} + +impl HotbarHud { + pub fn new(render_context: &RenderContext) -> Self { + let hotbar_blocks = [ + Some(BlockType::Dirt), + Some(BlockType::Stone), + Some(BlockType::Sand), + None, + Some(BlockType::Grass), + None, + None, + None, + None, + ]; + + Self { + blocks: hotbar_blocks, + last_blocks: [None; 9], + + geometry_buffers: GeometryBuffers::from_geometry( + render_context, + &Geometry::::default(), + BufferUsage::empty(), + ), + } + } + + pub fn update(&mut self, render_context: &RenderContext) { + if self.blocks != self.last_blocks { + self.geometry_buffers = GeometryBuffers::from_geometry( + render_context, + &self.block_vertices(), + wgpu::BufferUsage::empty(), + ); + } + } + + pub fn render<'a>( + &'a self, + render_context: &'a RenderContext, + render_pass: &mut RenderPass<'a>, + ) -> usize { + let texture_manager = render_context.texture_manager.as_ref().unwrap(); + + render_pass.set_bind_group(0, texture_manager.bind_group.as_ref().unwrap(), &[]); + self.geometry_buffers.apply_buffers(render_pass); + self.geometry_buffers.draw_indexed(render_pass) + } + + fn 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.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) + } +} diff --git a/src/hud/mod.rs b/src/hud/mod.rs new file mode 100644 index 0000000..94576ac --- /dev/null +++ b/src/hud/mod.rs @@ -0,0 +1,141 @@ +use wgpu::{CommandEncoder, RenderPipeline, SwapChainTexture}; + +use crate::{ + render_context::RenderContext, + state::PRIMITIVE_STATE, + vertex::{HudVertex, Vertex}, +}; + +use self::{debug_hud::DebugHud, hotbar_hud::HotbarHud, widgets_hud::WidgetsHud}; + +pub mod debug_hud; +pub mod hotbar_hud; +pub mod widgets_hud; + +// TODO update aspect ratio when resizing +pub const UI_SCALE_X: f32 = 0.0045; +pub const UI_SCALE_Y: f32 = 0.008; + +pub struct Hud { + pub widgets_hud: WidgetsHud, + pub debug_hud: DebugHud, + pub hotbar_hud: HotbarHud, + + pub pipeline: RenderPipeline, +} + +impl Hud { + pub fn new(render_context: &RenderContext) -> Self { + Self { + widgets_hud: WidgetsHud::new(render_context), + debug_hud: DebugHud::new(render_context), + hotbar_hud: HotbarHud::new(render_context), + + pipeline: Self::create_render_pipeline(render_context), + } + } + + fn create_render_pipeline(render_context: &RenderContext) -> wgpu::RenderPipeline { + let bind_group_layout = + render_context + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("GUI texture bind group layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler { + comparison: false, + filtering: true, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2Array, + multisampled: false, + }, + count: None, + }, + ], + }); + + let module = &render_context + .device + .create_shader_module(&wgpu::ShaderModuleDescriptor { + label: Some("UI shader"), + flags: wgpu::ShaderFlags::all(), + source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/ui.wgsl").into()), + }); + + let pipeline_layout = + render_context + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("UI render pipeline layout"), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + + render_context + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("UI render pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module, + entry_point: "main", + buffers: &[HudVertex::descriptor()], + }, + fragment: Some(wgpu::FragmentState { + module, + entry_point: "main", + targets: &[wgpu::ColorTargetState { + format: render_context.swap_chain_descriptor.format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrite::ALL, + }], + }), + primitive: PRIMITIVE_STATE, + depth_stencil: None, + multisample: Default::default(), + }) + } + + pub fn update( + &mut self, + render_context: &crate::render_context::RenderContext, + camera: &crate::camera::Camera, + ) { + self.debug_hud.update(render_context, &camera.position); + self.hotbar_hud.update(render_context); + } + + pub fn render<'a>( + &'a self, + render_context: &RenderContext, + encoder: &mut CommandEncoder, + frame: &SwapChainTexture, + ) -> usize { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachment { + view: &frame.view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: true, + }, + }], + ..Default::default() + }); + render_pass.set_pipeline(&self.pipeline); + + self.widgets_hud.render(&mut render_pass) + + self.debug_hud.render(&mut render_pass) + + self.hotbar_hud.render(render_context, &mut render_pass) + } +} diff --git a/src/hud/widgets_hud.rs b/src/hud/widgets_hud.rs new file mode 100644 index 0000000..0296b60 --- /dev/null +++ b/src/hud/widgets_hud.rs @@ -0,0 +1,175 @@ +// TODO Might want to move the hotbar outside +use wgpu::{BindGroup, BufferUsage, RenderPass}; + +use crate::{ + geometry::{Geometry, GeometryBuffers}, + hud::{UI_SCALE_X, UI_SCALE_Y}, + render_context::RenderContext, + texture::Texture, + vertex::{HudVertex, Vertex}, +}; + +pub struct WidgetsHud { + texture_bind_group: BindGroup, + geometry_buffers: GeometryBuffers, + hotbar_cursor_position: usize, +} + +impl WidgetsHud { + pub fn new(render_context: &RenderContext) -> Self { + let (_, texture_bind_group) = Self::create_textures(render_context); + + let geometry = Geometry { + vertices: VERTICES.to_vec(), + indices: INDICES.to_vec(), + }; + let geometry_buffers = + GeometryBuffers::from_geometry(render_context, &geometry, BufferUsage::COPY_DST); + + Self { + texture_bind_group, + geometry_buffers, + hotbar_cursor_position: 0, + } + } + + fn create_textures(render_context: &RenderContext) -> (wgpu::BindGroupLayout, wgpu::BindGroup) { + let texture = Texture::from_bytes( + render_context, + include_bytes!("../../assets/gui/widgets.png"), + "Texture GUI widgets", + ) + .unwrap(); + + let sampler = render_context + .device + .create_sampler(&wgpu::SamplerDescriptor { + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Linear, + ..wgpu::SamplerDescriptor::default() + }); + + let bind_group_layout = + render_context + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("GUI texture bind group layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler { + comparison: false, + filtering: true, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2Array, + multisampled: false, + }, + count: None, + }, + ], + }); + + let bind_group = render_context + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("GUI texture bind group"), + layout: &bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&texture.view), + }, + ], + }); + + (bind_group_layout, bind_group) + } + + 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 as i32 + delta).rem_euclid(9) as usize; + self.redraw_hotbar_cursor(render_context); + } + + pub fn redraw_hotbar_cursor(&self, render_context: &RenderContext) { + 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], 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( + &self.geometry_buffers.vertices, + HudVertex::descriptor().array_stride * 8, + bytemuck::cast_slice(&vertices), + ); + } + + pub fn render<'a>(&'a self, render_pass: &mut RenderPass<'a>) -> usize { + // Render the HUD elements + self.geometry_buffers.apply_buffers(render_pass); + render_pass.set_bind_group(0, &self.texture_bind_group, &[]); + self.geometry_buffers.draw_indexed(render_pass); + render_pass.draw_indexed(0..self.geometry_buffers.index_count as u32, 0, 0..1); + + INDICES.len() / 3 + } +} + +#[rustfmt::skip] +pub const 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], 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], 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], 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] +pub const INDICES: [u16; 18] = [ + // Crosshair + 1, 0, 3, + 1, 3, 2, + + // Hotbar + 5, 4, 7, + 5, 7, 6, + + // Hotbar cursor + 9, 8, 11, + 9, 11, 10, +]; diff --git a/src/main.rs b/src/main.rs index 15a12ab..f472438 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,18 @@ mod aabb; mod camera; mod geometry; +mod hud; mod npc; +mod player; mod render_context; -mod renderable; mod state; mod text_renderer; mod texture; mod time; +mod utils; mod vertex; mod view; mod world; -mod utils; -mod player; use std::time::{Duration, Instant}; use wgpu::SwapChainError; diff --git a/src/renderable.rs b/src/renderable.rs deleted file mode 100644 index 4fd2815..0000000 --- a/src/renderable.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::time::Duration; - -use wgpu::RenderPass; - -use crate::{camera::Camera, render_context::RenderContext, view::View}; - -pub trait Renderable { - fn update( - &mut self, - render_context: &RenderContext, - dt: Duration, - render_time: Duration, - camera: &Camera, - ); - fn render<'a>(&'a self, render_pass: &mut RenderPass<'a>, view: &View) -> usize; -} diff --git a/src/state/hud_state.rs b/src/state/hud_state.rs deleted file mode 100644 index 22f5ba3..0000000 --- a/src/state/hud_state.rs +++ /dev/null @@ -1,398 +0,0 @@ -use std::{ - convert::TryInto, - time::{Duration, Instant}, -}; - -use cgmath::Vector3; -use wgpu::{BufferUsage, CommandEncoder, SwapChainTexture}; - -use crate::{ - geometry::{Geometry, GeometryBuffers}, - render_context::RenderContext, - state::PRIMITIVE_STATE, - text_renderer::{self, TextRenderer}, - texture::Texture, - vertex::{HudVertex, Vertex}, - world::block::BlockType, -}; - -// TODO update aspect ratio when resizing -const UI_SCALE_X: f32 = 0.0045; -const UI_SCALE_Y: f32 = 0.008; - -pub struct HudState { - texture_bind_group: wgpu::BindGroup, - render_pipeline: wgpu::RenderPipeline, - hud_geometry_buffers: GeometryBuffers, - - text_renderer: TextRenderer, - - fps_geometry_buffers: GeometryBuffers, - fps_instant: Instant, - fps_frames: u32, - fps_elapsed: Duration, - - coordinates_geometry_buffers: GeometryBuffers, - coordinates_last: Vector3, - - pub hotbar_cursor_position: usize, - hotbar_blocks: [Option; 9], - hotbar_block_buffers: Option>, -} - -impl HudState { - pub fn new(render_context: &RenderContext) -> Self { - let (texture_bind_group_layout, texture_bind_group) = Self::create_textures(render_context); - - let render_pipeline = - Self::create_render_pipeline(render_context, &[&texture_bind_group_layout]); - - // HUD buffers - let hud_geometry = Geometry { - vertices: HUD_VERTICES.to_vec(), - indices: HUD_INDICES.to_vec(), - }; - let hud_geometry_buffers = - GeometryBuffers::from_geometry(render_context, &hud_geometry, BufferUsage::COPY_DST); - - // Text buffers - let text_renderer = TextRenderer::new(render_context).unwrap(); - let fps_geometry_buffers = - text_renderer.string_to_buffers(&render_context, -0.98, 0.97, ""); - let coordinates_geometry_buffers = - text_renderer.string_to_buffers(&render_context, -0.98, 0.97 - text_renderer::DY, ""); - - 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, - - hud_geometry_buffers, - - fps_geometry_buffers, - fps_instant: Instant::now(), - fps_frames: 0, - fps_elapsed: Duration::from_secs(0), - - coordinates_geometry_buffers, - 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) { - let elapsed = self.fps_instant.elapsed(); - self.fps_instant = Instant::now(); - self.fps_elapsed += elapsed; - self.fps_frames += 1; - - if self.fps_elapsed.as_millis() >= 500 { - let frametime = self.fps_elapsed / self.fps_frames; - let fps = 1.0 / frametime.as_secs_f32(); - - let string = format!("{:<5.0} fps", fps); - self.fps_geometry_buffers = - self.text_renderer - .string_to_buffers(render_context, -0.98, 0.97, &string); - - self.fps_elapsed = Duration::from_secs(0); - self.fps_frames = 0; - } - - if position != &self.coordinates_last { - let string = format!("({:.1},{:.1},{:.1})", position.x, position.y, position.z,); - self.coordinates_geometry_buffers = self.text_renderer.string_to_buffers( - render_context, - -0.98, - 0.97 - text_renderer::DY * 1.3, - &string, - ); - } - } - - pub fn render( - &self, - render_context: &RenderContext, - frame: &SwapChainTexture, - render_encoder: &mut CommandEncoder, - ) -> anyhow::Result { - let mut render_pass = render_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &frame.view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }, - }], - ..Default::default() - }); - - render_pass.set_pipeline(&self.render_pipeline); - - // Render the HUD elements - self.hud_geometry_buffers.apply_buffers(&mut render_pass); - render_pass.set_bind_group(0, &self.texture_bind_group, &[]); - 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 the FPS text - self.fps_geometry_buffers.apply_buffers(&mut render_pass); - render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); - self.fps_geometry_buffers.draw_indexed(&mut render_pass); - - // Render the coordinates text - self.coordinates_geometry_buffers - .apply_buffers(&mut render_pass); - render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]); - 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 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], 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( - &self.hud_geometry_buffers.vertices, - HudVertex::descriptor().array_stride * 8, - bytemuck::cast_slice(&vertices), - ); - } - - 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 as i32 + delta).rem_euclid(9) as usize; - self.redraw_hotbar_cursor(render_context); - } - - fn create_textures(render_context: &RenderContext) -> (wgpu::BindGroupLayout, wgpu::BindGroup) { - let texture = Texture::from_bytes( - render_context, - include_bytes!("../../assets/gui/widgets.png"), - "Texture GUI widgets", - ) - .unwrap(); - - let sampler = render_context - .device - .create_sampler(&wgpu::SamplerDescriptor { - mag_filter: wgpu::FilterMode::Nearest, - min_filter: wgpu::FilterMode::Linear, - ..wgpu::SamplerDescriptor::default() - }); - - let bind_group_layout = - render_context - .device - .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("GUI texture bind group layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - comparison: false, - filtering: true, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2Array, - multisampled: false, - }, - count: None, - }, - ], - }); - - let bind_group = render_context - .device - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("GUI texture bind group"), - layout: &bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Sampler(&sampler), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&texture.view), - }, - ], - }); - - (bind_group_layout, bind_group) - } - - fn create_render_pipeline( - render_context: &RenderContext, - bind_group_layouts: &[&wgpu::BindGroupLayout], - ) -> wgpu::RenderPipeline { - let module = &render_context - .device - .create_shader_module(&wgpu::ShaderModuleDescriptor { - label: Some("UI shader"), - flags: wgpu::ShaderFlags::all(), - source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/ui.wgsl").into()), - }); - - let pipeline_layout = - render_context - .device - .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("UI render pipeline layout"), - bind_group_layouts, - push_constant_ranges: &[], - }); - - render_context - .device - .create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("UI render pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module, - entry_point: "main", - buffers: &[HudVertex::descriptor()], - }, - fragment: Some(wgpu::FragmentState { - module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: render_context.swap_chain_descriptor.format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrite::ALL, - }], - }), - primitive: PRIMITIVE_STATE, - depth_stencil: None, - multisample: Default::default(), - }) - } -} - -#[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], 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], 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], 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] -pub const HUD_INDICES: [u16; 18] = [ - // Crosshair - 1, 0, 3, - 1, 3, 2, - - // Hotbar - 5, 4, 7, - 5, 7, 6, - - // Hotbar cursor - 9, 8, 11, - 9, 11, 10, -]; diff --git a/src/state/mod.rs b/src/state/mod.rs index d05b84c..467cccd 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -1,19 +1,16 @@ -pub mod hud_state; pub mod world_state; use std::time::{Duration, Instant}; -use cgmath::EuclideanSpace; use winit::{ dpi::PhysicalSize, event::{DeviceEvent, ElementState, MouseScrollDelta, VirtualKeyCode, WindowEvent}, window::Window, }; -use hud_state::HudState; use world_state::WorldState; -use crate::{render_context::RenderContext, texture::TextureManager}; +use crate::{hud::Hud, render_context::RenderContext, texture::TextureManager}; pub const PRIMITIVE_STATE: wgpu::PrimitiveState = wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, @@ -29,9 +26,10 @@ pub struct State { pub window_size: PhysicalSize, render_context: RenderContext, pub world_state: WorldState, - hud_state: HudState, pub mouse_grabbed: bool, + + pub hud: Hud, } impl State { @@ -110,16 +108,18 @@ impl State { render_context.texture_manager = Some(texture_manager); let world_state = WorldState::new(&render_context); - let hud_state = HudState::new(&render_context); + + let hud = Hud::new(&render_context); Self { window_size, render_context, world_state, - hud_state, mouse_grabbed: false, + + hud, } } @@ -137,18 +137,24 @@ impl State { ); } + fn set_hotbar_cursor(&mut self, i: usize) { + self.hud + .widgets_hud + .set_hotbar_cursor(&self.render_context, i); + } + fn input_keyboard(&mut self, key_code: VirtualKeyCode, state: ElementState) { if state == ElementState::Pressed { match key_code { - 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), + VirtualKeyCode::Key1 => self.set_hotbar_cursor(0), + VirtualKeyCode::Key2 => self.set_hotbar_cursor(1), + VirtualKeyCode::Key3 => self.set_hotbar_cursor(2), + VirtualKeyCode::Key4 => self.set_hotbar_cursor(3), + VirtualKeyCode::Key5 => self.set_hotbar_cursor(4), + VirtualKeyCode::Key6 => self.set_hotbar_cursor(5), + VirtualKeyCode::Key7 => self.set_hotbar_cursor(6), + VirtualKeyCode::Key8 => self.set_hotbar_cursor(7), + VirtualKeyCode::Key9 => self.set_hotbar_cursor(8), _ => self.world_state.input_keyboard(key_code, state), } } else { @@ -175,14 +181,15 @@ impl State { } if self.mouse_grabbed => self.world_state.input_mouse_button( button, &self.render_context, - self.hud_state.selected_block_type(), + None, // TODO ), WindowEvent::MouseWheel { delta: MouseScrollDelta::LineDelta(_, delta), .. } => self - .hud_state + .hud + .widgets_hud .move_hotbar_cursor(&self.render_context, -*delta as i32), _ => (), @@ -198,37 +205,35 @@ impl State { pub fn update(&mut self, dt: Duration, render_time: Duration) { self.world_state .update(dt, render_time, &self.render_context); - self.hud_state.update( - &self.render_context, - &self.world_state.player.view.camera.position.to_vec(), - ); + + self.hud + .update(&self.render_context, &self.world_state.player.view.camera) } pub fn render(&mut self) -> anyhow::Result<(usize, Duration)> { let render_start = Instant::now(); - Ok({ - let frame = self.render_context.swap_chain.get_current_frame()?.output; + let frame = self.render_context.swap_chain.get_current_frame()?.output; - let mut render_encoder = self - .render_context - .device - .create_command_encoder(&Default::default()); + let mut render_encoder = self + .render_context + .device + .create_command_encoder(&Default::default()); - let mut triangle_count = 0; - 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)?; + let mut triangle_count = 0; + triangle_count += + self.world_state + .render(&self.render_context, &frame, &mut render_encoder); - self.render_context - .queue - .submit(std::iter::once(render_encoder.finish())); - let render_time = render_start.elapsed(); + triangle_count += self + .hud + .render(&self.render_context, &mut render_encoder, &frame); - (triangle_count, render_time) - }) + self.render_context + .queue + .submit(std::iter::once(render_encoder.finish())); + let render_time = render_start.elapsed(); + + Ok((triangle_count, render_time)) } } diff --git a/src/state/world_state.rs b/src/state/world_state.rs index c22ea58..652d535 100644 --- a/src/state/world_state.rs +++ b/src/state/world_state.rs @@ -12,7 +12,6 @@ use winit::{ use crate::{ player::Player, render_context::RenderContext, - renderable::Renderable, texture::Texture, time::Time, vertex::{BlockVertex, Vertex}, @@ -123,8 +122,7 @@ impl WorldState { let (time, time_buffer, time_layout, time_bind_group) = Self::create_time(render_context); let player = Player::new(render_context); - let mut world = World::new(); - world.npc.load_geometry(render_context); + let world = World::new(render_context); let shader = render_context.device.create_shader_module( &(wgpu::ShaderModuleDescriptor { @@ -196,7 +194,6 @@ impl WorldState { stencil_ops: None, }), }); - render_pass.set_pipeline(&self.render_pipeline); let texture_manager = render_context.texture_manager.as_ref().unwrap(); @@ -236,24 +233,18 @@ impl WorldState { VirtualKeyCode::D => self.player.right_pressed = pressed, VirtualKeyCode::F2 if pressed => self.player.creative ^= true, VirtualKeyCode::Space => { - // TODO aaaaaaaaaaaaaaaaaa - self.player.up_speed = if pressed { - if self.player.creative { - 1.0 - } else { - if self.player.up_speed.abs() < 0.05 { - 0.6 - } else { - self.player.up_speed - } + self.player.up_speed = match (pressed, self.player.creative) { + // Creative + (true, true) => 1.0, + (false, true) => 0.0, + + // Not creative + (true, false) => { + // TODO Don't allow player to jump in mid-air + 0.6 } - } else { - if self.player.creative { - 0.0 - } else { - self.player.up_speed - } - } + (false, false) => self.player.up_speed, + }; } VirtualKeyCode::LShift if self.player.creative => { self.player.up_speed = if pressed { -1.0 } else { 0.0 } diff --git a/src/world/mod.rs b/src/world/mod.rs index 4c3d33f..ed84f31 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -12,7 +12,6 @@ use crate::{ camera::Camera, npc::Npc, render_context::RenderContext, - renderable::Renderable, view::View, world::{ block::{Block, BlockType}, @@ -42,9 +41,9 @@ pub const WORLD_HEIGHT: isize = 16 * 16 / CHUNK_ISIZE; const DEBUG_IO: bool = false; -impl Renderable for World { +impl World { #[allow(clippy::collapsible_else_if)] - fn update( + pub fn update( &mut self, render_context: &RenderContext, dt: Duration, @@ -148,10 +147,11 @@ impl Renderable for World { } } - fn render<'a>(&'a self, render_pass: &mut RenderPass<'a>, view: &View) -> usize { + pub fn render<'a>(&'a self, render_pass: &mut RenderPass<'a>, view: &View) -> usize { let mut triangle_count = 0; for (position, chunk) in &self.chunks { + // TODO Reimplement frustrum culling if !chunk.is_visible(position * CHUNK_ISIZE, &view) { continue; } @@ -171,9 +171,10 @@ impl Renderable for World { } impl World { - pub fn new() -> Self { + pub fn new(render_context: &RenderContext) -> Self { let chunks = AHashMap::new(); - let npc = Npc::load(); + let mut npc = Npc::load(); + npc.load_geometry(render_context); let chunk_database = sled::Config::new() .path("chunks")