aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:23 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:23 +0100
commitc9fb3ae28fe491bc55243fb80d8c6be93f37ad99 (patch)
treec8d6519479d3b17f5bd657495c4d7fe2d2830a1a
parentc52a05e6d3977efce6bd4479aa312dc90e0452e5 (diff)
feat(render): ui working
-rw-r--r--examples/render-bsp/Cargo.toml4
-rw-r--r--examples/render-bsp/src/main.rs32
-rw-r--r--stockton-render/Cargo.toml3
-rw-r--r--stockton-render/src/draw/context.rs20
-rw-r--r--stockton-render/src/draw/texture/load.rs21
-rw-r--r--stockton-render/src/draw/texture/loader.rs48
-rw-r--r--stockton-render/src/draw/texture/mod.rs1
-rw-r--r--stockton-render/src/draw/texture/repo.rs16
-rw-r--r--stockton-render/src/draw/texture/resolver.rs36
-rw-r--r--stockton-render/src/draw/ui/data/stockton.frag4
-rw-r--r--stockton-render/src/draw/ui/data/stockton.vert23
-rw-r--r--stockton-render/src/draw/ui/mod.rs4
-rw-r--r--stockton-render/src/draw/ui/pipeline.rs10
-rw-r--r--stockton-render/src/draw/ui/render.rs60
-rwxr-xr-xstockton-render/src/draw/ui/texture.rs48
-rw-r--r--stockton-render/src/lib.rs4
-rw-r--r--stockton-render/src/window.rs122
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]