You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
8.0 KiB
Rust

use crate::editor::prelude::*;
#[derive(Debug, Default)]
pub struct EditorAnimationPlugin;
impl Plugin for EditorAnimationPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ControlAnimation>()
.add_systems(Update, sync_asset_buttons::<AnimationClip>)
.add_systems(Update, sync_remove_asset_buttons::<AnimationClip>)
.add_systems(Update, set_epoch_animations)
.add_systems(Update, load_epoch_animations)
.add_systems(Update, init_animations_ui)
.add_systems(Update, remove_animations_ui)
.add_systems(Update, add_animations_ui)
.add_systems(Update, play_all_animations)
.add_systems(Update, play_animation)
.add_systems(Update, ui_control_animations)
.add_systems(Update, ui_active_animation);
}
}
#[derive(Debug, Component, Default)]
pub struct AnimationWidget;
#[derive(Debug, Component)]
pub struct AnimationPlayAll;
pub fn init_animations_ui(events: Query<Entity, Added<AnimationWidget>>, mut commands: Commands) {
events.iter().for_each(|entity| {
commands.entity(entity).with_children(|parent| {
parent.spawn((
AnimationPlayAll,
ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(1.0)),
padding: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Play All".into(),
..default()
},
));
});
})
}
/// When a new scene is loaded, add any newly compatible animations
/// TODO: Add target entity(s) too
pub fn add_animations_ui(
player_spawned: Query<&Name, Added<AnimationPlayer>>,
widget: Query<Entity, With<AnimationWidget>>,
mut commands: Commands,
gltfs: Res<Assets<Gltf>>,
clips: Res<Assets<AnimationClip>>,
) {
player_spawned.iter().for_each(|player_name| {
gltfs
.iter()
.flat_map(|(_, gltf)| gltf.named_animations.iter())
.filter_map(|(clip_name, handle)| {
clips.get(&handle).map(|clip| (clip_name, handle, clip))
})
.filter(|(_, _, clip)| clip.compatible_with(player_name))
.for_each(|(clip_name, handle, _)| {
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 removed_players: RemovedComponents<Handle<Scene>>,
current: Query<(Entity, &ui::TargetAsset<AnimationClip>)>,
clips: Res<Assets<AnimationClip>>,
targets: Query<(&AnimationPlayer, &Name)>,
mut commands: Commands,
) {
// For each removed scene
removed_players.iter().for_each(|_| {
// Iterate over the current animation buttons
current
.iter()
.filter(|(_, ui::TargetAsset { handle })| {
// Check if this clip is compatible with any remaining entities
// NOTE: We are checking this is *not* compatible with any entities
clips
.get(handle)
.map(|clip| !(targets.iter().any(|(_, name)| clip.compatible_with(name))))
.unwrap_or(true)
})
.for_each(|(_, ui::TargetAsset { handle })| {
// Destroy the buton if it is so
destroy_asset_button(
&current,
&mut commands,
&ui::TargetAsset {
handle: handle.clone(),
},
);
});
});
}
#[derive(Debug, Event)]
pub enum ControlAnimation {
Play(Handle<AnimationClip>),
Pause(Handle<AnimationClip>),
}
pub fn play_animation(
mut events: EventReader<ControlAnimation>,
mut targets: Query<(&mut AnimationPlayer, &Name), With<Transform>>,
clips: Res<Assets<AnimationClip>>,
) {
events.iter().for_each(|event| match event {
ControlAnimation::Play(handle) => {
let clip = clips.get(&handle).expect("Load animation clip");
targets
.iter_mut()
.filter(|(_, name)| clip.compatible_with(name))
.for_each(|(mut player, _)| {
if player.is_paused() {
player.resume();
} else {
player.play(handle.clone()).repeat();
}
});
}
ControlAnimation::Pause(handle) => {
let clip = clips.get(handle).expect("Load animation clip");
targets
.iter_mut()
.filter(|(_, name)| clip.compatible_with(name))
.for_each(|(mut player, _)| {
player.pause();
});
}
});
}
pub fn play_all_animations(
events: Query<
(Entity, &Interaction, Option<&ui::Active>),
(With<AnimationPlayAll>, Changed<Interaction>),
>,
clip_btns: Query<&ui::TargetAsset<AnimationClip>>,
mut writer: EventWriter<ControlAnimation>,
mut commands: Commands,
) {
events
.iter()
.filter_map(|(entity, &interaction, active)| {
(interaction == Interaction::Pressed).then_some((entity, active))
})
.for_each(|(entity, active)| match active {
Some(_) => {
commands.entity(entity).remove::<ui::Active>();
clip_btns.iter().for_each(|ui::TargetAsset { handle }| {
writer.send(ControlAnimation::Pause(handle.clone()));
});
}
None => {
commands.entity(entity).insert(ui::Active);
clip_btns.iter().for_each(|ui::TargetAsset { handle }| {
writer.send(ControlAnimation::Play(handle.clone()));
});
}
})
}
fn ui_control_animations(
events: Query<
(
&Interaction,
&ui::TargetAsset<AnimationClip>,
Option<&ui::Active>,
),
(With<Button>, Changed<Interaction>),
>,
mut writer: EventWriter<ControlAnimation>,
) {
events
.iter()
.filter_map(
|(interaction, ui::TargetAsset { handle }, active)| match interaction {
Interaction::Pressed => Some((handle, active)),
_ => None,
},
)
.for_each(|(handle, active)| match active {
Some(_) => writer.send(ControlAnimation::Pause(handle.clone())),
None => writer.send(ControlAnimation::Play(handle.clone())),
});
}
pub fn ui_active_animation(
mut events: EventReader<ControlAnimation>,
targets: Query<(Entity, &ui::TargetAsset<AnimationClip>)>,
mut commands: Commands,
) {
events.iter().for_each(|event| match event {
ControlAnimation::Play(this_handle) => {
targets
.iter()
.find_map(|(entity, ui::TargetAsset { handle })| {
(handle == this_handle).then_some(entity)
})
.into_iter()
.for_each(|entity| {
commands.entity(entity).insert(ui::Active);
});
}
ControlAnimation::Pause(this_handle) => {
targets
.iter()
.find_map(|(entity, ui::TargetAsset { handle })| {
(handle == this_handle).then_some(entity)
})
.into_iter()
.for_each(|entity| {
commands.entity(entity).remove::<ui::Active>();
});
}
});
}