diff --git a/bin/gltf-inspect.rs b/bin/gltf-inspect.rs index 64ca5b3..bfd224f 100644 --- a/bin/gltf-inspect.rs +++ b/bin/gltf-inspect.rs @@ -1,4 +1,4 @@ -use bevy::{asset::AssetEvents, gltf::Gltf, prelude::*, utils::HashMap}; +use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; use monologue_trees::debug::*; fn main() { @@ -18,6 +18,7 @@ fn main() { .add_event::() .init_resource::() .add_systems(Startup, spawn_ui) + .add_systems(PreUpdate, (add_camera_ui, add_scene_ui, add_animation_ui)) .add_systems( Update, ( @@ -25,13 +26,19 @@ fn main() { loading, reset_scene.before(spawn_scene), spawn_scene.after(reset_scene), - wire_up_scenes, control_scene, - wire_up_animations, control_animation, - wire_up_cameras, control_camera, control_default_camera, + control_default_light, + ), + ) + .add_systems( + PostUpdate, + ( + clean_ui::, + clean_ui::, + clean_ui::, ), ) .run(); @@ -41,7 +48,10 @@ fn main() { struct SceneMarker; #[derive(Component)] -struct MainCamera; +struct DefaultCamera; + +#[derive(Component)] +struct DefaultLight; #[derive(Component)] struct MainUi; @@ -58,6 +68,9 @@ struct SceneSelectUi; #[derive(Component, PartialEq, Debug)] struct SceneButton(Handle); +#[derive(Event)] +struct SpawnScene(Handle); + #[derive(Component)] struct AnimationSelectUi; @@ -80,8 +93,8 @@ struct Current { #[derive(Event)] struct ResetScene; -#[derive(Event)] -struct SpawnScene(Handle); +#[derive(Component)] +struct ResetAnimation; fn drag_and_drop( mut events: EventReader, @@ -90,13 +103,9 @@ fn drag_and_drop( ) { events .iter() - .filter_map(|event| { - if let FileDragAndDrop::DroppedFile { path_buf, .. } = event { - info!("Drag+Drop file: {:?}", path_buf); - Some(path_buf) - } else { - None - } + .filter_map(|event| match event { + FileDragAndDrop::DroppedFile { path_buf, .. } => Some(path_buf), + _ => None, }) .map(|path_buf| server.load(path_buf.to_str().expect("PathBuf to str"))) .for_each(|handle| current.gltf = handle.clone()); @@ -107,9 +116,19 @@ fn spawn_ui(mut commands: Commands) { // TODO: Scene select container // TODO: Animation Play/Pause Placeholder commands.spawn(( - Camera3dBundle { ..default() }, + Camera3dBundle { + transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }, UiCameraConfig { show_ui: true }, - MainCamera, + DefaultCamera, + )); + commands.spawn(( + DirectionalLightBundle { + transform: Transform::default().looking_at(Vec3::new(1.0, -1.0, -1.0), Vec3::Y), + ..default() + }, + DefaultLight, )); commands .spawn(( @@ -127,23 +146,46 @@ fn spawn_ui(mut commands: Commands) { MainUi, )) .with_children(|parent| { - // TOOD: Prompt to drag+drop Gltf/Gltf file - parent.spawn(( - TextBundle { - text: Text { - sections: vec![TextSection { - value: String::from("Drag and Drop .gltf/.glb file"), - style: TextStyle { - color: Color::WHITE, - ..default() - }, - }], + parent + .spawn(NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, ..default() }, ..default() - }, - InstructionsUi, - )); + }) + .with_children(|parent| { + parent.spawn(( + TextBundle::from_section( + "Drag and Drop .gltf/.glb file", + TextStyle { + color: Color::WHITE, + ..default() + }, + ), + InstructionsUi, + )); + parent.spawn(( + TextBundle::from_section( + "Using default camera", + TextStyle { + color: Color::WHITE, + ..default() + }, + ), + DefaultCamera, + )); + parent.spawn(( + TextBundle::from_section( + "Using default light", + TextStyle { + color: Color::WHITE, + ..default() + }, + ), + DefaultLight, + )); + }); parent .spawn(( NodeBundle { @@ -170,11 +212,11 @@ fn spawn_ui(mut commands: Commands) { }, ..default() }, - AnimationSelectUi, + CameraSelectUi, )) .with_children(|parent| { parent.spawn(( - TextBundle::from_section("Animations", TextStyle { ..default() }), + TextBundle::from_section("Cameras", TextStyle { ..default() }), UiTitle, )); }); @@ -187,91 +229,35 @@ fn spawn_ui(mut commands: Commands) { }, ..default() }, - CameraSelectUi, + AnimationSelectUi, )) .with_children(|parent| { parent.spawn(( - TextBundle::from_section("Cameras", TextStyle { ..default() }), + TextBundle::from_section("Animations", TextStyle { ..default() }), UiTitle, )); }); }); } -fn wire_up_scenes( - mut events: EventReader>, - root: Query>, - root_children: Query<&Children, With>, +/// When a new camera is loaded, clear the camera buttons +fn clean_ui( + mut spawn_events: EventReader, + mut clear_events: EventReader, + cameras: Query>, mut commands: Commands, - gltfs: Res>, - current: Res, - existing: Query<&SceneButton, Without>, ) { - events.iter().for_each(|event| { - // Cleanup unused buttons - root_children.iter().for_each(|children| { - // Iterate over UI children elements - children - .iter() - // Filter to just the buttons we want to despawn - .filter(|&child| { - // Check each child entity against the currently loaded scenes - // Note: !(_.any(...)) (not-any) - !((*current).scenes.iter().any(|(_, handle)| { - // If the current entitie's scene button corresponds to one of the scenes - // loaded, continue, otherwise - existing.get(*child).map_or(false, |scene_button| { - *scene_button == SceneButton(handle.cast_weak::()) - }) - })) - }) - .for_each(|&e| { - commands.entity(e).despawn_recursive(); - }); + // We don't care about the content of these events, just that we want to clear the UI after + // each spawn or reset event, so we map each event to () and chain the two event streams. + spawn_events + .iter() + .map(|_| ()) + .chain(clear_events.iter().map(|_| ())) + .for_each(|_| { + cameras.iter().for_each(|entity| { + commands.entity(entity).despawn_recursive(); + }); }); - - // Add buttons - match event { - AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { - let (name, _) = gltfs - .get(¤t.gltf) - .expect("Load current gltf") - .named_scenes - .iter() - .find(|(_, h)| h == &handle) - .expect("Find this scene"); - - let mut ui = commands.entity(root.single()); - - ui.with_children(|parent| { - parent - .spawn(( - ButtonBundle { - background_color: BackgroundColor(Color::NONE), - ..default() - }, - SceneButton(handle.clone()), - )) - .with_children(|parent| { - parent.spawn(TextBundle { - text: Text { - sections: vec![TextSection { - value: name.clone(), - style: TextStyle { - color: Color::WHITE, - ..default() - }, - }], - ..default() - }, - ..default() - }); - }); - }); - } - AssetEvent::Removed { .. } => warn!("IGNORED Remove scene in list"), - } - }); } /// Translate UI button presses into scene spawn events @@ -288,150 +274,88 @@ fn control_scene( }); } -fn wire_up_animations( - mut events: EventReader>, - root: Query>, - root_children: Query<&Children, With>, - mut commands: Commands, - gltfs: Res>, +/// Add scene buttons when a new scene is added/gltf is loaded +fn add_scene_ui( + mut events: EventReader, current: Res, - existing: Query<&AnimationButton, Without>, + root: Query>, + mut commands: Commands, ) { - events.iter().for_each(|event| { - // Cleanup unused buttons - root_children.iter().for_each(|children| { - // Iterate over UI children elements - children - .iter() - // Filter to just the buttons we want to despawn - .filter(|&child| { - // Check each child entity against the currently loaded animations - // Note: !(_.any(...)) (not-any) - !((*current).animations.iter().any(|(_, handle)| { - // If the current entitie's animation button corresponds to one of - // the animations loaded, continue, otherwise - existing.get(*child).map_or(false, |scene_button| { - *scene_button == AnimationButton(handle.cast_weak::()) - }) - })) - }) - .for_each(|&e| { - commands.entity(e).despawn_recursive(); - }); + events.iter().for_each(|_| { + commands.entity(root.single()).with_children(|parent| { + current.scenes.iter().for_each(|(name, handle)| { + parent + .spawn(( + ButtonBundle { + background_color: BackgroundColor(Color::NONE), + ..default() + }, + SceneButton(handle.clone()), + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section(name, TextStyle { ..default() })); + }); + }); }); - match event { - AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { - let (name, _) = gltfs - .get(¤t.gltf) - .expect("Load current gltf") - .named_animations - .iter() - .find(|(_, h)| h == &handle) - .expect("Find this animation"); - - commands.entity(root.single()).with_children(|parent| { - parent - .spawn(( - ButtonBundle { - background_color: BackgroundColor(Color::NONE), - ..default() - }, - AnimationButton(handle.clone()), - )) - .with_children(|parent| { - parent.spawn(TextBundle { - text: Text { - sections: vec![TextSection { - value: name.clone(), - style: TextStyle { - color: Color::WHITE, - ..default() - }, - }], - ..default() - }, - ..default() - }); - }); - }); - } - AssetEvent::Removed { .. } => warn!("Ignoring asset removal"), - } }); } fn control_animation( interactions: Query<(&Interaction, &AnimationButton), (Changed, With