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::time::Time; 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::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::reflection::Reflection; 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 bindings; } 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 bindings::Ui> { if !self.enabled { return None; } self.ctx.get_frame() } pub fn register_texture(&mut self, texture_data: &mut bindings::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, time: Single