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

Use workflows for model spawning #238

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
38 changes: 7 additions & 31 deletions rmf_site_editor/src/interaction/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
use crate::{
animate::*,
interaction::*,
site::{AnchorBundle, Pending, SiteAssets, Trashcan},
site::{AnchorBundle, ModelSpawningExt, Pending, SiteAssets},
};
use bevy::{ecs::system::SystemParam, prelude::*, window::PrimaryWindow};
use bevy_mod_raycast::primitives::{rays::Ray3d, Primitive3d};

use rmf_site_format::{FloorMarker, Model, WallMarker, WorkcellModel};
use rmf_site_format::{FloorMarker, Model, WallMarker};
use std::collections::HashSet;

/// A resource that keeps track of the unique entities that play a role in
Expand All @@ -37,7 +37,6 @@ pub struct Cursor {
pub level_anchor_placement: Entity,
pub site_anchor_placement: Entity,
pub frame_placement: Entity,
pub trashcan: Entity,
pub preview_model: Option<Entity>,
dependents: HashSet<Entity>,
/// Use a &str to label each mode that might want to turn the cursor on
Expand Down Expand Up @@ -116,37 +115,17 @@ impl Cursor {
}

pub fn remove_preview(&mut self, commands: &mut Commands) {
if let Some(current_preview) = self.preview_model {
commands.get_entity(current_preview).map(|mut e_mut| {
e_mut.set_parent(self.trashcan);
});
self.preview_model = None;
if let Some(current_preview) = self.preview_model.take() {
commands.entity(current_preview).despawn_recursive();
}
}

// TODO(luca) reduce duplication here
pub fn set_model_preview(&mut self, commands: &mut Commands, model: Option<Model>) {
self.remove_preview(commands);
self.preview_model = if let Some(model) = model {
let e = commands.spawn(model).insert(Pending).id();
commands.entity(self.frame).push_children(&[e]);
Some(e)
} else {
None
}
}

pub fn set_workcell_model_preview(
&mut self,
commands: &mut Commands,
model: Option<WorkcellModel>,
) {
self.remove_preview(commands);
self.preview_model = if let Some(model) = model {
let mut cmd = commands.spawn(Pending);
let e = cmd.id();
model.add_bevy_components(&mut cmd);
commands.entity(self.frame).push_children(&[e]);
let source = model.source.clone();
let e = commands.spawn((model, Pending)).set_parent(self.frame).id();
commands.spawn_model((e, source).into());
Some(e)
} else {
None
Expand Down Expand Up @@ -261,16 +240,13 @@ impl FromWorld for Cursor {
})
.id();

let trashcan = world.spawn(Trashcan).id();

Self {
frame: cursor,
halo,
dagger,
level_anchor_placement,
site_anchor_placement,
frame_placement,
trashcan,
preview_model: None,
dependents: Default::default(),
modes: Default::default(),
Expand Down
12 changes: 10 additions & 2 deletions rmf_site_editor/src/interaction/select/place_object_2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
*
*/

use crate::{interaction::select::*, site::Model};
use crate::{
interaction::select::*,
site::{Model, ModelSpawningExt},
};
use bevy::prelude::Input as UserInput;

pub const PLACE_OBJECT_2D_MODE_LABEL: &'static str = "place_object_2d";
Expand Down Expand Up @@ -201,7 +204,12 @@ pub fn on_placement_chosen_2d(
let mut state = access.pull().or_broken_state()?;

state.object.pose = placement.into();
commands.spawn(state.object).set_parent(state.level);
let source = state.object.source.clone();
let id = commands
.spawn((Category::Model, state.object))
.set_parent(state.level)
.id();
commands.spawn_model((id, source).into());

Ok(())
}
56 changes: 36 additions & 20 deletions rmf_site_editor/src/interaction/select/place_object_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
use crate::{
interaction::select::*,
site::{
Anchor, AnchorBundle, Dependents, FrameMarker, Model, NameInSite, NameInWorkcell, Pending,
SiteID, WorkcellModel,
Anchor, AnchorBundle, Dependents, FrameMarker, Model, ModelLoadingRequest,
ModelSpawningExt, NameInWorkcell, Pending, SiteID,
},
widgets::canvas_tooltips::CanvasTooltips,
workcell::flatten_loaded_model_hierarchy,
};
use bevy::{
ecs::system::{EntityCommands, SystemParam},
prelude::Input as UserInput,
};
use bevy::{ecs::system::SystemParam, prelude::Input as UserInput};
use bevy_mod_raycast::deferred::RaycastSource;
use std::borrow::Cow;

Expand Down Expand Up @@ -150,8 +154,8 @@ pub struct PlaceObject3d {
pub enum PlaceableObject {
Model(Model),
Anchor,
VisualMesh(WorkcellModel),
CollisionMesh(WorkcellModel),
VisualMesh(Model),
CollisionMesh(Model),
}

pub fn place_object_3d_setup(
Expand All @@ -174,18 +178,14 @@ pub fn place_object_3d_setup(
set_visibility(cursor.dagger, &mut visibility, true);
set_visibility(cursor.halo, &mut visibility, true);
}
PlaceableObject::Model(m) => {
PlaceableObject::Model(m)
| PlaceableObject::VisualMesh(m)
| PlaceableObject::CollisionMesh(m) => {
// Spawn the model as a child of the cursor
cursor.set_model_preview(&mut commands, Some(m.clone()));
set_visibility(cursor.dagger, &mut visibility, false);
set_visibility(cursor.halo, &mut visibility, false);
}
PlaceableObject::VisualMesh(m) | PlaceableObject::CollisionMesh(m) => {
// Spawn the model as a child of the cursor
cursor.set_workcell_model_preview(&mut commands, Some(m.clone()));
set_visibility(cursor.dagger, &mut visibility, false);
set_visibility(cursor.halo, &mut visibility, false);
}
}

if let Some(parent) = state.parent {
Expand Down Expand Up @@ -238,7 +238,7 @@ pub fn place_object_3d_find_placement(
hovering: Res<Hovering>,
mouse_button_input: Res<UserInput<MouseButton>>,
blockers: Option<Res<PickingBlockers>>,
meta: Query<(Option<&'static NameInSite>, Option<&'static SiteID>)>,
meta: Query<(Option<&'static NameInWorkcell>, Option<&'static SiteID>)>,
mut filter: PlaceObject3dFilter,
) {
let Some(mut orders) = orders.get_mut(&srv_key) else {
Expand Down Expand Up @@ -458,6 +458,10 @@ pub fn on_placement_chosen_3d(
let placement_tf = placement.compute_affine();
let pose = Transform::from_matrix((inv_tf * placement_tf).into()).into();

let flatten_models = flatten_loaded_model_hierarchy.into_blocking_callback();
let add_model_components = |object: Model, mut cmd: EntityCommands| {
cmd.insert((NameInWorkcell(object.name.0), object.pose, object.scale));
};
let id = match state.object {
PlaceableObject::Anchor => commands
.spawn((
Expand All @@ -467,7 +471,11 @@ pub fn on_placement_chosen_3d(
))
.id(),
PlaceableObject::Model(object) => {
let model_id = commands.spawn((object, VisualCue::outline())).id();
let model_id = commands.spawn(VisualCue::outline()).id();
let source = object.source.clone();
add_model_components(object, commands.entity(model_id));
let req = ModelLoadingRequest::new(model_id, source).then(flatten_models);
commands.spawn_model(req);
// Create a parent anchor to contain the new model in
commands
.spawn((
Expand All @@ -480,16 +488,24 @@ pub fn on_placement_chosen_3d(
.id()
}
PlaceableObject::VisualMesh(mut object) => {
let id = commands.spawn((VisualMeshMarker, Category::Visual)).id();
object.pose = pose;
let mut cmd = commands.spawn(VisualMeshMarker);
object.add_bevy_components(&mut cmd);
cmd.id()
let source = object.source.clone();
add_model_components(object, commands.entity(id));
let req = ModelLoadingRequest::new(id, source).then(flatten_models);
commands.spawn_model(req);
id
}
PlaceableObject::CollisionMesh(mut object) => {
let id = commands
.spawn((CollisionMeshMarker, Category::Collision))
.id();
object.pose = pose;
let mut cmd = commands.spawn(CollisionMeshMarker);
object.add_bevy_components(&mut cmd);
cmd.id()
let source = object.source.clone();
add_model_components(object, commands.entity(id));
let req = ModelLoadingRequest::new(id, source).then(flatten_models);
commands.spawn_model(req);
id
}
};

Expand Down
8 changes: 6 additions & 2 deletions rmf_site_editor/src/sdf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl AssetLoader for SdfLoader {
let mut bytes = Vec::new();
// TODO(luca) remove unwrap
reader.read_to_end(&mut bytes).await.unwrap();
Ok(load_model(bytes, load_context).await?)
Ok(load_model(bytes, load_context)?)
})
}

Expand Down Expand Up @@ -131,8 +131,12 @@ fn compute_model_source<'a, 'b>(
Ok(asset_source)
} else {
// It's a path relative to this model, concatenate it to the current context path.
// Note that since the current path is the file (i.e. path/subfolder/model.sdf) we need to
// concatenate to its parent
let path = load_context
.asset_path()
.parent()
.unwrap()
.resolve(subasset_uri)
.or_else(|e| Err(SdfError::UnsupportedAssetSource(e.to_string())))?;
AssetSource::try_from(path.to_string().as_str()).map_err(SdfError::UnsupportedAssetSource)
Expand Down Expand Up @@ -239,7 +243,7 @@ fn spawn_geometry<'a, 'b>(
Ok(geometry)
}

async fn load_model<'a, 'b>(
fn load_model<'a, 'b>(
bytes: Vec<u8>,
load_context: &'a mut LoadContext<'b>,
) -> Result<bevy::scene::Scene, SdfError> {
Expand Down
8 changes: 4 additions & 4 deletions rmf_site_editor/src/site/fuel_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*
*/

use crate::site::{ModelMarker, ModelSceneRoot, TentativeModelFormat};
use crate::site::{AssetSource, ModelScene, ModelSpawningExt};
use crate::site_asset_io::FUEL_API_KEY;
use crate::widgets::AssetGalleryStatus;
use bevy::prelude::*;
Expand Down Expand Up @@ -127,7 +127,7 @@ pub fn read_update_fuel_cache_results(
pub fn reload_failed_models_with_new_api_key(
mut commands: Commands,
mut api_key_events: EventReader<SetFuelApiKey>,
failed_models: Query<Entity, (With<ModelMarker>, Without<ModelSceneRoot>)>,
failed_models: Query<(Entity, &AssetSource), Without<ModelScene>>,
) {
if let Some(key) = api_key_events.read().last() {
info!("New API Key set, attempting to re-download failed models");
Expand All @@ -136,8 +136,8 @@ pub fn reload_failed_models_with_new_api_key(
Err(poisoned) => poisoned.into_inner(),
};
*key_guard = Some((**key).clone());
for e in &failed_models {
commands.entity(e).insert(TentativeModelFormat::default());
for (e, source) in &failed_models {
commands.spawn_model((e, source.clone()).into());
}
}
}
15 changes: 10 additions & 5 deletions rmf_site_editor/src/site/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,6 @@ fn generate_site_entities(
consider_id(*light_id);
}

for (model_id, model) in &level_data.models {
level.spawn(model.clone()).insert(SiteID(*model_id));
consider_id(*model_id);
}

for (physical_camera_id, physical_camera) in &level_data.physical_cameras {
level
.spawn(physical_camera.clone())
Expand All @@ -230,6 +225,16 @@ fn generate_site_entities(
}
});

for (model_id, model) in &level_data.models {
let source = model.source.clone();
let model_entity = commands
.spawn((Category::Model, model.clone(), SiteID(*model_id)))
.set_parent(level_entity)
.id();
commands.spawn_model((model_entity, source).into());
consider_id(*model_id);
}

// TODO(MXG): Log when a RecencyRanking fails to load correctly.
commands
.entity(level_entity)
Expand Down
17 changes: 1 addition & 16 deletions rmf_site_editor/src/site/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ impl Plugin for SitePlugin {
.init_resource::<PhysicalLightToggle>()
.init_resource::<FuelCacheUpdateChannel>()
.init_resource::<FuelCacheProgressChannel>()
.init_resource::<ModelTrashcan>()
.register_type::<NameInSite>()
.register_type::<AssetSource>()
.register_type::<Pose>()
Expand Down Expand Up @@ -263,6 +262,7 @@ impl Plugin for SitePlugin {
DeletionPlugin,
DrawingEditorPlugin,
SiteVisualizerPlugin,
ModelLoadingPlugin::default(),
))
.add_issue_type(&DUPLICATED_DOOR_NAME_ISSUE_UUID, "Duplicate door name")
.add_issue_type(&DUPLICATED_LIFT_NAME_ISSUE_UUID, "Duplicate lift name")
Expand All @@ -278,7 +278,6 @@ impl Plugin for SitePlugin {
(
update_lift_cabin,
update_lift_edge,
update_model_tentative_formats,
update_drawing_pixels_per_meter,
update_drawing_children_to_pixel_coordinates,
check_for_duplicated_door_names,
Expand Down Expand Up @@ -320,7 +319,6 @@ impl Plugin for SitePlugin {
add_category_to_graphs,
add_tags_to_lift,
add_material_for_display_colors,
clear_model_trashcan,
add_physical_lights,
)
.run_if(AppState::in_site_mode())
Expand Down Expand Up @@ -392,22 +390,9 @@ impl Plugin for SitePlugin {
.add_systems(
PostUpdate,
(
(
propagate_model_properties,
make_models_selectable,
// Counter-intuitively, we want to expand the model scenes
// after propagating the model properties through the scene.
// The reason is that the entities for the scene won't be
// available until the next cycle is finished, so we want to
// wait until the next cycle before propagating the properties
// of any newly added scenes.
handle_model_loaded_events,
)
.chain(),
add_measurement_visuals,
update_changed_measurement,
update_measurement_for_moved_anchors,
update_model_scenes,
update_affiliations,
update_members_of_groups.after(update_affiliations),
update_model_scales,
Expand Down
Loading
Loading