Merge pull request #21 from storytold/add-undo

Add undo
This commit is contained in:
rewin
2024-01-12 22:53:42 +03:00
committed by GitHub
12 changed files with 1153 additions and 651 deletions
Generated
+530 -97
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["./studio"]
[workspace.dependencies.bevy]
version = "0.12.0"
version = "0.12.1"
features = ["tonemapping_luts"]
[profile.dev-custom]
+1
View File
@@ -32,6 +32,7 @@ web-sys = { version = "0.3.65", optional = true, features = [
"HtmlElement",
"Window",
] }
space_editor = {git = "https://github.com/rewin123/space_editor", rev = "e150ecaf0647de6532d2967928982abf2558bfbc"}
[features]
wasm = [
+43 -11
View File
@@ -3,6 +3,7 @@ use bevy::{
prelude::*,
utils::HashSet,
};
use space_editor::prelude::space_undo::UndoRedo;
#[derive(SystemSet, Hash, Debug, PartialEq, Eq, Clone)]
pub struct StudioInputSystem;
@@ -11,6 +12,18 @@ pub struct StudioInputPlugin;
impl Plugin for StudioInputPlugin {
fn build(&self, app: &mut App) {
app.add_event::<InputAction>();
// Reading each frame of the mouse delta allows
// to get rid of the bug that delta is counted from the last reading,
// which leads to camera jumps if you move the mouse between clicks
app.add_systems(
PreUpdate,
read_mouse_delta
.in_set(StudioInputSystem)
.after(bevy::input::InputSystem),
);
app.init_resource::<LastFrameMouseDelta>();
app.add_systems(
PreUpdate,
(
@@ -18,9 +31,11 @@ impl Plugin for StudioInputPlugin {
translate_debug_toggle,
translate_dcc_camera_events,
translate_fps_camera_events,
send_undo_event,
)
.in_set(StudioInputSystem)
.after(bevy::input::InputSystem),
.after(bevy::input::InputSystem)
.after(read_mouse_delta),
);
}
}
@@ -96,14 +111,14 @@ fn translate_debug_toggle(keyboard: Res<Input<KeyCode>>, mut input: EventWriter<
}
fn translate_dcc_camera_events(
mouse_motion: EventReader<MouseMotion>,
mouse_motion: Res<LastFrameMouseDelta>,
mouse_buttons: Res<Input<MouseButton>>,
keyboard: Res<Input<KeyCode>>,
mut input: EventWriter<InputAction>,
) {
let delta = mouse_motion.get();
// Camera Pan
if mouse_buttons.pressed(MouseButton::Middle) {
let delta = read_mouse_delta(mouse_motion);
let lifecycle = if mouse_buttons.just_pressed(MouseButton::Middle) {
Lifecycle::Start
} else {
@@ -121,7 +136,6 @@ fn translate_dcc_camera_events(
// Camera Orbit/Zoom
else if keyboard.any_pressed([KeyCode::AltLeft, KeyCode::AltRight]) {
if mouse_buttons.any_pressed([MouseButton::Left, MouseButton::Right]) {
let delta = read_mouse_delta(mouse_motion);
let lifecycle =
if mouse_buttons.any_just_pressed([MouseButton::Left, MouseButton::Right]) {
Lifecycle::Start
@@ -167,7 +181,6 @@ fn translate_dcc_camera_events(
Lifecycle::Ongoing
};
let delta = read_mouse_delta(mouse_motion);
input.send(InputAction(lifecycle, CameraMotion::Rotate(delta).into()));
}
// Stop Camera Rotation
@@ -185,7 +198,6 @@ fn translate_dcc_camera_events(
Lifecycle::Ongoing
};
let delta = read_mouse_delta(mouse_motion);
input.send(InputAction(lifecycle, CameraMotion::Rotate(delta).into()));
}
// Stop Camera Rotation
@@ -285,9 +297,29 @@ fn translate_fps_camera_events(
}
}
#[inline]
fn read_mouse_delta(mut mouse_motion: EventReader<MouseMotion>) -> Vec2 {
mouse_motion
.read()
.fold(Vec2::ZERO, |accum, current| accum + current.delta)
#[derive(Resource, Default)]
pub struct LastFrameMouseDelta(pub Vec2);
impl LastFrameMouseDelta {
fn get(&self) -> Vec2 {
self.0
}
}
fn read_mouse_delta(
mut mouse_motion: EventReader<MouseMotion>,
mut last_delta: ResMut<LastFrameMouseDelta>,
) {
let delta = mouse_motion
.read()
.fold(Vec2::ZERO, |accum, current| accum + current.delta);
last_delta.0 = delta;
}
// TODO: Make it configurable
fn send_undo_event(mut events: EventWriter<UndoRedo>, keyboard: Res<Input<KeyCode>>) {
if keyboard.pressed(KeyCode::ControlLeft) && keyboard.just_pressed(KeyCode::Z) {
events.send(UndoRedo::Undo);
info!("Undo event sent");
}
}
+143 -142
View File
@@ -7,160 +7,161 @@ use super::{
on_gizmo_element_hover, on_gizmo_element_unhover,
rotation::{on_rotate, on_rotation_end, on_rotation_start, RotationWidget},
translation::{on_translate, on_translation_end, on_translation_start, TranslationWidget},
GizmoMats, GizmoMeshes, TransformGizmoBundle, TransformGizmoRoot, GIZMO_RENDER_LAYER,
GizmoMats, GizmoMeshes, TransformGizmoBundle, GIZMO_RENDER_LAYER,
};
#[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)
.insert(TransformGizmoRoot)
.with_children(|cmd| {
cmd.spawn(TransformGizmoBundle::new(xform, space, normalizing_scale))
.with_children(|cmd| {
use super::{ROT_X, ROT_Y, ROT_Z};
cmd.spawn(TransformGizmoBundle::new(
target,
target_global_transform,
space,
normalizing_scale,
))
.with_children(|cmd| {
use super::{ROT_X, ROT_Y, ROT_Z};
// Center sphere
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::ScreenSpace,
MaterialMeshBundle {
mesh: meshes.translate_ss.clone(),
material: mats.translate_ss.clone(),
transform: Transform::IDENTITY,
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::target_commands_mut(on_translation_end),
));
// Center sphere
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::ScreenSpace,
MaterialMeshBundle {
mesh: meshes.translate_ss.clone(),
material: mats.translate_ss.clone(),
transform: Transform::IDENTITY,
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::run(on_translation_end),
));
// Rotate X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_x.clone(),
transform: Transform::from_rotation(ROT_X),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
));
// Translate X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_x.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(1.2, 0., 0.),
..default()
},
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::target_commands_mut(on_translation_end),
));
// Rotate X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_x.clone(),
transform: Transform::from_rotation(ROT_X),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
));
// Translate X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_x.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(1.2, 0., 0.),
..default()
},
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::run(on_translation_end),
));
// Rotate Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_y.clone(),
transform: Transform::from_rotation(ROT_Y),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
));
// Translate Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_y.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(0., 1.2, 0.),
..default()
},
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::target_commands_mut(on_translation_end),
));
// Rotate Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_y.clone(),
transform: Transform::from_rotation(ROT_Y),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
));
// Translate Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_y.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(0., 1.2, 0.),
..default()
},
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::run(on_translation_end),
));
// Rotate Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_z.clone(),
transform: Transform::from_rotation(ROT_Z),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
));
// Translate Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_z.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(0., 0., 1.2),
..default()
},
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::target_commands_mut(on_translation_end),
));
});
});
// Rotate Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_z.clone(),
transform: Transform::from_rotation(ROT_Z),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
));
// Translate Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_z.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(0., 0., 1.2),
..default()
},
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::run(on_translation_end),
));
});
}
+19 -60
View File
@@ -1,105 +1,64 @@
use bevy::{math::Affine3A, prelude::*};
use bevy::prelude::*;
use bevy_mod_picking::prelude::*;
use crate::{
interaction::TransformSpace,
math::{IsNonUniform, MatrixExtras, VecExtras},
};
use crate::interaction::TransformSpace;
#[derive(Component)]
pub(super) struct TransformGizmoRoot;
#[derive(Component)]
pub(super) struct TransformGizmo;
#[derive(Event)]
pub(super) struct RebuildGizmo;
#[derive(Component, Reflect)]
pub(super) struct TransformGizmo {
pub(super) target: Entity,
pub(super) transform_space: TransformSpace,
}
#[derive(Bundle)]
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,
};
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),
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),
transform,
..default()
},
scale: nat_scale,
}
}
}
+56 -25
View File
@@ -35,8 +35,8 @@ pub struct GizmoPlugin;
impl Plugin for GizmoPlugin {
fn build(&self, app: &mut App) {
app.register_type::<NaturalScale>();
app.add_event::<RebuildGizmo>();
app.register_type::<TransformGizmo>();
app.add_systems(Startup, init_resources);
app.add_systems(
PreUpdate,
@@ -50,13 +50,15 @@ impl Plugin for GizmoPlugin {
(
despawn_existing_gizmo.run_if(
resource_changed_or_removed::<Selection>()
.or_else(resource_changed::<TransformMode>())
.or_else(on_event::<RebuildGizmo>()),
.or_else(resource_changed::<TransformMode>()),
),
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>())),
),
(update_target_global_trasform, sync_gizmo_transform)
.chain()
.after(CameraMotionSystem),
normalize_gizmo_scale.after(CameraMotionSystem),
),
);
@@ -67,17 +69,9 @@ 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>();
fn despawn_existing_gizmo(mut cmd: Commands, q_gizmos: Query<Entity, With<TransformGizmo>>) {
for ent in q_gizmos.iter() {
cmd.entity(ent).despawn_recursive();
}
}
@@ -90,8 +84,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 +101,7 @@ fn spawn_gizmo(
TransformType::TRANSLATE => {
spawn_translation_gizmo(
&mut cmd,
ent,
target,
xform,
r_xform_mode.space,
normalizing_scale,
@@ -118,7 +112,7 @@ fn spawn_gizmo(
TransformType::ROTATE => {
spawn_rotation_gizmo(
&mut cmd,
ent,
target,
xform,
r_xform_mode.space,
normalizing_scale,
@@ -129,7 +123,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 +137,57 @@ fn spawn_gizmo(
}
}
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<MainCamera>>,
mut q_gizmo: Query<
(&mut Transform, &GlobalTransform, &NaturalScale),
(&mut Transform, &GlobalTransform),
(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 {
let Ok((mut xform, _)) = q_gizmo.get_single_mut() else {
return;
};
let dist = Vec3::distance(cam_xform.translation, world_xform.translation());
xform.scale = **nat_scale * (dist / 15.);
let dist = Vec3::distance(cam_xform.translation, xform.translation);
xform.scale = Vec3::splat(dist / 15.);
}
+119 -120
View File
@@ -8,14 +8,14 @@ use bevy_mod_picking::prelude::*;
use bevy_mod_raycast::prelude::{Raycast, RaycastSettings};
use crate::{
interaction::{TransformMode, TransformSpace},
interaction::TransformSpace,
math::{MatrixExtras, NearlyZero},
MainCamera,
};
use super::{
on_gizmo_element_hover, on_gizmo_element_unhover, GizmoMats, GizmoMeshes, InitialTransform,
RebuildGizmo, TransformGizmo, TransformGizmoBundle, TransformGizmoRoot, GIZMO_RENDER_LAYER,
TransformGizmo, TransformGizmoBundle, GIZMO_RENDER_LAYER,
};
#[derive(Component)]
@@ -40,97 +40,93 @@ 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)
.insert(TransformGizmoRoot)
.with_children(|cmd| {
cmd.spawn((
TransformGizmoBundle::new(xform, space, normalizing_scale),
On::<Pointer<DragStart>>::run(on_rotation_start),
On::<Pointer<Drag>>::run(on_rotate),
On::<Pointer<DragEnd>>::run(on_rotation_end),
))
.with_children(|cmd| {
use super::{ROT_X, ROT_Y, ROT_Z};
cmd.spawn((
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),
))
.with_children(|cmd| {
use super::{ROT_X, ROT_Y, ROT_Z};
// Trackball
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Trackball,
MaterialMeshBundle {
mesh: meshes.rotate_ball.clone(),
material: mats.rotate_trackball.clone(),
..default()
},
On::<Pointer<Over>>::run(on_trackball_hover),
On::<Pointer<Out>>::run(on_trackball_unhover),
));
// Trackball
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Trackball,
MaterialMeshBundle {
mesh: meshes.rotate_ball.clone(),
material: mats.rotate_trackball.clone(),
..default()
},
On::<Pointer<Over>>::run(on_trackball_hover),
On::<Pointer<Out>>::run(on_trackball_unhover),
));
// Screen-space
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::ScreenSpace,
MaterialMeshBundle {
mesh: meshes.rotate_ss.clone(),
material: mats.rotate_ss.clone(),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
// Screen-space
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::ScreenSpace,
MaterialMeshBundle {
mesh: meshes.rotate_ss.clone(),
material: mats.rotate_ss.clone(),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
// X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_x.clone(),
transform: Transform::from_rotation(ROT_X),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
// Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_y.clone(),
transform: Transform::from_rotation(ROT_Y),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
// Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_z.clone(),
transform: Transform::from_rotation(ROT_Z),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
});
});
// X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_x.clone(),
transform: Transform::from_rotation(ROT_X),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
// Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_y.clone(),
transform: Transform::from_rotation(ROT_Y),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
// Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
RotationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.rotate_ring.clone(),
material: mats.rotate_z.clone(),
transform: Transform::from_rotation(ROT_Z),
..default()
},
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
));
});
}
pub(super) fn update_ss_ring_rotation(
@@ -182,58 +178,61 @@ fn on_trackball_unhover(
pub(super) fn on_rotation_start(
event: Listener<Pointer<DragStart>>,
mut cmd: Commands,
q_xform: Query<(Entity, &GlobalTransform), With<TransformGizmoRoot>>,
q_xform: Query<(Entity, &GlobalTransform)>,
q_gizmos: Query<&TransformGizmo>,
) {
info!("Rotation start");
if let Ok((ent, &world_xform)) = q_xform.get_single() {
let hit_point = event.hit.position.unwrap();
let Ok(gizmo) = q_gizmos.get_single() else {
error!("Rotation gizmo not found");
return;
};
cmd.entity(ent).insert((
InitialTransform(world_xform),
InitialHitPoint(hit_point),
LastHitPoint(hit_point),
LastRotation {
axis: Vec3::ZERO,
theta: 0.,
},
));
}
let Ok((ent, world_xform)) = q_xform.get(gizmo.target) else {
error!("Rotation gizmo target not found");
return;
};
let hit_point = event.hit.position.unwrap();
cmd.entity(ent).insert((
InitialTransform(*world_xform),
InitialHitPoint(hit_point),
LastHitPoint(hit_point),
LastRotation {
axis: Vec3::ZERO,
theta: 0.,
},
));
}
pub(super) fn on_rotation_end(
event: Listener<Pointer<DragEnd>>,
_: Listener<Pointer<DragEnd>>,
mut cmd: Commands,
r_xform_mode: Res<TransformMode>,
mut ev_rebuild_gizmo: EventWriter<RebuildGizmo>,
q_inited_components: Query<Entity, With<LastRotation>>,
) {
info!("Rotation end");
cmd.entity(event.target)
.remove::<InitialTransform>()
.remove::<InitialHitPoint>()
.remove::<LastHitPoint>()
.remove::<LastRotation>();
if matches!(r_xform_mode.space, TransformSpace::World) {
ev_rebuild_gizmo.send(RebuildGizmo);
for ent in q_inited_components.iter() {
cmd.entity(ent)
.remove::<InitialTransform>()
.remove::<InitialHitPoint>()
.remove::<LastHitPoint>()
.remove::<LastRotation>();
}
}
pub(super) fn on_rotate(
event: Listener<Pointer<Drag>>,
mut raycast: Raycast,
mut q_root: Query<
(
&mut Transform,
Option<&Parent>,
&InitialTransform,
&InitialHitPoint,
&mut LastHitPoint,
&mut LastRotation,
),
With<TransformGizmoRoot>,
>,
mut q_root: Query<(
&mut Transform,
Option<&Parent>,
&InitialTransform,
&InitialHitPoint,
&mut LastHitPoint,
&mut LastRotation,
)>,
q_camera: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
q_gizmo: Query<&GlobalTransform, (With<TransformGizmo>, Without<MainCamera>)>,
q_world_xforms: Query<&GlobalTransform, (With<Children>, Without<MainCamera>)>,
+217 -192
View File
@@ -1,16 +1,11 @@
use bevy::{
ecs::system::EntityCommands, math::vec3, pbr::NotShadowCaster, prelude::*,
render::view::RenderLayers,
};
use bevy_mod_picking::prelude::{
Drag, DragEnd, DragStart, Listener, ListenerInput, On, Out, Over, Pointer,
};
use bevy::{math::vec3, pbr::NotShadowCaster, prelude::*, render::view::RenderLayers};
use bevy_mod_picking::prelude::{Drag, DragEnd, DragStart, Listener, On, Out, Over, Pointer};
use crate::{interaction::TransformSpace, MainCamera};
use super::{
on_gizmo_element_hover, on_gizmo_element_unhover, GizmoMats, GizmoMeshes, InitialTransform,
TransformGizmo, TransformGizmoBundle, TransformGizmoRoot, GIZMO_RENDER_LAYER,
TransformGizmo, TransformGizmoBundle, GIZMO_RENDER_LAYER,
};
#[derive(Component)]
@@ -23,218 +18,226 @@ 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),
On::<Pointer<Over>>::run(on_gizmo_element_hover),
On::<Pointer<Out>>::run(on_gizmo_element_unhover),
On::<Pointer<DragStart>>::run(on_translation_start),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::target_commands_mut(on_translation_end),
))
.with_children(|cmd| {
use super::{CIRC_45, ROT_X, ROT_Y, ROT_Z};
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),
On::<Pointer<Drag>>::run(on_translate),
On::<Pointer<DragEnd>>::run(on_translation_end),
))
.with_children(|cmd| {
use super::{CIRC_45, ROT_X, ROT_Y, ROT_Z};
// Center sphere
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::ScreenSpace,
MaterialMeshBundle {
mesh: meshes.translate_ss.clone(),
material: mats.translate_ss.clone(),
transform: Transform::IDENTITY,
..default()
},
));
// Center sphere
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::ScreenSpace,
MaterialMeshBundle {
mesh: meshes.translate_ss.clone(),
material: mats.translate_ss.clone(),
transform: Transform::IDENTITY,
..default()
},
));
// X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_line.clone(),
material: mats.translate_x.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(0.6, 0., 0.),
..default()
},
..default()
},
));
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_x.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(1., 0., 0.),
..default()
},
..default()
},
));
// X
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_line.clone(),
material: mats.translate_x.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(0.6, 0., 0.),
..default()
},
..default()
},
));
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_x.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(1., 0., 0.),
..default()
},
..default()
},
));
// Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_line.clone(),
material: mats.translate_y.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(0., 0.6, 0.),
..default()
},
..default()
},
));
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_y.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(0., 1., 0.),
..default()
},
..default()
},
));
// Y
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_line.clone(),
material: mats.translate_y.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(0., 0.6, 0.),
..default()
},
..default()
},
));
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_y.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(0., 1., 0.),
..default()
},
..default()
},
));
// Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_line.clone(),
material: mats.translate_z.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(0., 0., 0.6),
..default()
},
..default()
},
));
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_z.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(0., 0., 1.),
..default()
},
..default()
},
));
// Z
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_line.clone(),
material: mats.translate_z.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(0., 0., 0.6),
..default()
},
..default()
},
));
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Axis(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_arrow.clone(),
material: mats.translate_z.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(0., 0., 1.),
..default()
},
..default()
},
));
// YZ Plane
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Plane(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_plane.clone(),
material: mats.translate_yz.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(0., CIRC_45, CIRC_45),
..default()
},
..default()
},
));
// XZ Plane
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Plane(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_plane.clone(),
material: mats.translate_xz.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(CIRC_45, 0., CIRC_45),
..default()
},
..default()
},
));
// XY Plane
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Plane(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_plane.clone(),
material: mats.translate_xy.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(CIRC_45, CIRC_45, 0.),
..default()
},
..default()
},
));
});
});
// YZ Plane
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Plane(Vec3::X),
MaterialMeshBundle {
mesh: meshes.translate_plane.clone(),
material: mats.translate_yz.clone(),
transform: Transform {
rotation: ROT_X,
translation: vec3(0., CIRC_45, CIRC_45),
..default()
},
..default()
},
));
// XZ Plane
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Plane(Vec3::Y),
MaterialMeshBundle {
mesh: meshes.translate_plane.clone(),
material: mats.translate_xz.clone(),
transform: Transform {
rotation: ROT_Y,
translation: vec3(CIRC_45, 0., CIRC_45),
..default()
},
..default()
},
));
// XY Plane
cmd.spawn((
NotShadowCaster,
RenderLayers::layer(GIZMO_RENDER_LAYER),
TranslationWidget::Plane(Vec3::Z),
MaterialMeshBundle {
mesh: meshes.translate_plane.clone(),
material: mats.translate_xy.clone(),
transform: Transform {
rotation: ROT_Z,
translation: vec3(CIRC_45, CIRC_45, 0.),
..default()
},
..default()
},
));
});
}
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(
_: &ListenerInput<Pointer<DragEnd>>,
cmd: &mut EntityCommands<'_, '_, '_>,
_: Listener<Pointer<DragEnd>>,
mut cmd: Commands,
q_initial_transforms: Query<Entity, With<InitialTransform>>,
) {
cmd.remove::<InitialTransform>();
for ent in q_initial_transforms.iter() {
cmd.entity(ent).remove::<InitialTransform>();
}
}
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,18 @@ 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 +295,18 @@ 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]
+19 -1
View File
@@ -3,6 +3,7 @@
use bevy::prelude::*;
use bevy_mod_picking::DefaultPickingPlugins;
use space_editor::prelude::space_undo::UndoPlugin;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
@@ -79,7 +80,19 @@ pub fn init(viewport_size: ViewportSize) {
SkyboxPlugin,
UiLayerPlugin,
WorldGridPlugin,
));
UndoPlugin,
))
.add_systems(
Startup,
move |mut cmd: Commands, mut ew: EventWriter<LoadSceneEvent>| {
cmd.insert_resource(SkyboxId("gum_trees_4k".to_string()));
ew.send(LoadSceneEvent("bookcase".to_string()));
},
);
// Only for registering components for undo
use space_editor::prelude::space_undo::AppAutoUndo;
app.auto_reflected_undo::<Transform>();
#[cfg(feature = "wasm")]
wasm::initialize();
@@ -134,6 +147,7 @@ pub fn start_viewer(
SkyboxPlugin,
UiLayerPlugin,
WorldGridPlugin,
UndoPlugin,
))
.add_systems(
Startup,
@@ -143,6 +157,10 @@ pub fn start_viewer(
},
);
// Only for registering components for undo
use space_editor::prelude::space_undo::AppAutoUndo;
app.auto_reflected_undo::<Transform>();
#[cfg(feature = "wasm")]
wasm::initialize();
+4 -1
View File
@@ -13,6 +13,7 @@ use bevy::{
scene::InstanceId,
};
use space_editor::prelude::{space_undo::OneFrameUndoIgnore, PrefabMarker};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
@@ -207,6 +208,8 @@ fn spawn_next_scene(
SceneRoot,
SceneElement,
SpatialBundle::default(),
PrefabMarker,
OneFrameUndoIgnore::default(), //To prevert undo try to register add event
))
.id();
@@ -254,7 +257,7 @@ fn tag_scene_elements(
};
if gltf.named_nodes.contains_key(&**name) {
cmd.entity(ent).insert(SceneElement);
cmd.entity(ent).insert(SceneElement).insert(PrefabMarker);
}
}
}