diff --git a/client/Cargo.toml b/client/Cargo.toml index bfe13572..3506ac5c 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -14,6 +14,8 @@ server = { path = "../server" } tracing = "0.1.10" ash = { version = "0.38.0", default-features = false, features = ["loaded", "debug", "std"] } lahar = { git = "https://github.com/Ralith/lahar", rev = "7963ae5750ea61fa0a894dbb73d3be0ac77255d2" } +yakui = { git = "https://github.com/SecondHalfGames/yakui", rev = "cbac6c06dbf948268df6d9e93346788e977cbef3" } +yakui-vulkan = { git = "https://github.com/SecondHalfGames/yakui", rev = "cbac6c06dbf948268df6d9e93346788e977cbef3" } winit = "0.29.10" ash-window = "0.13" raw-window-handle = "0.6" diff --git a/client/shaders/fog.frag b/client/shaders/fog.frag index 37ef0048..e8cf9da2 100644 --- a/client/shaders/fog.frag +++ b/client/shaders/fog.frag @@ -16,12 +16,6 @@ void main() { float view_length = length(view_pos); // Convert to true hyperbolic distance, taking care to respect atanh's domain float dist = view_length >= 1.0 ? INFINITY : atanh(view_length); - if (dot(scaled_view_pos.xy, scaled_view_pos.xy) < 0.0001) { - // Temporary code to add a cursor in the center of the window for placing/breaking blocks - // TODO: Replace with a UI element when UI exists - fog = vec4(0.0, 0.0, 0.0, 0.0); - } else { - // Exponential^k fog - fog = vec4(0.5, 0.65, 0.9, exp(-pow(dist * fog_density, 5))); - } + // Exponential^k fog + fog = vec4(0.5, 0.65, 0.9, exp(-pow(dist * fog_density, 5))); } diff --git a/client/src/graphics/base.rs b/client/src/graphics/base.rs index e3aa6fb2..0007c8da 100644 --- a/client/src/graphics/base.rs +++ b/client/src/graphics/base.rs @@ -139,7 +139,11 @@ impl Base { .queue_create_infos(&[vk::DeviceQueueCreateInfo::default() .queue_family_index(queue_family_index) .queue_priorities(&[1.0])]) - .enabled_extension_names(&device_exts), + .enabled_extension_names(&device_exts) + .push_next( + &mut vk::PhysicalDeviceDescriptorIndexingFeatures::default() + .descriptor_binding_partially_bound(true), + ), None, ) .unwrap(), diff --git a/client/src/graphics/core.rs b/client/src/graphics/core.rs index 377a35ed..5341b166 100644 --- a/client/src/graphics/core.rs +++ b/client/src/graphics/core.rs @@ -68,7 +68,7 @@ impl Core { .application_version(0) .engine_name(name) .engine_version(0) - .api_version(vk::make_api_version(0, 1, 1, 0)); + .api_version(vk::make_api_version(0, 1, 2, 0)); let mut instance_info = vk::InstanceCreateInfo::default() .application_info(&app_info) .enabled_extension_names(&exts); diff --git a/client/src/graphics/draw.rs b/client/src/graphics/draw.rs index eb7139eb..52c97dd4 100644 --- a/client/src/graphics/draw.rs +++ b/client/src/graphics/draw.rs @@ -6,6 +6,7 @@ use common::traversal; use lahar::Staged; use metrics::histogram; +use super::gui::{self, GuiState}; use super::{fog, voxels, Base, Fog, Frustum, GltfScene, Meshes, Voxels}; use crate::{Asset, Config, Loader, Sim}; use common::proto::{Character, Position}; @@ -50,6 +51,10 @@ pub struct Draw { /// Miscellany character_model: Asset, + /// Yakui Vulkan context + pub yakui_vulkan: yakui_vulkan::YakuiVulkan, + /// Yakui context + pub yak: yakui::Yakui, } /// Maximum number of simultaneous frames in flight @@ -188,6 +193,17 @@ impl Draw { }, ); + let mut yakui_vulkan_options = yakui_vulkan::Options::default(); + yakui_vulkan_options.render_pass = gfx.render_pass; + yakui_vulkan_options.subpass = 1; + let mut yakui_vulkan = yakui_vulkan::YakuiVulkan::new( + &yakui_vulkan::VulkanContext::new(device, gfx.queue, gfx.memory_properties), + yakui_vulkan_options, + ); + for _ in 0..PIPELINE_DEPTH { + yakui_vulkan.transfers_submitted(); + } + Self { gfx, cfg, @@ -209,6 +225,8 @@ impl Draw { image_barriers: Vec::new(), character_model, + yakui_vulkan, + yak: yakui::Yakui::new(), } } } @@ -253,9 +271,11 @@ impl Draw { /// /// Submits commands that wait on `image_acquired` before writing to `framebuffer`'s color /// attachment. + #[allow(clippy::too_many_arguments)] // Every argument is of a different type, making this less of a problem. pub unsafe fn draw( &mut self, mut sim: Option<&mut Sim>, + gui_state: &GuiState, framebuffer: vk::Framebuffer, depth_view: vk::ImageView, extent: vk::Extent2D, @@ -273,6 +293,25 @@ impl Draw { let state = &mut self.states[self.next_state]; let cmd = state.cmd; + let yakui_vulkan_context = + yakui_vulkan::VulkanContext::new(device, self.gfx.queue, self.gfx.memory_properties); + + self.yak + .set_surface_size([extent.width as f32, extent.height as f32].into()); + self.yak + .set_unscaled_viewport(yakui::geometry::Rect::from_pos_size( + Default::default(), + [extent.width as f32, extent.height as f32].into(), + )); + + self.yak.start(); + if let Some(sim) = sim.as_ref() { + gui::gui(gui_state, sim); + } + self.yak.finish(); + + let paint = self.yak.paint(); + // We're using this state again, so put the fence back in the unsignaled state and compute // the next frame to use device.reset_fences(&[state.fence]).unwrap(); @@ -340,6 +379,10 @@ impl Draw { ); timestamp_index += 1; + self.yakui_vulkan.transfers_finished(&yakui_vulkan_context); + self.yakui_vulkan + .transfer(paint, &yakui_vulkan_context, cmd); + // Schedule transfer of uniform data. Note that we defer actually preparing the data to just // before submitting the command buffer so time-sensitive values can be set with minimum // latency. @@ -470,6 +513,9 @@ impl Draw { self.fog.draw(device, state.common_ds, cmd); + self.yakui_vulkan + .paint(paint, &yakui_vulkan_context, cmd, extent); + // Finish up device.cmd_end_render_pass(cmd); device.cmd_write_timestamp( @@ -546,6 +592,7 @@ impl Drop for Draw { voxels.destroy(device); } } + self.yakui_vulkan.cleanup(&self.gfx.device); device.destroy_command_pool(self.cmd_pool, None); device.destroy_query_pool(self.timestamp_pool, None); device.destroy_descriptor_pool(self.common_descriptor_pool, None); diff --git a/client/src/graphics/gui.rs b/client/src/graphics/gui.rs new file mode 100644 index 00000000..ecd51f97 --- /dev/null +++ b/client/src/graphics/gui.rs @@ -0,0 +1,37 @@ +use yakui::{ + align, colored_box, colored_box_container, label, pad, widgets::Pad, Alignment, Color, +}; + +use crate::Sim; + +pub struct GuiState { + show_gui: bool, +} + +impl GuiState { + pub fn new() -> Self { + GuiState { show_gui: true } + } + + pub fn toggle_gui(&mut self) { + self.show_gui = !self.show_gui; + } +} + +pub fn gui(gui_state: &GuiState, sim: &Sim) { + if !gui_state.show_gui { + return; + } + + align(Alignment::CENTER, || { + colored_box(Color::BLACK.with_alpha(0.9), [5.0, 5.0]); + }); + + align(Alignment::TOP_LEFT, || { + pad(Pad::all(8.0), || { + colored_box_container(Color::BLACK.with_alpha(0.7), || { + label(format!("Selected material: {:?}", sim.selected_material())); + }); + }); + }); +} diff --git a/client/src/graphics/mod.rs b/client/src/graphics/mod.rs index 83622d0c..d39cd9ae 100644 --- a/client/src/graphics/mod.rs +++ b/client/src/graphics/mod.rs @@ -6,6 +6,7 @@ mod draw; mod fog; mod frustum; mod gltf_mesh; +mod gui; mod meshes; mod png_array; pub mod voxels; diff --git a/client/src/graphics/window.rs b/client/src/graphics/window.rs index 866726c2..c9b806a5 100644 --- a/client/src/graphics/window.rs +++ b/client/src/graphics/window.rs @@ -15,6 +15,7 @@ use winit::{ window::{CursorGrabMode, Window as WinitWindow, WindowBuilder}, }; +use super::gui::GuiState; use super::{Base, Core, Draw, Frustum}; use crate::Net; use crate::{net, Config, Sim}; @@ -57,6 +58,7 @@ pub struct Window { swapchain_needs_update: bool, draw: Option, sim: Option, + gui_state: GuiState, net: Net, } @@ -93,6 +95,7 @@ impl Window { swapchain_needs_update: false, draw: None, sim: None, + gui_state: GuiState::new(), net, } } @@ -261,6 +264,9 @@ impl Window { sim.toggle_no_clip(); } } + KeyCode::F1 if state == ElementState::Pressed => { + self.gui_state.toggle_gui(); + } KeyCode::Escape => { let _ = self.window.set_cursor_grab(CursorGrabMode::None); self.window.set_cursor_visible(true); @@ -352,6 +358,7 @@ impl Window { // Render the frame draw.draw( self.sim.as_mut(), + &self.gui_state, frame.buffer, frame.depth_view, swapchain.state.extent, @@ -366,6 +373,7 @@ impl Window { } Err(e) => panic!("queue_present: {e}"), }; + draw.yakui_vulkan.transfers_submitted(); } } } diff --git a/client/src/sim.rs b/client/src/sim.rs index 8abfe08c..9f59582f 100644 --- a/client/src/sim.rs +++ b/client/src/sim.rs @@ -161,6 +161,10 @@ impl Sim { self.selected_material = *MATERIAL_PALETTE.get(idx).unwrap_or(&MATERIAL_PALETTE[0]); } + pub fn selected_material(&self) -> Material { + self.selected_material + } + pub fn set_break_block_pressed_true(&mut self) { self.break_block_pressed = true; }