From 8bf6b796ae6c9054ef4335d091f2c3500a7334bd Mon Sep 17 00:00:00 2001 From: rewin Date: Wed, 10 Jan 2024 23:08:08 +0300 Subject: [PATCH] Gizmo refactor for translation local version --- studio/src/interaction/gizmos/combo.rs | 8 +- studio/src/interaction/gizmos/common.rs | 68 +++++------------ studio/src/interaction/gizmos/mod.rs | 78 ++++++++++++-------- studio/src/interaction/gizmos/rotation.rs | 8 +- studio/src/interaction/gizmos/translation.rs | 55 +++++++++----- studio/src/interaction/transformation.rs | 2 +- 6 files changed, 112 insertions(+), 107 deletions(-) diff --git a/studio/src/interaction/gizmos/combo.rs b/studio/src/interaction/gizmos/combo.rs index 80ab758..00ef78f 100644 --- a/studio/src/interaction/gizmos/combo.rs +++ b/studio/src/interaction/gizmos/combo.rs @@ -13,17 +13,17 @@ use super::{ #[inline] pub(super) fn spawn_combo_translation_rotation_gizmo( cmd: &mut Commands, - ent: Entity, - xform: &GlobalTransform, + target: Entity, + target_global_transform: &GlobalTransform, space: TransformSpace, normalizing_scale: f32, meshes: &GizmoMeshes, mats: &GizmoMats, ) { - cmd.entity(ent) + cmd.spawn_empty() .insert(TransformGizmoRoot) .with_children(|cmd| { - cmd.spawn(TransformGizmoBundle::new(xform, space, normalizing_scale)) + cmd.spawn(TransformGizmoBundle::new(target, target_global_transform, space, normalizing_scale)) .with_children(|cmd| { use super::{ROT_X, ROT_Y, ROT_Z}; diff --git a/studio/src/interaction/gizmos/common.rs b/studio/src/interaction/gizmos/common.rs index ed2da5a..98a7b0d 100644 --- a/studio/src/interaction/gizmos/common.rs +++ b/studio/src/interaction/gizmos/common.rs @@ -9,8 +9,11 @@ use crate::{ #[derive(Component)] pub(super) struct TransformGizmoRoot; -#[derive(Component)] -pub(super) struct TransformGizmo; +#[derive(Component, Reflect)] +pub(super) struct TransformGizmo { + pub(super) target : Entity, + pub(super) transform_space: TransformSpace, +} #[derive(Event)] pub(super) struct RebuildGizmo; @@ -20,86 +23,53 @@ pub(super) struct TransformGizmoBundle { name: Name, marker: TransformGizmo, spatial: SpatialBundle, - scale: NaturalScale, } -#[derive(Component, Deref, Reflect)] -pub(super) struct NaturalScale(pub Vec3); - #[derive(Component, Deref)] pub(super) struct InitialTransform(pub GlobalTransform); impl TransformGizmoBundle { pub fn new( - parent_xform: &GlobalTransform, + target : Entity, + target_global_transform : &GlobalTransform, space: TransformSpace, normalizing_scale: f32, ) -> Self { let name = Name::new("TransformGizmo"); - let marker = TransformGizmo; - - let Affine3A { - matrix3, - translation, - } = parent_xform.affine(); - - let (scale, rotation) = matrix3.decompose_scale_rotation(); - let inv_scale = scale.inverse(); - let nat_scale = NaturalScale(Vec3::from_scale_mat(inv_scale)); + let marker = TransformGizmo { + target, + transform_space: space.clone() + }; match space { TransformSpace::Local => { - let global_transform = GlobalTransform::from(Affine3A { - matrix3: rotation, - translation, - }); - - let transform = Transform::from_scale(*nat_scale * normalizing_scale); + + let mut transform = target_global_transform.compute_transform(); + transform.scale = Vec3::splat(normalizing_scale); Self { name, marker, spatial: SpatialBundle { - global_transform, + global_transform : GlobalTransform::from(transform.clone()), transform, ..default() }, - scale: nat_scale, } } TransformSpace::World => { - // FIXME: I'm struggling to figure out how to eliminate the - // "shear" from a fully inverted local transform when the - // object is non-uniformly scaled. As a workaround, we'll - // force these objects to operate in local-space mode. - // TODO: If this turns into a long-term solution, update the UI - // to reflect that world-space transformation is not - // possible for this object (e.g., by disabling the - // world-space toggle button, maybe with an explanatory - // tooltip). - let local_matrix = if nat_scale.is_non_uniform() { - inv_scale - } else { - matrix3.inverse() - }; - - let global_transform = GlobalTransform::from(Affine3A { - matrix3, - translation, - }); - - let mat4 = Mat4::from_mat3a(local_matrix * normalizing_scale); - let transform = Transform::from_matrix(mat4); + + let transform = Transform::from_translation(target_global_transform.translation()) + .with_scale(Vec3::splat(normalizing_scale)); Self { name, marker, spatial: SpatialBundle { - global_transform, + global_transform : GlobalTransform::from(transform.clone()), transform, ..default() }, - scale: nat_scale, } } } diff --git a/studio/src/interaction/gizmos/mod.rs b/studio/src/interaction/gizmos/mod.rs index 939c595..6aadd7a 100644 --- a/studio/src/interaction/gizmos/mod.rs +++ b/studio/src/interaction/gizmos/mod.rs @@ -33,9 +33,12 @@ const ROT_Z: Quat = Quat::from_xyzw(CIRC_45, 0., 0., CIRC_45); pub struct GizmoPlugin; + impl Plugin for GizmoPlugin { fn build(&self, app: &mut App) { - app.register_type::(); + + app.register_type::(); + app.add_event::(); app.add_systems(Startup, init_resources); app.add_systems( @@ -55,9 +58,9 @@ impl Plugin for GizmoPlugin { ), spawn_gizmo.after(despawn_existing_gizmo).run_if( resource_exists::() - .and_then(not(any_with_component::())), + .and_then(not(any_with_component::())), ), - normalize_gizmo_scale.after(CameraMotionSystem), + (update_target_global_trasform, sync_gizmo_transform).chain() ), ); app.add_systems( @@ -69,15 +72,10 @@ impl Plugin for GizmoPlugin { fn despawn_existing_gizmo( mut cmd: Commands, - q_gizmo_root: Query<(Entity, &Children), With>, q_gizmos: Query>, ) { - if let Ok((ent, children)) = q_gizmo_root.get_single() { - if let Some(child) = children.iter().find_map(|ent| q_gizmos.get(*ent).ok()) { - cmd.entity(child).despawn_recursive(); - } - - cmd.entity(ent).remove::(); + for ent in q_gizmos.iter() { + cmd.entity(ent).despawn_recursive(); } } @@ -90,8 +88,8 @@ fn spawn_gizmo( r_meshes: Res, r_mats: Res, ) { - let ent = **r_selection; - let Ok(xform) = q_xform.get(ent) else { + let target = **r_selection; + let Ok(xform) = q_xform.get(target) else { return; }; @@ -107,7 +105,7 @@ fn spawn_gizmo( TransformType::TRANSLATE => { spawn_translation_gizmo( &mut cmd, - ent, + target, xform, r_xform_mode.space, normalizing_scale, @@ -118,7 +116,7 @@ fn spawn_gizmo( TransformType::ROTATE => { spawn_rotation_gizmo( &mut cmd, - ent, + target, xform, r_xform_mode.space, normalizing_scale, @@ -129,7 +127,7 @@ fn spawn_gizmo( ttype if ttype.contains(TransformType::TRANSLATE | TransformType::ROTATE) => { spawn_combo_translation_rotation_gizmo( &mut cmd, - ent, + target, xform, r_xform_mode.space, normalizing_scale, @@ -143,20 +141,40 @@ fn spawn_gizmo( } } -fn normalize_gizmo_scale( - q_cam_xform: Query<&Transform, With>, - mut q_gizmo: Query< - (&mut Transform, &GlobalTransform, &NaturalScale), - (With, Without), - >, -) { - let Ok(cam_xform) = q_cam_xform.get_single() else { - return; - }; - let Ok((mut xform, world_xform, nat_scale)) = q_gizmo.get_single_mut() else { - return; - }; - let dist = Vec3::distance(cam_xform.translation, world_xform.translation()); - xform.scale = **nat_scale * (dist / 15.); +fn sync_gizmo_transform( + mut cmd : Commands, + mut q_gizmo : Query<(&mut Transform, &TransformGizmo)>, + mut q_targets : Query<&GlobalTransform> +) { + for (mut transform, gizmo) in q_gizmo.iter_mut() { + if let Ok(target) = q_targets.get(gizmo.target) { + match gizmo.transform_space { + super::TransformSpace::Local => { + let target_transform = target.compute_transform(); + transform.translation = target_transform.translation; + transform.rotation = target_transform.rotation; + }, + super::TransformSpace::World => { + transform.translation = target.translation(); + }, + } + } else { + cmd.entity(gizmo.target).despawn_recursive(); + } + } } + +fn update_target_global_trasform( + mut q_gizmo : Query<&TransformGizmo>, + mut q_targets : ParamSet<(TransformHelper,)>, + mut q_targets_global : Query<&mut GlobalTransform> +) { + for gizmo in q_gizmo.iter_mut() { + if let Ok(global_transform) = q_targets.p0().compute_global_transform(gizmo.target) { + if let Ok(mut global) = q_targets_global.get_mut(gizmo.target) { + *global = global_transform; + } + } + } +} \ No newline at end of file diff --git a/studio/src/interaction/gizmos/rotation.rs b/studio/src/interaction/gizmos/rotation.rs index d9a3b24..8073b89 100644 --- a/studio/src/interaction/gizmos/rotation.rs +++ b/studio/src/interaction/gizmos/rotation.rs @@ -40,18 +40,18 @@ pub(super) struct LastRotation { #[inline] pub(super) fn spawn_rotation_gizmo( cmd: &mut Commands, - ent: Entity, - xform: &GlobalTransform, + target: Entity, + target_global_transform: &GlobalTransform, space: TransformSpace, normalizing_scale: f32, meshes: &GizmoMeshes, mats: &GizmoMats, ) { - cmd.entity(ent) + cmd.spawn_empty() .insert(TransformGizmoRoot) .with_children(|cmd| { cmd.spawn(( - TransformGizmoBundle::new(xform, space, normalizing_scale), + TransformGizmoBundle::new(target, target_global_transform, space, normalizing_scale), On::>::run(on_rotation_start), On::>::run(on_rotate), On::>::run(on_rotation_end), diff --git a/studio/src/interaction/gizmos/translation.rs b/studio/src/interaction/gizmos/translation.rs index b4f3645..8a81597 100644 --- a/studio/src/interaction/gizmos/translation.rs +++ b/studio/src/interaction/gizmos/translation.rs @@ -23,18 +23,15 @@ pub(super) enum TranslationWidget { #[inline] pub(super) fn spawn_translation_gizmo( cmd: &mut Commands, - ent: Entity, - xform: &GlobalTransform, + target: Entity, + target_global_transform: &GlobalTransform, space: TransformSpace, normalizing_scale: f32, meshes: &GizmoMeshes, mats: &GizmoMats, ) { - cmd.entity(ent) - .insert(TransformGizmoRoot) - .with_children(|cmd| { - cmd.spawn(( - TransformGizmoBundle::new(xform, space, normalizing_scale), + cmd.spawn(( + TransformGizmoBundle::new(target, target_global_transform, space, normalizing_scale), On::>::run(on_gizmo_element_hover), On::>::run(on_gizmo_element_unhover), On::>::run(on_translation_start), @@ -202,17 +199,19 @@ pub(super) fn spawn_translation_gizmo( }, )); }); - }); } pub(super) fn on_translation_start( _: Listener>, mut cmd: Commands, - q_xform: Query<(Entity, &GlobalTransform), With>, + q_xform: Query<(Entity, &GlobalTransform)>, + q_gizmos: Query<&TransformGizmo> ) { - if let Ok((ent, &world_xform)) = q_xform.get_single() { - cmd.entity(ent).insert(InitialTransform(world_xform)); - }; + for gizmo_data in q_gizmos.iter() { + if let Ok((ent, &world_xform)) = q_xform.get(gizmo_data.target) { + cmd.entity(ent).insert(InitialTransform(world_xform)); + } + } } pub(super) fn on_translation_end( @@ -224,17 +223,21 @@ pub(super) fn on_translation_end( pub(super) fn on_translate( event: Listener>, - mut q_root: Query<(&mut Transform, &InitialTransform), With>, + mut q_root: Query<(&mut Transform, &InitialTransform, Option<&Parent>)>, q_camera: Query<(&Camera, &GlobalTransform), With>, - q_gizmo: Query<&GlobalTransform, (With, Without)>, + q_gizmo: Query<(&GlobalTransform, &TransformGizmo), Without>, q_widget: Query<&TranslationWidget>, + q_all_global_transforms : Query<&GlobalTransform> ) { - let (mut xform, init_xform) = q_root.single_mut(); - let gizmo_xform = q_gizmo.single(); + let (gizmo_xform, gizmo_data) = q_gizmo.single(); + let Ok((mut target_transform, target_global_transform, target_parent)) = q_root.get_mut(gizmo_data.target) else { + error!("Translation gizmo target not found"); + return; + }; let (cam, cam_xform) = q_camera.single(); // Find the initial origin point in screen-space and add the total screen-space drag distance - let origin_ws = init_xform.translation(); + let origin_ws = target_global_transform.translation(); let origin_ss = cam.world_to_viewport(cam_xform, origin_ws).unwrap(); let target_ss = origin_ss + event.distance; @@ -265,7 +268,14 @@ pub(super) fn on_translate( let projection_length = drag_vector.dot(axis_ws); let target_ws = origin_ws + axis_ws * projection_length; - xform.translation = target_ws; + if let Some(target_parent) = target_parent { + if let Ok(parent_global_transform) = q_all_global_transforms.get(target_parent.get()) { + let real_local_transform = GlobalTransform::from(Transform::from_translation(target_ws)).reparented_to(parent_global_transform); + target_transform.translation = real_local_transform.translation; + } + } else { + target_transform.translation = target_ws; + } } Ok(plane) => { // Find the world-space normal of the plane we're translating on @@ -281,7 +291,14 @@ pub(super) fn on_translate( }; let target_ws = ray.origin + ray.direction * hit_dist; - xform.translation = target_ws; + if let Some(target_parent) = target_parent { + if let Ok(parent_global_transform) = q_all_global_transforms.get(target_parent.get()) { + let real_local_transform = GlobalTransform::from(Transform::from_translation(target_ws)).reparented_to(parent_global_transform); + target_transform.translation = real_local_transform.translation; + } + } else { + target_transform.translation = target_ws; + } } _ => {} } diff --git a/studio/src/interaction/transformation.rs b/studio/src/interaction/transformation.rs index c1a5305..e067fb0 100644 --- a/studio/src/interaction/transformation.rs +++ b/studio/src/interaction/transformation.rs @@ -32,7 +32,7 @@ impl Default for TransformMode { } } -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug, Default, Reflect)] #[wasm_bindgen] pub enum TransformSpace { #[default]