summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock26
-rw-r--r--engine/Cargo.toml4
-rw-r--r--engine/src/ui/view.rs1
-rw-r--r--engine/src/ui/view/transform_3d.rs128
4 files changed, 159 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 29124da..8b73712 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -534,6 +534,31 @@ dependencies = [
]
[[package]]
+name = "dear-imguizmo"
+version = "0.16.0-alpha.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "444474eac0664b01b7717f64a4c29b6b39be54e081c0fcee8f84f866b10f2d76"
+dependencies = [
+ "bitflags 2.11.1",
+ "dear-imgui-rs",
+ "dear-imgui-sys",
+ "dear-imguizmo-sys",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "dear-imguizmo-sys"
+version = "0.16.0-alpha.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61304895d92846f80d4800f3cea7d4564afe0a8fd34e1afab87386b22dfefef5"
+dependencies = [
+ "cc",
+ "dear-imgui-build-support",
+ "dear-imgui-sys",
+ "mint",
+]
+
+[[package]]
name = "dispatch"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -586,6 +611,7 @@ dependencies = [
"criterion",
"crossbeam-queue",
"dear-imgui-rs",
+ "dear-imguizmo",
"engine-ecs",
"engine-macros",
"engine-reflection",
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()
+}