diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
commit | c9fb3ae28fe491bc55243fb80d8c6be93f37ad99 (patch) | |
tree | c8d6519479d3b17f5bd657495c4d7fe2d2830a1a | |
parent | c52a05e6d3977efce6bd4479aa312dc90e0452e5 (diff) |
feat(render): ui working
-rw-r--r-- | examples/render-bsp/Cargo.toml | 4 | ||||
-rw-r--r-- | examples/render-bsp/src/main.rs | 32 | ||||
-rw-r--r-- | stockton-render/Cargo.toml | 3 | ||||
-rw-r--r-- | stockton-render/src/draw/context.rs | 20 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/load.rs | 21 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/loader.rs | 48 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/mod.rs | 1 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/repo.rs | 16 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/resolver.rs | 36 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/data/stockton.frag | 4 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/data/stockton.vert | 23 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/mod.rs | 4 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/pipeline.rs | 10 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/render.rs | 60 | ||||
-rwxr-xr-x | stockton-render/src/draw/ui/texture.rs | 48 | ||||
-rw-r--r-- | stockton-render/src/lib.rs | 4 | ||||
-rw-r--r-- | stockton-render/src/window.rs | 122 |
17 files changed, 279 insertions, 177 deletions
diff --git a/examples/render-bsp/Cargo.toml b/examples/render-bsp/Cargo.toml index 093409e..bdbab89 100644 --- a/examples/render-bsp/Cargo.toml +++ b/examples/render-bsp/Cargo.toml @@ -13,8 +13,8 @@ stockton-levels = { path = "../../stockton-levels" } stockton-contrib = { path = "../../stockton-contrib", features = ["delta_time", "flycam"] } winit = "^0.21" log = "0.4.0" -simple_logger = "^1.11" +simplelog = "^0.10" image = "0.23.2" -egui = "^0.2" +egui = "^0.12" legion = { version = "^0.3" } anyhow = "1.0.40" diff --git a/examples/render-bsp/src/main.rs b/examples/render-bsp/src/main.rs index 543c9fe..fd8e1d5 100644 --- a/examples/render-bsp/src/main.rs +++ b/examples/render-bsp/src/main.rs @@ -11,6 +11,7 @@ use log::warn; use std::collections::BTreeMap; use winit::{event::Event, event_loop::EventLoop, window::WindowBuilder}; +use egui::{containers::CentralPanel, Frame}; use stockton_contrib::delta_time::*; use stockton_contrib::flycam::*; @@ -50,8 +51,11 @@ impl FlycamInput for MovementInputs { #[system] fn hello_world(#[resource] ui: &mut UiState) { - let ui = ui.ui(); - ui.heading("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + CentralPanel::default() + .frame(Frame::none()) + .show(ui.ctx(), |ui| { + ui.heading("Hello, World!"); + }); } fn main() { @@ -62,10 +66,16 @@ fn main() { fn try_main() -> Result<()> { // Initialise logger - simple_logger::SimpleLogger::new() - .with_level(log::LevelFilter::Debug) - .init() - .context("Error initialising logger")?; + simplelog::TermLogger::init( + log::LevelFilter::Debug, + simplelog::ConfigBuilder::new() + .set_max_level(log::LevelFilter::Debug) + .set_thread_mode(simplelog::ThreadLogMode::Names) + .build(), + simplelog::TerminalMode::Stderr, + simplelog::ColorChoice::Auto, + ) + .context("Error initialising logger")?; // Make a window let event_loop = EventLoop::new(); @@ -87,10 +97,16 @@ fn try_main() -> Result<()> { let bsp: Q3BspFile<Q3System> = bsp.context("Error loading bsp")?; let bsp: Q3BspFile<VulkanSystem> = bsp.swizzle_to(); + // Create the UI State + let mut ui = UiState::new(); + // Create the renderer - let (renderer, tx) = Renderer::new(&window, bsp)?; + let (renderer, tx) = Renderer::new(&window, &mut ui, bsp)?; let new_control_flow = renderer.update_control_flow.clone(); + // Populate the initial UI state + ui.populate_initial_state(&renderer); + // Create the input manager let manager = { use stockton_input::InputMutation::*; @@ -111,7 +127,7 @@ fn try_main() -> Result<()> { // Load everything into the session let mut session = Session::new( move |resources| { - resources.insert(UiState::new(&renderer)); + resources.insert(ui); resources.insert(renderer); resources.insert(manager); resources.insert(Timing::default()); diff --git a/stockton-render/Cargo.toml b/stockton-render/Cargo.toml index ecb08f3..6777a66 100644 --- a/stockton-render/Cargo.toml +++ b/stockton-render/Cargo.toml @@ -16,7 +16,8 @@ shaderc = "^0.7" log = "0.4.0" image = "0.23.11" legion = { version = "^0.3" } -egui = "^0.2" +egui = "^0.12" +epaint = "^0.12" rendy-memory = { path = "../rendy-memory" } rendy-descriptor = { path = "../rendy-descriptor" } anyhow = "1.0.40" diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index dae04ac..3de857a 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -22,7 +22,7 @@ use super::{ queue_negotiator::QueueNegotiator, render::do_render, target::{SwapchainProperties, TargetChain}, - texture::{resolver::BasicFsResolver, TextureRepo}, + texture::{resolver::FsResolver, TextureLoadConfig, TextureRepo}, ui::{ do_render as do_render_ui, ensure_textures as ensure_textures_ui, UiPipeline, UiPoint, UiTextures, @@ -87,7 +87,7 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { /// Create a new RenderingContext for the given window. - pub fn new(window: &Window, map: M) -> Result<Self> { + pub fn new(window: &Window, ui: &mut UiState, map: M) -> Result<Self> { let map = Arc::new(RwLock::new(map)); // Create surface let (instance, surface, mut adapters) = unsafe { @@ -180,8 +180,11 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { .ok_or(EnvironmentError::NoQueues) .context("Error getting 3D texture loader queue")?, &adapter, - map.clone(), - BasicFsResolver::new(std::path::Path::new(".")), + TextureLoadConfig { + resolver: FsResolver::new(std::path::Path::new("."), map.clone()), + filter: hal::image::Filter::Linear, + wrap_mode: hal::image::WrapMode::Tile, + }, ) .context("Error creating 3D Texture repo")?; // TODO @@ -194,8 +197,11 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { .ok_or(EnvironmentError::NoQueues) .context("Error getting UI texture loader queue")?, &adapter, - Arc::new(RwLock::new(UiTextures)), - BasicFsResolver::new(std::path::Path::new(".")), + TextureLoadConfig { + resolver: UiTextures::new(ui.ctx().clone()), + filter: hal::image::Filter::Linear, + wrap_mode: hal::image::WrapMode::Clamp, + }, ) .context("Error creating UI texture repo")?; // TODO @@ -262,6 +268,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { vp_matrix: Mat4::identity(), + // pixels_per_point: window.scale_factor() as f32, pixels_per_point: window.scale_factor() as f32, }) } @@ -352,6 +359,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { ensure_textures_ui(&mut self.ui_tex_repo, ui)?; // Get any textures that just finished loading + self.ui_tex_repo.process_responses(); self.tex_repo.process_responses(); // 3D Pass diff --git a/stockton-render/src/draw/texture/load.rs b/stockton-render/src/draw/texture/load.rs index eea820a..e278fa2 100644 --- a/stockton-render/src/draw/texture/load.rs +++ b/stockton-render/src/draw/texture/load.rs @@ -1,9 +1,8 @@ use super::{ - block::LoadedImage, block::TexturesBlock, loader::TextureLoader, repo::BLOCK_SIZE, - resolver::TextureResolver, staging_buffer::StagingBuffer, LoadableImage, PIXEL_SIZE, + block::LoadedImage, block::TexturesBlock, repo::BLOCK_SIZE, resolver::TextureResolver, + staging_buffer::StagingBuffer, LoadableImage, PIXEL_SIZE, }; use crate::types::*; -use stockton_levels::prelude::*; use anyhow::{Context, Result}; use arrayvec::ArrayVec; @@ -24,9 +23,6 @@ use thiserror::Error; pub enum TextureLoadError { #[error("No available resources")] NoResources, - - #[error("Texture could not be resolved")] - ResolveFailed(usize), } pub const FORMAT: Format = Format::Rgba8Srgb; @@ -43,6 +39,12 @@ pub const LAYERS: SubresourceLayers = SubresourceLayers { layers: 0..1, }; +pub struct TextureLoadConfig<R: TextureResolver> { + pub resolver: R, + pub filter: Filter, + pub wrap_mode: WrapMode, +} + pub struct QueuedLoad<B: Block<back::Backend>> { pub fence: FenceT, pub buf: CommandBufferT, @@ -62,8 +64,6 @@ impl<B: Block<back::Backend>> QueuedLoad<B> { } } -impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R, I> {} - pub fn tex_size_info<T: LoadableImage>(img: &T, obcpa: hal::buffer::Offset) -> (usize, usize) { let initial_row_size = PIXEL_SIZE * img.width() as usize; let row_alignment_mask = obcpa as u32 - 1; @@ -119,13 +119,14 @@ where Ok((block, image_ref)) } -pub unsafe fn load_image<I: LoadableImage>( +pub unsafe fn load_image<I: LoadableImage, R: TextureResolver>( device: &mut DeviceT, staging_allocator: &mut DynamicAllocator, tex_allocator: &mut DynamicAllocator, staging_memory_type: MemoryTypeId, obcpa: u64, img_data: I, + config: &TextureLoadConfig<R>, ) -> Result<(StagingBuffer, LoadedImage<DynamicBlock>)> { // Calculate buffer size let (row_size, total_size) = tex_size_info(&img_data, obcpa); @@ -172,7 +173,7 @@ pub unsafe fn load_image<I: LoadableImage>( // Create sampler let sampler = device - .create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile)) + .create_sampler(&SamplerDesc::new(config.filter, config.wrap_mode)) .context("Error creating sampler")?; Ok(( diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index 3d7d32e..96b1646 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -2,10 +2,10 @@ use super::{ block::{LoadedImage, TexturesBlock}, - load::{load_image, QueuedLoad, TextureLoadError, LAYERS, RESOURCES}, + load::{load_image, QueuedLoad, TextureLoadConfig, TextureLoadError, LAYERS, RESOURCES}, repo::BLOCK_SIZE, resolver::TextureResolver, - LoadableImage, PIXEL_SIZE, + PIXEL_SIZE, }; use crate::{draw::utils::find_memory_type_id, error::LockPoisoned, types::*}; @@ -13,7 +13,6 @@ use std::{ array::IntoIter, collections::VecDeque, iter::{empty, once}, - marker::PhantomData, mem::{drop, ManuallyDrop}, sync::{ mpsc::{Receiver, Sender}, @@ -38,7 +37,6 @@ use image::{Rgba, RgbaImage}; use log::*; use rendy_descriptor::{DescriptorRanges, DescriptorSetLayoutBinding, DescriptorType}; use rendy_memory::DynamicConfig; -use stockton_levels::prelude::HasTextures; use thiserror::Error; /// The number of command buffers to have in flight simultaneously. @@ -49,7 +47,7 @@ pub type BlockRef = usize; /// Manages the loading/unloading of textures /// This is expected to load the textures, then send the loaded blocks back -pub struct TextureLoader<T, R, I> { +pub struct TextureLoader<R: TextureResolver> { /// Blocks for which commands have been queued and are done loading once the fence is triggered. commands_queued: ArrayVec<[QueuedLoad<DynamicBlock>; NUM_SIMULTANEOUS_CMDS]>, @@ -82,11 +80,8 @@ pub struct TextureLoader<T, R, I> { /// From adapter, used for determining alignment optimal_buffer_copy_pitch_alignment: hal::buffer::Offset, - /// The textures lump to get info from - textures: Arc<RwLock<T>>, - - /// The resolver which gets image data for a given texture. - resolver: R, + /// Configuration for how to find and load textures + config: TextureLoadConfig<R>, /// The channel requests come in. /// Requests should reference a texture **block**, for example textures 8..16 is block 1. @@ -97,8 +92,6 @@ pub struct TextureLoader<T, R, I> { /// A filler image for descriptors that aren't needed but still need to be written to blank_image: ManuallyDrop<LoadedImage<DynamicBlock>>, - - _li: PhantomData<I>, } #[derive(Error, Debug)] @@ -107,7 +100,7 @@ pub enum TextureLoaderError { NoMemoryTypes, } -impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R, I> { +impl<R: TextureResolver> TextureLoader<R> { pub fn loop_until_exit(mut self) -> Result<TextureLoaderRemains> { debug!("TextureLoader starting main loop"); let mut res = Ok(false); @@ -196,8 +189,7 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, request_channel: Receiver<LoaderRequest>, return_channel: Sender<TexturesBlock<DynamicBlock>>, - texs: Arc<RwLock<T>>, - resolver: R, + config: TextureLoadConfig<R>, ) -> Result<Self> { let mut device = device_lock .write() @@ -310,6 +302,7 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R &mut tex_allocator, staging_memory_type, optimal_buffer_copy_pitch_alignment, + &config, ) } .context("Error creating blank image")?; @@ -333,10 +326,8 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R request_channel, return_channel, - textures: texs, - resolver, + config, blank_image: ManuallyDrop::new(blank_image), - _li: PhantomData::default(), }) } @@ -347,12 +338,6 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R .map_err(|_| LockPoisoned::Device) .context("Error getting device lock")?; - let textures = self - .textures - .read() - .map_err(|_| LockPoisoned::Map) - .context("Error getting map lock")?; - // Get assets to use let (mut fence, mut buf) = self .buffers @@ -407,9 +392,9 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R // For each texture in block for tex_idx in (block_ref * BLOCK_SIZE)..(block_ref + 1) * BLOCK_SIZE { - // Get texture and Resolve image - let tex = textures.get_texture(tex_idx as u32); - if tex.is_none() { + // Resolve texture + let img_data = self.config.resolver.resolve(tex_idx as u32); + if img_data.is_none() { // Write a blank descriptor device.write_descriptor_set(DescriptorSetWrite { set: descriptor_set.raw_mut(), @@ -430,12 +415,8 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R continue; } - let tex = tex.unwrap(); + let img_data = img_data.unwrap(); - let img_data = self - .resolver - .resolve(tex) - .ok_or(TextureLoadError::ResolveFailed(tex_idx))?; let array_offset = tex_idx % BLOCK_SIZE; let (staging_buffer, img) = load_image( @@ -445,6 +426,7 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R self.staging_memory_type, self.optimal_buffer_copy_pitch_alignment, img_data, + &self.config, )?; // Write to descriptor set @@ -555,6 +537,7 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R tex_allocator: &mut DynamicAllocator, staging_memory_type: MemoryTypeId, obcpa: u64, + config: &TextureLoadConfig<R>, ) -> Result<LoadedImage<DynamicBlock>> { let img_data = RgbaImage::from_pixel(1, 1, Rgba([0, 0, 0, 0])); @@ -572,6 +555,7 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R staging_memory_type, obcpa, img_data, + &config, )?; buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); diff --git a/stockton-render/src/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs index 43c89d5..62b28c5 100644 --- a/stockton-render/src/draw/texture/mod.rs +++ b/stockton-render/src/draw/texture/mod.rs @@ -10,6 +10,7 @@ mod staging_buffer; pub use self::block::TexturesBlock; pub use self::image::LoadableImage; +pub use self::load::TextureLoadConfig; pub use self::loader::BlockRef; pub use self::repo::TextureRepo; diff --git a/stockton-render/src/draw/texture/repo.rs b/stockton-render/src/draw/texture/repo.rs index 8191f7b..ef35610 100644 --- a/stockton-render/src/draw/texture/repo.rs +++ b/stockton-render/src/draw/texture/repo.rs @@ -1,10 +1,8 @@ -use stockton_levels::prelude::HasTextures; - use super::{ block::TexturesBlock, + load::TextureLoadConfig, loader::{BlockRef, LoaderRequest, TextureLoader, TextureLoaderRemains, NUM_SIMULTANEOUS_CMDS}, resolver::TextureResolver, - LoadableImage, }; use crate::error::LockPoisoned; use crate::types::*; @@ -49,17 +47,12 @@ impl<'a> TextureRepo<'a> { family.queue_type().supports_transfer() && family.max_queues() >= NUM_SIMULTANEOUS_CMDS } - pub fn new< - T: 'static + HasTextures + Send + Sync, - R: 'static + TextureResolver<I> + Send + Sync, - I: 'static + LoadableImage + Send, - >( + pub fn new<R: 'static + TextureResolver + Send + Sync>( device_lock: Arc<RwLock<DeviceT>>, family: QueueFamilyId, queue: Arc<RwLock<QueueT>>, adapter: &Adapter, - texs_lock: Arc<RwLock<T>>, - resolver: R, + config: TextureLoadConfig<R>, ) -> Result<Self> { // Create Channels let (req_send, req_recv) = channel(); @@ -112,8 +105,7 @@ impl<'a> TextureRepo<'a> { ds_lock.clone(), req_recv, resp_send, - texs_lock, - resolver, + config, )?; std::thread::spawn(move || loader.loop_until_exit()) diff --git a/stockton-render/src/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs index 20efa00..1ecb7a0 100644 --- a/stockton-render/src/draw/texture/resolver.rs +++ b/stockton-render/src/draw/texture/resolver.rs @@ -1,33 +1,47 @@ //! Resolves a texture in a BSP File to an image use crate::draw::texture::image::LoadableImage; -use stockton_levels::traits::textures::Texture; +use stockton_levels::prelude::HasTextures; -use std::path::Path; +use std::{ + mem::drop, + path::Path, + sync::{Arc, RwLock}, +}; use image::{io::Reader, RgbaImage}; /// An object that can be used to resolve a texture from a BSP File -pub trait TextureResolver<T: LoadableImage> { +pub trait TextureResolver { + type Image: LoadableImage; + /// Get the given texture, or None if it's corrupt/not there. - fn resolve(&mut self, texture: &Texture) -> Option<T>; + fn resolve(&mut self, texture_id: u32) -> Option<Self::Image>; } -/// A basic filesystem resolver which expects no file extension and guesses the image format -pub struct BasicFsResolver<'a> { +/// A basic filesystem resolver which gets the texture name from any HasTextures Object. +pub struct FsResolver<'a, T: HasTextures> { path: &'a Path, + map_lock: Arc<RwLock<T>>, } -impl<'a> BasicFsResolver<'a> { - pub fn new(path: &'a Path) -> BasicFsResolver<'a> { - BasicFsResolver { path } +impl<'a, T: HasTextures> FsResolver<'a, T> { + pub fn new(path: &'a Path, map_lock: Arc<RwLock<T>>) -> Self { + FsResolver { path, map_lock } } } -impl<'a> TextureResolver<RgbaImage> for BasicFsResolver<'a> { - fn resolve(&mut self, tex: &Texture) -> Option<RgbaImage> { +impl<'a, T: HasTextures> TextureResolver for FsResolver<'a, T> { + type Image = RgbaImage; + + fn resolve(&mut self, tex: u32) -> Option<Self::Image> { + let map = self.map_lock.read().unwrap(); + let tex = map.get_texture(tex)?; let path = self.path.join(&tex.name); + drop(tex); + drop(map); + if let Ok(file) = Reader::open(path) { if let Ok(guessed) = file.with_guessed_format() { if let Ok(decoded) = guessed.decode() { diff --git a/stockton-render/src/draw/ui/data/stockton.frag b/stockton-render/src/draw/ui/data/stockton.frag index 77685cc..c30c99e 100644 --- a/stockton-render/src/draw/ui/data/stockton.frag +++ b/stockton-render/src/draw/ui/data/stockton.frag @@ -6,10 +6,10 @@ layout(set = 0, binding = 0) uniform texture2D tex[8]; layout(set = 0, binding = 1) uniform sampler samp[8]; layout (location = 1) in vec2 frag_uv; -layout (location = 2) in flat uint frag_col; +layout (location = 2) in vec4 frag_col; layout (location = 0) out vec4 color; void main() { - color = texture(sampler2D(tex[0], samp[0]), frag_uv); + color = texture(sampler2D(tex[0], samp[0]), frag_uv) * frag_col; }
\ No newline at end of file diff --git a/stockton-render/src/draw/ui/data/stockton.vert b/stockton-render/src/draw/ui/data/stockton.vert index d84cc56..8912e96 100644 --- a/stockton-render/src/draw/ui/data/stockton.vert +++ b/stockton-render/src/draw/ui/data/stockton.vert @@ -4,23 +4,34 @@ layout (push_constant) uniform PushConsts { vec2 screen_size; } push; -layout(location = 0) in vec2 pos; +layout (location = 0) in vec2 pos; layout (location = 1) in vec2 uv; -layout (location = 2) in uint col; // rgba of u8s +layout (location = 2) in vec4 col; out gl_PerVertex { vec4 gl_Position; }; layout (location = 1) out vec2 frag_uv; -layout (location = 2) out uint frag_col; +layout (location = 2) out vec4 frag_col; + +vec3 linear_from_srgb(vec3 srgb) { + bvec3 cutoff = lessThan(srgb, vec3(10.31475)); + vec3 lower = srgb / vec3(3294.6); + vec3 higher = pow((srgb + vec3(14.025)) / vec3(269.025), vec3(2.4)); + return mix(higher, lower, cutoff); +} + +vec4 linear_from_srgba(vec4 srgba) { + return vec4(linear_from_srgb(srgba.rgb * 255.0), srgba.a); +} void main() { gl_Position = vec4( - ((pos.x / push.screen_size.x) * 2.0) - 1.0, - ((pos.y / push.screen_size.y) * 2.0) - 1.0, + 2.0 * pos.x / push.screen_size.x - 1.0, + 2.0 * pos.y / push.screen_size.y - 1.0, 0.0, 1.0 ); frag_uv = uv; - frag_col = col; + frag_col = linear_from_srgba(col); } diff --git a/stockton-render/src/draw/ui/mod.rs b/stockton-render/src/draw/ui/mod.rs index d50befc..5daa117 100644 --- a/stockton-render/src/draw/ui/mod.rs +++ b/stockton-render/src/draw/ui/mod.rs @@ -1,5 +1,3 @@ -use egui::paint::color::Srgba; - pub mod pipeline; pub mod render; pub mod texture; @@ -10,4 +8,4 @@ use stockton_types::Vector2; pub use texture::{ensure_textures, UiTextures}; #[derive(Debug)] -pub struct UiPoint(pub Vector2, pub Vector2, pub Srgba); +pub struct UiPoint(pub Vector2, pub Vector2, pub [f32; 4]); diff --git a/stockton-render/src/draw/ui/pipeline.rs b/stockton-render/src/draw/ui/pipeline.rs index 2a8b9fc..dfa4e88 100644 --- a/stockton-render/src/draw/ui/pipeline.rs +++ b/stockton-render/src/draw/ui/pipeline.rs @@ -183,8 +183,8 @@ impl UiPipeline { dst: Factor::OneMinusSrcAlpha, }, alpha: BlendOp::Add { - src: Factor::OneMinusSrcAlpha, - dst: Factor::Zero, + src: Factor::SrcAlpha, + dst: Factor::OneMinusSrcAlpha, }, }; @@ -203,7 +203,7 @@ impl UiPipeline { rect: extent.rect(), depth: (0.0..1.0), }), - scissor: Some(extent.rect()), + scissor: None, blend_constants: None, depth_bounds: None, }; @@ -212,7 +212,7 @@ impl UiPipeline { let primitive_assembler = PrimitiveAssemblerDesc::Vertex { buffers: &[VertexBufferDesc { binding: 0, - stride: (size_of::<f32>() * 6) as u32, + stride: (size_of::<f32>() * 8) as u32, rate: VertexInputRate::Vertex, }], attributes: &[ @@ -236,7 +236,7 @@ impl UiPipeline { location: 2, binding: 0, element: Element { - format: Format::R32Uint, + format: Format::Rgba32Sfloat, offset: (size_of::<f32>() * 4) as u32, }, }, diff --git a/stockton-render/src/draw/ui/render.rs b/stockton-render/src/draw/ui/render.rs index 40cc733..a88f811 100644 --- a/stockton-render/src/draw/ui/render.rs +++ b/stockton-render/src/draw/ui/render.rs @@ -1,11 +1,12 @@ use crate::draw::texture::TextureRepo; -use hal::pso::ShaderStageFlags; +use hal::pso::{Rect, ShaderStageFlags}; use super::UiPoint; use crate::draw::draw_buffers::DrawBuffers; use crate::types::*; use crate::UiState; -use anyhow::Result; +use anyhow::{anyhow, Result}; +use egui::{ClippedMesh, TextureId}; use std::{array::IntoIter, convert::TryInto, iter::empty}; use stockton_types::Vector2; @@ -17,19 +18,13 @@ pub fn do_render( ui: &mut UiState, ) -> Result<()> { // TODO: Actual UI Rendering - let (_out, paint) = ui.end_frame(); - let screen = ui.dimensions(); + let (_out, shapes) = ui.end_frame(); + let screen = ui.dimensions().ok_or(anyhow!("UI not set up properly."))?; + let shapes = ui.ctx().tessellate(shapes); - unsafe { - cmd_buffer.push_graphics_constants( - &pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - &[screen.x.to_bits(), screen.y.to_bits()], - ); - } + for ClippedMesh(rect, tris) in shapes.iter() { + assert!(tris.texture_id == TextureId::Egui); - for (_rect, tris) in paint.iter() { // Copy triangles/indicies for i in (0..tris.indices.len()).step_by(3) { draw_buffers.index_buffer[i / 3] = ( @@ -37,18 +32,53 @@ pub fn do_render( tris.indices[i + 1].try_into()?, tris.indices[i + 2].try_into()?, ); + // eprintln!( + // "{} {}", + // tris.vertices[tris.indices[i] as usize].uv.x, + // tris.vertices[tris.indices[i] as usize].uv.y + // ); + // eprintln!( + // "{} {}", + // tris.vertices[tris.indices[i + 1] as usize].uv.x, + // tris.vertices[tris.indices[i + 1] as usize].uv.y + // ); + // eprintln!( + // "{} {}", + // tris.vertices[tris.indices[i + 2] as usize].uv.x, + // tris.vertices[tris.indices[i + 2] as usize].uv.y + // ); } for (i, vertex) in tris.vertices.iter().enumerate() { draw_buffers.vertex_buffer[i] = UiPoint( Vector2::new(vertex.pos.x, vertex.pos.y), Vector2::new(vertex.uv.x, vertex.uv.y), - vertex.color, + [ + vertex.color.r() as f32 / 255.0, + vertex.color.g() as f32 / 255.0, + vertex.color.b() as f32 / 255.0, + vertex.color.a() as f32 / 255.0, + ], ); } - // TODO: *Properly* deal with textures if let Some(ds) = tex_repo.attempt_get_descriptor_set(0) { unsafe { + cmd_buffer.push_graphics_constants( + &pipeline_layout, + ShaderStageFlags::VERTEX, + 0, + &[screen.x.to_bits(), screen.y.to_bits()], + ); + + cmd_buffer.set_scissors( + 0, + IntoIter::new([Rect { + x: rect.min.x as i16, + y: rect.min.y as i16, + w: rect.width() as i16, + h: rect.height() as i16, + }]), + ); cmd_buffer.bind_graphics_descriptor_sets( pipeline_layout, 0, diff --git a/stockton-render/src/draw/ui/texture.rs b/stockton-render/src/draw/ui/texture.rs index f5ddb3e..7cba1ac 100755 --- a/stockton-render/src/draw/ui/texture.rs +++ b/stockton-render/src/draw/ui/texture.rs @@ -1,24 +1,32 @@ -use crate::draw::texture::{LoadableImage, TextureRepo}; +use crate::draw::texture::{resolver::TextureResolver, LoadableImage, TextureRepo}; use crate::UiState; use anyhow::Result; -use egui::Texture; -use stockton_levels::{prelude::HasTextures, traits::textures::Texture as LTexture}; +use egui::{CtxRef, Texture}; +use log::debug; +use std::{convert::TryInto, sync::Arc}; -pub struct UiTextures; - -impl HasTextures for UiTextures { - type TexturesIter<'a> = std::slice::Iter<'a, LTexture>; +pub struct UiTextures { + ctx: CtxRef, +} - fn textures_iter(&self) -> Self::TexturesIter<'_> { - (&[]).iter() +impl TextureResolver for UiTextures { + type Image = Arc<Texture>; + fn resolve(&mut self, tex: u32) -> Option<Self::Image> { + if tex == 0 { + Some(self.ctx.texture()) + } else { + None + } } +} - fn get_texture(&self, _idx: u32) -> Option<&stockton_levels::prelude::textures::Texture> { - None +impl UiTextures { + pub fn new(ctx: CtxRef) -> Self { + UiTextures { ctx } } } -impl LoadableImage for &Texture { +impl LoadableImage for Arc<Texture> { fn width(&self) -> u32 { self.width as u32 } @@ -31,22 +39,26 @@ impl LoadableImage for &Texture { for (i, x) in pixels.iter().enumerate() { unsafe { - *ptr.offset(i as isize * 3) = *x; - *ptr.offset((i as isize * 3) + 1) = *x; - *ptr.offset((i as isize * 3) + 2) = *x; + *ptr.offset(i as isize * 4) = 255; + *ptr.offset((i as isize * 4) + 1) = 255; + *ptr.offset((i as isize * 4) + 2) = 255; + *ptr.offset((i as isize * 4) + 3) = *x; } } } - unsafe fn copy_into(&self, _ptr: *mut u8, _row_size: usize) { - todo!() + unsafe fn copy_into(&self, ptr: *mut u8, row_size: usize) { + for y in 0..self.height() { + self.copy_row(y, ptr.offset((row_size * y as usize).try_into().unwrap())); + } } } pub fn ensure_textures(tex_repo: &mut TextureRepo, ui: &mut UiState) -> Result<()> { - let tex = ui.ctx.texture(); + let tex = ui.ctx().texture(); if tex.version != ui.last_tex_ver { + debug!("Queueing UI Texture reload"); tex_repo.force_queue_load(0)?; ui.last_tex_ver = tex.version; } diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs index 2d3da06..97cf6d3 100644 --- a/stockton-render/src/lib.rs +++ b/stockton-render/src/lib.rs @@ -51,13 +51,13 @@ pub struct Renderer<'a, M: 'static + MinBspFeatures<VulkanSystem>> { impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> Renderer<'a, M> { /// Create a new Renderer. - pub fn new(window: &Window, file: M) -> Result<(Self, Sender<WindowEvent>)> { + pub fn new(window: &Window, ui: &mut UiState, file: M) -> Result<(Self, Sender<WindowEvent>)> { let (tx, rx) = channel(); let update_control_flow = Arc::new(RwLock::new(ControlFlow::Poll)); Ok(( Renderer { - context: RenderingContext::new(window, file)?, + context: RenderingContext::new(window, ui, file)?, window_events: rx, update_control_flow, }, diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs index ea172bc..2496f3c 100644 --- a/stockton-render/src/window.rs +++ b/stockton-render/src/window.rs @@ -1,11 +1,11 @@ use crate::{error::full_error_display, Renderer}; -use egui::Context; +use egui::{Modifiers, Rect, Vec2}; use legion::systems::Runnable; use log::debug; -use std::sync::Arc; use stockton_levels::prelude::{MinBspFeatures, VulkanSystem}; -use egui::{Output, PaintJobs, Pos2, RawInput, Ui}; +use egui::{CtxRef, Event, Output, Pos2, RawInput}; +use epaint::ClippedShape; use log::error; use stockton_input::{Action as KBAction, InputManager, Mouse}; @@ -71,77 +71,111 @@ impl WindowEvent { } pub struct UiState { - pub(crate) ctx: Arc<Context>, - pub(crate) raw_input: RawInput, - ui: Option<Ui>, - + ctx: CtxRef, + raw_input: RawInput, pub(crate) last_tex_ver: u64, + + frame_active: bool, + + modifiers: Modifiers, + pointer_pos: Pos2, } impl UiState { - pub fn ui(&mut self) -> &mut Ui { - if self.ui.is_none() { - self.ui = Some(self.begin_frame()); + pub fn new() -> Self { + UiState { + ctx: CtxRef::default(), + raw_input: RawInput::default(), + last_tex_ver: 0, + frame_active: false, + modifiers: Default::default(), + pointer_pos: Pos2::new(0.0, 0.0), + } + } + + pub fn populate_initial_state<T: MinBspFeatures<VulkanSystem>>( + &mut self, + renderer: &Renderer<T>, + ) { + let props = &renderer.context.target_chain.properties; + self.set_dimensions(props.extent.width, props.extent.height); + self.set_pixels_per_point(Some(renderer.context.pixels_per_point)); + debug!("{:?}", self.raw_input); + } + + #[inline] + pub fn ctx(&mut self) -> &CtxRef { + if !self.frame_active { + self.begin_frame() } - self.ui.as_mut().unwrap() + &self.ctx } - fn begin_frame(&mut self) -> Ui { - self.ctx.begin_frame(self.raw_input.take()) + + #[inline] + fn begin_frame(&mut self) -> () { + #[allow(deprecated)] + let new_raw_input = RawInput { + scroll_delta: Vec2::new(0.0, 0.0), + zoom_delta: 0.0, + screen_size: self.raw_input.screen_size, + screen_rect: self.raw_input.screen_rect.clone(), + pixels_per_point: self.raw_input.pixels_per_point.clone(), + time: self.raw_input.time.clone(), + predicted_dt: self.raw_input.predicted_dt, + modifiers: self.modifiers, + events: Vec::new(), + }; + self.ctx.begin_frame(self.raw_input.take()); + self.raw_input = new_raw_input; + self.frame_active = true; } - pub fn end_frame(&mut self) -> (Output, PaintJobs) { - self.ui = None; + #[inline] + pub(crate) fn end_frame(&mut self) -> (Output, Vec<ClippedShape>) { + self.frame_active = false; self.ctx.end_frame() } + #[inline] + pub fn dimensions(&self) -> Option<egui::math::Vec2> { + Some(self.raw_input.screen_rect?.size()) + } + fn set_mouse_pos(&mut self, x: f32, y: f32) { - self.raw_input.mouse_pos = Some(Pos2 { x, y }) + self.raw_input + .events + .push(Event::PointerMoved(Pos2::new(x, y))); + + self.pointer_pos = Pos2::new(x, y); } fn set_mouse_left(&mut self) { - self.raw_input.mouse_pos = None; + self.raw_input.events.push(Event::PointerGone); } + fn set_dimensions(&mut self, w: u32, h: u32) { - self.raw_input.screen_size = egui::math::Vec2 { - x: w as f32, - y: h as f32, - } + self.raw_input.screen_rect = + Some(Rect::from_x_y_ranges(0.0..=(w as f32), 0.0..=(h as f32))); } fn set_pixels_per_point(&mut self, ppp: Option<f32>) { + debug!("Using {:?} pixels per point", ppp); self.raw_input.pixels_per_point = ppp; } - pub fn dimensions(&self) -> egui::math::Vec2 { - self.raw_input.screen_size - } - fn handle_action(&mut self, action: KBAction) { // TODO match action { KBAction::MousePress(stockton_input::MouseButton::Left) => { - self.raw_input.mouse_down = true; - } - KBAction::MouseRelease(stockton_input::MouseButton::Right) => { - self.raw_input.mouse_down = false; + self.raw_input.events.push(Event::PointerButton { + pos: self.pointer_pos, + button: egui::PointerButton::Primary, + pressed: true, + modifiers: self.modifiers, + }); } _ => (), } } - - pub fn new<T: MinBspFeatures<VulkanSystem>>(renderer: &Renderer<T>) -> Self { - let mut state = UiState { - ctx: Context::new(), - raw_input: RawInput::default(), - ui: None, - last_tex_ver: 0, - }; - - let props = &renderer.context.target_chain.properties; - state.set_dimensions(props.extent.width, props.extent.height); - state.set_pixels_per_point(Some(renderer.context.pixels_per_point)); - debug!("{:?}", state.raw_input); - state - } } #[system] |