Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split out workcell editor #239

Draft
wants to merge 26 commits into
base: luca/model_workflows
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
56c548c
Cleanup pre-workcell editor migration
luca-della-vedova Sep 5, 2024
d3ff2c6
Move pose implementation to right module
luca-della-vedova Sep 5, 2024
260d263
WIP removed bulk of workcell editor
luca-della-vedova Sep 6, 2024
f67733f
Further cleanup of UI / minor migrations
luca-della-vedova Sep 9, 2024
721696e
Change state settings for UserCameraDisplay
luca-della-vedova Sep 9, 2024
244df49
Cleanup
luca-della-vedova Sep 9, 2024
2884774
Make fields public to allow custom selectors
luca-della-vedova Sep 9, 2024
67b07e6
WIP removing further appearances of AppState
luca-della-vedova Sep 9, 2024
b6d2645
Minor refactor and cleanup
luca-della-vedova Sep 9, 2024
1bb998c
Try to make a configurable fuel asset browser
luca-della-vedova Sep 9, 2024
9251bb1
Remove workcell editor appstate
luca-della-vedova Sep 10, 2024
605fbe9
Cleanup workspace saving and transition to workflows
luca-della-vedova Sep 10, 2024
007f524
Make file buttons fully modular
luca-della-vedova Sep 10, 2024
12f9371
Minor cleanup
luca-della-vedova Sep 10, 2024
4d44e3a
Fix wasm condition
luca-della-vedova Sep 10, 2024
873ff9d
Minor initialization changes to make interaction plugin standalone,
luca-della-vedova Sep 11, 2024
11357ca
Add filters for file saving / loading
luca-della-vedova Sep 11, 2024
6039f32
Reexport bevy plugins that might be needed downstream
luca-della-vedova Sep 11, 2024
cbfeebf
Fix compile without bevy feature
luca-della-vedova Sep 11, 2024
d9b0ffe
Add missing event
luca-della-vedova Sep 12, 2024
1bc6bcf
Use git version of dependency
luca-della-vedova Sep 12, 2024
29f290a
Remove dependency on rmf_workcell_format
luca-della-vedova Sep 12, 2024
ef513f9
Make urdf-rs dependency optional for rmf_site_format
luca-della-vedova Sep 12, 2024
3496425
Minor cleanups
luca-della-vedova Sep 12, 2024
d7be917
Minor fixes
luca-della-vedova Sep 12, 2024
df86e3f
Merge branch 'luca/model_workflows' into luca/split_workcell_editor_2
luca-della-vedova Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
451 changes: 31 additions & 420 deletions Cargo.lock

Large diffs are not rendered by default.

83 changes: 0 additions & 83 deletions assets/demo_workcells/demo.workcell.json

This file was deleted.

