Move hud_state stuff into own "renderables"
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
11348f8086
commit
aa8d7aed83
10 changed files with 571 additions and 485 deletions
90
src/hud/debug_hud.rs
Normal file
90
src/hud/debug_hud.rs
Normal 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
97
src/hud/hotbar_hud.rs
Normal 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
141
src/hud/mod.rs
Normal 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
175
src/hud/widgets_hud.rs
Normal 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,
|
||||||
|
];
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
];
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in a new issue