use bevy::{prelude::*, transform::TransformSystem}; use crate::{camera_controller::CameraMotionSystem, MainCamera}; use self::{ camera::{spawn_gizmo_camera, GizmoCamera}, combo::spawn_combo_translation_rotation_gizmo, common::*, resources::{init_resources, GizmoMats, GizmoMeshes}, rotation::{spawn_rotation_gizmo, update_ss_ring_rotation}, translation::spawn_translation_gizmo, }; use super::{Selection, TransformMode, TransformType}; mod camera; mod combo; mod common; mod resources; mod rotation; mod translation; const GIZMO_RENDER_LAYER: u8 = 1; /// (sin|cos)(pi / 4) const CIRC_45: f32 = 0.70710677; /// Mat3 ((0 -1 0) (1 0 0) (0 0 1)) const ROT_X: Quat = Quat::from_xyzw(0., 0., CIRC_45, -CIRC_45); /// Mat3 ((1 0 0) (0 1 0) (0 0 1)) const ROT_Y: Quat = Quat::IDENTITY; /// Mat3 ((1 0 0) (0 0 1) (0 -1 0)) 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.add_systems(Startup, init_resources); app.add_systems( PreUpdate, spawn_gizmo_camera.run_if( any_with_component::() .and_then(not(any_with_component::())), ), ); app.add_systems( Update, ( despawn_existing_gizmo.run_if( resource_changed_or_removed::() .or_else(resource_changed::()), ), spawn_gizmo.after(despawn_existing_gizmo).run_if( resource_exists::() .and_then(not(any_with_component::())), ), (update_target_global_trasform, sync_gizmo_transform) .chain() .after(CameraMotionSystem), normalize_gizmo_scale.after(CameraMotionSystem), ), ); app.add_systems( PostUpdate, update_ss_ring_rotation.after(TransformSystem::TransformPropagate), ); } } fn despawn_existing_gizmo(mut cmd: Commands, q_gizmos: Query>) { for ent in q_gizmos.iter() { cmd.entity(ent).despawn_recursive(); } } fn spawn_gizmo( mut cmd: Commands, r_selection: Res, r_xform_mode: Res, q_xform: Query<&GlobalTransform>, q_cam_xform: Query<&Transform, With>, r_meshes: Res, r_mats: Res, ) { let target = **r_selection; let Ok(xform) = q_xform.get(target) else { return; }; let Ok(cam_xform) = q_cam_xform.get_single() else { return; }; let dist = Vec3::distance(cam_xform.translation, xform.translation()); let normalizing_scale = dist / 15.; // TODO: Look into using the new one-shot systems feature here // https://bevyengine.org/news/bevy-0-12/#one-shot-systems match r_xform_mode.ttype { TransformType::TRANSLATE => { spawn_translation_gizmo( &mut cmd, target, xform, r_xform_mode.space, normalizing_scale, &r_meshes, &r_mats, ); } TransformType::ROTATE => { spawn_rotation_gizmo( &mut cmd, target, xform, r_xform_mode.space, normalizing_scale, &r_meshes, &r_mats, ); } ttype if ttype.contains(TransformType::TRANSLATE | TransformType::ROTATE) => { spawn_combo_translation_rotation_gizmo( &mut cmd, target, xform, r_xform_mode.space, normalizing_scale, &r_meshes, &r_mats, ); } _ => { // TODO } } } fn sync_gizmo_transform( mut cmd: Commands, mut q_gizmo: Query<(&mut Transform, &TransformGizmo)>, 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; } } } } fn normalize_gizmo_scale( q_cam_xform: Query<&Transform, With>, mut q_gizmo: Query< (&mut Transform, &GlobalTransform), (With, Without), >, ) { let Ok(cam_xform) = q_cam_xform.get_single() else { return; }; let Ok((mut xform, _)) = q_gizmo.get_single_mut() else { return; }; let dist = Vec3::distance(cam_xform.translation, xform.translation); xform.scale = Vec3::splat(dist / 15.); }