From b1b609e9ae393b04bc5a249ba79f0a0d0734a73f Mon Sep 17 00:00:00 2001 From: Tim Blackbird Date: Mon, 4 Aug 2025 12:18:46 +0200 Subject: [PATCH] Enable Transform Gizmo --- bevy_editor_panes/bevy_3d_viewport/Cargo.toml | 1 + bevy_editor_panes/bevy_3d_viewport/src/lib.rs | 21 +++++++++++- crates/bevy_editor/Cargo.toml | 14 +++++--- crates/bevy_editor/src/lib.rs | 31 ++++++++++-------- .../src/controller/component.rs | 3 ++ crates/bevy_editor_cam/src/input.rs | 32 +++++++++++-------- crates/bevy_transform_gizmos/src/lib.rs | 2 +- 7 files changed, 69 insertions(+), 35 deletions(-) diff --git a/bevy_editor_panes/bevy_3d_viewport/Cargo.toml b/bevy_editor_panes/bevy_3d_viewport/Cargo.toml index bf29eefb..33ee7314 100644 --- a/bevy_editor_panes/bevy_3d_viewport/Cargo.toml +++ b/bevy_editor_panes/bevy_3d_viewport/Cargo.toml @@ -11,6 +11,7 @@ bevy_editor_styles.workspace = true bevy_infinite_grid.workspace = true bevy_editor_core.workspace = true bevy_render.workspace = true +bevy_transform_gizmos.workspace = true [lints] workspace = true diff --git a/bevy_editor_panes/bevy_3d_viewport/src/lib.rs b/bevy_editor_panes/bevy_3d_viewport/src/lib.rs index 0fca3488..951d9bb9 100644 --- a/bevy_editor_panes/bevy_3d_viewport/src/lib.rs +++ b/bevy_editor_panes/bevy_3d_viewport/src/lib.rs @@ -20,6 +20,7 @@ use bevy_editor_cam::prelude::{DefaultEditorCamPlugins, EditorCam}; use bevy_editor_styles::Theme; use bevy_infinite_grid::{InfiniteGrid, InfiniteGridPlugin, InfiniteGridSettings}; use bevy_pane_layout::prelude::*; +use bevy_transform_gizmos::{TransformGizmo, prelude::*}; use view_gizmo::ViewGizmoPlugin; use crate::{selection_box::SelectionBoxPlugin, view_gizmo::view_gizmo_node}; @@ -62,7 +63,10 @@ impl Plugin for Viewport3dPanePlugin { ) .add_systems( PostUpdate, - update_render_target_size.after(ui_layout_system), + ( + update_render_target_size.after(ui_layout_system), + disable_editor_cam_during_gizmo_interaction, + ), ) .add_observer( |trigger: On, @@ -79,6 +83,20 @@ impl Plugin for Viewport3dPanePlugin { } } +/// Temporary. We will need a proper design for mutually exclusive controls. +fn disable_editor_cam_during_gizmo_interaction( + transform_gizmo: Single>, + mut query: Query<&mut EditorCam>, +) { + if !transform_gizmo.is_changed() { + return; + } + let enable = transform_gizmo.interaction().is_none(); + for mut editor_cam in &mut query { + editor_cam.enabled = enable; + } +} + /// A viewport is considered active while the mouse is hovering over it. #[derive(Component)] struct Active; @@ -197,6 +215,7 @@ fn on_pane_creation( ..default() }, EditorCam::default(), + GizmoCamera, Transform::from_translation(Vec3::ONE * 5.).looking_at(Vec3::ZERO, Vec3::Y), RenderLayers::from_layers(&[0, 1]), MeshPickingCamera, diff --git a/crates/bevy_editor/Cargo.toml b/crates/bevy_editor/Cargo.toml index de83d588..0bb9eb7d 100644 --- a/crates/bevy_editor/Cargo.toml +++ b/crates/bevy_editor/Cargo.toml @@ -5,23 +5,27 @@ edition = "2024" [dependencies] bevy.workspace = true + +# Editor +bevy_editor_core.workspace = true bevy_pane_layout.workspace = true bevy_menu_bar.workspace = true bevy_footer_bar.workspace = true bevy_context_menu.workspace = true bevy_editor_styles.workspace = true - -serde.workspace = true -ron.workspace = true -rfd.workspace = true +bevy_transform_gizmos.workspace = true # Panes -bevy_editor_core.workspace = true bevy_3d_viewport.workspace = true bevy_2d_viewport.workspace = true bevy_scene_tree.workspace = true bevy_properties_pane.workspace = true bevy_asset_browser.workspace = true +# Third party +serde.workspace = true +ron.workspace = true +rfd.workspace = true + [lints] workspace = true diff --git a/crates/bevy_editor/src/lib.rs b/crates/bevy_editor/src/lib.rs index f9b1d5f6..f5e0f4b2 100644 --- a/crates/bevy_editor/src/lib.rs +++ b/crates/bevy_editor/src/lib.rs @@ -11,10 +11,12 @@ //! which transforms the user's application into an editor that runs their game. //! - Finally, it will be a standalone application that communicates with a running Bevy game via the Bevy Remote Protocol. +use std::f32::consts::TAU; use std::time::Duration; use bevy::app::App as BevyApp; use bevy::asset::UnapprovedPathMode; +use bevy::color::palettes::tailwind; use bevy::prelude::*; use bevy::{ core_widgets::CoreWidgetsPlugins, @@ -29,6 +31,7 @@ use bevy_context_menu::ContextMenuPlugin; use bevy_editor_core::EditorCorePlugin; use bevy_editor_core::selection::Selectable; use bevy_editor_styles::StylesPlugin; +use bevy_transform_gizmos::{GizmoTransformable, TransformGizmoPlugin}; // Panes use bevy_2d_viewport::Viewport2dPanePlugin; @@ -72,16 +75,12 @@ impl Plugin for EditorPlugin { AssetBrowserPanePlugin, LoadGltfPlugin, MeshPickingPlugin, + TransformGizmoPlugin, CoreWidgetsPlugins, InputDispatchPlugin, TabNavigationPlugin, FeathersPlugin, )) - .insert_resource(MeshPickingSettings { - // Workaround for the Mesh2d circle blocking picking in the 3d viewport (even though it is not visible). - require_markers: true, - ..default() - }) .insert_resource(UiTheme(create_dark_theme())) .insert_resource(WinitSettings { focused_mode: UpdateMode::reactive(Duration::from_secs_f64(1.0 / 60.0)), @@ -121,21 +120,24 @@ impl App { fn dummy_setup( mut commands: Commands, mut meshes: ResMut>, - mut materials_2d: ResMut>, mut materials_3d: ResMut>, ) { commands.spawn(( - Mesh2d(meshes.add(Circle::new(50.0))), - MeshMaterial2d(materials_2d.add(Color::WHITE)), - Name::new("Circle"), + Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(2.5)))), + MeshMaterial3d(materials_3d.add(Color::WHITE)), + Name::new("Plane"), + Selectable, + GizmoTransformable, )); commands.spawn(( - Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.5)))), - MeshMaterial3d(materials_3d.add(Color::WHITE)), - Name::new("Plane"), - Pickable::default(), + Mesh3d(meshes.add(Cuboid::from_size(Vec3::splat(1.)))), + MeshMaterial3d(materials_3d.add(Color::from(tailwind::BLUE_500))), + Transform::from_translation(vec3(1.1, 0.5, -1.3)) + .with_rotation(Quat::from_rotation_y(TAU * 0.05)), + Name::new("Box"), Selectable, + GizmoTransformable, )); commands.spawn(( @@ -143,7 +145,8 @@ fn dummy_setup( shadows_enabled: true, ..default() }, - Transform::default().looking_to(Vec3::NEG_ONE, Vec3::Y), + Transform::default().looking_to(vec3(-1., -1., 1.), Vec3::Y), + GizmoTransformable, Name::new("DirectionalLight"), )); } diff --git a/crates/bevy_editor_cam/src/controller/component.rs b/crates/bevy_editor_cam/src/controller/component.rs index 7bd74fac..404b624f 100644 --- a/crates/bevy_editor_cam/src/controller/component.rs +++ b/crates/bevy_editor_cam/src/controller/component.rs @@ -47,6 +47,8 @@ use super::{ /// 3. When the motion should end, call [`EditorCam::end_move`]. #[derive(Debug, Clone, Reflect, Component)] pub struct EditorCam { + /// Controls whether any motions are allowed. The ongoing motion will be canceled if set to `false`. + pub enabled: bool, /// What input motions are currently allowed? pub enabled_motion: EnabledMotion, /// The type of camera orbit to use. @@ -83,6 +85,7 @@ pub struct EditorCam { impl Default for EditorCam { fn default() -> Self { EditorCam { + enabled: true, orbit_constraint: Default::default(), zoom_limits: Default::default(), smoothing: Default::default(), diff --git a/crates/bevy_editor_cam/src/input.rs b/crates/bevy_editor_cam/src/input.rs index 4185ab2f..dac9e1a2 100644 --- a/crates/bevy_editor_cam/src/input.rs +++ b/crates/bevy_editor_cam/src/input.rs @@ -87,32 +87,36 @@ pub fn default_camera_inputs( continue; } - if let Some(&camera) = pointer_map.get(&pointer) { - let camera_query = cameras.get(camera).ok(); - let is_in_zoom_mode = camera_query - .map(|(.., editor_cam)| editor_cam.current_motion.is_zooming_only()) + if let Some(&camera) = pointer_map.get(&pointer) + && let Ok((entity, _camera, editor_cam)) = cameras.get(camera) + { + let is_in_zoom_mode = editor_cam.current_motion.is_zooming_only(); + let zoom_amount_abs = editor_cam + .current_motion + .inputs() + .map(|inputs| inputs.zoom_velocity_abs(editor_cam.smoothing.zoom.mul_f32(2.0))) .unwrap_or_default(); - let zoom_amount_abs = camera_query - .and_then(|(.., editor_cam)| { - editor_cam.current_motion.inputs().map(|inputs| { - inputs.zoom_velocity_abs(editor_cam.smoothing.zoom.mul_f32(2.0)) - }) - }) - .unwrap_or(0.0); let should_zoom_end = is_in_zoom_mode && zoom_amount_abs <= zoom_stop; - if mouse_input.any_just_released([orbit_start, pan_start]) || should_zoom_end { - controller.write(EditorCamInputEvent::End { camera }); + if !editor_cam.enabled + || mouse_input.any_just_released([orbit_start, pan_start]) + || should_zoom_end + { + controller.write(EditorCamInputEvent::End { camera: entity }); } } - let Some((camera, ..)) = cameras + let Some((camera, .., editor_cam)) = cameras .iter() .find(|(_, camera, _)| pointer_location.is_in_viewport(camera, &primary_window)) else { continue; // Pointer must be in viewport to start a motion. }; + if !editor_cam.enabled { + continue; + } + if mouse_input.just_pressed(orbit_start) { controller.write(EditorCamInputEvent::Start { kind: MotionKind::OrbitZoom, diff --git a/crates/bevy_transform_gizmos/src/lib.rs b/crates/bevy_transform_gizmos/src/lib.rs index f2c214a3..cc16f9d5 100644 --- a/crates/bevy_transform_gizmos/src/lib.rs +++ b/crates/bevy_transform_gizmos/src/lib.rs @@ -558,7 +558,7 @@ fn gizmo_cam_copy_settings( } if main_cam.is_changed() { *gizmo_cam = main_cam.clone(); - gizmo_cam.order += 10; + gizmo_cam.order += 1; } if main_proj.is_changed() { *proj = main_proj.clone();