diff options
| -rw-r--r-- | Cargo.lock | 76 | ||||
| -rw-r--r-- | engine/Cargo.toml | 4 | ||||
| -rw-r--r-- | engine/res/imgui_shader.slang | 54 | ||||
| -rw-r--r-- | engine/src/lib.rs | 1 | ||||
| -rw-r--r-- | engine/src/ui.rs | 1 | ||||
| -rw-r--r-- | engine/src/ui/imgui.rs | 853 |
6 files changed, 980 insertions, 9 deletions
@@ -63,7 +63,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -274,7 +274,7 @@ dependencies = [ "polling", "rustix 0.38.44", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -557,6 +557,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] +name = "dear-imgui-build-support" +version = "0.14.0" +source = "git+https://github.com/HampusMat/dear-imgui-rs?branch=windows-always-static-libstdc%2B%2B#02e1a976d94f4a8c95447c0bfbe21f90b6c07c22" + +[[package]] +name = "dear-imgui-rs" +version = "0.14.0" +source = "git+https://github.com/HampusMat/dear-imgui-rs?branch=windows-always-static-libstdc%2B%2B#02e1a976d94f4a8c95447c0bfbe21f90b6c07c22" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "dear-imgui-sys", + "mint", + "parking_lot", + "thiserror 2.0.18", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "dear-imgui-sys" +version = "0.14.0" +source = "git+https://github.com/HampusMat/dear-imgui-rs?branch=windows-always-static-libstdc%2B%2B#02e1a976d94f4a8c95447c0bfbe21f90b6c07c22" +dependencies = [ + "cc", + "cfg-if", + "dear-imgui-build-support", + "mint", +] + +[[package]] name = "dispatch" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -608,6 +639,7 @@ dependencies = [ "bitflags 2.11.1", "build-rs", "crossbeam-queue", + "dear-imgui-rs", "engine-ecs", "engine-macros", "glutin", @@ -624,7 +656,7 @@ dependencies = [ "serde", "serde_json", "shader-slang", - "thiserror", + "thiserror 1.0.69", "tracing", "util-macros", "winit", @@ -643,7 +675,7 @@ dependencies = [ "parking_lot", "paste", "seq-macro", - "thiserror", + "thiserror 1.0.69", "tracing", "tracing-subscriber", "util-macros", @@ -1163,7 +1195,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -1319,6 +1351,12 @@ dependencies = [ ] [[package]] +name = "mint" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" + +[[package]] name = "moxcms" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1340,7 +1378,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1694,7 +1732,7 @@ dependencies = [ "gl_generator", "glutin", "safer-ffi", - "thiserror", + "thiserror 1.0.69", "toml", "util-macros", ] @@ -2311,7 +2349,7 @@ dependencies = [ "log", "memmap2", "rustix 0.38.44", - "thiserror", + "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -2430,7 +2468,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -2445,6 +2492,17 @@ dependencies = [ ] [[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 4e3968b..9803978 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -50,6 +50,10 @@ version = "0.17.2" default-features = false features = ["wayland", "gtk3"] +[dependencies.dear-imgui-rs] +git = "https://github.com/HampusMat/dear-imgui-rs" +branch = "windows-always-static-libstdc++" + [build-dependencies] build-rs = "0.3.4" serde = { version = "1.0.228", features = ["derive"] } diff --git a/engine/res/imgui_shader.slang b/engine/res/imgui_shader.slang new file mode 100644 index 0000000..6ce51d6 --- /dev/null +++ b/engine/res/imgui_shader.slang @@ -0,0 +1,54 @@ +struct Vertex +{ + float3 pos; + float4 color; + float2 texture_coords; +}; + +struct VertexData +{ + float4 color; + float2 texture_coords; +}; + +struct VertexStageOutput +{ + VertexData vertex_data : VertexData; + float4 sv_position : SV_Position; +}; + +struct Fragment +{ + float4 color; +}; + +cbuffer Uniforms +{ + float4x4 projection; +}; + +Sampler2D main_texture; + +[shader("vertex")] +VertexStageOutput vertex_main(Vertex vertex: VERTEX) +{ + VertexStageOutput stage_output; + + stage_output.sv_position = mul(projection, float4(vertex.pos.xy, 0.f, 1.f)); + + stage_output.vertex_data.color = vertex.color; + stage_output.vertex_data.texture_coords = vertex.texture_coords; + + return stage_output; +} + +[shader("fragment")] +Fragment fragment_main(VertexData vertex_data: VertexData) : SV_Target +{ + Fragment fragment; + + fragment.color = vertex_data.color * main_texture.Sample(vertex_data.texture_coords); + + return fragment; +} + diff --git a/engine/src/lib.rs b/engine/src/lib.rs index d6ca6df..b1c41d6 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -37,6 +37,7 @@ pub mod rendering; pub mod shader; pub mod texture; pub mod transform; +pub mod ui; pub mod windowing; pub extern crate engine_ecs as ecs; diff --git a/engine/src/ui.rs b/engine/src/ui.rs new file mode 100644 index 0000000..9dd3280 --- /dev/null +++ b/engine/src/ui.rs @@ -0,0 +1 @@ +pub mod imgui; diff --git a/engine/src/ui/imgui.rs b/engine/src/ui/imgui.rs new file mode 100644 index 0000000..ad59555 --- /dev/null +++ b/engine/src/ui/imgui.rs @@ -0,0 +1,853 @@ +use std::path::{Path, PathBuf}; +use std::sync::LazyLock; + +use dear_imgui_rs::texture::TextureFormat as ImguiTextureFormat; +use dear_imgui_rs::{ + DrawCmd as ImguiDrawCmd, + Key as ImguiKey, + MouseButton as ImguiMouseButton, + TextureId as ImguiTextureId, +}; +use ecs::component::local::Local; +use ecs::component::Component; +use ecs::event::component::{Changed, EventMatchExt}; +use ecs::pair::Pair; +use ecs::query::term::With; +use ecs::sole::Single; +use ecs::system::initializable::Initializable; +use ecs::system::observer::Observe; +use ecs::system::Into; +use ecs::{Component, Query, Sole}; + +use crate::asset::{Assets, Handle as AssetHandle, Label as AssetLabel}; +use crate::data_types::dimens::Dimens; +use crate::delta_time::DeltaTime; +use crate::image::{ + ColorType as ImageColorType, + FromBytesError as ImageFromBytesError, + Image, +}; +use crate::input::keyboard::{Key, Keyboard}; +use crate::input::mouse::{Button as MouseButton, Buttons as MouseButtons, Mouse}; +use crate::mesh::vertex_buffer::{ + NamedVertexAttr as MeshNamedVertexAttr, + VertexAttrInfo as MeshVertexAttrInfo, + VertexBuffer as MeshVertexBuffer, +}; +use crate::mesh::{ + Mesh, + VertexAttrType as MeshVertexAttrType, + POSITION_VERTEX_ATTRIB_NAME, +}; +use crate::projection::{ + ClipVolume as ProjectionClipVolume, + Orthographic as OrthographicProjection, +}; +use crate::rendering::blending::{ + Config as RenderingBlendingConfiguration, + Equation as RenderingBlendingEquation, + Factor as RenderingBlendingFactor, +}; +use crate::rendering::object::{Id as RenderingObjectId, Store as RenderingObjectStore}; +use crate::rendering::{ + Command as RenderingCommand, + DrawMeshOptions, + DrawProperties, + MeshUsage, + RenderPass, + RenderPasses, + SurfaceId, + SurfaceSpec, + PRE_RENDER_PHASE, +}; +use crate::shader::cursor::{BindingValue as ShaderBindingValue, Cursor as ShaderCursor}; +use crate::shader::{ + Context as ShaderContext, + EntrypointFlags as ShaderEntrypointFlags, + ModuleSource as ShaderModuleSource, +}; +use crate::texture::{ + Filtering as TextureFiltering, + Properties as TextureProperties, + Texture, + Wrapping as TextureWrapping, +}; +use crate::vector::Vec2; +use crate::windowing::window::Window; + +mod reexports +{ + pub use dear_imgui_rs as dear_imgui; +} + +pub use reexports::*; + +pub static SHADER_ASSET_LABEL: LazyLock<AssetLabel> = LazyLock::new(|| AssetLabel { + path: Path::new("").into(), + name: Some("imgui_shader".into()), +}); + +/// Imgui context +#[derive(Sole)] +pub struct Context +{ + pub enabled: bool, + ctx: inner_context_wrapper::InnerContextWrapper, +} + +impl Context +{ + pub fn frame(&mut self) -> Option<&mut dear_imgui_rs::Ui> + { + if !self.enabled { + return None; + } + + self.ctx.get_frame() + } +} + +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct Extension +{ + start_enabled: bool, + settings_ini_file_path: Option<PathBuf>, +} + +impl Extension +{ + pub fn with_start_enabled(mut self, start_enabled: bool) -> Self + { + self.start_enabled = start_enabled; + self + } + + pub fn with_settings_ini_file( + mut self, + settings_ini_file_path: Option<PathBuf>, + ) -> Self + { + self.settings_ini_file_path = settings_ini_file_path; + self + } +} + +impl ecs::extension::Extension for Extension +{ + fn collect(self, mut collector: ecs::extension::Collector<'_>) + { + let mut context = Context { + enabled: self.start_enabled, + ctx: inner_context_wrapper::InnerContextWrapper::new(), + }; + + if let Err(err) = context + .ctx + .set_settings_ini_file_path(self.settings_ini_file_path) + { + tracing::error!("Failed to set path of imgui settings ini file: {err}"); + } + + collector.add_sole(context).ok(); + + collector.add_system( + *PRE_RENDER_PHASE, + update.into_system().initialize((State::default(),)), + ); + + collector.add_observer(handle_window_changed); + } +} + +#[tracing::instrument(skip_all)] +fn handle_window_changed( + observe: Observe<Pair<Changed, Window>>, + mut context: Single<Context>, +) +{ + let Some(event_match) = observe + .iter() + .find(|event_match| event_match.get_entity().has_component(TargetWindow::id())) + else { + return; + }; + + let window = event_match.get_ent_target_comp(); + + let hidpi_factor = window.scale_factor().round(); + + context + .ctx + .get_io_mut() + .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]); + + let window_size = window.inner_size(); + + let window_size = Dimens { + width: window_size.width as f32, + height: window_size.height as f32, + }; + + let window_logical_size = window_size / (hidpi_factor as f32); + + context + .ctx + .get_io_mut() + .set_display_size([window_logical_size.width, window_logical_size.height]); +} + +fn update( + target_window_query: Query<(&Window, &SurfaceSpec), (With<TargetWindow>,)>, + mut context: Single<Context>, + delta_time: Single<DeltaTime>, + mut assets: Single<Assets>, + mut render_passes: Single<RenderPasses>, + renderer_object_store: Single<RenderingObjectStore>, + shader_context: Single<ShaderContext>, + keyboard: Single<Keyboard>, + mouse: Single<Mouse>, + mouse_buttons: Single<MouseButtons>, + mut state: Local<State>, +) +{ + let Some((window, window_surface_spec)) = target_window_query.iter().next() else { + return; + }; + + let curr_state = std::mem::replace(&mut *state, State::Unreachable); + + match curr_state { + State::NotInitialized => { + let Some((font_texture_asset, shader_asset, mesh_obj_id, mesh)) = + initialize_context( + &mut context, + &window, + window_surface_spec.id, + &mut assets, + &mut render_passes, + &shader_context, + ) + else { + *state = State::NotInitialized; + return; + }; + + *state = State::Initializing { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + } + State::Initializing { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + } => { + let Some(font_texture_obj) = renderer_object_store + .get_obj(&RenderingObjectId::Asset(font_texture_asset.id())) + else { + *state = State::Initializing { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + return; + }; + + context + .ctx + .get_font_atlas_mut() + .set_texture_id(ImguiTextureId::new(font_texture_obj.as_raw().into())); + + context + .ctx + .get_io_mut() + .set_delta_time(delta_time.duration.as_secs() as f32); + + context.ctx.new_frame(); + + *state = State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + } + State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mut mesh, + } => { + if !context.enabled { + *state = State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + return; + } + + context + .ctx + .get_io_mut() + .set_delta_time(delta_time.duration.as_secs_f32()); + + update_inputs(&mut context, &window, &keyboard, &mouse, &mouse_buttons); + + add_drawing_render_pass( + &mut context, + &mut render_passes, + &shader_context, + window_surface_spec.id, + font_texture_asset.clone(), + shader_asset.clone(), + mesh_obj_id, + &mut mesh, + ); + + context.ctx.new_frame(); + + *state = State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + } + State::Unreachable => unreachable!(), + } +} + +#[derive(Debug, Component)] +pub struct TargetWindow; + +#[derive(Debug, Default, Component)] +enum State +{ + #[default] + NotInitialized, + Initializing + { + font_texture_asset: AssetHandle<Texture>, + shader_asset: AssetHandle<ShaderModuleSource>, + mesh_obj_id: RenderingObjectId, + mesh: Mesh, + }, + Ready + { + font_texture_asset: AssetHandle<Texture>, + shader_asset: AssetHandle<ShaderModuleSource>, + mesh_obj_id: RenderingObjectId, + mesh: Mesh, + }, + Unreachable, +} + +fn initialize_context( + context: &mut Context, + window: &Window, + window_surface_id: SurfaceId, + assets: &mut Assets, + render_passes: &mut RenderPasses, + shader_context: &ShaderContext, +) -> Option<( + AssetHandle<Texture>, + AssetHandle<ShaderModuleSource>, + RenderingObjectId, + Mesh, +)> +{ + let shader_asset = if let Some(shader_asset) = + assets.get_handle_to_loaded::<ShaderModuleSource>(SHADER_ASSET_LABEL.clone()) + { + shader_asset + } else { + assets.store_with_label( + SHADER_ASSET_LABEL.clone(), + ShaderModuleSource { + name: "imgui_shader.slang".into(), + file_path: Path::new("@engine/imgui_shader").into(), + source: include_str!("../../res/imgui_shader.slang").into(), + link_entrypoints: ShaderEntrypointFlags::VERTEX + | ShaderEntrypointFlags::FRAGMENT, + }, + ); + + // We want to wait with initializing until the shader have been processed by the + // shader context + return None; + }; + + let hidpi_factor = window.scale_factor().round(); + + context + .ctx + .get_io_mut() + .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]); + + let window_size = window.inner_size(); + + let window_size = Dimens { + width: window_size.width as f32, + height: window_size.height as f32, + }; + + let window_logical_size = window_size / (hidpi_factor as f32); + + context + .ctx + .get_io_mut() + .set_display_size([window_logical_size.width, window_logical_size.height]); + + let mut fonts = context.ctx.get_font_atlas_mut(); + + if !fonts.is_built() { + fonts.build(); + } + + let Some(font_texture_data) = fonts.tex_data_mut() else { + tracing::error!("No texture data exists for font"); + return None; + }; + + let font_texture = match create_font_texture(font_texture_data) { + Ok(font_texture) => font_texture, + Err(err) => { + tracing::error!("Failed to create font texture: {err}"); + return None; + } + }; + + let font_texture_asset = assets.store_with_name("imgui_font_texture", font_texture); + + let mesh_obj_id = RenderingObjectId::new_sequential(); + + let mesh = Mesh::builder() + .vertices(MeshVertexBuffer::with_capacity( + &[ + MeshVertexAttrInfo { + name: POSITION_VERTEX_ATTRIB_NAME.into(), + ty: MeshVertexAttrType::Float32Array { length: 3 }, + }, + MeshVertexAttrInfo { + name: "color".into(), + ty: MeshVertexAttrType::Float32Array { length: 4 }, + }, + MeshVertexAttrInfo { + name: "texture_coords".into(), + ty: MeshVertexAttrType::Float32Array { length: 2 }, + }, + ], + 128, + )) + .indices([]) + .build(); + + render_passes.passes.push_back(RenderPass { + commands: vec![ + RenderingCommand::MakeCurrent(window_surface_id), + RenderingCommand::CreateTexture(font_texture_asset.clone()), + RenderingCommand::CreateShaderProgram( + RenderingObjectId::Asset(shader_asset.id()), + shader_context + .get_program(&shader_asset.id()) + .expect("Not possible") + .clone(), + ), + RenderingCommand::ActivateShader(RenderingObjectId::Asset(shader_asset.id())), + RenderingCommand::CreateMesh { + obj_id: mesh_obj_id, + mesh: Some(mesh.clone()), + usage: MeshUsage::Stream, + }, + ], + draw_properties: DrawProperties::default(), + }); + + Some((font_texture_asset, shader_asset, mesh_obj_id, mesh)) +} + +fn update_inputs( + context: &mut Context, + window: &Window, + keyboard: &Keyboard, + mouse: &Mouse, + mouse_buttons: &MouseButtons, +) +{ + for (key, key_state) in keyboard.new_key_states() { + if let Some(key) = key_to_imgui_key(key) { + context + .ctx + .get_io_mut() + .add_key_event(key, key_state.is_pressed()); + } + } + + for character in keyboard.text_keys().chars() { + if character == '\u{7f}' { + continue; + } + + context.ctx.get_io_mut().add_input_character(character); + } + + let mouse_pos = mouse.position / window.scale_factor(); + + context + .ctx + .get_io_mut() + .add_mouse_pos_event([mouse_pos.x as f32, mouse_pos.y as f32]); + + for (mouse_button, mouse_button_state) in mouse_buttons.all_current() { + if let Some(mouse_button) = mouse_button_to_imgui_mouse_button(mouse_button) { + context + .ctx + .get_io_mut() + .add_mouse_button_event(mouse_button, mouse_button_state.is_pressed()); + } + } +} + +fn add_drawing_render_pass( + context: &mut Context, + render_passes: &mut RenderPasses, + shader_context: &ShaderContext, + window_surface_id: SurfaceId, + font_texture_asset: AssetHandle<Texture>, + shader_asset: AssetHandle<ShaderModuleSource>, + mesh_obj_id: RenderingObjectId, + mesh: &mut Mesh, +) +{ + let render_pass = render_passes.passes.push_back_mut(RenderPass { + commands: Vec::new(), + draw_properties: DrawProperties { + blending_enabled: true, + blending_config: RenderingBlendingConfiguration { + equation: RenderingBlendingEquation::Add, + source_factor: RenderingBlendingFactor::SrcAlpha, + destination_factor: RenderingBlendingFactor::OneMinusSrcAlpha, + }, + depth_test_enabled: false, + face_culling_enabled: false, + ..Default::default() + }, + }); + + let shader_program = shader_context + .get_program(&shader_asset.id()) + .expect("Not possible"); + + let shader_cursor = ShaderCursor::new( + shader_program + .reflection(0) + .unwrap() + .global_params_var_layout() + .unwrap(), + ); + + let draw_data = context.ctx.render(); + + let [display_width, display_height] = draw_data.display_size; + + // let [scale_width, scale_height] = draw_data.framebuffer_scale; + + render_pass.commands.extend([ + RenderingCommand::MakeCurrent(window_surface_id), + RenderingCommand::ActivateShader(RenderingObjectId::Asset(shader_asset.id())), + RenderingCommand::SetShaderBinding( + shader_cursor + .field("Uniforms") + .field("projection") + .into_binding_location(), + ShaderBindingValue::FMat4x4( + OrthographicProjection::builder() + .near(1.0) + .far(-1.0) + .viewport_origin(Vec2 { x: 0.0, y: 1.0 }) + .size(crate::projection::OrthographicSize::WindowSize) + .build() + .to_matrix_rh( + Dimens { + width: display_width, + height: -display_height, + }, + ProjectionClipVolume::NegOneToOne, + ), + ), + ), + RenderingCommand::SetShaderBinding( + shader_cursor.field("main_texture").into_binding_location(), + ShaderBindingValue::Texture(font_texture_asset), + ), + ]); + + for draw_list in draw_data.draw_lists() { + mesh.vertex_buf_mut().clear(); + + for vertex in draw_list.vtx_buffer() { + mesh.vertex_buf_mut().push(( + MeshNamedVertexAttr::<[f32; 3]> { + name: POSITION_VERTEX_ATTRIB_NAME, + value: [vertex.pos[0], vertex.pos[1], 0.0], + }, + MeshNamedVertexAttr { + name: "color", + value: vertex.rgba().map(|elem| (elem as f32) / 255.0), + }, + MeshNamedVertexAttr { + name: "texture_coords", + value: vertex.uv, + }, + )); + } + + mesh.set_indices(draw_list.idx_buffer().iter().map(|index| (*index).into())); + + render_pass.commands.push(RenderingCommand::UpdateMesh { + obj_id: mesh_obj_id, + mesh: mesh.clone(), + usage: MeshUsage::Stream, + }); + + for command in draw_list.commands() { + match command { + ImguiDrawCmd::Elements { count, cmd_params, raw_cmd: _ } => { + render_pass.commands.push(RenderingCommand::DrawMesh( + mesh_obj_id, + DrawMeshOptions::builder() + .element_offset(cmd_params.idx_offset.try_into().unwrap()) + .vertex_offset(cmd_params.vtx_offset.try_into().unwrap()) + .element_cnt(count.try_into().unwrap()) + .build(), + )); + } + _ => {} + } + } + } +} + +fn create_font_texture( + font_texture_data: &dear_imgui_rs::texture::TextureData, +) -> Result<Texture, ImageFromBytesError> +{ + Ok(Texture { + image: Image::try_from_bytes( + font_texture_data + .pixels() + .expect("Font texture data does not contain any pixels"), + Dimens { + width: font_texture_data.width().try_into().expect( + "Font texture width value does not fit in 32-bit unsigned int", + ), + height: font_texture_data.height().try_into().expect( + "Font texture height value does not fit in 32-bit unsigned int", + ), + }, + match font_texture_data.format() { + ImguiTextureFormat::RGBA32 => ImageColorType::Rgba8, + ImguiTextureFormat::Alpha8 => unimplemented!(), + }, + )?, + properties: TextureProperties::builder() + .minifying_filter(TextureFiltering::Linear) + .magnifying_filter(TextureFiltering::Linear) + .wrap(TextureWrapping::ClampToBorder) + .build(), + }) +} + +fn key_to_imgui_key(key: Key) -> Option<ImguiKey> +{ + match key { + Key::Backquote => Some(ImguiKey::GraveAccent), + Key::Backslash => Some(ImguiKey::Backslash), + Key::BracketLeft => Some(ImguiKey::LeftBracket), + Key::BracketRight => Some(ImguiKey::RightBracket), + Key::Comma => Some(ImguiKey::Comma), + Key::Digit0 => Some(ImguiKey::Key0), + Key::Digit1 => Some(ImguiKey::Key1), + Key::Digit2 => Some(ImguiKey::Key2), + Key::Digit3 => Some(ImguiKey::Key3), + Key::Digit4 => Some(ImguiKey::Key4), + Key::Digit5 => Some(ImguiKey::Key5), + Key::Digit6 => Some(ImguiKey::Key6), + Key::Digit7 => Some(ImguiKey::Key7), + Key::Digit8 => Some(ImguiKey::Key8), + Key::Digit9 => Some(ImguiKey::Key9), + Key::Equal => Some(ImguiKey::Equal), + Key::A => Some(ImguiKey::A), + Key::B => Some(ImguiKey::B), + Key::C => Some(ImguiKey::C), + Key::D => Some(ImguiKey::D), + Key::E => Some(ImguiKey::E), + Key::F => Some(ImguiKey::F), + Key::G => Some(ImguiKey::G), + Key::H => Some(ImguiKey::H), + Key::I => Some(ImguiKey::I), + Key::J => Some(ImguiKey::J), + Key::K => Some(ImguiKey::K), + Key::L => Some(ImguiKey::L), + Key::M => Some(ImguiKey::M), + Key::N => Some(ImguiKey::N), + Key::O => Some(ImguiKey::O), + Key::P => Some(ImguiKey::P), + Key::Q => Some(ImguiKey::Q), + Key::R => Some(ImguiKey::R), + Key::S => Some(ImguiKey::S), + Key::T => Some(ImguiKey::T), + Key::U => Some(ImguiKey::U), + Key::V => Some(ImguiKey::V), + Key::W => Some(ImguiKey::W), + Key::X => Some(ImguiKey::X), + Key::Y => Some(ImguiKey::Y), + Key::Z => Some(ImguiKey::Z), + Key::Minus => Some(ImguiKey::Minus), + Key::Period => Some(ImguiKey::Period), + Key::Quote => Some(ImguiKey::Apostrophe), + Key::Semicolon => Some(ImguiKey::Semicolon), + Key::Slash => Some(ImguiKey::Slash), + Key::AltLeft => Some(ImguiKey::LeftAlt), + Key::AltRight => Some(ImguiKey::RightAlt), + Key::Backspace => Some(ImguiKey::Backspace), + Key::CapsLock => Some(ImguiKey::CapsLock), + Key::ControlLeft => Some(ImguiKey::LeftCtrl), + Key::ControlRight => Some(ImguiKey::RightCtrl), + Key::Enter => Some(ImguiKey::Enter), + Key::SuperLeft => Some(ImguiKey::LeftSuper), + Key::SuperRight => Some(ImguiKey::RightSuper), + Key::ShiftLeft => Some(ImguiKey::LeftShift), + Key::ShiftRight => Some(ImguiKey::RightShift), + Key::Space => Some(ImguiKey::Space), + Key::Tab => Some(ImguiKey::Tab), + Key::Delete => Some(ImguiKey::Delete), + Key::End => Some(ImguiKey::End), + Key::Home => Some(ImguiKey::Home), + Key::Insert => Some(ImguiKey::Insert), + Key::PageDown => Some(ImguiKey::PageDown), + Key::PageUp => Some(ImguiKey::PageUp), + Key::ArrowDown => Some(ImguiKey::DownArrow), + Key::ArrowLeft => Some(ImguiKey::LeftArrow), + Key::ArrowRight => Some(ImguiKey::RightArrow), + Key::ArrowUp => Some(ImguiKey::UpArrow), + Key::NumLock => Some(ImguiKey::NumLock), + Key::Numpad0 => Some(ImguiKey::Keypad0), + Key::Numpad1 => Some(ImguiKey::Keypad1), + Key::Numpad2 => Some(ImguiKey::Keypad2), + Key::Numpad3 => Some(ImguiKey::Keypad3), + Key::Numpad4 => Some(ImguiKey::Keypad4), + Key::Numpad5 => Some(ImguiKey::Keypad5), + Key::Numpad6 => Some(ImguiKey::Keypad6), + Key::Numpad7 => Some(ImguiKey::Keypad7), + Key::Numpad8 => Some(ImguiKey::Keypad8), + Key::Numpad9 => Some(ImguiKey::Keypad9), + Key::NumpadAdd => Some(ImguiKey::KeypadAdd), + Key::NumpadDecimal => Some(ImguiKey::KeypadDecimal), + Key::NumpadDivide => Some(ImguiKey::KeypadDivide), + Key::NumpadEnter => Some(ImguiKey::KeypadEnter), + Key::NumpadEqual => Some(ImguiKey::KeypadEqual), + Key::NumpadMultiply => Some(ImguiKey::KeypadMultiply), + Key::NumpadSubtract => Some(ImguiKey::KeypadSubtract), + Key::Escape => Some(ImguiKey::Escape), + Key::PrintScreen => Some(ImguiKey::PrintScreen), + Key::ScrollLock => Some(ImguiKey::ScrollLock), + Key::Pause => Some(ImguiKey::Pause), + Key::Meta => Some(ImguiKey::ModSuper), + Key::F1 => Some(ImguiKey::F1), + Key::F2 => Some(ImguiKey::F2), + Key::F3 => Some(ImguiKey::F3), + Key::F4 => Some(ImguiKey::F4), + Key::F5 => Some(ImguiKey::F5), + Key::F6 => Some(ImguiKey::F6), + Key::F7 => Some(ImguiKey::F7), + Key::F8 => Some(ImguiKey::F8), + Key::F9 => Some(ImguiKey::F9), + Key::F10 => Some(ImguiKey::F10), + Key::F11 => Some(ImguiKey::F11), + Key::F12 => Some(ImguiKey::F12), + _ => None, + } +} + +fn mouse_button_to_imgui_mouse_button( + mouse_button: MouseButton, +) -> Option<ImguiMouseButton> +{ + match mouse_button { + MouseButton::Left | MouseButton::Other(0) => Some(ImguiMouseButton::Left), + MouseButton::Right | MouseButton::Other(1) => Some(ImguiMouseButton::Right), + MouseButton::Middle | MouseButton::Other(2) => Some(ImguiMouseButton::Middle), + MouseButton::Other(3) => Some(ImguiMouseButton::Extra1), + MouseButton::Other(4) => Some(ImguiMouseButton::Extra2), + _ => None, + } +} + +mod inner_context_wrapper +{ + use std::path::PathBuf; + use std::pin::Pin; + use std::ptr::null_mut; + + pub struct InnerContextWrapper + { + ctx: Pin<Box<dear_imgui_rs::Context>>, + frame: *mut dear_imgui_rs::Ui, + } + + impl InnerContextWrapper + { + pub fn new() -> Self + { + let mut ctx = Box::pin(dear_imgui_rs::Context::create()); + + let _ = ctx.set_platform_name(Some("engine-windowing")); + let _ = ctx.set_renderer_name(Some("engine-rendering")); + + Self { ctx, frame: null_mut() } + } + + pub fn set_settings_ini_file_path( + &mut self, + path: Option<PathBuf>, + ) -> Result<(), dear_imgui_rs::ImGuiError> + { + self.ctx.set_ini_filename(path) + } + + pub fn get_io_mut(&mut self) -> &mut dear_imgui_rs::Io + { + self.ctx.io_mut() + } + + pub fn get_font_atlas_mut(&mut self) -> dear_imgui_rs::fonts::FontAtlas + { + self.ctx.font_atlas_mut() + } + + pub fn render(&mut self) -> &dear_imgui_rs::DrawData + { + self.ctx.render() + } + + pub fn new_frame(&mut self) + { + let frame = &raw mut *self.ctx.frame(); + + self.frame = frame; + } + + pub fn get_frame(&mut self) -> Option<&mut dear_imgui_rs::Ui> + { + unsafe { self.frame.as_mut() } + } + } +} |
