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

Proof of concept for headless SDF exporting #215

Merged
merged 92 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
a0f1a9d
WIP, proof of concept of SDF export
luca-della-vedova Dec 5, 2023
1ce7c4e
Continue iterating, walls and floors are now exported
luca-della-vedova Dec 6, 2023
f0006a8
Point floors / walls to generated model
luca-della-vedova Dec 11, 2023
2796b55
Split collision and visual meshes, add sun
luca-della-vedova Dec 13, 2023
3933523
Cleanup
luca-della-vedova Dec 13, 2023
955f2ec
WIP adding doors
luca-della-vedova Dec 15, 2023
edae72a
Tuning to door exports
luca-della-vedova Dec 15, 2023
7587db5
Fix door transform
luca-della-vedova Dec 21, 2023
946a2ab
Finish edge cases for door plugins
luca-della-vedova Dec 22, 2023
e9e7e45
Add lift meshes
luca-della-vedova Dec 22, 2023
dfaf379
WIP adding lifts
luca-della-vedova Jan 9, 2024
8730428
WIP lift doors not working yet
luca-della-vedova Jan 24, 2024
2ecb418
WIP, lift door meshes generated, parenthood TODO
luca-della-vedova Jan 24, 2024
5258342
WIP, lift doors working
luca-della-vedova Jan 25, 2024
fe4d675
Lifts fully working
luca-della-vedova Jan 26, 2024
7314c76
Format
luca-della-vedova Jan 26, 2024
4d0c4c5
Add robot spawning
luca-della-vedova Jan 30, 2024
614a9a3
Add orientation constraint, doors and lifts
luca-della-vedova Jan 30, 2024
2873565
Add TODOs for documentation
luca-della-vedova Jan 31, 2024
70e4ad4
Merge branch 'main' into luca/export_sdf
luca-della-vedova Jan 31, 2024
8d893f4
Merge branch 'luca/export_navgraphs' into luca/export_sdf
luca-della-vedova Jan 31, 2024
d76f218
Fix lanes being skipped when they share an anchor
luca-della-vedova Jan 31, 2024
3e94974
Fix duplicated unnamed waypoints when exporting
luca-della-vedova Jan 31, 2024
6a04acf
Merge branch 'luca/export_navgraphs' into luca/export_sdf
luca-della-vedova Jan 31, 2024
0009908
Fix compilation without bevy
luca-della-vedova Jan 31, 2024
b4d0644
Add anchors in lifts, fix lift center rotation
luca-della-vedova Feb 1, 2024
3d59740
Merge branch 'luca/export_navgraphs' into luca/export_sdf
luca-della-vedova Feb 1, 2024
1d6537f
Add joint limit for lift cabins
luca-della-vedova Feb 2, 2024
ddea381
Export graphs together with SDF
luca-della-vedova Feb 5, 2024
6b30bee
First draft of headless support, app can run
luca-della-vedova Feb 5, 2024
e57f962
Restore features
luca-della-vedova Feb 5, 2024
8c2ff61
Fix running when no X server is available
luca-della-vedova Feb 6, 2024
3b6b334
Minor cleanup
luca-della-vedova Feb 6, 2024
a5b40d6
Remove unneeded changes
luca-della-vedova Feb 6, 2024
9c51079
Fix tests
luca-della-vedova Feb 6, 2024
16bb3b9
Fix web
luca-della-vedova Feb 6, 2024
6d3a4b6
Style
luca-della-vedova Feb 6, 2024
1065ae5
Skeleton of GUI plugins
luca-della-vedova Feb 29, 2024
80062f4
Minor cleanup
luca-della-vedova Feb 29, 2024
26f9af3
Add template, fix preliminary implementation of toggle floors
luca-della-vedova Mar 1, 2024
c9dff66
Navgraph fixes for legacy compatibility
luca-della-vedova Mar 1, 2024
d815e22
Merge remote-tracking branch 'origin/luca/headless_support' into luca…
luca-della-vedova Mar 1, 2024
739c417
POC of headless mode for SDF export
luca-della-vedova Mar 1, 2024
a16a41f
Make sure reference level is not translated
luca-della-vedova Mar 4, 2024
f7ca197
Add saved camera poses field and computation
luca-della-vedova Mar 7, 2024
6441a14
Merge branch 'luca/export_sdf' into luca/headless_sdf_export_poc
luca-della-vedova Mar 7, 2024
dd4c3e5
Abort if site loading failed
luca-della-vedova Mar 7, 2024
8890a52
Update camera pose
luca-della-vedova Mar 7, 2024
95c49d8
Merge branch 'luca/export_sdf' into luca/headless_sdf_export_poc
luca-della-vedova Mar 7, 2024
21c1426
Additional wait before saving
luca-della-vedova Mar 7, 2024
28d28d1
Fix camera pose, add dispensers
luca-della-vedova Mar 19, 2024
31a66e6
Add non static meshes export
luca-della-vedova Mar 19, 2024
ea6c8b2
Merge branch 'luca/export_sdf' into luca/headless_sdf_export_poc
luca-della-vedova Mar 19, 2024
e40c3b5
Merge branch 'main' into luca/export_sdf
luca-della-vedova Mar 19, 2024
6b657d2
Merge branch 'main' into luca/export_sdf
luca-della-vedova Mar 19, 2024
c65e1e0
Clear simple warnings
luca-della-vedova Mar 19, 2024
af648f1
Propagate errors when exporting SDF meshes
luca-della-vedova Mar 20, 2024
87ddcce
Add remaining models to level transparency plugin
luca-della-vedova Mar 20, 2024
96792a4
Merge branch 'luca/export_sdf' into luca/headless_sdf_export_poc
luca-della-vedova Mar 20, 2024
22ccab3
Fix embedded asset path panic
luca-della-vedova Mar 21, 2024
62e65ef
Merge branch 'luca/embedded_asset_workaround' into luca/headless_sdf_…
luca-della-vedova Mar 21, 2024
80d6906
Move to for_each_mut API
luca-della-vedova Mar 21, 2024
fd12d20
Merge branch 'luca/export_sdf' into luca/headless_sdf_export_poc
luca-della-vedova Mar 21, 2024
8d97dac
Update sdformat_rs dependency
luca-della-vedova Mar 21, 2024
f2933a4
Minor refactor for shared code
luca-della-vedova Mar 21, 2024
264b364
Further cleanup
luca-della-vedova Mar 21, 2024
012382e
Cleanup pose sdf conversion
luca-della-vedova Mar 21, 2024
4ffe3b9
Merge branch 'luca/export_sdf' into luca/headless_sdf_export_poc
luca-della-vedova Mar 21, 2024
1cef31f
Run at lower frequency to save CPU
luca-della-vedova Mar 21, 2024
63fe98d
WIP remove floor transparency
luca-della-vedova Apr 2, 2024
16f5e46
Merge branch 'luca/export_sdf' into luca/headless_sdf_export_poc
luca-della-vedova Apr 2, 2024
4f3f40b
Revert "WIP remove floor transparency"
luca-della-vedova Apr 2, 2024
16f0fff
Minor API cleanup
luca-della-vedova Apr 2, 2024
0cd5d19
Cleanup and refactor
luca-della-vedova Apr 2, 2024
1c1380e
Add simple json saving / loading
luca-della-vedova Apr 3, 2024
640fe5c
Fix level alignment when levels have no measurements
luca-della-vedova Apr 5, 2024
f5cd473
Merge remote-tracking branch 'origin/main' into luca/headless_sdf_exp…
luca-della-vedova Apr 5, 2024
216370c
Merge remote-tracking branch 'origin/luca/fix_nan_alignment' into luc…
luca-della-vedova Apr 5, 2024
59d168f
Fix migrate relative paths computation
luca-della-vedova Apr 5, 2024
60737cb
Merge remote-tracking branch 'origin/luca/migrate_path_fix' into luca…
luca-della-vedova Apr 5, 2024
6618a27
Merge branch 'main' into luca/headless_sdf_export_poc
mxgrey Apr 9, 2024
665811c
Fix test
luca-della-vedova May 23, 2024
11bcfb0
Address simpler feedback
luca-della-vedova May 31, 2024
6426ece
Only set camera pose on changed site
luca-della-vedova May 31, 2024
abb7544
Make each pose a UserCameraPose Bundle
luca-della-vedova May 31, 2024
65d9f8a
Make sure non static models use generated meshes
luca-della-vedova May 31, 2024
bc50892
Make SDF export more consistent with old building tools
mxgrey Jun 5, 2024
0e5e43b
Style
luca-della-vedova Jun 12, 2024
4f78ab3
Remove unusued import
luca-della-vedova Jun 14, 2024
bb43fcb
Use site ID to ensure unique file names (#219)
mxgrey Jun 14, 2024
93a14e2
Merge branch 'main' into luca/headless_sdf_export_poc
luca-della-vedova Jun 14, 2024
9a50685
Merge branch 'main' into luca/headless_sdf_export_poc
mxgrey Jun 19, 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
410 changes: 246 additions & 164 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion rmf_site_editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ 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"
bevy_obj = { version = "0.12.1", features = ["scene"] }
Expand All @@ -43,8 +44,9 @@ tracing = "0.1.37"
tracing-subscriber = "0.3.1"
rfd = "0.12"
urdf-rs = "0.7"
yaserde = "0.7"
utm = "0.1.6"
sdformat_rs = { git = "https://github.com/open-rmf/sdf_rust_experimental", rev = "a5daef0"}
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 = "luca/ehttp" }
pathdiff = "*"
tera = "1.19.1"
Expand Down
4 changes: 3 additions & 1 deletion rmf_site_editor/examples/extending_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,7 @@ impl Plugin for MyMenuPlugin {

/// Lets embed site editor in our application with our own plugin
fn main() {
App::new().add_plugins((SiteEditor, MyMenuPlugin)).run();
App::new()
.add_plugins((SiteEditor::default(), MyMenuPlugin))
.run();
}
11 changes: 8 additions & 3 deletions rmf_site_editor/src/interaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ use bevy_polyline::PolylinePlugin;
pub struct SiteRaycastSet;

#[derive(Default)]
pub struct InteractionPlugin;
pub struct InteractionPlugin {
pub headless: bool,
}

#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, States)]
pub enum InteractionState {
Expand Down Expand Up @@ -171,8 +173,10 @@ impl Plugin for InteractionPlugin {
CategoryVisibilityPlugin::<WallMarker>::visible(true),
CategoryVisibilityPlugin::<WorkcellVisualizationMarker>::visible(true),
))
.add_plugins((CameraControlsPlugin, ModelPreviewPlugin))
.add_systems(
.add_plugins((CameraControlsPlugin, ModelPreviewPlugin));

if !self.headless {
app.add_systems(
Update,
(
make_lift_doormat_gizmo,
Expand Down Expand Up @@ -255,6 +259,7 @@ impl Plugin for InteractionPlugin {
.run_if(in_state(InteractionState::Enable)),
)
.add_systems(First, (update_picked, update_interaction_mode));
}
}
}

Expand Down
11 changes: 9 additions & 2 deletions rmf_site_editor/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
site::{AlignSiteDrawings, Delete},
CreateNewWorkspace, CurrentWorkspace, LoadWorkspace, SaveWorkspace,
};
use bevy::prelude::*;
use bevy::{prelude::*, window::PrimaryWindow};
use bevy_egui::EguiContexts;

#[derive(Debug, Clone, Copy, Resource)]
Expand Down Expand Up @@ -55,8 +55,15 @@ fn handle_keyboard_input(
mut debug_mode: ResMut<DebugMode>,
mut align_site: EventWriter<AlignSiteDrawings>,
current_workspace: Res<CurrentWorkspace>,
primary_windows: Query<Entity, With<PrimaryWindow>>,
) {
let egui_context = egui_context.ctx_mut();
let Some(egui_context) = primary_windows
.get_single()
.ok()
.and_then(|w| egui_context.try_ctx_for_window_mut(w))
else {
return;
};
let ui_has_focus = egui_context.wants_pointer_input()
|| egui_context.wants_keyboard_input()
|| egui_context.is_pointer_over_area();
Expand Down
89 changes: 68 additions & 21 deletions rmf_site_editor/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bevy::{log::LogPlugin, pbr::DirectionalLightShadowMap, prelude::*};
use bevy::{app::ScheduleRunnerPlugin, log::LogPlugin, pbr::DirectionalLightShadowMap, prelude::*};
use bevy_egui::EguiPlugin;
use main_menu::MainMenuPlugin;
// use warehouse_generator::WarehouseGeneratorPlugin;
Expand Down Expand Up @@ -56,7 +56,7 @@ use wireframe::*;
use aabb::AabbUpdatePlugin;
use animate::AnimationPlugin;
use interaction::InteractionPlugin;
use site::{OSMViewPlugin, SitePlugin};
use site::{OSMViewPlugin, SiteFileMenuPlugin, SitePlugin};
use site_asset_io::SiteAssetIoPlugin;

pub mod osm_slippy_map;
Expand All @@ -75,6 +75,9 @@ pub struct CommandLineArgs {
/// Name of a Site (.site.ron) file to import on top of the base FILENAME.
#[cfg_attr(not(target_arch = "wasm32"), arg(short, long))]
pub import: Option<String>,
/// Run in headless mode and export the loaded site to the requested path.
#[cfg_attr(not(target_arch = "wasm32"), arg(long))]
pub headless_export: Option<String>,
}

#[derive(Clone, Default, Eq, PartialEq, Debug, Hash, States)]
Expand Down Expand Up @@ -117,6 +120,7 @@ pub fn run_js() {

pub fn run(command_line_args: Vec<String>) {
let mut app = App::new();
let mut headless_export = None;

#[cfg(not(target_arch = "wasm32"))]
{
Expand All @@ -127,34 +131,59 @@ pub fn run(command_line_args: Vec<String>) {
command_line_args.import.map(Into::into),
));
}
headless_export = command_line_args.headless_export;
}

app.add_plugins(SiteEditor);
app.add_plugins(SiteEditor { headless_export });
app.run();
}

pub struct SiteEditor;
#[derive(Default)]
pub struct SiteEditor {
/// Contains Some(path) if the site editor is running in headless mode exporting its site.
pub headless_export: Option<String>,
}

impl Plugin for SiteEditor {
fn build(&self, app: &mut App) {
let mut plugins = DefaultPlugins.build();
let headless = {
#[cfg(not(target_arch = "wasm32"))]
{
self.headless_export.is_some()
}
#[cfg(target_arch = "wasm32")]
{
false
}
};
plugins = if headless {
plugins
.set(WindowPlugin {
primary_window: None,
exit_condition: bevy::window::ExitCondition::DontExit,
close_when_requested: false,
})
.disable::<bevy::winit::WinitPlugin>()
} else {
plugins.set(WindowPlugin {
primary_window: Some(Window {
title: "RMF Site Editor".to_owned(),
#[cfg(not(target_arch = "wasm32"))]
resolution: (1600., 900.).into(),
#[cfg(target_arch = "wasm32")]
canvas: Some(String::from("#rmf_site_editor_canvas")),
#[cfg(target_arch = "wasm32")]
fit_canvas_to_parent: true,
..default()
}),
..default()
})
};
app.add_plugins((
SiteAssetIoPlugin,
DefaultPlugins
.build()
plugins
.disable::<LogPlugin>()
.set(WindowPlugin {
primary_window: Some(Window {
title: "RMF Site Editor".to_owned(),
#[cfg(not(target_arch = "wasm32"))]
resolution: (1600., 900.).into(),
#[cfg(target_arch = "wasm32")]
canvas: Some(String::from("#rmf_site_editor_canvas")),
#[cfg(target_arch = "wasm32")]
fit_canvas_to_parent: true,
..default()
}),
..default()
})
.set(ImagePlugin {
default_sampler: SamplerDescriptor {
address_mode_u: AddressMode::Repeat,
Expand All @@ -174,6 +203,7 @@ impl Plugin for SiteEditor {
..default()
}),
));

app.insert_resource(DirectionalLightShadowMap { size: 2048 })
.add_state::<AppState>()
.add_plugins((
Expand All @@ -185,8 +215,12 @@ impl Plugin for SiteEditor {
MainMenuPlugin,
WorkcellEditorPlugin,
SitePlugin,
InteractionPlugin,
StandardUiLayout,
InteractionPlugin {
headless: self.headless_export.is_some(),
},
StandardUiLayout {
headless: self.headless_export.is_some(),
},
AnimationPlugin,
OccupancyPlugin,
WorkspacePlugin,
Expand All @@ -197,10 +231,23 @@ impl Plugin for SiteEditor {
IssuePlugin,
OSMViewPlugin,
SiteWireframePlugin,
SiteFileMenuPlugin,
));

// Ref https://github.com/bevyengine/bevy/issues/10877. The default behavior causes issues
// with events being accumulated when not read (i.e. scrolling mouse wheel on a UI widget).
app.world
.remove_resource::<bevy::ecs::event::EventUpdateSignal>();

if let Some(path) = &self.headless_export {
// We really don't need a high update rate here since we are IO bound, set a low rate
// to save CPU.
// TODO(luca) this still seems to take quite some time, check where the bottleneck is.
app.add_plugins(ScheduleRunnerPlugin::run_loop(
std::time::Duration::from_secs_f64(1.0 / 10.0),
));
app.insert_resource(site::HeadlessSdfExportState::new(path));
app.add_systems(Last, site::headless_sdf_export);
}
}
}
13 changes: 11 additions & 2 deletions rmf_site_editor/src/main_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

use super::demo_world::*;
use crate::{AppState, LoadWorkspace, WorkspaceData};
use bevy::{app::AppExit, prelude::*, tasks::Task};
use bevy::{app::AppExit, prelude::*, tasks::Task, window::PrimaryWindow};
use bevy_egui::{egui, EguiContexts};
use std::path::PathBuf;

Expand All @@ -44,6 +44,7 @@ fn egui_ui(
mut _load_workspace: EventWriter<LoadWorkspace>,
mut _app_state: ResMut<State<AppState>>,
autoload: Option<ResMut<Autoload>>,
primary_windows: Query<Entity, With<PrimaryWindow>>,
) {
if let Some(mut autoload) = autoload {
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -56,12 +57,20 @@ fn egui_ui(
return;
}

let Some(ctx) = primary_windows
.get_single()
.ok()
.and_then(|w| egui_context.try_ctx_for_window_mut(w))
else {
return;
};

egui::Window::new("Welcome!")
.collapsible(false)
.resizable(false)
.title_bar(false)
.anchor(egui::Align2::CENTER_CENTER, egui::vec2(0., 0.))
.show(egui_context.ctx_mut(), |ui| {
.show(ctx, |ui| {
ui.heading("Welcome to The RMF Site Editor!");
ui.add_space(10.);

Expand Down
13 changes: 13 additions & 0 deletions rmf_site_editor/src/site/door.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ impl DoorBodyType {
| DoorBodyType::DoubleSliding { left, right } => vec![*left, *right],
}
}

pub fn links(&self) -> Vec<&str> {
match self {
DoorBodyType::SingleSwing { .. }
| DoorBodyType::SingleSliding { .. }
| DoorBodyType::Model { .. } => {
vec!["body"]
}
DoorBodyType::DoubleSwing { .. } | DoorBodyType::DoubleSliding { .. } => {
vec!["left", "right"]
}
}
}
}

#[derive(Debug, Clone, Copy, Component)]
Expand Down
80 changes: 80 additions & 0 deletions rmf_site_editor/src/site/file_menu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2023 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

use crate::menu_bar::{FileMenu, MenuEvent, MenuItem, MenuVisualizationStates};
use crate::{AppState, ExportFormat, SaveWorkspace, SaveWorkspaceDestination};
use bevy::prelude::*;
use std::collections::HashSet;

/// Keeps track of which entity is associated to the export sdf button.
#[derive(Resource)]
pub struct ExportSdfMenu {
export_sdf: Entity,
luca-della-vedova marked this conversation as resolved.
Show resolved Hide resolved
}

impl ExportSdfMenu {
pub fn get(&self) -> Entity {
self.export_sdf
}
}

impl FromWorld for ExportSdfMenu {
fn from_world(world: &mut World) -> Self {
let site_states = HashSet::from([
AppState::SiteEditor,
AppState::SiteVisualizer,
AppState::SiteDrawingEditor,
]);
let file_header = world.resource::<FileMenu>().get();
let export_sdf = world
.spawn((
MenuItem::Text("Export Sdf".to_string()),
MenuVisualizationStates(site_states),
))
.set_parent(file_header)
.id();

ExportSdfMenu { export_sdf }
}
}

pub fn handle_export_sdf_menu_events(
mut menu_events: EventReader<MenuEvent>,
sdf_menu: Res<ExportSdfMenu>,
mut save_events: EventWriter<SaveWorkspace>,
) {
for event in menu_events.read() {
if event.clicked() && event.source() == sdf_menu.get() {
save_events.send(SaveWorkspace {
destination: SaveWorkspaceDestination::Dialog,
format: ExportFormat::Sdf,
});
}
}
}

#[derive(Default)]
pub struct SiteFileMenuPlugin;

impl Plugin for SiteFileMenuPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ExportSdfMenu>().add_systems(
Update,
handle_export_sdf_menu_events.run_if(AppState::in_site_mode()),
);
}
}
2 changes: 1 addition & 1 deletion rmf_site_editor/src/site/floor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub const FLOOR_LAYER_START: f32 = DRAWING_LAYER_START + 0.001;

#[derive(Debug, Clone, Copy, Component)]
pub struct FloorSegments {
mesh: Entity,
pub mesh: Entity,
}

fn make_fallback_floor_mesh(p: Vec3) -> Mesh {
Expand Down
Loading
Loading