Move hud_state stuff into own "renderables"
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Sijmen 2021-06-10 13:18:50 +02:00
parent 11348f8086
commit aa8d7aed83
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
10 changed files with 571 additions and 485 deletions

90
src/hud/debug_hud.rs Normal file
View file

@ -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<u16>,
coordinates_last: Point3<f32>,
coordinates_geometry_buffers: GeometryBuffers<u16>,
}
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<f32>) {
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
}
}

97
src/hud/hotbar_hud.rs Normal file
View file

@ -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<BlockType>; 9],
pub last_blocks: [Option<BlockType>; 9],
pub geometry_buffers: GeometryBuffers<u16>,
}
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::<HudVertex, _>::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<HudVertex, u16> {
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)
}
}

141
src/hud/mod.rs Normal file
View file

@ -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)
}
}

175
src/hud/widgets_hud.rs Normal file
View file

@ -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<u16>,
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,
];

View file

@ -1,18 +1,18 @@
mod aabb; mod aabb;
mod camera; mod camera;
mod geometry; mod geometry;
mod hud;
mod npc; mod npc;
mod player;
mod render_context; mod render_context;
mod renderable;
mod state; mod state;
mod text_renderer; mod text_renderer;
mod texture; mod texture;
mod time; mod time;
mod utils;
mod vertex; mod vertex;
mod view; mod view;
mod world; mod world;
mod utils;
mod player;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use wgpu::SwapChainError; use wgpu::SwapChainError;

View file

@ -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;
}

View file

