diff --git a/src/game/components.rs b/src/game/components.rs new file mode 100644 index 0000000..ba4f111 --- /dev/null +++ b/src/game/components.rs @@ -0,0 +1,14 @@ +use crate::{input::InputState, text::update_text, vulkan::{VulkanRenderer, gameobject::{GameObject, GameObjectHandle, Updatable}}}; + +use super::GameState; + +pub struct FpsCounter { + pub game_object: GameObjectHandle +} + +impl Updatable for FpsCounter { + fn update(&mut self, delta_time: f32, _input: &InputState, game_state: &mut GameState, game_objects: &mut Vec, renderer: &mut VulkanRenderer) { + let text = format!("{}ms\n{}fps", delta_time * 1000., 1. / delta_time); + update_text(self.game_object, &text, 30., renderer, &mut game_state.brush, game_objects) + } +} \ No newline at end of file diff --git a/src/game/mod.rs b/src/game/mod.rs index 084f796..0afca79 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,36 +1,38 @@ use std::time::SystemTime; use cgmath::{Deg, Euler, Quaternion, vec3}; -use glyph_brush::{BrushAction, BrushError, GlyphBrush, GlyphBrushBuilder, GlyphVertex, Rectangle, Section, Text}; -use glyph_brush::ab_glyph::FontArc; -use vulkano::format::Format; -use vulkano::image::Dimensions; -use vulkano::sampler::{Filter, SamplerAddressMode}; +use glyph_brush::GlyphBrush; use winit::event::Event; use level::{load_level, save_level}; use player::Player; +use crate::game::components::FpsCounter; +use crate::text::{create_brush, create_text_object}; use crate::{config::LogConfig, vulkan}; use crate::input::InputState; -use crate::vulkan::{Game, MeshHandle, TextVertex, TextureHandle, Vertex, VulkanRenderer}; +use crate::vulkan::{Game, MeshHandle, TextVertex, Vertex, VulkanRenderer}; use crate::vulkan::gameobject::{GameObject, GameObjectHandle, Updatable}; -use crate::vulkan::mesh::{self, CPUMesh, CPUVertexList}; +use crate::vulkan::mesh::{self}; use crate::vulkan::pipelines::vs::ty::ObjectUniformData; pub mod player; mod level; +mod components; pub struct TestGame { pub input: InputState, pub player: Player, pub game_objects: Vec, - pub text_objects: Vec, pub log_config: LogConfig, pub texture_index_counter: usize, pub last_time: f32, pub components: Vec>, + pub game_state: GameState, +} + +pub struct GameState { pub paused: bool, - pub font: FontArc, + pub brush: GlyphBrush>, pub test_str: String, } @@ -53,11 +55,14 @@ impl Game for TestGame { let input = &self.input; let objs = &mut self.game_objects; let components = &mut self.components; + let paused = self.game_state.paused; + let state = &mut self.game_state; - if !self.paused { + if !paused { components.iter_mut().for_each(|component| { - component.update(frame_time, &input, renderer, objs); + component.update(frame_time, &input, state, objs, renderer); }); + self.player.update(frame_time, &input, state, objs, renderer); } // User interaction @@ -85,22 +90,18 @@ impl Game for TestGame { } if self.input.button_just_pressed("test") { - self.paused = !self.paused; + self.game_state.paused = !self.game_state.paused; } for char in self.input.typed_characters.iter() { match char { - '\u{8}' => { self.test_str.pop(); }, - c => { self.test_str.push(*c); }, + '\u{8}' => { self.game_state.test_str.pop(); }, + c => { self.game_state.test_str.push(*c); }, } } - self.text_objects[0].update_text(&self.test_str, 30., renderer, &mut self.game_objects); // Custom game object stuff let light_pos = vec3(2.0, 0.5, 2.0); - if !self.paused { - self.player.update(frame_time, &self.input, renderer, &mut self.game_objects); - } // End frame self.last_time = time; @@ -127,14 +128,15 @@ impl TestGame { input: InputState::new(toml_path, log_config), player: Player::new(3., 30.), game_objects: vec![], - text_objects: vec![], log_config, texture_index_counter: 0, last_time: 0.0, components: vec![], - paused: false, - font: FontArc::try_from_slice(include_bytes!("../../models/FiraCode-Regular.ttf")).unwrap(), - test_str: String::new() + game_state: GameState { + brush: create_brush(), + paused: false, + test_str: "".to_string(), + } } } @@ -142,8 +144,9 @@ impl TestGame { load_level("levels/test.lvl", self, renderer).unwrap(); println!("Game loaded!"); - let text_obj = self.create_text_object(renderer, "aaxx", 128.); - self.text_objects.push(text_obj); + let text_mesh = create_text_object(&mut self.game_state.brush, renderer, "aaxx", 30.); + let fps = Box::new(FpsCounter { game_object: self.add_game_object(renderer, text_mesh) }); + self.components.push(fps); } pub fn offset_texture_id(&mut self, local_tex_id: Option) -> usize { @@ -197,45 +200,6 @@ impl TestGame { self.game_objects.len() - 1 } - fn convert_vertices(vertex_data: GlyphVertex) -> Vec { - let result = vec![ - TextVertex { position: [vertex_data.pixel_coords.min.x, vertex_data.pixel_coords.min.y, 0.], uv: [vertex_data.tex_coords.min.x, vertex_data.tex_coords.min.y] }, - TextVertex { position: [vertex_data.pixel_coords.min.x, vertex_data.pixel_coords.max.y, 0.], uv: [vertex_data.tex_coords.min.x, vertex_data.tex_coords.max.y] }, - TextVertex { position: [vertex_data.pixel_coords.max.x, vertex_data.pixel_coords.min.y, 0.], uv: [vertex_data.tex_coords.max.x, vertex_data.tex_coords.min.y] }, - TextVertex { position: [vertex_data.pixel_coords.max.x, vertex_data.pixel_coords.max.y, 0.], uv: [vertex_data.tex_coords.max.x, vertex_data.tex_coords.max.y] }, - ]; - result - } - - pub fn create_text_object(&mut self, renderer: &mut VulkanRenderer, text: &str, size: f32) -> TextObject { - let mut uploaded_texture = None; - let mut uploaded_mesh = None; - - let mut glyph_brush = GlyphBrushBuilder::using_font(self.font.clone()).build(); - - glyph_brush.queue(Section::default() - .add_text(Text::new(text).with_scale(size)) - .with_bounds((renderer.game_data.dimensions[0] as f32, renderer.game_data.dimensions[1] as f32)) - ); - match glyph_brush.process_queued(|rect, text_data| { - uploaded_texture = update_text_texture(None, renderer, rect, text_data); - self.texture_index_counter += 1; - }, Self::convert_vertices) { - Ok(BrushAction::Draw(quads)) => { - uploaded_mesh = update_text_quads(quads, self.texture_index_counter - 1, None, renderer); - }, - Ok(BrushAction::ReDraw) => {}, - Err(BrushError::TextureTooSmall { suggested }) => { - glyph_brush.resize_texture(suggested.0, suggested.1); - }, - }; - - TextObject { - brush: glyph_brush, - game_object: self.add_game_object(renderer, uploaded_mesh.unwrap()), - } - } - pub fn clear_level(&mut self, renderer: &mut VulkanRenderer) { self.game_objects.clear(); self.texture_index_counter = 0; @@ -247,79 +211,3 @@ pub fn print_quat_as_euler(quat: Quaternion) { let euler = Euler::from(quat); print!("({:?},{:?},{:?})", Deg::from(euler.x), Deg::from(euler.y), Deg::from(euler.z)); } - -pub fn update_text_texture(old_texture: Option, renderer: &mut VulkanRenderer, rect: Rectangle, text_data: &[u8]) -> Option { - let size = u32::max(rect.width(), rect.height()); - - if let Some(tex_handle) = old_texture { - renderer.update_texture(tex_handle, text_data, [rect.width(), rect.height(), 1], [rect.min[0], rect.min[1], 0], renderer.device.clone()); - None - } else { - let tex = renderer.upload_texture(text_data, size, size, Format::R8Unorm, Filter::Nearest, SamplerAddressMode::ClampToEdge, renderer.device.clone()); - renderer.game_data.textures.push(tex.clone()); - Some(renderer.game_data.textures.len() - 1) - } -} - -pub fn update_text_quads(quads: Vec>, texture_index: usize, mesh_index: Option, renderer: &mut VulkanRenderer) -> Option { - let mut final_vertices = vec![]; - let mut final_indices: Vec = vec![]; - let mut index_offset = 0; - - for mut quad in quads { - let len = quad.len(); - final_vertices.append(&mut quad); - final_indices.append(&mut [0, 2, 3, 0, 3, 1].iter().map(|x| *x + index_offset).collect()); - index_offset += len as u32; - } - if let Some(idx) = mesh_index { - renderer.update_mesh(idx, CPUVertexList::VertexText(final_vertices), final_indices); - None - } else { - let mesh = CPUMesh { - vertices: CPUVertexList::VertexText(final_vertices), - indices: final_indices, - local_texture_index: Some(texture_index), - local_normal_map_index: None, - name: Some("font_texture".to_string()), - }; - - let mesh_index = renderer.upload_mesh(mesh, None); - Some(MeshHandle { - index: mesh_index, - diffuse_handle: texture_index, - normal_handle: None, - original_path: None, - pipeline_index: 1 - }) - } -} - -pub struct TextObject { - pub brush: GlyphBrush>, - pub game_object: GameObjectHandle, -} - -impl TextObject { - pub fn update_text(&mut self, new_text: &str, new_size: f32, renderer: &mut VulkanRenderer, game_objects: &mut Vec) { - self.brush.queue(Section::default().add_text(Text::new(new_text).with_scale(new_size))); - - let go = &mut game_objects[self.game_object]; - let mesh_index = go.mesh_index; - - match self.brush.process_queued(|rect, text_data| { - update_text_texture(Some(go.textures.texture_index), renderer, rect, text_data); - }, TestGame::convert_vertices) { - Ok(BrushAction::Draw(quads)) => { - update_text_quads(quads, 420, Some(mesh_index), renderer); - }, - Ok(BrushAction::ReDraw) => {}, - Err(BrushError::TextureTooSmall { suggested }) => { - let size = Dimensions::Dim2d { width: suggested.0, height: suggested.1 }; - renderer.resize_texture(go, go.textures.texture_index, size); - self.brush.resize_texture(suggested.0, suggested.1); - self.update_text(new_text, new_size, renderer, game_objects); - }, - } - } -} \ No newline at end of file diff --git a/src/game/player.rs b/src/game/player.rs index ac16c36..30f7f7f 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -11,6 +11,8 @@ use crate::vulkan::{ VulkanRenderer }; +use super::GameState; + pub struct Camera { pub fov_y: f32, pub position: Vector3, @@ -178,7 +180,7 @@ pub fn intersect_triangle(ray_origin: Vector3, ray_direction: Vector3, } impl Updatable for Player { - fn update(&mut self, delta_time: f32, input: &InputState, renderer: &mut VulkanRenderer, game_objects: &mut Vec) { + fn update(&mut self, delta_time: f32, input: &InputState, game_state: &mut GameState, game_objects: &mut Vec, renderer: &mut VulkanRenderer) { // Edit mode if input.button_just_pressed("toggle_edit") { if self.movement_mode == FirstPerson { diff --git a/src/main.rs b/src/main.rs index 63b77ef..11e4797 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ mod input; mod config; mod game; mod tests; +mod text; fn main() { let log_config = LogConfig::from_file("config/log.toml"); diff --git a/src/text.rs b/src/text.rs new file mode 100644 index 0000000..815398e --- /dev/null +++ b/src/text.rs @@ -0,0 +1,113 @@ +use glyph_brush::{BrushAction, BrushError, GlyphBrush, GlyphBrushBuilder, GlyphVertex, Rectangle, Section, Text, ab_glyph::FontArc}; +use vulkano::{format::Format, image::Dimensions, sampler::{Filter, SamplerAddressMode}}; + +use crate::vulkan::{MeshHandle, TextVertex, TextureHandle, VulkanRenderer, gameobject::{GameObject, GameObjectHandle}, mesh::{CPUMesh, CPUVertexList}}; + +pub fn update_text(game_object_handle: GameObjectHandle, new_text: &str, new_size: f32, renderer: &mut VulkanRenderer, brush: &mut GlyphBrush>, game_objects: &mut Vec) { + brush.queue(Section::default() + .add_text(Text::new(new_text).with_scale(new_size)) + .with_bounds((renderer.game_data.dimensions[0] as f32, renderer.game_data.dimensions[1] as f32))); + + let go = &mut game_objects[game_object_handle]; + let mesh_index = go.mesh_index; + + match brush.process_queued(|rect, text_data| { + update_text_texture(Some(go.textures.texture_index), renderer, rect, text_data); + }, convert_vertices) { + Ok(BrushAction::Draw(quads)) => { + update_text_quads(quads, 420, Some(mesh_index), renderer); + }, + Ok(BrushAction::ReDraw) => {}, + Err(BrushError::TextureTooSmall { suggested }) => { + let size = Dimensions::Dim2d { width: suggested.0, height: suggested.1 }; + renderer.resize_texture(go, go.textures.texture_index, size); + brush.resize_texture(suggested.0, suggested.1); + update_text(game_object_handle, new_text, new_size, renderer, brush, game_objects); + }, + } +} + +pub fn create_brush() -> GlyphBrush { + let font = FontArc::try_from_slice(include_bytes!("../models/FiraCode-Regular.ttf")).unwrap(); + GlyphBrushBuilder::using_font(font).build() +} + +pub fn create_text_object(brush: &mut GlyphBrush>, renderer: &mut VulkanRenderer, text: &str, size: f32) -> MeshHandle { + let mut uploaded_texture = None; + let mut uploaded_mesh = None; + + brush.queue(Section::default() + .add_text(Text::new(text).with_scale(size)) + .with_bounds((renderer.game_data.dimensions[0] as f32, renderer.game_data.dimensions[1] as f32)) + ); + match brush.process_queued(|rect, text_data| { + uploaded_texture = update_text_texture(None, renderer, rect, text_data); + }, convert_vertices) { + Ok(BrushAction::Draw(quads)) => { + uploaded_mesh = update_text_quads(quads, uploaded_texture.unwrap(), None, renderer); + }, + Ok(BrushAction::ReDraw) => {}, + Err(BrushError::TextureTooSmall { suggested }) => { + brush.resize_texture(suggested.0, suggested.1); + }, + }; + + uploaded_mesh.unwrap() +} + +pub fn update_text_texture(old_texture: Option, renderer: &mut VulkanRenderer, rect: Rectangle, text_data: &[u8]) -> Option { + let size = u32::max(rect.width(), rect.height()); + + if let Some(tex_handle) = old_texture { + renderer.update_texture(tex_handle, text_data, [rect.width(), rect.height(), 1], [rect.min[0], rect.min[1], 0], renderer.device.clone()); + None + } else { + let tex = renderer.upload_texture(text_data, size, size, Format::R8Unorm, Filter::Nearest, SamplerAddressMode::ClampToEdge, renderer.device.clone()); + renderer.game_data.textures.push(tex.clone()); + Some(renderer.game_data.textures.len() - 1) + } +} + +pub fn update_text_quads(quads: Vec>, texture_index: usize, mesh_index: Option, renderer: &mut VulkanRenderer) -> Option { + let mut final_vertices = vec![]; + let mut final_indices: Vec = vec![]; + let mut index_offset = 0; + + for mut quad in quads { + let len = quad.len(); + final_vertices.append(&mut quad); + final_indices.append(&mut [0, 2, 3, 0, 3, 1].iter().map(|x| *x + index_offset).collect()); + index_offset += len as u32; + } + if let Some(idx) = mesh_index { + renderer.update_mesh(idx, CPUVertexList::VertexText(final_vertices), final_indices); + None + } else { + let mesh = CPUMesh { + vertices: CPUVertexList::VertexText(final_vertices), + indices: final_indices, + local_texture_index: Some(texture_index), + local_normal_map_index: None, + name: Some("font_texture".to_string()), + }; + + let mesh_index = renderer.upload_mesh(mesh, None); + Some(MeshHandle { + index: mesh_index, + diffuse_handle: texture_index, + normal_handle: None, + original_path: None, + pipeline_index: 1 + }) + } +} + +fn convert_vertices(vertex_data: GlyphVertex) -> Vec { + let result = vec![ + TextVertex { position: [vertex_data.pixel_coords.min.x, vertex_data.pixel_coords.min.y, 0.], uv: [vertex_data.tex_coords.min.x, vertex_data.tex_coords.min.y] }, + TextVertex { position: [vertex_data.pixel_coords.min.x, vertex_data.pixel_coords.max.y, 0.], uv: [vertex_data.tex_coords.min.x, vertex_data.tex_coords.max.y] }, + TextVertex { position: [vertex_data.pixel_coords.max.x, vertex_data.pixel_coords.min.y, 0.], uv: [vertex_data.tex_coords.max.x, vertex_data.tex_coords.min.y] }, + TextVertex { position: [vertex_data.pixel_coords.max.x, vertex_data.pixel_coords.max.y, 0.], uv: [vertex_data.tex_coords.max.x, vertex_data.tex_coords.max.y] }, + ]; + result +} \ No newline at end of file diff --git a/src/vulkan/gameobject.rs b/src/vulkan/gameobject.rs index 52b03d9..b6303e0 100644 --- a/src/vulkan/gameobject.rs +++ b/src/vulkan/gameobject.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use cgmath::{Deg, Euler, Matrix4, Quaternion, Vector3}; +use crate::game::GameState; use crate::input::InputState; use crate::vulkan::{RendererDescriptorSets, TextureHandle}; use crate::vulkan::{MeshHandle, VulkanRenderer}; @@ -87,5 +88,5 @@ impl GameObject { pub type GameObjectHandle = usize; pub trait Updatable { - fn update(&mut self, delta_time: f32, input: &InputState, renderer: &mut VulkanRenderer, game_objects: &mut Vec); + fn update(&mut self, delta_time: f32, input: &InputState, game_state: &mut GameState, game_objects: &mut Vec, renderer: &mut VulkanRenderer); } diff --git a/src/vulkan/pipelines.rs b/src/vulkan/pipelines.rs index bd09c7d..8709d47 100644 --- a/src/vulkan/pipelines.rs +++ b/src/vulkan/pipelines.rs @@ -319,7 +319,6 @@ impl TextShader { .build(device.clone()) .unwrap()); - println!("layout: {:?}", vulkano::descriptor::PipelineLayoutAbstract::descriptor_set_layout(&gp, 0).unwrap().descriptors_count()); gp } }