Gizmo refactor for translation local version

This commit is contained in:
rewin
2024-01-10 23:08:08 +03:00
parent a032561e40
commit 8bf6b796ae
6 changed files with 112 additions and 107 deletions
+4 -4
View File
@@ -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};
+19 -49
View File
@@ -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,
}
}
}
+48 -30
View File
@@ -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::<NaturalScale>();
app.register_type::<TransformGizmo>();
app.add_event::<RebuildGizmo>();
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::<Selection>()
.and_then(not(any_with_component::<TransformGizmoRoot>())),
.and_then(not(any_with_component::<TransformGizmo>())),
),
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<TransformGizmoRoot>>,
q_gizmos: Query<Entity, With<TransformGizmo>>,
) {
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::<TransformGizmoRoot>();
for ent in q_gizmos.iter() {
cmd.entity(ent).despawn_recursive();
}
}
@@ -90,8 +88,8 @@ fn spawn_gizmo(
r_meshes: Res<GizmoMeshes>,
r_mats: Res<GizmoMats>,
) {
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<MainCamera>>,
mut q_gizmo: Query<
(&mut Transform, &GlobalTransform, &NaturalScale),
(With<TransformGizmo>, Without<MainCamera>),
>,
) {
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;
}
}
}
}
+4 -4
View File
@@ -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::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
+36 -19
View File
@@ -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::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
@@ -202,17 +199,19 @@ pub(super) fn spawn_translation_gizmo(
},
));
});
});
}
pub(super) fn on_translation_start(
_: Listener<Pointer<DragStart>>,
mut cmd: Commands,
q_xform: Query<(Entity, &GlobalTransform), With<TransformGizmoRoot>>,
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<Pointer<Drag>>,
mut q_root: Query<(&mut Transform, &InitialTransform), With<TransformGizmoRoot>>,
mut q_root: Query<(&mut Transform, &InitialTransform, Option<&Parent>)>,
q_camera: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
q_gizmo: Query<&GlobalTransform, (With<TransformGizmo>, Without<MainCamera>)>,
q_gizmo: Query<(&GlobalTransform, &TransformGizmo), Without<MainCamera>>,
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;
}
}
_ => {}
}
+1 -1
View File
@@ -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]