Skip to content
This repository was archived by the owner on Apr 16, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bevy_editor_panes/bevy_3d_viewport/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
21 changes: 20 additions & 1 deletion bevy_editor_panes/bevy_3d_viewport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<Remove, Bevy3dViewport>,
Expand All @@ -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<Ref<TransformGizmo>>,
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;
Expand Down Expand Up @@ -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,
Expand Down
14 changes: 9 additions & 5 deletions crates/bevy_editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 17 additions & 14 deletions crates/bevy_editor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -121,29 +120,33 @@ impl App {
fn dummy_setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials_2d: ResMut<Assets<ColorMaterial>>,
mut materials_3d: ResMut<Assets<StandardMaterial>>,
) {
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((
DirectionalLight {
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"),
));
}
3 changes: 3 additions & 0 deletions crates/bevy_editor_cam/src/controller/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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(),
Expand Down
32 changes: 18 additions & 14 deletions crates/bevy_editor_cam/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_transform_gizmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down