Skip to content

Commit 8409e5c

Browse files
ActuallyHappeningjakobhellermann
authored andcommitted
wip: basic example works
but a bit buggy
1 parent 8152d36 commit 8409e5c

File tree

8 files changed

+154
-77
lines changed

8 files changed

+154
-77
lines changed

Cargo.toml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ description = "In-App editor tools for bevy apps"
1111
readme = "README.md"
1212

1313
[workspace.dependencies]
14-
bevy_editor_pls = { version = "0.8.0", path = "crates/bevy_editor_pls" }
15-
bevy_editor_pls_core = { version = "0.8.0", path = "crates/bevy_editor_pls_core" }
16-
bevy_editor_pls_default_windows = { version = "0.8.0", path = "crates/bevy_editor_pls_default_windows" }
14+
bevy_editor_pls = { version = "0.8.1", path = "crates/bevy_editor_pls" }
15+
bevy_editor_pls_core = { version = "0.8.1", path = "crates/bevy_editor_pls_core" }
16+
bevy_editor_pls_default_windows = { version = "0.8.1", path = "crates/bevy_editor_pls_default_windows" }
1717

18-
bevy-inspector-egui = "0.23.0"
19-
egui = "0.26"
20-
egui_dock = "0.11"
21-
egui-gizmo = "0.16"
18+
bevy-inspector-egui = "0.24.0"
19+
egui = "0.27"
20+
egui_dock = "0.12"
21+
# used to be egui-gizmo 0.16
22+
transform-gizmo-bevy = "0.2"
2223

2324
[profile.dev.package."*"]
2425
opt-level = 2

crates/bevy_editor_pls/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ bevy_editor_pls_core.workspace = true
2424
bevy_editor_pls_default_windows = { workspace = true, optional = true }
2525
bevy = { version = "0.13", default-features = false, features = ["x11"] }
2626
egui.workspace = true
27-
egui-gizmo.workspace = true
27+
transform-gizmo-bevy.workspace = true
2828
# bevy_framepace = { version = "0.12", default-features = false }
2929

3030
[dev-dependencies]

crates/bevy_editor_pls/examples/basic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn setup(
4747
material: materials.add(Color::rgb(0.8, 0.7, 0.6)),
4848
transform: Transform::from_xyz(0.0, 0.5, 0.0),
4949
..Default::default()
50-
});
50+
}).insert(transform_gizmo_bevy::GizmoTarget::default());
5151
// light
5252
commands.spawn(PointLightBundle {
5353
transform: Transform::from_xyz(4.0, 8.0, 4.0),
@@ -61,5 +61,5 @@ fn setup(
6161
commands.spawn(Camera3dBundle {
6262
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
6363
..Default::default()
64-
});
64+
}).insert(transform_gizmo_bevy::GizmoCamera);
6565
}

crates/bevy_editor_pls/src/controls.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ pub fn editor_controls_system(
240240
editor
241241
.window_state_mut::<bevy_editor_pls_default_windows::gizmos::GizmoWindow>()
242242
.unwrap()
243-
.gizmo_mode = egui_gizmo::GizmoMode::Translate;
243+
.gizmo_mode = transform_gizmo_bevy::GizmoMode::all_translate();
244244
}
245245
if controls.just_pressed(
246246
Action::SetGizmoModeRotate,
@@ -251,7 +251,7 @@ pub fn editor_controls_system(
251251
editor
252252
.window_state_mut::<bevy_editor_pls_default_windows::gizmos::GizmoWindow>()
253253
.unwrap()
254-
.gizmo_mode = egui_gizmo::GizmoMode::Rotate;
254+
.gizmo_mode = transform_gizmo_bevy::GizmoMode::all_rotate();
255255
}
256256
if controls.just_pressed(
257257
Action::SetGizmoModeScale,
@@ -262,7 +262,7 @@ pub fn editor_controls_system(
262262
editor
263263
.window_state_mut::<bevy_editor_pls_default_windows::gizmos::GizmoWindow>()
264264
.unwrap()
265-
.gizmo_mode = egui_gizmo::GizmoMode::Scale;
265+
.gizmo_mode = transform_gizmo_bevy::GizmoMode::all_scale();
266266
}
267267
}
268268
}

