diff --git a/bin/editor.rs b/bin/editor.rs index 9404d0e..b95bebb 100644 --- a/bin/editor.rs +++ b/bin/editor.rs @@ -7,12 +7,13 @@ // * Multi-GLTF UX is bad. // * Consider GLTF hierarchy (GLTF1 > Scene1a/Scene1b, GlTF2 > Scene2a/Scene2b, etc) // * Easy despawn when de-selecting gltf +// * Load new GLTF -> Despawn all level entities // // TODO: // * Disable auto start/load -// * Make GLTF nested menu // * Better handle hide/close monologue // * (hard) Harden Active Camera +// * (medium) Despawn entire scene when GLTF changed? // * (medium) Select Font -> "Default Font" Resource // * (medium) Pre-compute animation target entities // * (medium) Animation buttons only visible when playable @@ -76,16 +77,16 @@ fn main() { .add_systems( Update, ( - manage_gltf_animation_ui, init_animations_ui, - animations_ui, + remove_animations_ui, + add_animations_ui, play_all_animations, play_animation, ), ) .add_systems( Update, - (manage_gltf_scene_ui, scenes_ui, control_active_scenes), + (remove_scenes_ui, add_scenes_ui, control_active_scenes), ) .add_systems( Update, @@ -640,103 +641,6 @@ mod gltf { } }); } - - pub fn manage_gltf_animation_ui( - added: Query, Added)>, - mut removed: RemovedComponents, - targets_gltf: Query<&ui::TargetAsset>, - gltfs: Res>, - mut animation_clip_events: EventWriter>, - ) { - removed - .iter() - .filter_map(|entity| { - if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) { - gltfs.get(handle) - } else { - None - } - }) - .for_each(|gltf| { - gltf.named_animations - .iter() - .for_each(|(animation_name, animation_handle)| { - info!("Named animation: {:?}", animation_name); - animation_clip_events.send(CustomAssetEvent::Remove { - handle: animation_handle.clone(), - }); - }); - }); - added - .iter() - .filter_map(|entity| { - if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) { - gltfs.get(handle) - } else { - None - } - }) - .for_each(|gltf| { - // Populate animations tab - gltf.named_animations - .iter() - .for_each(|(animation_name, animation_handle)| { - animation_clip_events.send(CustomAssetEvent::Add { - name: animation_name.clone(), - handle: animation_handle.clone(), - }) - }); - }); - } - - pub fn manage_gltf_scene_ui( - added: Query, Added)>, - mut removed: RemovedComponents, - targets_gltf: Query<&ui::TargetAsset>, - gltfs: Res>, - mut scene_events: EventWriter>, - ) { - removed - .iter() - .filter_map(|entity| { - if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) { - gltfs.get(handle) - } else { - None - } - }) - .for_each(|gltf| { - gltf.named_scenes - .iter() - .for_each(|(scene_name, scene_handle)| { - info!("Named scene: {:?}", scene_name); - scene_events.send(CustomAssetEvent::Remove { - handle: scene_handle.clone(), - }); - }); - }); - added - .iter() - .filter_map(|entity| { - if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) { - gltfs.get(handle) - } else { - None - } - }) - .for_each(|gltf| { - // Populate scenes tab - gltf.named_scenes - .iter() - .for_each(|(scene_name, scene_handle)| { - info!("Named scene: {:?}", scene_name); - scene_events.send(CustomAssetEvent::Add { - name: scene_name.clone(), - handle: scene_handle.clone(), - }) - }) - }); - } } // TODO: Mark loaded animation as active @@ -747,18 +651,16 @@ mod scenes { #[derive(Debug, Component, Default)] pub struct SceneWidget; - pub fn scenes_ui( - mut events: EventReader>, + pub fn add_scenes_ui( + gltf_selected: Query<&ui::TargetAsset, Added>, mut commands: Commands, + gltfs: Res>, widget: Query>, - current: Query<(Entity, &ui::TargetAsset)>, ) { - events.iter().for_each(|event| { - match event { - CustomAssetEvent::Add { name, handle } => { - info!("Asset loading! {:?}({:?})", name, handle); - // Spawn new tree - let e = create_asset_button( + gltf_selected.iter().for_each(|ui::TargetAsset { handle }| { + if let Some(gltf) = gltfs.get(&handle.clone()) { + gltf.named_scenes.iter().for_each(|(name, handle)| { + create_asset_button( &widget, &mut commands, ui::TargetAsset { @@ -767,8 +669,24 @@ mod scenes { name.clone(), None, ); - } - CustomAssetEvent::Remove { handle } => { + }) + } + }); + } + + pub fn remove_scenes_ui( + mut gltf_unselected: RemovedComponents, + target_assets: Query<&ui::TargetAsset>, + current: Query<(Entity, &ui::TargetAsset)>, + gltfs: Res>, + mut commands: Commands, + ) { + gltf_unselected + .iter() + .filter_map(|entity| target_assets.get(entity).ok()) + .filter_map(|ui::TargetAsset { handle }| gltfs.get(handle)) + .for_each(|gltf| { + gltf.scenes.iter().for_each(|handle| { destroy_asset_button( ¤t, &mut commands, @@ -776,12 +694,8 @@ mod scenes { handle: handle.clone(), }, ); - } - CustomAssetEvent::Clear => { - commands.entity(widget.single()).despawn_descendants(); - } - } - }); + }); + }); } pub fn control_active_scenes( @@ -862,40 +776,67 @@ mod animations { }) } - pub fn animations_ui( - mut events: EventReader>, - mut commands: Commands, + /// When a new scene is loaded, add any newly compatible animations + pub fn add_animations_ui( + player_spawned: Query<&Name, Added>, widget: Query>, + gltfs: Res>, + mut commands: Commands, + clips: Res>, + ) { + player_spawned.iter().for_each(|player_name| { + gltfs.iter().for_each(|(_, gltf)| { + gltf.named_animations + .iter() + .for_each(|(clip_name, handle)| { + info!("Checking clip {:?}", clip_name); + let clip = clips.get(&handle).expect("load animation clip"); + clip.compatible_with(player_name).then(|| { + create_asset_button( + &widget, + &mut commands, + ui::TargetAsset { + handle: handle.clone(), + }, + clip_name.clone(), + None, + ); + }); + }); + }); + }); + } + + // When a scene is de-selected, remove any outdated animation options + pub fn remove_animations_ui( + mut events: RemovedComponents>, current: Query<(Entity, &ui::TargetAsset)>, + clips: Res>, + targets: Query<(&AnimationPlayer, &Name)>, + mut commands: Commands, ) { - events.iter().for_each(|event| { - match event { - CustomAssetEvent::Add { name, handle } => { - info!("Asset loading! {:?}({:?})", name, handle); - // Spawn new tree - create_asset_button( - &widget, - &mut commands, - ui::TargetAsset { - handle: handle.clone(), - }, - name.clone(), - None, - ); - } - CustomAssetEvent::Remove { handle } => { - destroy_asset_button( - ¤t, - &mut commands, - &ui::TargetAsset { - handle: handle.clone(), - }, - ); - } - CustomAssetEvent::Clear => { - commands.entity(widget.single()).despawn_descendants(); + // For each removed scene + events.iter().for_each(|_| { + info!("Scene despawn!"); + // Iterate over the current animation buttons + current.iter().for_each(|(_, ui::TargetAsset { handle })| { + info!("Checking button for {:?}", handle); + // If the button points to a valid clip + if let Some(clip) = clips.get(handle) { + // Check if any active animation players are compatible with this clip + let compatible = targets.iter().any(|(_, name)| clip.compatible_with(name)); + // If not, despawn the button + if !compatible { + destroy_asset_button( + ¤t, + &mut commands, + &ui::TargetAsset { + handle: handle.clone(), + }, + ); + } } - } + }); }); } @@ -1082,7 +1023,6 @@ mod monologues { )); } - // TODO: Load .txt files for monologues pub fn texts_ui( mut events: EventReader>, mut commands: Commands, @@ -1215,7 +1155,6 @@ mod cameras { #[derive(Debug, Component, Default)] pub struct CameraWidget; - // TODO: Despawn camera button when camera removed pub fn cameras_ui( mut added: Query<(Entity, &mut Camera, &Name), (Added, Without)>, mut removed: RemovedComponents, @@ -1229,7 +1168,7 @@ mod cameras { }); added.iter_mut().for_each(|(entity, mut camera, name)| { info!("Camera added {:?} {:?}", entity, name); - let e = create_entity_button( + create_entity_button( &widget, &mut commands, ui::TargetEntity { entity }, @@ -1268,13 +1207,11 @@ mod cameras { mut cameras: Query<&mut Camera>, ) { removed.iter().for_each(|entity| { - info!("Setting {:?} to inactive camera", entity); if let Ok(mut camera) = cameras.get_mut(entity) { camera.is_active = false; } }); added.iter().for_each(|entity| { - info!("Setting {:?} to active camera", entity); if let Ok(mut camera) = cameras.get_mut(entity) { camera.is_active = true; }