From af7e3ed5b4afdf78972749b627c2765c9e5945fb Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 16 Jul 2026 17:39:21 +0200 Subject: refactor(engine): rename ui::imgui mod to dear_imgui --- engine/src/ui/dear_imgui.rs | 1039 +++++++++++++++++++++++++++++++++++++++++++ engine/src/ui/imgui.rs | 1039 ------------------------------------------- 2 files changed, 1039 insertions(+), 1039 deletions(-) create mode 100644 engine/src/ui/dear_imgui.rs delete mode 100644 engine/src/ui/imgui.rs (limited to 'engine/src/ui') diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs new file mode 100644 index 0000000..7c5dbc3 --- /dev/null +++ b/engine/src/ui/dear_imgui.rs @@ -0,0 +1,1039 @@ +use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::LazyLock; + +use dear_imgui_rs::{ + DrawCmd as ImguiDrawCmd, + Key as ImguiKey, + MouseButton as ImguiMouseButton, +}; +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::color::Rgba; +use crate::data_types::dimens::Dimens; +use crate::delta_time::DeltaTime; +use crate::image::{ + Image, + PixelBuffer as ImagePixelBuffer, + PixelBufferLenIncorrectForSize as ImagePixelBufferLenIncorrectForSize, +}; +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; +use crate::rendering::shader::cursor::{ + BindingValue as ShaderBindingValue, + Cursor as ShaderCursor, +}; +use crate::rendering::shader::{ + Context as ShaderContext, + EntrypointFlags as ShaderEntrypointFlags, + ModuleSource as ShaderModuleSource, +}; +use crate::rendering::{ + AssetOrValue, + Command as RenderingCommand, + DrawMeshOptions, + DrawProperties, + DrawPropertiesUpdateFlags, + MeshUsage, + RenderPass, + RenderPasses, + ScissorBox, + SurfaceId, + SurfaceSpec, + PRE_RENDER_PHASE, +}; +use crate::texture::{Properties as TextureProperties, Texture}; +use crate::util::MapVec; +use crate::vector::Vec2; +use crate::windowing::dpi::PhysicalSize; +use crate::windowing::window::Window; + +mod reexports +{ + pub use dear_imgui_rs as dear_imgui; +} + +pub use reexports::*; + +pub static SHADER_ASSET_LABEL: LazyLock = LazyLock::new(|| AssetLabel { + path: Path::new("").into(), + name: Some("imgui_shader".into()), +}); + +/// Dear Imgui context +#[derive(Sole)] +pub struct Context +{ + pub enabled: bool, + ctx: inner_context_wrapper::InnerContextWrapper, + texture_lookup: MapVec, +} + +impl Context +{ + pub fn frame(&mut self) -> Option<&mut dear_imgui_rs::Ui> + { + if !self.enabled { + return None; + } + + self.ctx.get_frame() + } + + pub fn register_texture(&mut self, texture_data: &mut dear_imgui_rs::OwnedTextureData) + { + self.ctx.register_texture(texture_data); + } +} + +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct Extension +{ + start_enabled: bool, + settings_ini_file_path: Option, +} + +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, + ) -> 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(), + texture_lookup: MapVec::with_capacity(8), + }; + + 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>, + mut context: Single, +) +{ + let Ok(context) = context.get_mut() else { + unreachable!(); + }; + + 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_logical = PhysicalSize { + width: window.inner_size.width as f64, + height: window.inner_size.height as f64, + } + .to_logical::(hidpi_factor); + + context.ctx.get_io_mut().set_display_size([ + window_size_logical.width as f32, + window_size_logical.height as f32, + ]); +} + +fn update( + target_window_query: Query<(&Window, &SurfaceSpec), (With,)>, + mut context: Single, + delta_time: Single, + mut assets: Single, + mut render_passes: Single, + shader_context: Single, + keyboard: Single, + mouse: Single, + mouse_buttons: Single, + mut state: Local, +) -> Result<(), crate::Error> +{ + let Ok(context) = context.get_mut() else { + unreachable!(); + }; + + let delta_time = delta_time.get()?; + let assets = assets.get_mut()?; + let render_passes = render_passes.get_mut()?; + let shader_context = shader_context.get()?; + let keyboard = keyboard.get()?; + let mouse = mouse.get()?; + let mouse_buttons = mouse_buttons.get()?; + + let Some((window, window_surface_spec)) = target_window_query.iter().next() else { + return Ok(()); + }; + + let curr_state = std::mem::replace(&mut *state, State::Unreachable); + + match curr_state { + State::NotInitialized => { + let Some((shader_asset, mesh_obj_id, mesh)) = initialize_context( + context, + &window, + window_surface_spec.id, + assets, + render_passes, + &shader_context, + ) else { + *state = State::NotInitialized; + return Ok(()); + }; + + context.ctx.new_frame(); + + *state = State::Ready { shader_asset, mesh_obj_id, mesh }; + } + State::Ready { shader_asset, mesh_obj_id, mut mesh } => { + if !context.enabled { + *state = State::Ready { shader_asset, mesh_obj_id, mesh }; + return Ok(()); + } + + context + .ctx + .get_io_mut() + .set_delta_time(delta_time.duration.as_secs_f32()); + + update_inputs(context, &window, keyboard, mouse, mouse_buttons); + + add_drawing_render_pass( + context, + render_passes, + shader_context, + window_surface_spec.id, + shader_asset.clone(), + mesh_obj_id, + &mut mesh, + ); + + context.ctx.new_frame(); + + *state = State::Ready { shader_asset, mesh_obj_id, mesh }; + } + State::Unreachable => unreachable!(), + } + + Ok(()) +} + +#[derive(Debug, Component)] +pub struct TargetWindow; + +#[derive(Debug, Default, Component)] +enum State +{ + #[default] + NotInitialized, + Ready + { + shader_asset: AssetHandle, + 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, RenderingObjectId, Mesh)> +{ + let shader_asset = if let Some(shader_asset) = + assets.get_handle_to_loaded::(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 = Dimens { + width: window.inner_size.width as f32, + height: window.inner_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 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::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: AssetOrValue::Value(mesh.clone()), + usage: MeshUsage::Stream, + }, + ], + draw_properties: DrawProperties::default(), + }); + + Some((shader_asset, mesh_obj_id, mesh)) +} + +fn update_inputs( + context: &mut Context, + window: &Window, + keyboard: &Keyboard, + mouse: &Mouse, + mouse_buttons: &MouseButtons, +) +{ + let io = context.ctx.get_io_mut(); + + for (key, key_state) in keyboard.new_key_states() { + if let Some(key) = key_to_imgui_key(key) { + io.add_key_event(key, key_state.is_pressed()); + } + } + + for character in keyboard.text_keys().chars() { + if character == '\u{7f}' { + continue; + } + + io.add_input_character(character); + } + + io.add_mouse_source_event(dear_imgui_rs::input::MouseSource::Mouse); + + let mouse_pos = mouse.position.to_logical::(window.scale_factor()); + + io.add_mouse_pos_event([mouse_pos.x as f32, mouse_pos.y as f32]); + + if !mouse.curr_tick_scroll_delta.is_zero() { + io.add_mouse_wheel_event([ + mouse.curr_tick_scroll_delta.hor_lines, + mouse.curr_tick_scroll_delta.vert_lines, + ]); + } + + for (mouse_button, mouse_button_state) in mouse_buttons.all_current() { + if let Some(mouse_button) = mouse_button_to_imgui_mouse_button(mouse_button) { + io.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, + shader_asset: AssetHandle, + 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, + scissor_test_enabled: true, + ..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 Context { enabled: _, ctx, texture_lookup } = context; + + let draw_data = ctx.render(); + + let mut textures = draw_data.textures_mut(); + + while let Some(mut texture_data) = textures.next() { + match texture_data.status() { + dear_imgui_rs::TextureStatus::WantCreate => { + let Some(texture_pixels) = texture_data.pixels() else { + texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + continue; + }; + + if !matches!(texture_data.format(), dear_imgui_rs::TextureFormat::RGBA32) + { + unimplemented!(); + } + + let image = + match Image::from_pixels(ImagePixelBuffer::, _>::new( + texture_pixels, + Dimens { + width: texture_data.width(), + height: texture_data.height(), + }, + )) { + Ok(image) => image, + Err(ImagePixelBufferLenIncorrectForSize) => { + tracing::error!("Not enough texture pixels for texture size"); + continue; + } + }; + + let texture_object_id = RenderingObjectId::new_sequential(); + + render_pass.commands.push(RenderingCommand::CreateTexture( + texture_object_id, + AssetOrValue::Value(Texture { + image, + properties: TextureProperties::default(), + }), + )); + + let texture_lookup_id = TextureLookupId::new(); + + texture_data.set_tex_id(texture_lookup_id.into()); + + texture_lookup.insert(texture_lookup_id, texture_object_id); + + texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + } + dear_imgui_rs::TextureStatus::WantUpdates => { + if texture_data.tex_id().is_null() { + tracing::error!(concat!( + "Update was requested for texture with a null ID. Did you forget ", + "to set the texture status to TextureStatus::WantCreate?")); + + continue; + } + + if texture_data.pixels().is_none() { + texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + continue; + } + + let mut rects: Vec = + texture_data.updates().collect(); + + if rects.is_empty() { + let rect = texture_data.update_rect(); + if rect.w > 0 && rect.h > 0 { + rects.push(rect); + } + } + + if rects.is_empty() { + texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + continue; + } + + let Some(texture_object_id) = + texture_lookup.get(&TextureLookupId::from(texture_data.tex_id())) + else { + tracing::error!( + "Unknown texture {}. Skipping update", + texture_data.tex_id().id() + ); + texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + continue; + }; + + render_pass.commands.extend(rects.iter().filter_map(|rect| { + let Some(rect_image) = get_texture_rect_image(&texture_data, rect) + else { + return None; + }; + + Some(RenderingCommand::UpdateTexture { + obj_id: texture_object_id.clone(), + image: rect_image, + offset: Vec2 { x: rect.x as u32, y: rect.y as u32 }, + }) + })); + + texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + } + dear_imgui_rs::TextureStatus::WantDestroy => { + let Some(texture_object_id) = + texture_lookup.remove(TextureLookupId::from(texture_data.tex_id())) + else { + tracing::error!( + "Unknown texture {}. Skipping destroyal", + texture_data.tex_id().id() + ); + texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + continue; + }; + + render_pass + .commands + .push(RenderingCommand::RemoveTexture(texture_object_id)); + } + _ => {} + } + } + + 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, + ), + ), + ), + ]); + + 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 } => { + let Some(scissor_box) = + calc_draw_cmd_scissor_box(draw_data, &cmd_params) + else { + continue; + }; + + let tex_id = dear_imgui_rs::TextureId::new(u64::from(unsafe { + let mut cmd_copy = *raw_cmd; + + dear_imgui_rs::sys::ImDrawCmd_GetTexID(&mut cmd_copy) + })); + + let Some(texture_object_id) = + texture_lookup.get(&TextureLookupId::from(tex_id)) + else { + tracing::error!( + "Unknown texture {}. Skipping skipping draw command", + tex_id.id() + ); + continue; + }; + + render_pass.commands.extend([ + RenderingCommand::UpdateDrawProperties( + DrawProperties { scissor_box, ..Default::default() }, + DrawPropertiesUpdateFlags::SCISSOR_BOX, + ), + RenderingCommand::SetShaderBinding( + shader_cursor.field("main_texture").into_binding_location(), + ShaderBindingValue::Texture(texture_object_id.clone()), + ), + 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 get_texture_rect_image( + texture_data: &dear_imgui_rs::TextureData, + rect: &dear_imgui_rs::TextureRect, +) -> Option +{ + let texture_pixels = texture_data.pixels()?; + + let tex_width = texture_data.width(); + let tex_height = texture_data.height(); + + if tex_width == 0 || tex_height == 0 { + return None; + } + + let rect_pos = Vec2:: { x: rect.x.into(), y: rect.y.into() }; + + let rect_size = Dimens:: { + width: rect.w.into(), + height: rect.h.into(), + }; + + if rect_size.width == 0 + || rect_size.height == 0 + || rect_pos.x >= tex_width + || rect_pos.y >= tex_height + { + return None; + } + + let rect_size = Dimens { + width: rect_size.width.min(tex_width.saturating_sub(rect_pos.x)), + height: rect_size.height.min(tex_height.saturating_sub(rect_pos.y)), + }; + + match texture_data.format() { + dear_imgui_rs::TextureFormat::RGBA32 => { + let Ok(texture_image) = + Image::from_pixels(ImagePixelBuffer::, _>::new( + texture_pixels, + Dimens { width: tex_width, height: tex_height }, + )) + else { + return None; + }; + + Some(texture_image.sub_view(rect_pos, rect_size).to_image()) + } + dear_imgui_rs::TextureFormat::Alpha8 => { + unimplemented!(); + } + } +} + +fn calc_draw_cmd_scissor_box( + draw_data: &dear_imgui_rs::DrawData, + cmd_params: &dear_imgui_rs::DrawCmdParams, +) -> Option +{ + let clip_rect = cmd_params.clip_rect; + + let clip_min_x = + (clip_rect[0] - draw_data.display_pos[0]) * draw_data.framebuffer_scale[0]; + + let clip_min_y = + (clip_rect[1] - draw_data.display_pos[1]) * draw_data.framebuffer_scale[1]; + + let clip_max_x = + (clip_rect[2] - draw_data.display_pos[0]) * draw_data.framebuffer_scale[0]; + + let clip_max_y = + (clip_rect[3] - draw_data.display_pos[1]) * draw_data.framebuffer_scale[1]; + + if clip_max_x <= clip_min_x || clip_max_y <= clip_min_y { + return None; + } + + let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1]; + + Some(ScissorBox { + size: Some(Dimens { + width: (clip_max_x - clip_min_x) as u16, + height: (clip_max_y - clip_min_y) as u16, + }), + lower_left_corner_pos: Vec2 { + x: clip_min_x as u16, + y: (fb_height - clip_max_y) as u16, + }, + }) +} + +fn key_to_imgui_key(key: Key) -> Option +{ + 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 +{ + 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>, + 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")); + + ctx.io_mut() + .set_backend_flags(dear_imgui_rs::BackendFlags::RENDERER_HAS_TEXTURES); + + Self { ctx, frame: null_mut() } + } + + pub fn set_settings_ini_file_path( + &mut self, + path: Option, + ) -> Result<(), dear_imgui_rs::ImGuiError> + { + self.ctx.set_ini_filename(path) + } + + pub fn register_texture( + &mut self, + texture_data: &mut dear_imgui_rs::texture::OwnedTextureData, + ) + { + self.ctx.register_user_texture(texture_data); + } + + pub fn get_io_mut(&mut self) -> &mut dear_imgui_rs::Io + { + self.ctx.io_mut() + } + + pub fn render(&mut self) -> &mut 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() } + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct TextureLookupId +{ + inner: u64, +} + +impl TextureLookupId +{ + fn new() -> Self + { + static NEXT: AtomicU64 = AtomicU64::new(1); + + Self { + inner: NEXT.fetch_add(1, Ordering::Relaxed), + } + } +} + +impl From for TextureLookupId +{ + fn from(texture_id: dear_imgui_rs::TextureId) -> Self + { + assert!(!texture_id.is_null(), "Bad texture ID: is null"); + + Self { inner: texture_id.id() } + } +} + +impl From for dear_imgui_rs::TextureId +{ + fn from(texture_id: TextureLookupId) -> Self + { + Self::new(texture_id.inner) + } +} diff --git a/engine/src/ui/imgui.rs b/engine/src/ui/imgui.rs deleted file mode 100644 index f9b4130..0000000 --- a/engine/src/ui/imgui.rs +++ /dev/null @@ -1,1039 +0,0 @@ -use std::path::{Path, PathBuf}; -use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::LazyLock; - -use dear_imgui_rs::{ - DrawCmd as ImguiDrawCmd, - Key as ImguiKey, - MouseButton as ImguiMouseButton, -}; -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::color::Rgba; -use crate::data_types::dimens::Dimens; -use crate::delta_time::DeltaTime; -use crate::image::{ - Image, - PixelBuffer as ImagePixelBuffer, - PixelBufferLenIncorrectForSize as ImagePixelBufferLenIncorrectForSize, -}; -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; -use crate::rendering::shader::cursor::{ - BindingValue as ShaderBindingValue, - Cursor as ShaderCursor, -}; -use crate::rendering::shader::{ - Context as ShaderContext, - EntrypointFlags as ShaderEntrypointFlags, - ModuleSource as ShaderModuleSource, -}; -use crate::rendering::{ - AssetOrValue, - Command as RenderingCommand, - DrawMeshOptions, - DrawProperties, - DrawPropertiesUpdateFlags, - MeshUsage, - RenderPass, - RenderPasses, - ScissorBox, - SurfaceId, - SurfaceSpec, - PRE_RENDER_PHASE, -}; -use crate::texture::{Properties as TextureProperties, Texture}; -use crate::util::MapVec; -use crate::vector::Vec2; -use crate::windowing::dpi::PhysicalSize; -use crate::windowing::window::Window; - -mod reexports -{ - pub use dear_imgui_rs as dear_imgui; -} - -pub use reexports::*; - -pub static SHADER_ASSET_LABEL: LazyLock = 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, - texture_lookup: MapVec, -} - -impl Context -{ - pub fn frame(&mut self) -> Option<&mut dear_imgui_rs::Ui> - { - if !self.enabled { - return None; - } - - self.ctx.get_frame() - } - - pub fn register_texture(&mut self, texture_data: &mut dear_imgui_rs::OwnedTextureData) - { - self.ctx.register_texture(texture_data); - } -} - -#[derive(Debug, Default)] -#[non_exhaustive] -pub struct Extension -{ - start_enabled: bool, - settings_ini_file_path: Option, -} - -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, - ) -> 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(), - texture_lookup: MapVec::with_capacity(8), - }; - - 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>, - mut context: Single, -) -{ - let Ok(context) = context.get_mut() else { - unreachable!(); - }; - - 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_logical = PhysicalSize { - width: window.inner_size.width as f64, - height: window.inner_size.height as f64, - } - .to_logical::(hidpi_factor); - - context.ctx.get_io_mut().set_display_size([ - window_size_logical.width as f32, - window_size_logical.height as f32, - ]); -} - -fn update( - target_window_query: Query<(&Window, &SurfaceSpec), (With,)>, - mut context: Single, - delta_time: Single, - mut assets: Single, - mut render_passes: Single, - shader_context: Single, - keyboard: Single, - mouse: Single, - mouse_buttons: Single, - mut state: Local, -) -> Result<(), crate::Error> -{ - let Ok(context) = context.get_mut() else { - unreachable!(); - }; - - let delta_time = delta_time.get()?; - let assets = assets.get_mut()?; - let render_passes = render_passes.get_mut()?; - let shader_context = shader_context.get()?; - let keyboard = keyboard.get()?; - let mouse = mouse.get()?; - let mouse_buttons = mouse_buttons.get()?; - - let Some((window, window_surface_spec)) = target_window_query.iter().next() else { - return Ok(()); - }; - - let curr_state = std::mem::replace(&mut *state, State::Unreachable); - - match curr_state { - State::NotInitialized => { - let Some((shader_asset, mesh_obj_id, mesh)) = initialize_context( - context, - &window, - window_surface_spec.id, - assets, - render_passes, - &shader_context, - ) else { - *state = State::NotInitialized; - return Ok(()); - }; - - context.ctx.new_frame(); - - *state = State::Ready { shader_asset, mesh_obj_id, mesh }; - } - State::Ready { shader_asset, mesh_obj_id, mut mesh } => { - if !context.enabled { - *state = State::Ready { shader_asset, mesh_obj_id, mesh }; - return Ok(()); - } - - context - .ctx - .get_io_mut() - .set_delta_time(delta_time.duration.as_secs_f32()); - - update_inputs(context, &window, keyboard, mouse, mouse_buttons); - - add_drawing_render_pass( - context, - render_passes, - shader_context, - window_surface_spec.id, - shader_asset.clone(), - mesh_obj_id, - &mut mesh, - ); - - context.ctx.new_frame(); - - *state = State::Ready { shader_asset, mesh_obj_id, mesh }; - } - State::Unreachable => unreachable!(), - } - - Ok(()) -} - -#[derive(Debug, Component)] -pub struct TargetWindow; - -#[derive(Debug, Default, Component)] -enum State -{ - #[default] - NotInitialized, - Ready - { - shader_asset: AssetHandle, - 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, RenderingObjectId, Mesh)> -{ - let shader_asset = if let Some(shader_asset) = - assets.get_handle_to_loaded::(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 = Dimens { - width: window.inner_size.width as f32, - height: window.inner_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 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::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: AssetOrValue::Value(mesh.clone()), - usage: MeshUsage::Stream, - }, - ], - draw_properties: DrawProperties::default(), - }); - - Some((shader_asset, mesh_obj_id, mesh)) -} - -fn update_inputs( - context: &mut Context, - window: &Window, - keyboard: &Keyboard, - mouse: &Mouse, - mouse_buttons: &MouseButtons, -) -{ - let io = context.ctx.get_io_mut(); - - for (key, key_state) in keyboard.new_key_states() { - if let Some(key) = key_to_imgui_key(key) { - io.add_key_event(key, key_state.is_pressed()); - } - } - - for character in keyboard.text_keys().chars() { - if character == '\u{7f}' { - continue; - } - - io.add_input_character(character); - } - - io.add_mouse_source_event(dear_imgui_rs::input::MouseSource::Mouse); - - let mouse_pos = mouse.position.to_logical::(window.scale_factor()); - - io.add_mouse_pos_event([mouse_pos.x as f32, mouse_pos.y as f32]); - - if !mouse.curr_tick_scroll_delta.is_zero() { - io.add_mouse_wheel_event([ - mouse.curr_tick_scroll_delta.hor_lines, - mouse.curr_tick_scroll_delta.vert_lines, - ]); - } - - for (mouse_button, mouse_button_state) in mouse_buttons.all_current() { - if let Some(mouse_button) = mouse_button_to_imgui_mouse_button(mouse_button) { - io.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, - shader_asset: AssetHandle, - 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, - scissor_test_enabled: true, - ..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 Context { enabled: _, ctx, texture_lookup } = context; - - let draw_data = ctx.render(); - - let mut textures = draw_data.textures_mut(); - - while let Some(mut texture_data) = textures.next() { - match texture_data.status() { - dear_imgui_rs::TextureStatus::WantCreate => { - let Some(texture_pixels) = texture_data.pixels() else { - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; - }; - - if !matches!(texture_data.format(), dear_imgui_rs::TextureFormat::RGBA32) - { - unimplemented!(); - } - - let image = - match Image::from_pixels(ImagePixelBuffer::, _>::new( - texture_pixels, - Dimens { - width: texture_data.width(), - height: texture_data.height(), - }, - )) { - Ok(image) => image, - Err(ImagePixelBufferLenIncorrectForSize) => { - tracing::error!("Not enough texture pixels for texture size"); - continue; - } - }; - - let texture_object_id = RenderingObjectId::new_sequential(); - - render_pass.commands.push(RenderingCommand::CreateTexture( - texture_object_id, - AssetOrValue::Value(Texture { - image, - properties: TextureProperties::default(), - }), - )); - - let texture_lookup_id = TextureLookupId::new(); - - texture_data.set_tex_id(texture_lookup_id.into()); - - texture_lookup.insert(texture_lookup_id, texture_object_id); - - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - } - dear_imgui_rs::TextureStatus::WantUpdates => { - if texture_data.tex_id().is_null() { - tracing::error!(concat!( - "Update was requested for texture with a null ID. Did you forget ", - "to set the texture status to TextureStatus::WantCreate?")); - - continue; - } - - if texture_data.pixels().is_none() { - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; - } - - let mut rects: Vec = - texture_data.updates().collect(); - - if rects.is_empty() { - let rect = texture_data.update_rect(); - if rect.w > 0 && rect.h > 0 { - rects.push(rect); - } - } - - if rects.is_empty() { - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; - } - - let Some(texture_object_id) = - texture_lookup.get(&TextureLookupId::from(texture_data.tex_id())) - else { - tracing::error!( - "Unknown texture {}. Skipping update", - texture_data.tex_id().id() - ); - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; - }; - - render_pass.commands.extend(rects.iter().filter_map(|rect| { - let Some(rect_image) = get_texture_rect_image(&texture_data, rect) - else { - return None; - }; - - Some(RenderingCommand::UpdateTexture { - obj_id: texture_object_id.clone(), - image: rect_image, - offset: Vec2 { x: rect.x as u32, y: rect.y as u32 }, - }) - })); - - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - } - dear_imgui_rs::TextureStatus::WantDestroy => { - let Some(texture_object_id) = - texture_lookup.remove(TextureLookupId::from(texture_data.tex_id())) - else { - tracing::error!( - "Unknown texture {}. Skipping destroyal", - texture_data.tex_id().id() - ); - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; - }; - - render_pass - .commands - .push(RenderingCommand::RemoveTexture(texture_object_id)); - } - _ => {} - } - } - - 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, - ), - ), - ), - ]); - - 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 } => { - let Some(scissor_box) = - calc_draw_cmd_scissor_box(draw_data, &cmd_params) - else { - continue; - }; - - let tex_id = dear_imgui_rs::TextureId::new(u64::from(unsafe { - let mut cmd_copy = *raw_cmd; - - dear_imgui_rs::sys::ImDrawCmd_GetTexID(&mut cmd_copy) - })); - - let Some(texture_object_id) = - texture_lookup.get(&TextureLookupId::from(tex_id)) - else { - tracing::error!( - "Unknown texture {}. Skipping skipping draw command", - tex_id.id() - ); - continue; - }; - - render_pass.commands.extend([ - RenderingCommand::UpdateDrawProperties( - DrawProperties { scissor_box, ..Default::default() }, - DrawPropertiesUpdateFlags::SCISSOR_BOX, - ), - RenderingCommand::SetShaderBinding( - shader_cursor.field("main_texture").into_binding_location(), - ShaderBindingValue::Texture(texture_object_id.clone()), - ), - 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 get_texture_rect_image( - texture_data: &dear_imgui_rs::TextureData, - rect: &dear_imgui_rs::TextureRect, -) -> Option -{ - let texture_pixels = texture_data.pixels()?; - - let tex_width = texture_data.width(); - let tex_height = texture_data.height(); - - if tex_width == 0 || tex_height == 0 { - return None; - } - - let rect_pos = Vec2:: { x: rect.x.into(), y: rect.y.into() }; - - let rect_size = Dimens:: { - width: rect.w.into(), - height: rect.h.into(), - }; - - if rect_size.width == 0 - || rect_size.height == 0 - || rect_pos.x >= tex_width - || rect_pos.y >= tex_height - { - return None; - } - - let rect_size = Dimens { - width: rect_size.width.min(tex_width.saturating_sub(rect_pos.x)), - height: rect_size.height.min(tex_height.saturating_sub(rect_pos.y)), - }; - - match texture_data.format() { - dear_imgui_rs::TextureFormat::RGBA32 => { - let Ok(texture_image) = - Image::from_pixels(ImagePixelBuffer::, _>::new( - texture_pixels, - Dimens { width: tex_width, height: tex_height }, - )) - else { - return None; - }; - - Some(texture_image.sub_view(rect_pos, rect_size).to_image()) - } - dear_imgui_rs::TextureFormat::Alpha8 => { - unimplemented!(); - } - } -} - -fn calc_draw_cmd_scissor_box( - draw_data: &dear_imgui_rs::DrawData, - cmd_params: &dear_imgui_rs::DrawCmdParams, -) -> Option -{ - let clip_rect = cmd_params.clip_rect; - - let clip_min_x = - (clip_rect[0] - draw_data.display_pos[0]) * draw_data.framebuffer_scale[0]; - - let clip_min_y = - (clip_rect[1] - draw_data.display_pos[1]) * draw_data.framebuffer_scale[1]; - - let clip_max_x = - (clip_rect[2] - draw_data.display_pos[0]) * draw_data.framebuffer_scale[0]; - - let clip_max_y = - (clip_rect[3] - draw_data.display_pos[1]) * draw_data.framebuffer_scale[1]; - - if clip_max_x <= clip_min_x || clip_max_y <= clip_min_y { - return None; - } - - let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1]; - - Some(ScissorBox { - size: Some(Dimens { - width: (clip_max_x - clip_min_x) as u16, - height: (clip_max_y - clip_min_y) as u16, - }), - lower_left_corner_pos: Vec2 { - x: clip_min_x as u16, - y: (fb_height - clip_max_y) as u16, - }, - }) -} - -fn key_to_imgui_key(key: Key) -> Option -{ - 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 -{ - 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>, - 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")); - - ctx.io_mut() - .set_backend_flags(dear_imgui_rs::BackendFlags::RENDERER_HAS_TEXTURES); - - Self { ctx, frame: null_mut() } - } - - pub fn set_settings_ini_file_path( - &mut self, - path: Option, - ) -> Result<(), dear_imgui_rs::ImGuiError> - { - self.ctx.set_ini_filename(path) - } - - pub fn register_texture( - &mut self, - texture_data: &mut dear_imgui_rs::texture::OwnedTextureData, - ) - { - self.ctx.register_user_texture(texture_data); - } - - pub fn get_io_mut(&mut self) -> &mut dear_imgui_rs::Io - { - self.ctx.io_mut() - } - - pub fn render(&mut self) -> &mut 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() } - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -struct TextureLookupId -{ - inner: u64, -} - -impl TextureLookupId -{ - fn new() -> Self - { - static NEXT: AtomicU64 = AtomicU64::new(1); - - Self { - inner: NEXT.fetch_add(1, Ordering::Relaxed), - } - } -} - -impl From for TextureLookupId -{ - fn from(texture_id: dear_imgui_rs::TextureId) -> Self - { - assert!(!texture_id.is_null(), "Bad texture ID: is null"); - - Self { inner: texture_id.id() } - } -} - -impl From for dear_imgui_rs::TextureId -{ - fn from(texture_id: TextureLookupId) -> Self - { - Self::new(texture_id.inner) - } -} -- cgit v1.2.3-18-g5258