3 changes: 0 additions & 3 deletions rmf_site_editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ path = "examples/extending_menu.rs"
bevy_egui = "0.23"
bevy_mod_raycast = "0.16"
bevy_mod_outline = "0.6"
# PR merged after 0.10 but not released yet, bump to 0.10.1 once merged
bevy_infinite_grid = { git = "https://github.com/ForesightMiningSoftwareCorporation/bevy_infinite_grid", rev = "86018dd" }
bevy_gltf_export = { git = "https://github.com/luca-della-vedova/bevy_gltf_export", branch = "luca/transform_api"}
bevy_polyline = "0.8.1"
bevy_stl = "0.12"
Expand Down Expand Up @@ -49,7 +47,6 @@ utm = "0.1.6"
sdformat_rs = { git = "https://github.com/open-rmf/sdf_rust_experimental", rev = "9fc35f2"}
gz-fuel = { git = "https://github.com/open-rmf/gz-fuel-rs", branch = "main" }
pathdiff = "*"
tera = "1.19.1"
ehttp = { version = "0.4", features = ["native-async"] }
nalgebra = "0.32.5"
anyhow = "*"
Expand Down
12 changes: 4 additions & 8 deletions rmf_site_editor/examples/extending_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ impl FromWorld for MyMenuHandler {
// This is all it takes to register a new menu item
// We need to keep track of the entity in order to make
// sure that we can check the callback
let unique_export = world
.spawn(MenuItem::Text("My unique export".to_string()))
.id();
let unique_export = world.spawn(MenuItem::Text("My unique export".into())).id();

// Make it a child of the "File Menu"
let file_header = world.resource::<FileMenu>().get();
Expand All @@ -37,9 +35,7 @@ impl FromWorld for MyMenuHandler {
world.entity_mut(menu).push_children(&[sub_menu]);

// Finally we can create a custom action
let custom_nested_menu = world
.spawn(MenuItem::Text("My Awesome Action".to_string()))
.id();
let custom_nested_menu = world.spawn(MenuItem::Text("My Awesome Action".into())).id();
world
.entity_mut(sub_menu)
.push_children(&[custom_nested_menu]);
Expand All @@ -56,7 +52,7 @@ impl FromWorld for MyMenuHandler {
/// an event that is of the same type as the one we are supposed to
/// handle.
fn watch_unique_export_click(mut reader: EventReader<MenuEvent>, menu_handle: Res<MyMenuHandler>) {
for event in reader.iter() {
for event in reader.read() {
if event.clicked() && event.source() == menu_handle.unique_export {
println!("Doing our epic export");
}
Expand All @@ -67,7 +63,7 @@ fn watch_unique_export_click(mut reader: EventReader<MenuEvent>, menu_handle: Re
/// an event that is of the same type as the one we are supposed to
/// handle.
fn watch_submenu_click(mut reader: EventReader<MenuEvent>, menu_handle: Res<MyMenuHandler>) {
for event in reader.iter() {
for event in reader.read() {
if event.clicked() && event.source() == menu_handle.custom_nested_menu {
println!("Submenu clicked");
}
Expand Down
6 changes: 0 additions & 6 deletions rmf_site_editor/src/demo_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,3 @@ pub fn demo_office() -> Vec<u8> {
.as_bytes()
.to_vec();
}

pub fn demo_workcell() -> Vec<u8> {
return include_str!("../../assets/demo_workcells/demo.workcell.json")
.as_bytes()
.to_vec();
}
19 changes: 12 additions & 7 deletions rmf_site_editor/src/interaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
*/

use crate::site::{
update_anchor_transforms, CollisionMeshMarker, ConstraintMarker, DoorMarker, FiducialMarker,
FloorMarker, LaneMarker, LiftCabin, LiftCabinDoorMarker, LocationTags, MeasurementMarker,
SiteUpdateSet, VisualMeshMarker, WallMarker,
update_anchor_transforms, CollisionMeshMarker, CurrentEditDrawing, CurrentLevel, DoorMarker,
FiducialMarker, FloorMarker, LaneMarker, LiftCabin, LiftCabinDoorMarker, LocationTags,
MeasurementMarker, SiteUpdateSet, ToggleLiftDoorAvailability, VisualMeshMarker, WallMarker,
};
use crate::workcell::WorkcellVisualizationMarker;
use crate::widgets::UserCameraDisplayPlugin;

pub mod anchor;
pub use anchor::*;
Expand Down Expand Up @@ -151,11 +151,14 @@ impl Plugin for InteractionPlugin {
.init_resource::<Picked>()
.init_resource::<PickingBlockers>()
.init_resource::<GizmoState>()
.init_resource::<CurrentEditDrawing>()
.init_resource::<CurrentLevel>()
.insert_resource(HighlightAnchors(false))
.add_event::<ChangePick>()
.add_event::<MoveTo>()
.add_event::<GizmoClicked>()
.add_event::<SpawnPreview>()
.add_event::<ToggleLiftDoorAvailability>()
Comment on lines +154 to +161
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This resource initialization has been moved here from other plugins since the InteractionPlugin will panic if used in a third party app without them.
An alternative solution could be to split the interaction plugin into several sub-plugins and allow users to only include the ones they need, but that would have been a much larger refactor so I left it out for now.

.add_plugins((
OutlinePlugin,
CategoryVisibilityPlugin::<DoorMarker>::visible(true),
Expand All @@ -165,14 +168,16 @@ impl Plugin for InteractionPlugin {
CategoryVisibilityPlugin::<LiftCabinDoorMarker>::visible(true),
CategoryVisibilityPlugin::<LocationTags>::visible(true),
CategoryVisibilityPlugin::<FiducialMarker>::visible(true),
CategoryVisibilityPlugin::<ConstraintMarker>::visible(true),
CategoryVisibilityPlugin::<VisualMeshMarker>::visible(true),
CategoryVisibilityPlugin::<CollisionMeshMarker>::visible(false),
CategoryVisibilityPlugin::<MeasurementMarker>::visible(true),
CategoryVisibilityPlugin::<WallMarker>::visible(true),
CategoryVisibilityPlugin::<WorkcellVisualizationMarker>::visible(true),
))
.add_plugins((CameraControlsPlugin, ModelPreviewPlugin));
.add_plugins((
CameraControlsPlugin,
ModelPreviewPlugin,
UserCameraDisplayPlugin::default(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to resource initialization, the interaction plugin panics if this plugin is not present, so I added it here rather than keeping it in the widgets to decouple widget plugin and interaction plugin.

));

if !self.headless {
app.add_plugins(SelectionPlugin::default())
Expand Down
5 changes: 2 additions & 3 deletions rmf_site_editor/src/interaction/outline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use crate::{interaction::*, site::DrawingMarker};
use bevy::render::view::RenderLayers;
use bevy_mod_outline::{OutlineBundle, OutlineMode, OutlineRenderLayers, OutlineVolume};
use rmf_site_format::{
ConstraintMarker, DoorType, FiducialMarker, FloorMarker, LiftCabin, LightKind, LocationTags,
MeasurementMarker, ModelMarker, PhysicalCameraProperties, PrimitiveShape, WallMarker,
DoorType, FiducialMarker, FloorMarker, LiftCabin, LightKind, LocationTags, MeasurementMarker,
ModelMarker, PhysicalCameraProperties, PrimitiveShape, WallMarker,
};
use smallvec::SmallVec;

Expand Down Expand Up @@ -110,7 +110,6 @@ pub fn add_outline_visualization(
Added<DoorType>,
Added<LiftCabin<Entity>>,
Added<MeasurementMarker>,
Added<ConstraintMarker>,
Added<FiducialMarker>,
Added<FloorMarker>,
Added<DrawingMarker>,
Expand Down
12 changes: 3 additions & 9 deletions rmf_site_editor/src/interaction/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ pub use place_object::*;
pub mod place_object_2d;
pub use place_object_2d::*;

pub mod place_object_3d;
pub use place_object_3d::*;

pub mod replace_parent_3d;
use replace_parent_3d::*;

pub mod replace_point;
use replace_point::*;

Expand Down Expand Up @@ -239,15 +233,15 @@ fn process_new_selector(
pub struct RunSelector {
/// The select workflow will run this service until it terminates and then
/// revert back to the inspector selector.
selector: Service<Option<Entity>, ()>,
pub selector: Service<Option<Entity>, ()>,
/// If there is input for the selector, it will be stored in a [`SelectorInput`]
/// component in this entity. The entity will be despawned as soon as the
/// input is extracted.
input: Option<Entity>,
pub input: Option<Entity>,
}

#[derive(Component)]
pub struct SelectorInput<T>(T);
pub struct SelectorInput<T>(pub T);

/// This component is put on entities with meshes to mark them as items that can
/// be interacted with to
Expand Down
83 changes: 34 additions & 49 deletions rmf_site_editor/src/interaction/select/place_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
*
*/

use crate::{interaction::select::*, site::Model};
use bevy::ecs::system::SystemParam;
use crate::{
interaction::select::*,
site::{CurrentLevel, Model},
};
use bevy::ecs::system::{Command, SystemParam, SystemState};

#[derive(Default)]
pub struct ObjectPlacementPlugin {}
Expand All @@ -31,38 +34,28 @@ impl Plugin for ObjectPlacementPlugin {
#[derive(Resource, Clone, Copy)]
pub struct ObjectPlacementServices {
pub place_object_2d: Service<Option<Entity>, ()>,
pub place_object_3d: Service<Option<Entity>, ()>,
pub replace_parent_3d: Service<Option<Entity>, ()>,
pub hover_service_object_3d: Service<(), (), Hover>,
}

impl ObjectPlacementServices {
pub fn from_app(app: &mut App) -> Self {
let hover_service_object_3d = app.spawn_continuous_service(
Update,
hover_service::<PlaceObject3dFilter>
.configure(|config: SystemConfigs| config.in_set(SelectionServiceStages::Hover)),
);
let place_object_2d = spawn_place_object_2d_workflow(app);
let place_object_3d = spawn_place_object_3d_workflow(hover_service_object_3d, app);
let replace_parent_3d = spawn_replace_parent_3d_workflow(hover_service_object_3d, app);
Self {
place_object_2d,
place_object_3d,
replace_parent_3d,
hover_service_object_3d,
}
Self { place_object_2d }
}
}

#[derive(SystemParam)]
pub struct ObjectPlacement<'w, 's> {
pub services: Res<'w, ObjectPlacementServices>,
pub commands: Commands<'w, 's>,
current_level: Res<'w, CurrentLevel>,
}

impl<'w, 's> ObjectPlacement<'w, 's> {
pub fn place_object_2d(&mut self, object: Model, level: Entity) {
pub fn place_object_2d(&mut self, object: Model) {
let Some(level) = self.current_level.0 else {
warn!("Unble to create [object:?] outside a level");
return;
};
Comment on lines -65 to +58
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of my effort to reduce code duplication. I refactored both 2d and 3d object placement to take the same input (just a Model).
Specifically for the fuel widget, it was really the same between this app and the workcell editor, with the only different that the spawning function was different. I introduced a field in the plugin that allows users to pass a spawning function that takes as an input Commands and a Model, and will be called when the Spawn model button is clicked. This allowed reusing the code between the two applications but comes with the drawback that this minor API change is necessary.

pub struct FuelAssetBrowserPlugin {
pub model_spawner: fn(&mut Commands, Model),
}
impl Default for FuelAssetBrowserPlugin {
fn default() -> Self {
Self {
model_spawner: |commands: &mut Commands, model: Model| {
commands.place_object_2d(model);
},
}
}
}

let state = self
.commands
.spawn(SelectorInput(PlaceObject2d { object, level }))
Expand All @@ -73,40 +66,32 @@ impl<'w, 's> ObjectPlacement<'w, 's> {
});
}

pub fn place_object_3d(
&mut self,
object: PlaceableObject,
parent: Option<Entity>,
workspace: Entity,
) {
let state = self
.commands
.spawn(SelectorInput(PlaceObject3d {
object,
parent,
workspace,
}))
.id();
self.send(RunSelector {
selector: self.services.place_object_3d,
input: Some(state),
fn send(&mut self, run: RunSelector) {
self.commands.add(move |world: &mut World| {
world.send_event(run);
});
}
}

pub fn replace_parent_3d(&mut self, object: Entity, workspace: Entity) {
let state = self
.commands
.spawn(SelectorInput(ReplaceParent3d { object, workspace }))
.id();
self.send(RunSelector {
selector: self.services.replace_parent_3d,
input: Some(state),
});
/// Trait to be implemented to allow placing models with commands
pub trait ObjectPlacementExt<'w, 's> {
fn place_object_2d(&mut self, object: Model);
}

impl<'w, 's> ObjectPlacementExt<'w, 's> for Commands<'w, 's> {
fn place_object_2d(&mut self, object: Model) {
self.add(ObjectPlaceCommand(object));
}
}

fn send(&mut self, run: RunSelector) {
self.commands.add(move |world: &mut World| {
world.send_event(run);
});
#[derive(Deref, DerefMut)]
pub struct ObjectPlaceCommand(Model);

impl Command for ObjectPlaceCommand {
fn apply(self, world: &mut World) {
let mut system_state: SystemState<ObjectPlacement> = SystemState::new(world);
let mut placement = system_state.get_mut(world);
placement.place_object_2d(self.0);
system_state.apply(world);
}
}
Loading
Loading