loaded animation

This commit is contained in:
Lee-Orr
2024-04-05 14:00:35 -04:00
parent d2ae2f5744
commit b5d0aa7a54
+137 -38
View File
@@ -1,6 +1,6 @@
use std::{str::FromStr, time::Duration}; use std::{str::FromStr, time::Duration};
use bevy::{prelude::*, utils::hashbrown::HashMap}; use bevy::{asset::AssetPath, prelude::*, utils::hashbrown::HashMap};
use bevy_tweening::{ use bevy_tweening::{
lens::{TransformPositionLens, TransformRotationLens, TransformScaleLens}, lens::{TransformPositionLens, TransformRotationLens, TransformScaleLens},
@@ -192,12 +192,20 @@ impl Default for Keyframe {
#[derive(Component, Clone, Debug, Reflect, PartialEq)] #[derive(Component, Clone, Debug, Reflect, PartialEq)]
#[reflect(Component)] #[reflect(Component)]
pub struct TimedAnimationClipAsset { pub struct TimedAnimationClipAsset {
clip: Handle<AnimationClip>, clip_path: String,
#[reflect(ignore)]
clip: Option<Handle<AnimationClip>>,
speed: f32, speed: f32,
cut_in: Option<f32>,
cut_out: Option<f32>,
} }
#[derive(Component, Clone, Debug, PartialEq)] #[derive(Component, Clone, Debug, PartialEq)]
pub struct TimedAnimationClipDuration(f32); pub struct TimedAnimationClipDuration {
duration: f32,
cut_in: f32,
full_clip_duration: f32,
}
#[derive(Component, Clone, Debug)] #[derive(Component, Clone, Debug)]
enum RetargetedClipReference { enum RetargetedClipReference {
@@ -238,8 +246,11 @@ impl PartialOrd for TimedAnimationClipAsset {
impl Default for TimedAnimationClipAsset { impl Default for TimedAnimationClipAsset {
fn default() -> Self { fn default() -> Self {
Self { Self {
clip_path: Default::default(),
clip: Default::default(), clip: Default::default(),
speed: 1.0, speed: 1.0,
cut_in: None,
cut_out: None,
} }
} }
} }
@@ -414,6 +425,7 @@ fn process_keyframe_events(
} => { } => {
if let Ok((_target, transform, animation_target)) = q_animation_targets.get(*target) if let Ok((_target, transform, animation_target)) = q_animation_targets.get(*target)
{ {
let clip_path = format!("{clip_path}#Animation0");
commands.spawn(( commands.spawn((
Name::new(display_name.to_owned()), Name::new(display_name.to_owned()),
PrefabMarker, PrefabMarker,
@@ -427,8 +439,11 @@ fn process_keyframe_events(
target: *animation_target, target: *animation_target,
}, },
TimedAnimationClipAsset { TimedAnimationClipAsset {
clip: asset_server.load(format!("{clip_path}#Animation0")), clip: Some(asset_server.load(&clip_path)),
clip_path,
speed: 1.0, speed: 1.0,
cut_in: None,
cut_out: None,
}, },
retargeting_type.clone(), retargeting_type.clone(),
)); ));
@@ -679,11 +694,12 @@ pub fn timeline_bound_component_animator_system<T: Component>(
struct TimedAnimationClipLens { struct TimedAnimationClipLens {
clip: Handle<AnimationClip>, clip: Handle<AnimationClip>,
duration: f32, duration: f32,
inner_start: f32,
} }
impl bevy_tweening::lens::Lens<AnimationPlayer> for TimedAnimationClipLens { impl bevy_tweening::lens::Lens<AnimationPlayer> for TimedAnimationClipLens {
fn lerp(&mut self, target: &mut AnimationPlayer, ratio: f32) { fn lerp(&mut self, target: &mut AnimationPlayer, ratio: f32) {
let inner_time = self.duration * ratio; let inner_time = self.duration * ratio + self.inner_start;
if target.animation_clip() != &self.clip { if target.animation_clip() != &self.clip {
target.play(self.clip.clone()); target.play(self.clip.clone());
target.pause(); target.pause();
@@ -753,15 +769,17 @@ fn generate_clip_animation(
let (last, mut clip_timeline) = keyframes.fold( let (last, mut clip_timeline) = keyframes.fold(
( (
Option::<(f32, f32, Handle<AnimationClip>)>::None, Option::<(f32, f32, f32, Handle<AnimationClip>)>::None,
Vec::<BoxedTweenable<AnimationPlayer>>::new(), Vec::<BoxedTweenable<AnimationPlayer>>::new(),
), ),
|(last_clip, mut clip_timeline), (keyframe, asset, duration, reference, _entity)| { |(last_clip, mut clip_timeline), (keyframe, asset, duration, reference, _entity)| {
let time = keyframe.time; let time = keyframe.time;
let (last_start, last_duration, last_clip) = match last_clip { let (last_start, last_duration, inner_start, last_clip) = match last_clip {
Some((start, duration, clip)) => (start, duration, Some(clip)), Some((start, duration, inner_start, clip)) => {
None => (0., 0., None), (start, duration, inner_start, Some(clip))
}
None => (0., 0., 0., None),
}; };
let mut reached_now = false; let mut reached_now = false;
@@ -782,6 +800,7 @@ fn generate_clip_animation(
TimedAnimationClipLens { TimedAnimationClipLens {
clip: last_clip, clip: last_clip,
duration, duration,
inner_start,
}, },
) )
.into(), .into(),
@@ -796,25 +815,36 @@ fn generate_clip_animation(
} }
} }
let duration = duration.0; let inner_start = duration.cut_in;
let duration = duration.duration;
let clip = match reference { let clip = match reference {
RetargetedClipReference::UseOrigin => &asset.clip, RetargetedClipReference::UseOrigin => asset.clip.clone(),
RetargetedClipReference::RenamedRoot { retargeted } => retargeted, RetargetedClipReference::RenamedRoot { retargeted } => Some(retargeted.clone()),
RetargetedClipReference::Mixamo { retargeted } => retargeted, RetargetedClipReference::Mixamo { retargeted } => Some(retargeted.clone()),
RetargetedClipReference::MocapNet { retargeted } => retargeted, RetargetedClipReference::MocapNet { retargeted } => Some(retargeted.clone()),
}; };
(Some((time, duration, clip.clone())), clip_timeline) (
match clip {
Some(clip) => Some((time, duration, inner_start, clip)),
None => None
},
clip_timeline,
)
}, },
); );
if let Some((_, duration, clip)) = last { if let Some((_, duration, inner_start, clip)) = last {
if duration > 0f32 { if duration > 0f32 {
clip_timeline.push( clip_timeline.push(
Tween::new( Tween::new(
EaseMethod::Linear, EaseMethod::Linear,
Duration::from_secs_f32(duration), Duration::from_secs_f32(duration),
TimedAnimationClipLens { clip, duration }, TimedAnimationClipLens {
clip,
duration,
inner_start,
},
) )
.into(), .into(),
); );
@@ -843,13 +873,28 @@ pub fn update_timed_animation_clip_duration(
mut commands: Commands, mut commands: Commands,
query: Query<(Entity, &TimedAnimationClipAsset), Without<TimedAnimationClipDuration>>, query: Query<(Entity, &TimedAnimationClipAsset), Without<TimedAnimationClipDuration>>,
clips: Res<Assets<AnimationClip>>, clips: Res<Assets<AnimationClip>>,
asset_server: Res<AssetServer>,
) { ) {
for (entity, clip_asset) in &query { for (entity, clip_asset) in &query {
if let Some(clip) = clips.get(&clip_asset.clip) { if let Some(clip) = &clip_asset.clip {
let duration = clip.duration(); if let Some(clip) = clips.get(clip) {
commands let full_clip_duration = clip.duration();
.entity(entity) let cut_in = clip_asset.cut_in.unwrap_or_default();
.insert(TimedAnimationClipDuration(duration)); let duration = if let Some(cut_out) = clip_asset.cut_out {
full_clip_duration.min(cut_out)
} else {
full_clip_duration
};
commands.entity(entity).insert(TimedAnimationClipDuration {
duration,
cut_in,
full_clip_duration,
});
}
} else {
let mut clip_asset = clip_asset.clone();
clip_asset.clip = Some(asset_server.load(&clip_asset.clip_path));
commands.entity(entity).insert(clip_asset);
} }
} }
} }
@@ -869,6 +914,7 @@ fn retarget_timed_animation_clip(
), ),
>, >,
animation_targets: Query<(Entity, &AnimationTarget, &Name), With<Skeleton>>, animation_targets: Query<(Entity, &AnimationTarget, &Name), With<Skeleton>>,
children: Query<(&Name, Option<&Children>)>,
mut clips: ResMut<Assets<AnimationClip>>, mut clips: ResMut<Assets<AnimationClip>>,
) { ) {
let targets = animation_targets let targets = animation_targets
@@ -878,7 +924,10 @@ fn retarget_timed_animation_clip(
for (entity, asset, retarget, keyframe) in &clip_keys { for (entity, asset, retarget, keyframe) in &clip_keys {
info!("Added Animation: {asset:?} with retargeting {retarget:?}"); info!("Added Animation: {asset:?} with retargeting {retarget:?}");
let Some(clip) = clips.get(&asset.clip) else { let Some(clip) = &asset.clip else {
continue
};
let Some(clip) = clips.get(clip) else {
continue; continue;
}; };
info!("Animation exists"); info!("Animation exists");
@@ -892,11 +941,15 @@ fn retarget_timed_animation_clip(
continue; continue;
}; };
let Some((_target_entity, target_name)) = targets.get(&keyframe.target) else { let Some((target_entity, target_name)) = targets.get(&keyframe.target) else {
error!("Animation has no target: {keyframe:?}"); error!("Animation has no target: {keyframe:?}");
continue; continue;
}; };
let Some((_, target_children)) = children.get(*target_entity).ok() else {
continue;
};
match &retarget { match &retarget {
ClipRetargeting::UseOrigin => { ClipRetargeting::UseOrigin => {
info!("Use Origin"); info!("Use Origin");
@@ -907,28 +960,28 @@ fn retarget_timed_animation_clip(
.insert(RetargetedClipReference::UseOrigin); .insert(RetargetedClipReference::UseOrigin);
} else { } else {
let mut new_clip = AnimationClip::default(); let mut new_clip = AnimationClip::default();
let first_path = paths.keys().next().cloned(); let mut empty = true;
let new_first_path = first_path.as_ref().map(|path| {
let mut new_path = path.clone();
if let Some(root_name) = new_path.parts.first_mut() {
root_name.set(target_name.to_string());
}
new_path
});
for (path, bone_id) in paths.iter() { for (path, bone_id) in paths.iter() {
let mut new_path = path.clone(); let Some(new_path) =
if let Some(root_name) = new_path.parts.first_mut() { rebuild_entity_path(target_name, target_children, path, &children)
root_name.set(target_name.to_string()); else {
} continue;
};
let Some(curves) = clip.get_curves(*bone_id) else { let Some(curves) = clip.get_curves(*bone_id) else {
continue; continue;
}; };
for curve in curves { for curve in curves {
new_clip.add_curve_to_path(new_path.clone(), curve.clone()); new_clip.add_curve_to_path(new_path.clone(), curve.clone());
empty = false;
} }
} }
info!("Inserted Animation With Renamed Root - original {first_path:?} becomes {new_first_path:?}"); if empty {
error!("Failed to add any curves to animation");
commands.entity(entity).remove::<ClipRetargeting>();
continue;
}
info!("Inserted Animation With Renamed Root");
let retargeted = clips.add(new_clip); let retargeted = clips.add(new_clip);
commands commands
.entity(entity) .entity(entity)
@@ -949,6 +1002,52 @@ fn retarget_timed_animation_clip(
} }
} }
fn rebuild_entity_path(
new_root_name: &Name,
root_children: Option<&Children>,
original: &EntityPath,
named: &Query<(&Name, Option<&Children>)>,
) -> Option<EntityPath> {
if original.parts.is_empty() {
error!("Empty Entity Path");
return None;
}
let mut current_children = root_children;
let mut new_parts = Vec::with_capacity(original.parts.len());
let mut parts = original.parts.iter();
new_parts.push(new_root_name.clone());
let _ = parts.next(); // Skipping the first one
for part in parts {
let children = current_children?;
let mut found = false;
for child in children.iter() {
if let Ok((name, children)) = named.get(*child) {
info!("Checking {name:?}");
if name.as_str() == part.as_str() {
found = true;
current_children = children;
new_parts.push(name.clone());
break;
}
}
}
if !found {
warn!(r#"Couldn't find Path: {original:?}
Failed at: {part:?}
Children are: {children:?}"#);
return None;
}
}
let new_path = EntityPath { parts: new_parts };
info!("Retargeting: Replaced {original:?} with {new_path:?}");
Some(new_path)
}
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
mod wasm { mod wasm {
use bevy::{prelude::*, utils::hashbrown::HashMap, window::PrimaryWindow}; use bevy::{prelude::*, utils::hashbrown::HashMap, window::PrimaryWindow};
@@ -1302,7 +1401,7 @@ mod wasm {
time: keyframe.time, time: keyframe.time,
clip: name.to_string(), clip: name.to_string(),
speed: clip.speed, speed: clip.speed,
duration: duration.map(|v| v.0).unwrap_or_default(), duration: duration.map(|v| v.duration).unwrap_or_default(),
}) })
.map_err(|e| format!("{e}")) .map_err(|e| format!("{e}"))
{ {