crates/bevy_editor_pls/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ impl Plugin for EditorPlugin {
128128

129129
app.add_plugins(bevy::pbr::wireframe::WireframePlugin);
130130

131+
// required for the GizmoWindow
132+
if !app.is_plugin_added::<transform_gizmo_bevy::TransformGizmoPlugin>() {
133+
app.add_plugins(transform_gizmo_bevy::TransformGizmoPlugin);
134+
}
135+
131136
app.insert_resource(controls::EditorControls::default_bindings())
132137
.add_systems(Update, controls::editor_controls_system);
133138

crates/bevy_editor_pls_default_windows/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ indexmap = "2"
3131
pretty-type-name = "1.0"
3232
bevy_mod_debugdump = "0.10"
3333
opener = "0.6.0"
34-
egui-gizmo.workspace = true
34+
transform-gizmo-bevy.workspace = true

crates/bevy_editor_pls_default_windows/src/cameras/mod.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use bevy_editor_pls_core::{
1313
Editor, EditorEvent,
1414
};
1515
use bevy_inspector_egui::egui;
16+
use transform_gizmo_bevy::GizmoCamera;
1617
// use bevy_mod_picking::prelude::PickRaycastSource;
1718

1819
use crate::hierarchy::{HideInEditor, HierarchyWindow};
@@ -28,6 +29,9 @@ pub struct EditorCamera;
2829
// Present only one the one currently active camera
2930
#[derive(Component)]
3031
pub struct ActiveEditorCamera;
32+
// /// Changed to a type alias to guarentee being in sync with the GizmoCamera component,
33+
// /// this should be considered a 'hack' that maybe should be fixed down the line
34+
// pub type ActiveEditorCamera = transform_gizmo_bevy::GizmoCamera;
3135

3236
// Marker component for the 3d free camera
3337
#[derive(Component)]
@@ -168,7 +172,7 @@ fn set_active_editor_camera_marker(world: &mut World, editor_cam: EditorCamKind)
168172
state.iter(world).next().unwrap()
169173
}
170174
};
171-
world.entity_mut(entity).insert(ActiveEditorCamera);
175+
world.entity_mut(entity).insert(ActiveEditorCamera {});
172176
}
173177

174178
fn cameras_ui(ui: &mut egui::Ui, world: &mut World) {
@@ -232,6 +236,7 @@ fn spawn_editor_cameras(mut commands: Commands, editor: Res<Editor>) {
232236
HideInEditor,
233237
Name::new("Editor Camera 3D Free"),
234238
NotInScene,
239+
GizmoCamera,
235240
render_layers,
236241
));
237242

@@ -254,6 +259,7 @@ fn spawn_editor_cameras(mut commands: Commands, editor: Res<Editor>) {
254259
HideInEditor,
255260
Name::new("Editor Camera 3D Pan/Orbit"),
256261
NotInScene,
262+
GizmoCamera,
257263
render_layers,
258264
));
259265

@@ -275,6 +281,7 @@ fn spawn_editor_cameras(mut commands: Commands, editor: Res<Editor>) {
275281
HideInEditor,
276282
Name::new("Editor Camera 2D Pan/Zoom"),
277283
NotInScene,
284+
GizmoCamera,
278285
render_layers,
279286
));
280287
}
@@ -496,21 +503,21 @@ fn initial_camera_setup(
496503
camera_state.editor_cam = EditorCamKind::D2PanZoom;
497504
commands
498505
.entity(cameras.p0().single().0)
499-
.insert(ActiveEditorCamera);
506+
.insert(ActiveEditorCamera {});
500507
*has_decided_initial_cam = true;
501508
}
502509
(false, true) => {
503510
camera_state.editor_cam = EditorCamKind::D3PanOrbit;
504511
commands
505512
.entity(cameras.p2().single().0)
506-
.insert(ActiveEditorCamera);
513+
.insert(ActiveEditorCamera {});
507514
*has_decided_initial_cam = true;
508515
}
509516
(true, true) => {
510517
camera_state.editor_cam = EditorCamKind::D3PanOrbit;
511518
commands
512519
.entity(cameras.p2().single().0)
513-
.insert(ActiveEditorCamera);
520+
.insert(ActiveEditorCamera {});
514521
*has_decided_initial_cam = true;
515522
}
516523
(false, false) => return,

crates/bevy_editor_pls_default_windows/src/gizmos.rs

Lines changed: 123 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use bevy::{
2-
ecs::query::QueryFilter,
2+
ecs::{query::QueryFilter, system::RunSystemOnce},
33
prelude::*,
44
render::{camera::CameraProjection, view::RenderLayers},
55
};
66

77
use bevy_editor_pls_core::editor_window::{EditorWindow, EditorWindowContext};
88
use bevy_inspector_egui::{bevy_inspector::hierarchy::SelectedEntities, egui};
9-
use egui_gizmo::GizmoMode;
9+
use transform_gizmo_bevy::{EnumSet, GizmoMode};
10+
use transform_gizmo_bevy::GizmoTarget;
1011