@ -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<u16>,
text_renderer: TextRenderer,
fps_geometry_buffers: GeometryBuffers<u16>,
fps_instant: Instant,
fps_frames: u32,
fps_elapsed: Duration,
coordinates_geometry_buffers: GeometryBuffers<u16>,
coordinates_last: Vector3<f32>,
pub hotbar_cursor_position: usize,
hotbar_blocks: [Option<BlockType>; 9],
hotbar_block_buffers: Option<GeometryBuffers<u16>>,
}
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<f32>) {
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<usize> {
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<BlockType> {
self.hotbar_blocks[self.hotbar_cursor_position]
}
fn hotbar_block_vertices(&self) -> Geometry<HudVertex, u16> {
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,
];

View file

@ -1,19 +1,16 @@
pub mod hud_state;
pub mod world_state; pub mod world_state;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use cgmath::EuclideanSpace;
use winit::{ use winit::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{DeviceEvent, ElementState, MouseScrollDelta, VirtualKeyCode, WindowEvent}, event::{DeviceEvent, ElementState, MouseScrollDelta, VirtualKeyCode, WindowEvent},
window::Window, window::Window,
}; };
use hud_state::HudState;
use world_state::WorldState; 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 { pub const PRIMITIVE_STATE: wgpu::PrimitiveState = wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
@ -29,9 +26,10 @@ pub struct State {
pub window_size: PhysicalSize<u32>, pub window_size: PhysicalSize<u32>,
render_context: RenderContext, render_context: RenderContext,
pub world_state: WorldState, pub world_state: WorldState,
hud_state: HudState,
pub mouse_grabbed: bool, pub mouse_grabbed: bool,
pub hud: Hud,
} }
impl State { impl State {
@ -110,16 +108,18 @@ impl State {
render_context.texture_manager = Some(texture_manager); render_context.texture_manager = Some(texture_manager);
let world_state = WorldState::new(&render_context); let world_state = WorldState::new(&render_context);
let hud_state = HudState::new(&render_context);
let hud = Hud::new(&render_context);
Self { Self {
window_size, window_size,
render_context, render_context,
world_state, world_state,
hud_state,
mouse_grabbed: false, 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) { fn input_keyboard(&mut self, key_code: VirtualKeyCode, state: ElementState) {
if state == ElementState::Pressed { if state == ElementState::Pressed {
match key_code { match key_code {
VirtualKeyCode::Key1 => self.hud_state.set_hotbar_cursor(&self.render_context, 0), VirtualKeyCode::Key1 => self.set_hotbar_cursor(0),
VirtualKeyCode::Key2 => self.hud_state.set_hotbar_cursor(&self.render_context, 1), VirtualKeyCode::Key2 => self.set_hotbar_cursor(1),
VirtualKeyCode::Key3 => self.hud_state.set_hotbar_cursor(&self.render_context, 2), VirtualKeyCode::Key3 => self.set_hotbar_cursor(2),
VirtualKeyCode::Key4 => self.hud_state.set_hotbar_cursor(&self.render_context, 3), VirtualKeyCode::Key4 => self.set_hotbar_cursor(3),
VirtualKeyCode::Key5 => self.hud_state.set_hotbar_cursor(&self.render_context, 4), VirtualKeyCode::Key5 => self.set_hotbar_cursor(4),
VirtualKeyCode::Key6 => self.hud_state.set_hotbar_cursor(&self.render_context, 5), VirtualKeyCode::Key6 => self.set_hotbar_cursor(5),
VirtualKeyCode::Key7 => self.hud_state.set_hotbar_cursor(&self.render_context, 6), VirtualKeyCode::Key7 => self.set_hotbar_cursor(6),
VirtualKeyCode::Key8 => self.hud_state.set_hotbar_cursor(&self.render_context, 7), VirtualKeyCode::Key8 => self.set_hotbar_cursor(7),
VirtualKeyCode::Key9 => self.hud_state.set_hotbar_cursor(&self.render_context, 8), VirtualKeyCode::Key9 => self.set_hotbar_cursor(8),
_ => self.world_state.input_keyboard(key_code, state), _ => self.world_state.input_keyboard(key_code, state),
} }
} else { } else {
@ -175,14 +181,15 @@ impl State {
} if self.mouse_grabbed => self.world_state.input_mouse_button( } if self.mouse_grabbed => self.world_state.input_mouse_button(
button, button,
&self.render_context, &self.render_context,
self.hud_state.selected_block_type(), None, // TODO
), ),
WindowEvent::MouseWheel { WindowEvent::MouseWheel {
delta: MouseScrollDelta::LineDelta(_, delta), delta: MouseScrollDelta::LineDelta(_, delta),
.. ..
} => self } => self
.hud_state .hud
.widgets_hud
.move_hotbar_cursor(&self.render_context, -*delta as i32), .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) { pub fn update(&mut self, dt: Duration, render_time: Duration) {
self.world_state self.world_state
.update(dt, render_time, &self.render_context); .update(dt, render_time, &self.render_context);
self.hud_state.update(
&self.render_context, self.hud
&self.world_state.player.view.camera.position.to_vec(), .update(&self.render_context, &self.world_state.player.view.camera)
);
} }
pub fn render(&mut self) -> anyhow::Result<(usize, Duration)> { pub fn render(&mut self) -> anyhow::Result<(usize, Duration)> {
let render_start = Instant::now(); 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 let mut render_encoder = self
.render_context .render_context
.device .device
.create_command_encoder(&Default::default()); .create_command_encoder(&Default::default());
let mut triangle_count = 0; let mut triangle_count = 0;
triangle_count += triangle_count +=
self.world_state self.world_state
.render(&self.render_context, &frame, &mut render_encoder); .render(&self.render_context, &frame, &mut render_encoder);
triangle_count +=
self.hud_state
.render(&self.render_context, &frame, &mut render_encoder)?;
self.render_context triangle_count += self
.queue .hud
.submit(std::iter::once(render_encoder.finish())); .render(&self.render_context, &mut render_encoder, &frame);
let render_time = render_start.elapsed();
(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))
} }
} }

View file

@ -12,7 +12,6 @@ use winit::{
use crate::{ use crate::{
player::Player, player::Player,
render_context::RenderContext, render_context::RenderContext,
renderable::Renderable,
texture::Texture, texture::Texture,
time::Time, time::Time,
vertex::{BlockVertex, Vertex}, vertex::{BlockVertex, Vertex},
@ -123,8 +122,7 @@ impl WorldState {
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);
let player = Player::new(render_context); let player = Player::new(render_context);
let mut world = World::new(); let world = World::new(render_context);
world.npc.load_geometry(render_context);
let shader = render_context.device.create_shader_module( let shader = render_context.device.create_shader_module(
&(wgpu::ShaderModuleDescriptor { &(wgpu::ShaderModuleDescriptor {
@ -196,7 +194,6 @@ impl WorldState {
stencil_ops: None, stencil_ops: None,
}), }),
}); });
render_pass.set_pipeline(&self.render_pipeline); render_pass.set_pipeline(&self.render_pipeline);
let texture_manager = render_context.texture_manager.as_ref().unwrap(); let texture_manager = render_context.texture_manager.as_ref().unwrap();
@ -236,24 +233,18 @@ impl WorldState {
VirtualKeyCode::D => self.player.right_pressed = pressed, VirtualKeyCode::D => self.player.right_pressed = pressed,
VirtualKeyCode::F2 if pressed => self.player.creative ^= true, VirtualKeyCode::F2 if pressed => self.player.creative ^= true,
VirtualKeyCode::Space => { VirtualKeyCode::Space => {
// TODO aaaaaaaaaaaaaaaaaa self.player.up_speed = match (pressed, self.player.creative) {
self.player.up_speed = if pressed { // Creative
if self.player.creative { (true, true) => 1.0,
1.0 (false, true) => 0.0,
} else {
if self.player.up_speed.abs() < 0.05 { // Not creative
0.6 (true, false) => {
} else { // TODO Don't allow player to jump in mid-air
self.player.up_speed 0.6
}
} }
} else { (false, false) => self.player.up_speed,
if self.player.creative { };
0.0
} else {
self.player.up_speed
}
}
} }
VirtualKeyCode::LShift if self.player.creative => { VirtualKeyCode::LShift if self.player.creative => {
self.player.up_speed = if pressed { -1.0 } else { 0.0 } self.player.up_speed = if pressed { -1.0 } else { 0.0 }

View file

@ -12,7 +12,6 @@ use crate::{
camera::Camera, camera::Camera,
npc::Npc, npc::Npc,
render_context::RenderContext, render_context::RenderContext,
renderable::Renderable,
view::View, view::View,
world::{ world::{
block::{Block, BlockType}, block::{Block, BlockType},
@ -42,9 +41,9 @@ pub const WORLD_HEIGHT: isize = 16 * 16 / CHUNK_ISIZE;
const DEBUG_IO: bool = false; const DEBUG_IO: bool = false;
impl Renderable for World { impl World {
#[allow(clippy::collapsible_else_if)] #[allow(clippy::collapsible_else_if)]
fn update( pub fn update(
&mut self, &mut self,
render_context: &RenderContext, render_context: &RenderContext,
dt: Duration, 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; let mut triangle_count = 0;
for (position, chunk) in &self.chunks { for (position, chunk) in &self.chunks {
// TODO Reimplement frustrum culling
if !chunk.is_visible(position * CHUNK_ISIZE, &view) { if !chunk.is_visible(position * CHUNK_ISIZE, &view) {
continue; continue;
} }
@ -171,9 +171,10 @@ impl Renderable for World {
} }
impl World { impl World {
pub fn new() -> Self { pub fn new(render_context: &RenderContext) -> Self {
let chunks = AHashMap::new(); let chunks = AHashMap::new();
let npc = Npc::load(); let mut npc = Npc::load();
npc.load_geometry(render_context);
let chunk_database = sled::Config::new() let chunk_database = sled::Config::new()
.path("chunks") .path("chunks")