diff options
| author | HampusM <hampus@hampusmat.com> | 2026-09-14 15:58:13 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-09-14 15:58:13 +0200 |
| commit | 36c5e8407e332b9f72f9e116acfbc3a35c218245 (patch) | |
| tree | 2c01136226e6af9e2d90661adf1add35ca5c481f /engine | |
| parent | a55f4d3aa9db4eef4eaf329f921c60cba6f89a2c (diff) | |
feat(engine): add 3D transforming ui view
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/Cargo.toml | 4 | ||||
| -rw-r--r-- | engine/src/ui/view.rs | 1 | ||||
| -rw-r--r-- | engine/src/ui/view/transform_3d.rs | 128 |
3 files changed, 133 insertions, 0 deletions
diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 322e6c3..f46433e 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -48,6 +48,10 @@ branch = "prebuilt-and-artifacts" version = "=0.16.0-alpha.1" features = ["freetype"] +[dependencies.dear-imguizmo] +version = "=0.16.0-alpha.1" +default-features = false # A dependency on glam is not desired + [build-dependencies] build-rs = "0.3.4" serde = { version = "1.0.228", features = ["derive"] } diff --git a/engine/src/ui/view.rs b/engine/src/ui/view.rs index ed714d6..42601a3 100644 --- a/engine/src/ui/view.rs +++ b/engine/src/ui/view.rs @@ -1 +1,2 @@ +pub mod transform_3d; pub mod world; diff --git a/engine/src/ui/view/transform_3d.rs b/engine/src/ui/view/transform_3d.rs new file mode 100644 index 0000000..a426b2a --- /dev/null +++ b/engine/src/ui/view/transform_3d.rs @@ -0,0 +1,128 @@ +use dear_imguizmo::GuizmoExt; + +use crate::camera::{Active as ActiveCamera, Camera}; +use crate::data_types::dimens::Dimens3; +use crate::ecs::pair::ChildOf; +use crate::ecs::query::term::{Traverse, TraverseUp, With, Without}; +use crate::ecs::sole::Single; +use crate::ecs::Query; +use crate::projection::ClipVolume as ProjectionClipVolume; +use crate::scene::{Active as ActiveScene, Scene}; +use crate::transform::Transform; +use crate::ui::dear_imgui::{ + Context as DearImguiContext, + TargetWindow as DearImguiTargetWindow, +}; +use crate::vector::Vec3; +use crate::windowing::window::{Closed as WindowClosed, Window}; + +pub fn show( + subject_query: Query<( + &mut Transform, + Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>, + Without<ActiveCamera>, + )>, + camera_query: Query<( + &Camera, + &Transform, + Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>, + With<ActiveCamera>, + )>, + target_window_query: Query<( + &Window, + With<DearImguiTargetWindow>, + Without<WindowClosed>, + )>, + mut dear_imgui_context: Single<DearImguiContext>, +) -> Result<(), crate::Error> +{ + let dear_imgui_context = dear_imgui_context.get_mut()?; + + let Some(frame) = dear_imgui_context.frame() else { + return Ok(()); + }; + + let Some((camera, camera_world_pos)) = camera_query.iter().next() else { + return Ok(()); + }; + + let Some((target_window,)) = target_window_query.iter().next() else { + return Ok(()); + }; + + let gizmo = frame.guizmo(); + + let [display_width, display_height] = frame.io().display_size(); + + gizmo.set_rect(0.0, 0.0, display_width, display_height); + + // The widgets should appear under dear imgui windows + gizmo.set_drawlist_background(); + + let col_major_view = flatten_matrix( + camera + .to_view_matrix(camera_world_pos.position) + .to_column_major(), + ); + + let col_major_projection = flatten_matrix( + camera + .projection + .to_matrix_rh(target_window.inner_size, ProjectionClipVolume::NegOneToOne) + .to_column_major(), + ); + + for (subject_index, (mut subject_transform,)) in subject_query.iter().enumerate() { + let subject_index = match i32::try_from(subject_index) { + Ok(subject_index) => subject_index, + Err(err) => { + tracing::error!("Invalid subject index {subject_index}: {err}"); + break; + } + }; + + // No rotation + let rotation = [0.0, 0.0, 0.0]; + + let mut col_major_model_mat = dear_imguizmo::recompose_matrix( + &subject_transform.position.into(), + &rotation, + &subject_transform.scale.into(), + ); + + let _id_token = gizmo.push_id(subject_index); + + let used = gizmo + .manipulate_config( + &col_major_view, + &col_major_projection, + &mut col_major_model_mat, + ) + .operation( + dear_imguizmo::Operation::TRANSLATE | dear_imguizmo::Operation::SCALE, + ) + .mode(dear_imguizmo::Mode::World) + .build(); + + if used { + let (translation, _rotation, scale) = + dear_imguizmo::decompose_matrix(&col_major_model_mat); + + subject_transform.position = Vec3::from(translation); + subject_transform.scale = Dimens3::from(scale); + } + } + + Ok(()) +} + +fn flatten_matrix(mat: [[f32; 4]; 4]) -> [f32; 4 * 4] +{ + let flattened = mat.as_flattened(); + + let Some(flattened) = flattened.as_array::<{ 4 * 4 }>() else { + unreachable!(); + }; + + flattened.clone() +} |
