From 59e7eacf9d172f9f0647eb7532b5ed3a6c955dea Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Tue, 20 Jun 2023 11:35:16 +0800 Subject: [PATCH 1/9] Add dynamics based door behavior Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/lib.rs | 3 + rmf_site_editor/src/site/door.rs | 224 +++++++++++++++++++++++++--- rmf_site_editor/src/site/mod.rs | 1 + rmf_site_editor/src/workcell/mod.rs | 4 - 4 files changed, 204 insertions(+), 28 deletions(-) diff --git a/rmf_site_editor/src/lib.rs b/rmf_site_editor/src/lib.rs index eeeacfc4..e57ba936 100644 --- a/rmf_site_editor/src/lib.rs +++ b/rmf_site_editor/src/lib.rs @@ -2,6 +2,7 @@ use bevy::{ log::LogPlugin, pbr::DirectionalLightShadowMap, prelude::*, render::renderer::RenderAdapterInfo, }; use bevy_egui::EguiPlugin; +use bevy_rapier3d::prelude::*; use main_menu::MainMenuPlugin; // use warehouse_generator::WarehouseGeneratorPlugin; #[cfg(not(target_arch = "wasm32"))] @@ -166,6 +167,8 @@ pub fn run(command_line_args: Vec) { app.init_resource::() .add_startup_system(init_settings) .insert_resource(DirectionalLightShadowMap { size: 2048 }) + .add_plugin(RapierPhysicsPlugin::::default()) + .add_plugin(RapierDebugRenderPlugin::default()) .add_plugin(AabbUpdatePlugin) .add_plugin(EguiPlugin) .add_plugin(KeyboardInputPlugin) diff --git a/rmf_site_editor/src/site/door.rs b/rmf_site_editor/src/site/door.rs index 781820a0..7e00bcef 100644 --- a/rmf_site_editor/src/site/door.rs +++ b/rmf_site_editor/src/site/door.rs @@ -15,23 +15,60 @@ * */ -use crate::{interaction::Selectable, shapes::*, site::*}; +use crate::{interaction::Selectable, shapes::*, site::*, CurrentWorkspace}; use bevy::{ prelude::*, render::mesh::{Indices, PrimitiveTopology}, }; +use bevy_rapier3d::prelude::*; +use itertools::Itertools; use rmf_site_format::{Category, DoorType, Edge, DEFAULT_LEVEL_HEIGHT}; pub const DOOR_CUE_HEIGHT: f32 = 0.004; pub const DOOR_STOP_LINE_THICKNESS: f32 = 0.01; pub const DOOR_STOP_LINE_LENGTH: f32 = 3.0 * DEFAULT_DOOR_THICKNESS; pub const DOOR_SWEEP_THICKNESS: f32 = 0.05; +pub const DOUBLE_DOOR_GAP: f32 = 0.05; + +#[derive(Debug, Clone, Copy)] +pub enum DoorBodyType { + SingleSwing { body: Entity }, + DoubleSwing { left: Entity, right: Entity }, + SingleSliding { body: Entity }, + DoubleSliding { left: Entity, right: Entity }, +} + +impl DoorBodyType { + pub fn from_door_type(door_type: &DoorType, entities: &Vec) -> Self { + match door_type { + DoorType::SingleSwing(_) => DoorBodyType::SingleSwing { body: entities[0] }, + DoorType::DoubleSwing(_) => DoorBodyType::DoubleSwing { + left: entities[0], + right: entities[1], + }, + DoorType::SingleSliding(_) => DoorBodyType::SingleSliding { body: entities[0] }, + DoorType::DoubleSliding(_) => DoorBodyType::DoubleSliding { + left: entities[0], + right: entities[1], + }, + DoorType::Model(_) => todo!("Model doors not implemented yet"), + } + } + + pub fn entities(&self) -> Vec { + match self { + DoorBodyType::SingleSwing { body } | DoorBodyType::SingleSliding { body } => { + vec![*body] + } + DoorBodyType::DoubleSwing { left, right } + | DoorBodyType::DoubleSliding { left, right } => vec![*left, *right], + } + } +} #[derive(Debug, Clone, Copy, Component)] pub struct DoorSegments { - // TODO(MXG): When it's time to animate the doors we should replace this - // with an enum for the different possible door types: Single/Double Swing/Sliding - pub body: Entity, + pub body: DoorBodyType, pub cue_inner: Entity, pub cue_outline: Entity, } @@ -41,7 +78,7 @@ fn make_door_visuals( edge: &Edge, anchors: &AnchorParams, kind: &DoorType, -) -> (Transform, Transform, Mesh, Mesh) { +) -> (Transform, Vec, Mesh, Mesh) { let p_start = anchors .point_in_parent_frame_of(edge.left(), Category::Door, entity) .unwrap(); @@ -55,17 +92,46 @@ fn make_door_visuals( let center = (p_start + p_end) / 2.0; let (inner, outline) = make_door_cues(length, kind); + // First is pose, second is shape + let door_tfs = match kind { + DoorType::SingleSwing(_) | DoorType::SingleSliding(_) => vec![Transform { + translation: Vec3::new(0., 0., DEFAULT_LEVEL_HEIGHT / 2.0), + scale: Vec3::new(DEFAULT_DOOR_THICKNESS, length, DEFAULT_LEVEL_HEIGHT), + ..default() + }], + DoorType::DoubleSwing(_) | DoorType::DoubleSliding(_) => { + // TODO(luca) implement left_to_right ratio for double doors + let door_length = (length - DOUBLE_DOOR_GAP) / 2.0; + vec![ + Transform { + translation: Vec3::new( + 0., + (length + DOUBLE_DOOR_GAP) / 4.0, + DEFAULT_LEVEL_HEIGHT / 2.0, + ), + scale: Vec3::new(DEFAULT_DOOR_THICKNESS, door_length, DEFAULT_LEVEL_HEIGHT), + ..default() + }, + Transform { + translation: Vec3::new( + 0., + -(length + DOUBLE_DOOR_GAP) / 4.0, + DEFAULT_LEVEL_HEIGHT / 2.0, + ), + scale: Vec3::new(DEFAULT_DOOR_THICKNESS, door_length, DEFAULT_LEVEL_HEIGHT), + ..default() + }, + ] + } + DoorType::Model(_) => todo!("Model doors not implemented yet"), + }; ( Transform { translation: Vec3::new(center.x, center.y, 0.), rotation: Quat::from_rotation_z(yaw), ..default() }, - Transform { - translation: Vec3::new(0., 0., DEFAULT_LEVEL_HEIGHT / 2.0), - scale: Vec3::new(DEFAULT_DOOR_THICKNESS, length, DEFAULT_LEVEL_HEIGHT), - ..default() - }, + door_tfs, inner, outline, ) @@ -164,10 +230,111 @@ fn make_door_cues(door_width: f32, kind: &DoorType) -> (Mesh, Mesh) { } } +// TODO(luca) handle door type +pub fn add_joints_to_new_doors( + mut commands: Commands, + new_doors: Query< + (Entity, &Edge, &DoorSegments, &DoorType, &Parent), + Or<(Changed, Added)>, + >, + transforms: Query<&Transform>, + anchors: AnchorParams, +) { + return; + for (e, edge, segment, door_type, parent) in &new_doors { + commands + .entity(**parent) + .insert(RigidBody::Fixed) + .insert(Collider::halfspace(Vec3::Z).unwrap()); + // TODO(luca) we could use motor_position and let the physics engine do PID control but + // position setpoints for angles outside of +-90 degrees are broken, + // tracking issue https://github.com/dimforge/rapier/issues/378 + let joints = match door_type { + DoorType::SingleSwing(door) => { + // TODO(luca) joint limit on swing angle + let swing = door.swing; + let door_entity = segment.body.entities()[0]; + let door_tf = transforms + .get(door_entity) + .expect("Door segment transform not found"); + let pivot_point = edge.side(door.pivot_on); + let p_start = anchors + .point_in_parent_frame_of(pivot_point, Category::Door, e) + .unwrap(); + let half_y = match door.pivot_on { + Side::Left => door_tf.scale.y / 2.0, + Side::Right => -door_tf.scale.y / 2.0, + }; + let half_z = door_tf.scale.z / 2.0; + vec![( + door_entity, + RevoluteJointBuilder::new(Vec3::Z) + .local_anchor1(Vec3::new(p_start.x, p_start.y, p_start.z)) + .local_anchor2(Vec3::new(0.0, half_y, -half_z)) + .motor_velocity(0.0, Real::MAX), + )] + } + DoorType::DoubleSwing(door) => { + let swing = door.swing; + let entities = segment.body.entities(); + let (left_entity, right_entity) = entities.iter().next_tuple().unwrap(); + let left_tf = transforms + .get(*left_entity) + .expect("Door segment transform not found"); + let right_tf = transforms + .get(*right_entity) + .expect("Door segment transform not found"); + let p_left = anchors + .point_in_parent_frame_of(edge.left(), Category::Door, e) + .unwrap(); + let p_right = anchors + .point_in_parent_frame_of(edge.right(), Category::Door, e) + .unwrap(); + let offset_left = left_tf.scale.y / 2.0; + let offset_right = -right_tf.scale.y / 2.0; + let half_z = left_tf.scale.z / 2.0; + vec![ + ( + *left_entity, + RevoluteJointBuilder::new(Vec3::Z) + .local_anchor1(Vec3::new(p_left.x, p_left.y, p_left.z)) + .local_anchor2(Vec3::new(0.0, offset_left, -half_z)) + .motor_velocity(0.0, Real::MAX), + ), + ( + *right_entity, + RevoluteJointBuilder::new(Vec3::Z) + .local_anchor1(Vec3::new(p_right.x, p_right.y, p_right.z)) + .local_anchor2(Vec3::new(0.0, offset_right, -half_z)) + .motor_velocity(0.0, Real::MAX), + ), + ] + } + _ => continue, + }; + for (entity, joint) in joints.iter() { + commands + .entity(*entity) + // TODO(luca) It seems KinematicVelocityBased doesn't support joint movement, + // change if it ever gets implemented upstream since we only need kinematics + .insert(RigidBody::Dynamic) + // Scale is inherited from mesh, so collider is unit cuboid (uses half extents) + .insert(Collider::cuboid(0.5, 0.5, 0.5)) + .insert(ImpulseJoint::new(**parent, *joint)); + } + } +} + pub fn add_door_visuals( mut commands: Commands, new_doors: Query< - (Entity, &Edge, &DoorType, Option<&Visibility>), + ( + Entity, + &Edge, + &DoorType, + Option<&Visibility>, + &Parent, + ), ( Or<(Added, Added>)>, Without, @@ -178,21 +345,27 @@ pub fn add_door_visuals( assets: Res, mut meshes: ResMut>, ) { - for (e, edge, kind, visibility) in &new_doors { - let (pose_tf, shape_tf, cue_inner_mesh, cue_outline_mesh) = + for (e, edge, kind, visibility, p) in &new_doors { + let (pose_tf, door_tfs, cue_inner_mesh, cue_outline_mesh) = make_door_visuals(e, edge, &anchors, kind); let mut commands = commands.entity(e); let (body, cue_inner, cue_outline) = commands.add_children(|parent| { - let body = parent - .spawn(PbrBundle { - mesh: assets.box_mesh.clone(), - material: assets.door_body_material.clone(), - transform: shape_tf, - ..default() + let bodies = door_tfs + .iter() + .map(|tf| { + parent + .spawn(PbrBundle { + mesh: assets.box_mesh.clone(), + material: assets.door_body_material.clone(), + transform: *tf, + ..default() + }) + .insert(Selectable::new(e)) + .id() }) - .insert(Selectable::new(e)) - .id(); + .collect::>(); + let body = DoorBodyType::from_door_type(kind, &bodies); let cue_inner = parent .spawn(PbrBundle { @@ -254,12 +427,15 @@ fn update_door_visuals( mesh_handles: &mut Query<&mut Handle>, mesh_assets: &mut ResMut>, ) { - let (pose_tf, shape_tf, cue_inner_mesh, cue_outline_mesh) = + let (pose_tf, door_tfs, cue_inner_mesh, cue_outline_mesh) = make_door_visuals(entity, edge, anchors, kind); let mut door_transform = transforms.get_mut(entity).unwrap(); *door_transform = pose_tf; - let mut shape_transform = transforms.get_mut(segments.body).unwrap(); - *shape_transform = shape_tf; + let entities = segments.body.entities(); + for (door_tf, e) in door_tfs.iter().zip(entities.iter()) { + let mut door_transform = transforms.get_mut(*e).unwrap(); + *door_transform = *door_tf; + } let mut cue_inner = mesh_handles.get_mut(segments.cue_inner).unwrap(); *cue_inner = mesh_assets.add(cue_inner_mesh); let mut cue_outline = mesh_handles.get_mut(segments.cue_outline).unwrap(); diff --git a/rmf_site_editor/src/site/mod.rs b/rmf_site_editor/src/site/mod.rs index fa526f75..3e336aa8 100644 --- a/rmf_site_editor/src/site/mod.rs +++ b/rmf_site_editor/src/site/mod.rs @@ -226,6 +226,7 @@ impl Plugin for SitePlugin { .after(VisibilitySystems::VisibilityPropagate) .with_system(update_anchor_transforms) .with_system(add_door_visuals) + .with_system(add_joints_to_new_doors) .with_system(update_changed_door) .with_system(update_door_for_moved_anchors) .with_system(add_floor_visuals) diff --git a/rmf_site_editor/src/workcell/mod.rs b/rmf_site_editor/src/workcell/mod.rs index d74aced0..e6faf948 100644 --- a/rmf_site_editor/src/workcell/mod.rs +++ b/rmf_site_editor/src/workcell/mod.rs @@ -50,8 +50,6 @@ use crate::{ use rmf_site_format::ModelMarker; -use bevy_rapier3d::prelude::*; - #[derive(Default)] pub struct WorkcellEditorPlugin; @@ -89,8 +87,6 @@ impl Plugin for WorkcellEditorPlugin { ..default() }) .add_plugin(WireframePlugin) - .add_plugin(RapierPhysicsPlugin::::default()) - .add_plugin(RapierDebugRenderPlugin::default()) .add_event::() .add_event::() .add_event::() From 9b71961f197c1efdbc0bb5cd5ed5345327da6f18 Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Tue, 20 Jun 2023 14:28:39 +0800 Subject: [PATCH 2/9] Remove rapier simulation, add update from double to single door Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/interaction/outline.rs | 2 +- rmf_site_editor/src/site/door.rs | 143 ++++++--------------- rmf_site_editor/src/site/mod.rs | 1 - 3 files changed, 40 insertions(+), 106 deletions(-) diff --git a/rmf_site_editor/src/interaction/outline.rs b/rmf_site_editor/src/interaction/outline.rs index 1d1a699e..9a02dff4 100644 --- a/rmf_site_editor/src/interaction/outline.rs +++ b/rmf_site_editor/src/interaction/outline.rs @@ -134,7 +134,7 @@ pub fn update_outline_visualization( mut commands: Commands, outlinable: Query< (Entity, &Hovered, &Selected, &OutlineVisualization), - Or<(Changed, Changed)>, + Or<(Changed, Changed, Changed)>, >, descendants: Query<(Option<&Children>, Option<&ComputedVisualCue>)>, ) { diff --git a/rmf_site_editor/src/site/door.rs b/rmf_site_editor/src/site/door.rs index 7e00bcef..3f05dc52 100644 --- a/rmf_site_editor/src/site/door.rs +++ b/rmf_site_editor/src/site/door.rs @@ -230,101 +230,6 @@ fn make_door_cues(door_width: f32, kind: &DoorType) -> (Mesh, Mesh) { } } -// TODO(luca) handle door type -pub fn add_joints_to_new_doors( - mut commands: Commands, - new_doors: Query< - (Entity, &Edge, &DoorSegments, &DoorType, &Parent), - Or<(Changed, Added)>, - >, - transforms: Query<&Transform>, - anchors: AnchorParams, -) { - return; - for (e, edge, segment, door_type, parent) in &new_doors { - commands - .entity(**parent) - .insert(RigidBody::Fixed) - .insert(Collider::halfspace(Vec3::Z).unwrap()); - // TODO(luca) we could use motor_position and let the physics engine do PID control but - // position setpoints for angles outside of +-90 degrees are broken, - // tracking issue https://github.com/dimforge/rapier/issues/378 - let joints = match door_type { - DoorType::SingleSwing(door) => { - // TODO(luca) joint limit on swing angle - let swing = door.swing; - let door_entity = segment.body.entities()[0]; - let door_tf = transforms - .get(door_entity) - .expect("Door segment transform not found"); - let pivot_point = edge.side(door.pivot_on); - let p_start = anchors - .point_in_parent_frame_of(pivot_point, Category::Door, e) - .unwrap(); - let half_y = match door.pivot_on { - Side::Left => door_tf.scale.y / 2.0, - Side::Right => -door_tf.scale.y / 2.0, - }; - let half_z = door_tf.scale.z / 2.0; - vec![( - door_entity, - RevoluteJointBuilder::new(Vec3::Z) - .local_anchor1(Vec3::new(p_start.x, p_start.y, p_start.z)) - .local_anchor2(Vec3::new(0.0, half_y, -half_z)) - .motor_velocity(0.0, Real::MAX), - )] - } - DoorType::DoubleSwing(door) => { - let swing = door.swing; - let entities = segment.body.entities(); - let (left_entity, right_entity) = entities.iter().next_tuple().unwrap(); - let left_tf = transforms - .get(*left_entity) - .expect("Door segment transform not found"); - let right_tf = transforms - .get(*right_entity) - .expect("Door segment transform not found"); - let p_left = anchors - .point_in_parent_frame_of(edge.left(), Category::Door, e) - .unwrap(); - let p_right = anchors - .point_in_parent_frame_of(edge.right(), Category::Door, e) - .unwrap(); - let offset_left = left_tf.scale.y / 2.0; - let offset_right = -right_tf.scale.y / 2.0; - let half_z = left_tf.scale.z / 2.0; - vec![ - ( - *left_entity, - RevoluteJointBuilder::new(Vec3::Z) - .local_anchor1(Vec3::new(p_left.x, p_left.y, p_left.z)) - .local_anchor2(Vec3::new(0.0, offset_left, -half_z)) - .motor_velocity(0.0, Real::MAX), - ), - ( - *right_entity, - RevoluteJointBuilder::new(Vec3::Z) - .local_anchor1(Vec3::new(p_right.x, p_right.y, p_right.z)) - .local_anchor2(Vec3::new(0.0, offset_right, -half_z)) - .motor_velocity(0.0, Real::MAX), - ), - ] - } - _ => continue, - }; - for (entity, joint) in joints.iter() { - commands - .entity(*entity) - // TODO(luca) It seems KinematicVelocityBased doesn't support joint movement, - // change if it ever gets implemented upstream since we only need kinematics - .insert(RigidBody::Dynamic) - // Scale is inherited from mesh, so collider is unit cuboid (uses half extents) - .insert(Collider::cuboid(0.5, 0.5, 0.5)) - .insert(ImpulseJoint::new(**parent, *joint)); - } - } -} - pub fn add_door_visuals( mut commands: Commands, new_doors: Query< @@ -418,24 +323,46 @@ pub fn add_door_visuals( } fn update_door_visuals( + commands: &mut Commands, entity: Entity, edge: &Edge, kind: &DoorType, - segments: &DoorSegments, + segments: &mut DoorSegments, anchors: &AnchorParams, transforms: &mut Query<&mut Transform>, mesh_handles: &mut Query<&mut Handle>, mesh_assets: &mut ResMut>, + assets: &Res, ) { let (pose_tf, door_tfs, cue_inner_mesh, cue_outline_mesh) = make_door_visuals(entity, edge, anchors, kind); let mut door_transform = transforms.get_mut(entity).unwrap(); *door_transform = pose_tf; - let entities = segments.body.entities(); + let mut entities = segments.body.entities(); for (door_tf, e) in door_tfs.iter().zip(entities.iter()) { let mut door_transform = transforms.get_mut(*e).unwrap(); *door_transform = *door_tf; } + for door_tf in door_tfs.iter().skip(entities.len()) { + // We need to spawn an extra door + let id = commands + .spawn(PbrBundle { + mesh: assets.box_mesh.clone(), + material: assets.door_body_material.clone(), + transform: *door_tfs.last().unwrap(), + ..default() + }) + .insert(Selectable::new(entity)) + .id(); + entities.push(id); + commands.entity(entity).add_child(id); + } + for e in entities.iter().skip(door_tfs.len()) { + // We need to despawn the extra door + commands.entity(*e).despawn_recursive(); + } + segments.body = DoorBodyType::from_door_type(kind, &entities); + // If we moved from a single to double door let mut cue_inner = mesh_handles.get_mut(segments.cue_inner).unwrap(); *cue_inner = mesh_assets.add(cue_inner_mesh); let mut cue_outline = mesh_handles.get_mut(segments.cue_outline).unwrap(); @@ -443,31 +370,36 @@ fn update_door_visuals( } pub fn update_changed_door( - doors: Query< - (Entity, &Edge, &DoorType, &DoorSegments), + mut commands: Commands, + mut doors: Query< + (Entity, &Edge, &DoorType, &mut DoorSegments), Or<(Changed>, Changed)>, >, anchors: AnchorParams, mut transforms: Query<&mut Transform>, mut mesh_handles: Query<&mut Handle>, mut mesh_assets: ResMut>, + assets: Res, ) { - for (entity, edge, kind, segments) in &doors { + for (entity, edge, kind, mut segments) in &mut doors { update_door_visuals( + &mut commands, entity, edge, kind, - segments, + &mut segments, &anchors, &mut transforms, &mut mesh_handles, &mut mesh_assets, + &assets, ); } } pub fn update_door_for_moved_anchors( - doors: Query<(Entity, &Edge, &DoorType, &DoorSegments)>, + mut commands: Commands, + mut doors: Query<(Entity, &Edge, &DoorType, &mut DoorSegments)>, anchors: AnchorParams, changed_anchors: Query< &Dependents, @@ -479,19 +411,22 @@ pub fn update_door_for_moved_anchors( mut transforms: Query<&mut Transform>, mut mesh_handles: Query<&mut Handle>, mut mesh_assets: ResMut>, + assets: Res, ) { for dependents in &changed_anchors { for dependent in dependents.iter() { - if let Some((entity, edge, kind, segments)) = doors.get(*dependent).ok() { + if let Some((entity, edge, kind, mut segments)) = doors.get_mut(*dependent).ok() { update_door_visuals( + &mut commands, entity, edge, kind, - segments, + &mut segments, &anchors, &mut transforms, &mut mesh_handles, &mut mesh_assets, + &assets, ); } } diff --git a/rmf_site_editor/src/site/mod.rs b/rmf_site_editor/src/site/mod.rs index 3e336aa8..fa526f75 100644 --- a/rmf_site_editor/src/site/mod.rs +++ b/rmf_site_editor/src/site/mod.rs @@ -226,7 +226,6 @@ impl Plugin for SitePlugin { .after(VisibilitySystems::VisibilityPropagate) .with_system(update_anchor_transforms) .with_system(add_door_visuals) - .with_system(add_joints_to_new_doors) .with_system(update_changed_door) .with_system(update_door_for_moved_anchors) .with_system(add_floor_visuals) From 365d74f31b29cbd6c5b0f15201fbc21dcb1230d3 Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Tue, 20 Jun 2023 14:29:30 +0800 Subject: [PATCH 3/9] Restore rapier to only workcell mode Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/lib.rs | 3 --- rmf_site_editor/src/workcell/mod.rs | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rmf_site_editor/src/lib.rs b/rmf_site_editor/src/lib.rs index e57ba936..eeeacfc4 100644 --- a/rmf_site_editor/src/lib.rs +++ b/rmf_site_editor/src/lib.rs @@ -2,7 +2,6 @@ use bevy::{ log::LogPlugin, pbr::DirectionalLightShadowMap, prelude::*, render::renderer::RenderAdapterInfo, }; use bevy_egui::EguiPlugin; -use bevy_rapier3d::prelude::*; use main_menu::MainMenuPlugin; // use warehouse_generator::WarehouseGeneratorPlugin; #[cfg(not(target_arch = "wasm32"))] @@ -167,8 +166,6 @@ pub fn run(command_line_args: Vec) { app.init_resource::() .add_startup_system(init_settings) .insert_resource(DirectionalLightShadowMap { size: 2048 }) - .add_plugin(RapierPhysicsPlugin::::default()) - .add_plugin(RapierDebugRenderPlugin::default()) .add_plugin(AabbUpdatePlugin) .add_plugin(EguiPlugin) .add_plugin(KeyboardInputPlugin) diff --git a/rmf_site_editor/src/workcell/mod.rs b/rmf_site_editor/src/workcell/mod.rs index e6faf948..d74aced0 100644 --- a/rmf_site_editor/src/workcell/mod.rs +++ b/rmf_site_editor/src/workcell/mod.rs @@ -50,6 +50,8 @@ use crate::{ use rmf_site_format::ModelMarker; +use bevy_rapier3d::prelude::*; + #[derive(Default)] pub struct WorkcellEditorPlugin; @@ -87,6 +89,8 @@ impl Plugin for WorkcellEditorPlugin { ..default() }) .add_plugin(WireframePlugin) + .add_plugin(RapierPhysicsPlugin::::default()) + .add_plugin(RapierDebugRenderPlugin::default()) .add_event::() .add_event::() .add_event::() From 23356fcd644620e3eb1607837883c718a0d43d1c Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Tue, 20 Jun 2023 14:36:15 +0800 Subject: [PATCH 4/9] Cleanup Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/site/door.rs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/rmf_site_editor/src/site/door.rs b/rmf_site_editor/src/site/door.rs index 3f05dc52..cfd4fbbe 100644 --- a/rmf_site_editor/src/site/door.rs +++ b/rmf_site_editor/src/site/door.rs @@ -15,13 +15,11 @@ * */ -use crate::{interaction::Selectable, shapes::*, site::*, CurrentWorkspace}; +use crate::{interaction::Selectable, shapes::*, site::*}; use bevy::{ prelude::*, render::mesh::{Indices, PrimitiveTopology}, }; -use bevy_rapier3d::prelude::*; -use itertools::Itertools; use rmf_site_format::{Category, DoorType, Edge, DEFAULT_LEVEL_HEIGHT}; pub const DOOR_CUE_HEIGHT: f32 = 0.004; @@ -92,7 +90,6 @@ fn make_door_visuals( let center = (p_start + p_end) / 2.0; let (inner, outline) = make_door_cues(length, kind); - // First is pose, second is shape let door_tfs = match kind { DoorType::SingleSwing(_) | DoorType::SingleSliding(_) => vec![Transform { translation: Vec3::new(0., 0., DEFAULT_LEVEL_HEIGHT / 2.0), @@ -233,13 +230,7 @@ fn make_door_cues(door_width: f32, kind: &DoorType) -> (Mesh, Mesh) { pub fn add_door_visuals( mut commands: Commands, new_doors: Query< - ( - Entity, - &Edge, - &DoorType, - Option<&Visibility>, - &Parent, - ), + (Entity, &Edge, &DoorType, Option<&Visibility>), ( Or<(Added, Added>)>, Without, @@ -250,7 +241,7 @@ pub fn add_door_visuals( assets: Res, mut meshes: ResMut>, ) { - for (e, edge, kind, visibility, p) in &new_doors { + for (e, edge, kind, visibility) in &new_doors { let (pose_tf, door_tfs, cue_inner_mesh, cue_outline_mesh) = make_door_visuals(e, edge, &anchors, kind); @@ -344,7 +335,7 @@ fn update_door_visuals( *door_transform = *door_tf; } for door_tf in door_tfs.iter().skip(entities.len()) { - // We need to spawn an extra door + // New doors were added, we need to spawn them let id = commands .spawn(PbrBundle { mesh: assets.box_mesh.clone(), @@ -358,11 +349,10 @@ fn update_door_visuals( commands.entity(entity).add_child(id); } for e in entities.iter().skip(door_tfs.len()) { - // We need to despawn the extra door + // Doors were removed, we need to despawn them commands.entity(*e).despawn_recursive(); } segments.body = DoorBodyType::from_door_type(kind, &entities); - // If we moved from a single to double door let mut cue_inner = mesh_handles.get_mut(segments.cue_inner).unwrap(); *cue_inner = mesh_assets.add(cue_inner_mesh); let mut cue_outline = mesh_handles.get_mut(segments.cue_outline).unwrap(); From 9ac1f720eff07e8759f5eb9c6b0631cf176bb7a3 Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Tue, 20 Jun 2023 14:48:51 +0800 Subject: [PATCH 5/9] Add stub for door model to avoid todo panics Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/site/door.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/rmf_site_editor/src/site/door.rs b/rmf_site_editor/src/site/door.rs index cfd4fbbe..f33190db 100644 --- a/rmf_site_editor/src/site/door.rs +++ b/rmf_site_editor/src/site/door.rs @@ -34,6 +34,7 @@ pub enum DoorBodyType { DoubleSwing { left: Entity, right: Entity }, SingleSliding { body: Entity }, DoubleSliding { left: Entity, right: Entity }, + Model { body: Entity }, } impl DoorBodyType { @@ -49,13 +50,15 @@ impl DoorBodyType { left: entities[0], right: entities[1], }, - DoorType::Model(_) => todo!("Model doors not implemented yet"), + DoorType::Model(_) => DoorBodyType::Model { body: entities[0] }, } } pub fn entities(&self) -> Vec { match self { - DoorBodyType::SingleSwing { body } | DoorBodyType::SingleSliding { body } => { + DoorBodyType::SingleSwing { body } + | DoorBodyType::SingleSliding { body } + | DoorBodyType::Model { body } => { vec![*body] } DoorBodyType::DoubleSwing { left, right } @@ -91,11 +94,14 @@ fn make_door_visuals( let (inner, outline) = make_door_cues(length, kind); let door_tfs = match kind { - DoorType::SingleSwing(_) | DoorType::SingleSliding(_) => vec![Transform { - translation: Vec3::new(0., 0., DEFAULT_LEVEL_HEIGHT / 2.0), - scale: Vec3::new(DEFAULT_DOOR_THICKNESS, length, DEFAULT_LEVEL_HEIGHT), - ..default() - }], + // TODO(luca) implement model variant + DoorType::SingleSwing(_) | DoorType::SingleSliding(_) | DoorType::Model(_) => { + vec![Transform { + translation: Vec3::new(0., 0., DEFAULT_LEVEL_HEIGHT / 2.0), + scale: Vec3::new(DEFAULT_DOOR_THICKNESS, length, DEFAULT_LEVEL_HEIGHT), + ..default() + }] + } DoorType::DoubleSwing(_) | DoorType::DoubleSliding(_) => { // TODO(luca) implement left_to_right ratio for double doors let door_length = (length - DOUBLE_DOOR_GAP) / 2.0; @@ -120,7 +126,6 @@ fn make_door_visuals( }, ] } - DoorType::Model(_) => todo!("Model doors not implemented yet"), }; ( Transform { From 9c6ab748efa9c1fc107bf17d6d121730cae61f8b Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Tue, 20 Jun 2023 16:48:13 +0800 Subject: [PATCH 6/9] Add support for door left_right_ratio Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/site/door.rs | 83 ++++++++++++------- .../src/widgets/inspector/inspect_door.rs | 9 ++ rmf_site_format/src/door.rs | 14 ++++ rmf_site_format/src/legacy/door.rs | 1 + 4 files changed, 76 insertions(+), 31 deletions(-) diff --git a/rmf_site_editor/src/site/door.rs b/rmf_site_editor/src/site/door.rs index f33190db..30815de7 100644 --- a/rmf_site_editor/src/site/door.rs +++ b/rmf_site_editor/src/site/door.rs @@ -93,6 +93,40 @@ fn make_door_visuals( let center = (p_start + p_end) / 2.0; let (inner, outline) = make_door_cues(length, kind); + + let get_double_door_tfs = |mid_offset: f32| -> Vec { + let left_door_length = (length - DOUBLE_DOOR_GAP) / 2.0 - mid_offset; + let right_door_length = (length - DOUBLE_DOOR_GAP) / 2.0 + mid_offset; + vec![ + Transform { + translation: Vec3::new( + 0., + (length + DOUBLE_DOOR_GAP) / 4.0 + mid_offset / 2.0, + DEFAULT_LEVEL_HEIGHT / 2.0, + ), + scale: Vec3::new( + DEFAULT_DOOR_THICKNESS, + left_door_length, + DEFAULT_LEVEL_HEIGHT, + ), + ..default() + }, + Transform { + translation: Vec3::new( + 0., + -(length + DOUBLE_DOOR_GAP) / 4.0 + mid_offset / 2.0, + DEFAULT_LEVEL_HEIGHT / 2.0, + ), + scale: Vec3::new( + DEFAULT_DOOR_THICKNESS, + right_door_length, + DEFAULT_LEVEL_HEIGHT, + ), + ..default() + }, + ] + }; + let door_tfs = match kind { // TODO(luca) implement model variant DoorType::SingleSwing(_) | DoorType::SingleSliding(_) | DoorType::Model(_) => { @@ -102,30 +136,8 @@ fn make_door_visuals( ..default() }] } - DoorType::DoubleSwing(_) | DoorType::DoubleSliding(_) => { - // TODO(luca) implement left_to_right ratio for double doors - let door_length = (length - DOUBLE_DOOR_GAP) / 2.0; - vec![ - Transform { - translation: Vec3::new( - 0., - (length + DOUBLE_DOOR_GAP) / 4.0, - DEFAULT_LEVEL_HEIGHT / 2.0, - ), - scale: Vec3::new(DEFAULT_DOOR_THICKNESS, door_length, DEFAULT_LEVEL_HEIGHT), - ..default() - }, - Transform { - translation: Vec3::new( - 0., - -(length + DOUBLE_DOOR_GAP) / 4.0, - DEFAULT_LEVEL_HEIGHT / 2.0, - ), - scale: Vec3::new(DEFAULT_DOOR_THICKNESS, door_length, DEFAULT_LEVEL_HEIGHT), - ..default() - }, - ] - } + DoorType::DoubleSwing(door) => get_double_door_tfs(door.compute_offset(length)), + DoorType::DoubleSliding(door) => get_double_door_tfs(door.compute_offset(length)), }; ( Transform { @@ -165,10 +177,16 @@ fn door_slide_arrows(start: f32, stop: f32) -> MeshBuffer { door_slide_arrow(start, stop, -1.0).merge_with(door_slide_arrow(start, stop, 1.0)) } -fn door_swing_arc(door_width: f32, door_count: u32, pivot_on: Side, swing: Swing) -> MeshBuffer { +fn door_swing_arc( + door_width: f32, + door_count: u32, + offset: f32, + pivot_on: Side, + swing: Swing, +) -> MeshBuffer { let pivot = pivot_on.sign() * door_width / 2.0; let pivot = Vec3::new(0.0, pivot, DOOR_CUE_HEIGHT); - let door_width = door_width / door_count as f32; + let door_width = door_width / door_count as f32 + offset; let (initial_angle, sweep) = swing.swing_on_pivot(pivot_on); flat_arc( pivot, @@ -217,11 +235,14 @@ fn make_door_cues(door_width: f32, kind: &DoorType) -> (Mesh, Mesh) { .into_mesh_and_outline() } DoorType::SingleSwing(door) => { - door_swing_arc(door_width, 1, door.pivot_on, door.swing).into_mesh_and_outline() + door_swing_arc(door_width, 1, 0.0, door.pivot_on, door.swing).into_mesh_and_outline() + } + DoorType::DoubleSwing(door) => { + let mid = door.compute_offset(door_width); + door_swing_arc(door_width, 2, -mid, Side::Left, door.swing) + .merge_with(door_swing_arc(door_width, 2, mid, Side::Right, door.swing)) + .into_mesh_and_outline() } - DoorType::DoubleSwing(door) => door_swing_arc(door_width, 2, Side::Left, door.swing) - .merge_with(door_swing_arc(door_width, 2, Side::Right, door.swing)) - .into_mesh_and_outline(), _ => { let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, Vec::<[f32; 3]>::new()); @@ -345,7 +366,7 @@ fn update_door_visuals( .spawn(PbrBundle { mesh: assets.box_mesh.clone(), material: assets.door_body_material.clone(), - transform: *door_tfs.last().unwrap(), + transform: *door_tf, ..default() }) .insert(Selectable::new(entity)) diff --git a/rmf_site_editor/src/widgets/inspector/inspect_door.rs b/rmf_site_editor/src/widgets/inspector/inspect_door.rs index db56bcb2..07e85f11 100644 --- a/rmf_site_editor/src/widgets/inspector/inspect_door.rs +++ b/rmf_site_editor/src/widgets/inspector/inspect_door.rs @@ -77,6 +77,15 @@ impl<'a> InspectDoorType<'a> { } DoorType::DoubleSwing(door) => { InspectSwing::new(&mut door.swing).show(ui); + ui.horizontal(|ui| { + ui.label("Left : Right"); + ui.add( + DragValue::new(&mut door.left_right_ratio) + .speed(0.01) + .clamp_range(0.0..=std::f32::INFINITY), + ) + .on_hover_text("(Left Door Length)/(Right Door Length)"); + }); } DoorType::Model(_) => { ui.label("Not yet supported"); diff --git a/rmf_site_format/src/door.rs b/rmf_site_format/src/door.rs index e41b7dca..e28fc3e7 100644 --- a/rmf_site_format/src/door.rs +++ b/rmf_site_format/src/door.rs @@ -185,12 +185,26 @@ impl From for DoorType { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct DoubleSwingDoor { pub swing: Swing, + /// Length of the left door divided by the length of the right door + pub left_right_ratio: f32, +} + +impl DoubleSwingDoor { + /// Get the offset from the door center of the point where the doors + /// separate. A value of 0.0 means the doors are even. A negative value + /// means the left door is smaller while a positive value means the right + /// door is smaller. + pub fn compute_offset(&self, door_width: f32) -> f32 { + let l = self.left_right_ratio * door_width / (self.left_right_ratio + 1.0); + return door_width / 2.0 - l; + } } impl Default for DoubleSwingDoor { fn default() -> Self { Self { swing: Swing::Forward(Angle::Deg(90.0)), + left_right_ratio: 1.0, } } } diff --git a/rmf_site_format/src/legacy/door.rs b/rmf_site_format/src/legacy/door.rs index 9014b8e5..afe694b3 100644 --- a/rmf_site_format/src/legacy/door.rs +++ b/rmf_site_format/src/legacy/door.rs @@ -95,6 +95,7 @@ impl Door { .into(), DoorType::DoubleSwing | DoorType::DoubleHinged => DoubleSwingDoor { swing: self.to_swing()?, + left_right_ratio: 1. / self.2.right_left_ratio.1 as f32, } .into(), DoorType::Unknown => return Err(PortingError::InvalidType(self.2.type_.1.clone())), From 2d567db777b46640430b89aff7439fc5439723c6 Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Wed, 21 Jun 2023 11:06:40 +0800 Subject: [PATCH 7/9] Minor cleanup Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/site/door.rs | 25 +++++++++------ .../src/widgets/inspector/inspect_door.rs | 32 ++++++++----------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/rmf_site_editor/src/site/door.rs b/rmf_site_editor/src/site/door.rs index 30815de7..99fb5036 100644 --- a/rmf_site_editor/src/site/door.rs +++ b/rmf_site_editor/src/site/door.rs @@ -28,7 +28,7 @@ pub const DOOR_STOP_LINE_LENGTH: f32 = 3.0 * DEFAULT_DOOR_THICKNESS; pub const DOOR_SWEEP_THICKNESS: f32 = 0.05; pub const DOUBLE_DOOR_GAP: f32 = 0.05; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum DoorBodyType { SingleSwing { body: Entity }, DoubleSwing { left: Entity, right: Entity }, @@ -344,13 +344,13 @@ fn update_door_visuals( entity: Entity, edge: &Edge, kind: &DoorType, - segments: &mut DoorSegments, + segments: &DoorSegments, anchors: &AnchorParams, transforms: &mut Query<&mut Transform>, mesh_handles: &mut Query<&mut Handle>, mesh_assets: &mut ResMut>, assets: &Res, -) { +) -> Option { let (pose_tf, door_tfs, cue_inner_mesh, cue_outline_mesh) = make_door_visuals(entity, edge, anchors, kind); let mut door_transform = transforms.get_mut(entity).unwrap(); @@ -378,11 +378,16 @@ fn update_door_visuals( // Doors were removed, we need to despawn them commands.entity(*e).despawn_recursive(); } - segments.body = DoorBodyType::from_door_type(kind, &entities); let mut cue_inner = mesh_handles.get_mut(segments.cue_inner).unwrap(); *cue_inner = mesh_assets.add(cue_inner_mesh); let mut cue_outline = mesh_handles.get_mut(segments.cue_outline).unwrap(); *cue_outline = mesh_assets.add(cue_outline_mesh); + let new_segments = DoorBodyType::from_door_type(kind, &entities); + if new_segments != segments.body { + Some(new_segments) + } else { + None + } } pub fn update_changed_door( @@ -398,24 +403,26 @@ pub fn update_changed_door( assets: Res, ) { for (entity, edge, kind, mut segments) in &mut doors { - update_door_visuals( + if let Some(new_body) = update_door_visuals( &mut commands, entity, edge, kind, - &mut segments, + &segments, &anchors, &mut transforms, &mut mesh_handles, &mut mesh_assets, &assets, - ); + ) { + segments.body = new_body; + } } } pub fn update_door_for_moved_anchors( mut commands: Commands, - mut doors: Query<(Entity, &Edge, &DoorType, &mut DoorSegments)>, + mut doors: Query<(Entity, &Edge, &DoorType, &DoorSegments)>, anchors: AnchorParams, changed_anchors: Query< &Dependents, @@ -437,7 +444,7 @@ pub fn update_door_for_moved_anchors( entity, edge, kind, - &mut segments, + &segments, &anchors, &mut transforms, &mut mesh_handles, diff --git a/rmf_site_editor/src/widgets/inspector/inspect_door.rs b/rmf_site_editor/src/widgets/inspector/inspect_door.rs index 07e85f11..de754ad0 100644 --- a/rmf_site_editor/src/widgets/inspector/inspect_door.rs +++ b/rmf_site_editor/src/widgets/inspector/inspect_door.rs @@ -48,6 +48,18 @@ impl<'a> InspectDoorType<'a> { }); }); + fn left_right_ratio_ui(ui: &mut Ui, mut ratio: &mut f32) { + ui.horizontal(|ui| { + ui.label("Left : Right"); + ui.add( + DragValue::new(ratio) + .speed(0.01) + .clamp_range(0.01..=std::f32::INFINITY), + ) + .on_hover_text("(Left Door Length)/(Right Door Length)"); + }); + }; + match &mut new_kind { DoorType::SingleSliding(door) => { ui.horizontal(|ui| { @@ -57,15 +69,7 @@ impl<'a> InspectDoorType<'a> { }); } DoorType::DoubleSliding(door) => { - ui.horizontal(|ui| { - ui.label("Left : Right"); - ui.add( - DragValue::new(&mut door.left_right_ratio) - .speed(0.01) - .clamp_range(0.0..=std::f32::INFINITY), - ) - .on_hover_text("(Left Door Length)/(Right Door Length)"); - }); + left_right_ratio_ui(ui, &mut door.left_right_ratio); } DoorType::SingleSwing(door) => { ui.horizontal(|ui| { @@ -77,15 +81,7 @@ impl<'a> InspectDoorType<'a> { } DoorType::DoubleSwing(door) => { InspectSwing::new(&mut door.swing).show(ui); - ui.horizontal(|ui| { - ui.label("Left : Right"); - ui.add( - DragValue::new(&mut door.left_right_ratio) - .speed(0.01) - .clamp_range(0.0..=std::f32::INFINITY), - ) - .on_hover_text("(Left Door Length)/(Right Door Length)"); - }); + left_right_ratio_ui(ui, &mut door.left_right_ratio); } DoorType::Model(_) => { ui.label("Not yet supported"); From 5461c93e68ecf678a1871c9c525f83b06baf756e Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Fri, 23 Jun 2023 15:16:40 +0800 Subject: [PATCH 8/9] Optimize outline update for new meshes Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/interaction/mod.rs | 1 + rmf_site_editor/src/interaction/outline.rs | 24 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rmf_site_editor/src/interaction/mod.rs b/rmf_site_editor/src/interaction/mod.rs index ff1484dd..4288d042 100644 --- a/rmf_site_editor/src/interaction/mod.rs +++ b/rmf_site_editor/src/interaction/mod.rs @@ -161,6 +161,7 @@ impl Plugin for InteractionPlugin { .with_system(update_point_visual_cues.after(maintain_selected_entities)) .with_system(update_path_visual_cues.after(maintain_selected_entities)) .with_system(update_outline_visualization.after(maintain_selected_entities)) + .with_system(update_outline_for_new_meshes.after(maintain_selected_entities)) .with_system( update_cursor_hover_visualization.after(maintain_selected_entities), ) diff --git a/rmf_site_editor/src/interaction/outline.rs b/rmf_site_editor/src/interaction/outline.rs index 9a02dff4..6c53fb4a 100644 --- a/rmf_site_editor/src/interaction/outline.rs +++ b/rmf_site_editor/src/interaction/outline.rs @@ -134,7 +134,7 @@ pub fn update_outline_visualization( mut commands: Commands, outlinable: Query< (Entity, &Hovered, &Selected, &OutlineVisualization), - Or<(Changed, Changed, Changed)>, + Or<(Changed, Changed)>, >, descendants: Query<(Option<&Children>, Option<&ComputedVisualCue>)>, ) { @@ -185,3 +185,25 @@ pub fn update_outline_visualization( } } } + +pub fn update_outline_for_new_meshes( + mut commands: Commands, + new_meshes: Query>>, + outlines: Query<(&OutlineVolume, &SetOutlineDepth)>, + parents: Query<&Parent>, +) { + for e in &new_meshes { + for p in AncestorIter::new(&parents, e) { + if let Ok((outline, depth)) = outlines.get(p) { + commands + .entity(e) + .insert(OutlineBundle { + outline: outline.clone(), + ..default() + }) + .insert(depth.clone()); + break; + } + } + } +} From e2b35dd839abbe397cb2378b60e45325754e3b2a Mon Sep 17 00:00:00 2001 From: Luca Della Vedova Date: Thu, 31 Aug 2023 10:43:01 +0800 Subject: [PATCH 9/9] Change outline update to use set_changed() Signed-off-by: Luca Della Vedova --- rmf_site_editor/src/interaction/mod.rs | 1 - rmf_site_editor/src/interaction/outline.rs | 22 ---------------------- rmf_site_editor/src/site/door.rs | 22 +++++++++++++++++++--- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/rmf_site_editor/src/interaction/mod.rs b/rmf_site_editor/src/interaction/mod.rs index a8905922..0c702aa5 100644 --- a/rmf_site_editor/src/interaction/mod.rs +++ b/rmf_site_editor/src/interaction/mod.rs @@ -197,7 +197,6 @@ impl Plugin for InteractionPlugin { .with_system(update_point_visual_cues.after(maintain_selected_entities)) .with_system(update_path_visual_cues.after(maintain_selected_entities)) .with_system(update_outline_visualization.after(maintain_selected_entities)) - .with_system(update_outline_for_new_meshes.after(maintain_selected_entities)) .with_system(update_highlight_visualization.after(maintain_selected_entities)) .with_system( update_cursor_hover_visualization.after(maintain_selected_entities), diff --git a/rmf_site_editor/src/interaction/outline.rs b/rmf_site_editor/src/interaction/outline.rs index 88cd79db..0f6df80a 100644 --- a/rmf_site_editor/src/interaction/outline.rs +++ b/rmf_site_editor/src/interaction/outline.rs @@ -209,25 +209,3 @@ pub fn update_outline_visualization( } } } - -pub fn update_outline_for_new_meshes( - mut commands: Commands, - new_meshes: Query>>, - outlines: Query<(&OutlineVolume, &SetOutlineDepth)>, - parents: Query<&Parent>, -) { - for e in &new_meshes { - for p in AncestorIter::new(&parents, e) { - if let Ok((outline, depth)) = outlines.get(p) { - commands - .entity(e) - .insert(OutlineBundle { - outline: outline.clone(), - ..default() - }) - .insert(depth.clone()); - break; - } - } - } -} diff --git a/rmf_site_editor/src/site/door.rs b/rmf_site_editor/src/site/door.rs index 99fb5036..1135bb5f 100644 --- a/rmf_site_editor/src/site/door.rs +++ b/rmf_site_editor/src/site/door.rs @@ -15,7 +15,11 @@ * */ -use crate::{interaction::Selectable, shapes::*, site::*}; +use crate::{ + interaction::{Hovered, Selectable}, + shapes::*, + site::*, +}; use bevy::{ prelude::*, render::mesh::{Indices, PrimitiveTopology}, @@ -393,7 +397,13 @@ fn update_door_visuals( pub fn update_changed_door( mut commands: Commands, mut doors: Query< - (Entity, &Edge, &DoorType, &mut DoorSegments), + ( + Entity, + &Edge, + &DoorType, + &mut DoorSegments, + &mut Hovered, + ), Or<(Changed>, Changed)>, >, anchors: AnchorParams, @@ -402,7 +412,8 @@ pub fn update_changed_door( mut mesh_assets: ResMut>, assets: Res, ) { - for (entity, edge, kind, mut segments) in &mut doors { + for (entity, edge, kind, mut segments, mut hovered) in &mut doors { + let old_door_count = segments.body.entities().len(); if let Some(new_body) = update_door_visuals( &mut commands, entity, @@ -416,6 +427,11 @@ pub fn update_changed_door( &assets, ) { segments.body = new_body; + if segments.body.entities().len() > old_door_count { + // A new door was spawned, trigger hovered change detection to update the outline + // for the new mesh + hovered.set_changed(); + } } } }