1112
use crate::{
1213
cameras::{ActiveEditorCamera, CameraWindow, EditorCamera, EDITOR_RENDER_LAYER},
@@ -15,14 +16,15 @@ use crate::{
1516

1617
pub struct GizmoState {
1718
pub camera_gizmo_active: bool,
18-
pub gizmo_mode: GizmoMode,
19+
/// TODO: Take these settings into account
20+
pub gizmo_mode: EnumSet<GizmoMode>,
1921
}
2022

2123
impl Default for GizmoState {
2224
fn default() -> Self {
2325
Self {
2426
camera_gizmo_active: true,
25-
gizmo_mode: GizmoMode::Translate,
27+
gizmo_mode: GizmoMode::all_translate(),
2628
}
2729
}
2830
}
@@ -42,10 +44,62 @@ impl EditorWindow for GizmoWindow {
4244
let gizmo_state = cx.state::<GizmoWindow>().unwrap();
4345

4446
if gizmo_state.camera_gizmo_active {
45-
if let (Some(hierarchy_state), Some(_camera_state)) =
46-
(cx.state::<HierarchyWindow>(), cx.state::<CameraWindow>())
47-
{
48-
draw_gizmo(ui, world, &hierarchy_state.selected, gizmo_state.gizmo_mode);
47+
// if let (Some(hierarchy_state), Some(_camera_state)) =
48+
// (cx.state::<HierarchyWindow>(), cx.state::<CameraWindow>())
49+
// {
50+
// draw_gizmo(ui, world, &hierarchy_state.selected, gizmo_state.gizmo_mode);
51+
// }
52+
53+
/// Before [hydrate_gizmos] and [deconstruct_gizmos] are run, this system resets the state of all entities that have a [EntityShouldShowGizmo] component.
54+
/// Then, according to selection logic some entities are marked as focussed, and [hydrate_gizmos] and [deconstruct_gizmos] is run to sync the gizmo state with the selection state.
55+
fn reset_gizmos_selected_state(
56+
mut commands: Commands,
57+
entities: Query<Entity, With<EntityShouldShowGizmo>>,
58+
) {
59+
for entity in entities.iter() {
60+
commands.entity(entity).remove::<EntityShouldShowGizmo>();
61+
}
62+
}
63+
64+
/// Takes all entities marked with [EntityShouldShowGizmo] and adds the [GizmoTarget] component to them.
65+
fn hydrate_gizmos(
66+
mut commands: Commands,
67+
entities: Query<Entity, (With<EntityShouldShowGizmo>, Without<GizmoTarget>)>,
68+
) {
69+
for entity in entities.iter() {
70+
trace!("Hydrating a gizmo on entity {:?} because it is selected", entity);
71+
// TODO: Maybe change the exact gizmo target instance instead of using default? should this load from some config?
72+
commands.entity(entity).insert(GizmoTarget::default());
73+
}
74+
}
75+
76+
/// Takes all entities that should have their [GizmoTarget] removed because they are no longer selected.
77+
fn deconstruct_gizmos(
78+
mut commands: Commands,
79+
entities: Query<Entity, (With<GizmoTarget>, Without<EntityShouldShowGizmo>)>,
80+
) {
81+
for entity in entities.iter() {
82+
commands.entity(entity).remove::<GizmoTarget>();
83+
debug!(
84+
"Removing GizmoTarget from entity {:?} because it has lost focus",
85+
entity
86+
);
87+
}
88+
}
89+
90+
if let Some(hierarchy_state) = cx.state::<HierarchyWindow>() {
91+
// here should assign the `EntityShouldShowGizmo` component, which is later synced
92+
// with the actual gizmo ui system
93+
94+
world.run_system_once(reset_gizmos_selected_state);
95+
96+
let selected_entities = hierarchy_state.selected.iter();
97+
for entity in selected_entities {
98+
world.entity_mut(entity).insert(EntityShouldShowGizmo);
99+
}
100+
101+
world.run_system_once(hydrate_gizmos);
102+
world.run_system_once(deconstruct_gizmos);
49103
}
50104
}
51105
}
@@ -93,9 +147,15 @@ struct GizmoMarkerConfig {
93147
camera_material: Handle<StandardMaterial>,
94148
}
95149

150+
/// can somebody document what this does? is it a duplicate of [EntityShouldShowGizmo]?
96151
#[derive(Component)]
97152
struct HasGizmoMarker;
98153

154+
/// When on an entity, this entity should be controllable using some sort of user gizmo.
155+
/// Currently uses [transform_gizmo_bevy], and puts the [GizmoTarget] on the entity.
156+
#[derive(Component)]
157+
struct EntityShouldShowGizmo;
158+
99159
type GizmoMarkerQuery<'w, 's, T, F = ()> =
100160
Query<'w, 's, Entity, (With<T>, Without<HasGizmoMarker>, F)>;
101161

@@ -166,55 +226,59 @@ fn add_gizmo_markers(
166226
}
167227
}
168228

169-
fn draw_gizmo(
170-
ui: &mut egui::Ui,
171-
world: &mut World,
172-
selected_entities: &SelectedEntities,
173-
gizmo_mode: GizmoMode,
174-
) {
175-
let Ok((cam_transform, projection)) = world
176-
.query_filtered::<(&GlobalTransform, &Projection), With<ActiveEditorCamera>>()
177-
.get_single(world)
178-
else {
179-
return;
180-
};
181-
let view_matrix = Mat4::from(cam_transform.affine().inverse());
182-
let projection_matrix = projection.get_projection_matrix();
183-
184-
if selected_entities.len() != 1 {
185-
return;
186-
}
229+
// fn draw_gizmo(
230+
// ui: &mut egui::Ui,
231+
// world: &mut World,
232+
// selected_entities: &SelectedEntities,
233+
// gizmo_mode: GizmoMode,
234+
// ) {
235+
// for entity in selected_entities.iter() {
236+
// world.entity_mut(entity).insert(transform_gizmo_bevy::GizmoTarget::default());
237+
// info!("Inserted GizmoTarget to entity: {:?}", entity);
238+
// }
239+
// // let Ok((cam_transform, projection)) = world
240+
// // .query_filtered::<(&GlobalTransform, &Projection), With<ActiveEditorCamera>>()
241+
// // .get_single(world)
242+
// // else {
243+
// // return;
244+
// // };
245+
// // let view_matrix = Mat4::from(cam_transform.affine().inverse());
246+
// // let projection_matrix = projection.get_projection_matrix();
187247

188-
for selected in selected_entities.iter() {
189-
let Some(global_transform) = world.get::<GlobalTransform>(selected) else {
190-
continue;
191-
};
192-
let model_matrix = global_transform.compute_matrix();
193-
194-
let Some(result) = egui_gizmo::Gizmo::new(selected)
195-
.model_matrix(model_matrix.into())
196-
.view_matrix(view_matrix.into())
197-
.projection_matrix(projection_matrix.into())
198-
.orientation(egui_gizmo::GizmoOrientation::Local)
199-
.mode(gizmo_mode)
200-
.interact(ui)
201-
else {
202-
continue;
203-
};
204-
205-
let global_affine = global_transform.affine();
206-
207-
let mut transform = world.get_mut::<Transform>(selected).unwrap();
208-
209-
let parent_affine = global_affine * transform.compute_affine().inverse();
210-
let inverse_parent_transform = GlobalTransform::from(parent_affine.inverse());
211-
212-
let global_transform = Transform {
213-
translation: result.translation.into(),
214-
rotation: result.rotation.into(),
215-
scale: result.scale.into(),
216-
};
217-
218-
*transform = (inverse_parent_transform * global_transform).into();
219-
}
220-
}
248+
// // if selected_entities.len() != 1 {
249+
// // return;
250+
// // }
251+
252+
// // for selected in selected_entities.iter() {
253+
// // // let Some(global_transform) = world.get::<GlobalTransform>(selected) else {
254+
// // // continue;
255+
// // // };
256+
// // // let model_matrix = global_transform.compute_matrix();
257+
258+
// // // let Some(result) = transform_gizmo_bevy::Gizmo::new(selected)
259+
// // // .model_matrix(model_matrix.into())
260+
// // // .view_matrix(view_matrix.into())
261+
// // // .projection_matrix(projection_matrix.into())
262+
// // // .orientation(transform_gizmo_bevy::GizmoOrientation::Local)
263+
// // // .mode(gizmo_mode)
264+
// // // .interact(ui)
265+
// // // else {
266+
// // // continue;
267+
// // // };
268+
269+
// // // let global_affine = global_transform.affine();
270+
271+
// // // let mut transform = world.get_mut::<Transform>(selected).unwrap();
272+
273+
// // // let parent_affine = global_affine * transform.compute_affine().inverse();
274+
// // // let inverse_parent_transform = GlobalTransform::from(parent_affine.inverse());
275+
276+
// // // let global_transform = Transform {
277+
// // // translation: result.translation.into(),
278+
// // // rotation: result.rotation.into(),
279+
// // // scale: result.scale.into(),
280+
// // // };
281+
282+
// // // *transform = (inverse_parent_transform * global_transform).into();
283+
// // }
284+
// }

0 commit comments

Comments
 (0)