From 2523691513ec0a228b1070adb6f3bebab3df48a2 Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Sat, 30 Mar 2024 23:43:09 +0100 Subject: [PATCH] refactor(animation): cleanups , reorg & tweaks --- crates/bevy_gltf_blueprints/src/animation.rs | 2 - testing/bevy_example/assets/registry.json | 124 ++++++++- testing/bevy_example/src/game/animation.rs | 249 ++++++++++++++++++ testing/bevy_example/src/game/mod.rs | 259 +------------------ 4 files changed, 367 insertions(+), 267 deletions(-) create mode 100644 testing/bevy_example/src/game/animation.rs diff --git a/crates/bevy_gltf_blueprints/src/animation.rs b/crates/bevy_gltf_blueprints/src/animation.rs index 2185142..ca55507 100644 --- a/crates/bevy_gltf_blueprints/src/animation.rs +++ b/crates/bevy_gltf_blueprints/src/animation.rs @@ -29,8 +29,6 @@ pub struct InstanceAnimations { /// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid pub struct InstanceAnimationPlayerLink(pub Entity); - - /// Stores Animation information: name, frame informations etc #[derive(Reflect, Default, Debug)] pub struct AnimationInfo { diff --git a/testing/bevy_example/assets/registry.json b/testing/bevy_example/assets/registry.json index 2cdc5bb..03cf205 100644 --- a/testing/bevy_example/assets/registry.json +++ b/testing/bevy_example/assets/registry.json @@ -110,6 +110,19 @@ "type": "array", "typeInfo": "List" }, + "alloc::vec::Vec": { + "isComponent": false, + "isResource": false, + "items": { + "type": { + "$ref": "#/$defs/bevy_gltf_blueprints::animation::AnimationInfo" + } + }, + "short_name": "Vec", + "title": "alloc::vec::Vec", + "type": "array", + "typeInfo": "List" + }, "alloc::vec::Vec": { "isComponent": false, "isResource": false, @@ -2947,36 +2960,36 @@ "type": "object", "typeInfo": "Struct" }, - "bevy_example::game::Marker1": { + "bevy_example::game::animation::Marker1": { "additionalProperties": false, "isComponent": true, "isResource": false, "properties": {}, "required": [], "short_name": "Marker1", - "title": "bevy_example::game::Marker1", + "title": "bevy_example::game::animation::Marker1", "type": "object", "typeInfo": "Struct" }, - "bevy_example::game::Marker2": { + "bevy_example::game::animation::Marker2": { "additionalProperties": false, "isComponent": true, "isResource": false, "properties": {}, "required": [], "short_name": "Marker2", - "title": "bevy_example::game::Marker2", + "title": "bevy_example::game::animation::Marker2", "type": "object", "typeInfo": "Struct" }, - "bevy_example::game::Marker3": { + "bevy_example::game::animation::Marker3": { "additionalProperties": false, "isComponent": true, "isResource": false, "properties": {}, "required": [], "short_name": "Marker3", - "title": "bevy_example::game::Marker3", + "title": "bevy_example::game::animation::Marker3", "type": "object", "typeInfo": "Struct" }, @@ -3549,25 +3562,90 @@ "type": "object", "typeInfo": "Struct" }, - "bevy_gltf_blueprints::animation::Animated": { + "bevy_gltf_blueprints::animation::AnimationInfo": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "properties": { + "frame_end": { + "type": { + "$ref": "#/$defs/f32" + } + }, + "frame_end_override": { + "type": { + "$ref": "#/$defs/f32" + } + }, + "frame_start": { + "type": { + "$ref": "#/$defs/f32" + } + }, + "frame_start_override": { + "type": { + "$ref": "#/$defs/f32" + } + }, + "frames_length": { + "type": { + "$ref": "#/$defs/f32" + } + }, + "name": { + "type": { + "$ref": "#/$defs/alloc::string::String" + } + } + }, + "required": [ + "name", + "frame_start", + "frame_end", + "frames_length", + "frame_start_override", + "frame_end_override" + ], + "short_name": "AnimationInfo", + "title": "bevy_gltf_blueprints::animation::AnimationInfo", + "type": "object", + "typeInfo": "Struct" + }, + "bevy_gltf_blueprints::animation::AnimationInfos": { "additionalProperties": false, "isComponent": true, "isResource": false, "properties": { "animations": { "type": { - "$ref": "#/$defs/alloc::vec::Vec" + "$ref": "#/$defs/alloc::vec::Vec" } } }, "required": [ "animations" ], - "short_name": "Animated", - "title": "bevy_gltf_blueprints::animation::Animated", + "short_name": "AnimationInfos", + "title": "bevy_gltf_blueprints::animation::AnimationInfos", "type": "object", "typeInfo": "Struct" }, + "bevy_gltf_blueprints::animation::AnimationMarkers": { + "isComponent": true, + "isResource": false, + "items": false, + "prefixItems": [ + { + "type": { + "$ref": "#/$defs/bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>" + } + } + ], + "short_name": "AnimationMarkers", + "title": "bevy_gltf_blueprints::animation::AnimationMarkers", + "type": "array", + "typeInfo": "TupleStruct" + }, "bevy_gltf_blueprints::animation::BlueprintAnimations": { "additionalProperties": false, "isComponent": true, @@ -10934,6 +11012,32 @@ "type": "object", "typeInfo": "Map" }, + "bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": { + "additionalProperties": { + "type": { + "$ref": "#/$defs/bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>" + } + }, + "isComponent": false, + "isResource": false, + "short_name": "HashMap, DefaultHashBuilder>, DefaultHashBuilder>", + "title": "bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>", + "type": "object", + "typeInfo": "Map" + }, + "bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>": { + "additionalProperties": { + "type": { + "$ref": "#/$defs/alloc::vec::Vec" + } + }, + "isComponent": false, + "isResource": false, + "short_name": "HashMap, DefaultHashBuilder>", + "title": "bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>", + "type": "object", + "typeInfo": "Map" + }, "bevy_utils::smallvec::SmallVec<[bevy_ecs::entity::Entity; 8]>": { "isComponent": false, "isResource": false, diff --git a/testing/bevy_example/src/game/animation.rs b/testing/bevy_example/src/game/animation.rs new file mode 100644 index 0000000..71cba7e --- /dev/null +++ b/testing/bevy_example/src/game/animation.rs @@ -0,0 +1,249 @@ +use std::time::Duration; + +use bevy_gltf_blueprints::{AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations}; + +use bevy::{ + gltf::Gltf, prelude::* +}; +use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState}; + + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// flag component for testing +pub struct Marker1; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// flag component for testing +pub struct Marker2; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// flag component for testing +pub struct Marker3; + +#[derive(Resource)] +pub struct AnimTest(Handle); + + +pub fn setup_main_scene_animations( + asset_server: Res, + mut commands: Commands, +) { + commands.insert_resource(AnimTest(asset_server.load("models/World.glb"))); +} + +pub fn animations( + added_animation_players:Query<(Entity, &Name, &AnimationPlayer)>, + added_animation_infos:Query<(Entity, &Name, &AnimationInfos),(Added)>, + animtest: Res, + mut commands: Commands, + assets_gltf: Res>, + parents: Query<&Parent>, +) { + for (entity, name, animation_infos) in added_animation_infos.iter() { + //println!("animated stuf {:?} on entity {}", animation_infos, name); + let gltf = assets_gltf.get(&animtest.0).unwrap(); + let mut matching_data = true; + for animation_info in &animation_infos.animations { + if !gltf.named_animations.contains_key(&animation_info.name){ + matching_data = false; + break; + } + } + if matching_data { + println!("inserting Animations components into {} ({:?})", name, entity); + println!("Found match {:?}", gltf.named_animations); + commands.entity(entity).insert( + InstanceAnimations { + named_animations: gltf.named_animations.clone(), + }, + ); + for ancestor in parents.iter_ancestors(entity) { + if added_animation_players.contains(ancestor) { + // println!("found match with animationPlayer !! {:?}",names.get(ancestor)); + commands.entity(entity).insert(InstanceAnimationPlayerLink(ancestor)); + } + // info!("{:?} is an ancestor of {:?}", ancestor, player); + } + } + println!(""); + } +} + +pub fn play_animations( + animated_marker1: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With, With)>, + animated_marker2: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With, With)>, + animated_marker3: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations, &BlueprintAnimationPlayerLink, &BlueprintAnimations), (With, With)>, + + mut animation_players: Query<&mut AnimationPlayer>, + keycode: Res>, + +) { + if keycode.just_pressed(KeyCode::KeyM) { + for (link, animations) in animated_marker1.iter() { + println!("animations {:?}", animations.named_animations); + let mut animation_player = animation_players.get_mut(link.0).unwrap(); + let anim_name = "Blueprint1_move"; + animation_player + .play_with_transition( + animations + .named_animations + .get(anim_name) + .expect("animation name should be in the list") + .clone(), + Duration::from_secs(5), + ) + .repeat(); + } + } + if keycode.just_pressed(KeyCode::KeyJ) { + for (link, animations) in animated_marker1.iter() { + println!("animations {:?}", animations.named_animations); + let mut animation_player = animation_players.get_mut(link.0).unwrap(); + let anim_name = "Blueprint1_jump"; + animation_player + .play_with_transition( + animations + .named_animations + .get(anim_name) + .expect("animation name should be in the list") + .clone(), + Duration::from_secs(5), + ) + .repeat(); + } + } + + if keycode.just_pressed(KeyCode::KeyA) { + for (link, animations) in animated_marker2.iter() { + println!("animations {:?}", animations.named_animations); + let mut animation_player = animation_players.get_mut(link.0).unwrap(); + let anim_name = "Blueprint1_move"; + animation_player + .play_with_transition( + animations + .named_animations + .get(anim_name) + .expect("animation name should be in the list") + .clone(), + Duration::from_secs(5), + ) + .repeat(); + } + } + if keycode.just_pressed(KeyCode::KeyB) { + for (link, animations) in animated_marker2.iter() { + println!("animations {:?}", animations.named_animations); + let mut animation_player = animation_players.get_mut(link.0).unwrap(); + let anim_name = "Blueprint1_jump"; + animation_player + .play_with_transition( + animations + .named_animations + .get(anim_name) + .expect("animation name should be in the list") + .clone(), + Duration::from_secs(5), + ) + .repeat(); + } + } + + // play instance animation + if keycode.just_pressed(KeyCode::KeyW) { + for (link, animations, _, _) in animated_marker3.iter() { + println!("animations {:?}", animations.named_animations); + let mut animation_player = animation_players.get_mut(link.0).unwrap(); + let anim_name = "Blueprint8_move"; + animation_player + .play_with_transition( + animations + .named_animations + .get(anim_name) + .expect("animation name should be in the list") + .clone(), + Duration::from_secs(5), + ) + .repeat(); + } + } + // play blueprint animation + if keycode.just_pressed(KeyCode::KeyX) { + for (_, _, link, animations) in animated_marker3.iter() { + println!("animations {:?}", animations.named_animations); + let mut animation_player = animation_players.get_mut(link.0).unwrap(); + let anim_name = "Walk"; + animation_player + .play_with_transition( + animations + .named_animations + .get(anim_name) + .expect("animation name should be in the list") + .clone(), + Duration::from_secs(5), + ) + .repeat(); + } + } +} + +pub fn trigger_event_based_on_animation_marker( + animation_infos: Query<(Entity, &AnimationMarkers, &InstanceAnimationPlayerLink, &InstanceAnimations, &AnimationInfos)>, + animation_players: Query<&AnimationPlayer>, + animation_clips: Res>, + mut animation_marker_events: EventWriter +) { + for (entity, markers, link, animations, animation_infos) in animation_infos.iter() { + let animation_player = animation_players.get(link.0).unwrap(); + let animation_clip = animation_clips.get(animation_player.animation_clip()); + + if animation_clip.is_some(){ + // if marker_trackers.0.contains_key(k) + // marker_trackers.0 + // println!("Entity {:?} markers {:?}", entity, markers); + // println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions()); + + // FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing + let animation_name = animations.named_animations.iter().find_map(|(key, value)| if value == animation_player.animation_clip(){ Some(key) }else { None}); + if animation_name.is_some() { + let animation_name = animation_name.unwrap(); + + let animation_length_seconds = animation_clip.unwrap().duration(); + let animation_length_frames = animation_infos.animations.iter().find(|anim| &anim.name == animation_name).unwrap().frames_length; + // TODO: we also need to take playback speed into account + let time_in_animation = animation_player.elapsed() - (animation_player.completions() as f32) * animation_length_seconds; + let frame_seconds = (animation_length_frames as f32 / animation_length_seconds) * time_in_animation ; + let frame = frame_seconds as u32; + + + let matching_animation_marker = &markers.0[animation_name]; + if matching_animation_marker.contains_key(&frame) { + let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap(); + // println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame); + //emit an event , something like AnimationMarkerReached(entity, animation_name, frame, marker_name) + // FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc + for marker_name in matching_markers_per_frame { + animation_marker_events.send(AnimationMarkerReached { + entity: entity, + animation_name: animation_name.clone(), + frame: frame, + marker_name: marker_name.clone() + }); + } + } + } + + } + } +} + +pub fn react_to_animation_markers( + mut animation_marker_events: EventReader +) +{ + for event in animation_marker_events.read() { + println!("animation marker event {:?}", event) + } +} \ No newline at end of file diff --git a/testing/bevy_example/src/game/mod.rs b/testing/bevy_example/src/game/mod.rs index 1d368d9..3c55ed1 100644 --- a/testing/bevy_example/src/game/mod.rs +++ b/testing/bevy_example/src/game/mod.rs @@ -1,10 +1,13 @@ pub mod in_game; +pub mod animation; +pub use in_game::*; +pub use animation::*; + use std::{ collections::HashMap, fs, time::Duration }; use bevy_gltf_blueprints::{AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations}; -pub use in_game::*; use bevy::{ ecs::query, gltf::Gltf, prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, window::PrimaryWindow @@ -118,260 +121,6 @@ fn exit_game(mut app_exit_events: ResMut>) { app_exit_events.send(bevy::app::AppExit); } - -#[derive(Resource)] -struct MainAnimations(Vec>); - -#[derive(Resource)] -struct AnimTest(Handle); -fn setup_main_scene_animations( - asset_server: Res, - mut commands: Commands, -) { - /*commands.insert_resource(MainAnimations(vec![ - asset_server.load("models/World.glb#Blueprint1_jump"), - asset_server.load("models/World.glb#Blueprint1_move"), - - // asset_server.load("models/library/Blueprint6_animated.glb#Run"), - - ]));*/ - - commands.insert_resource(AnimTest(asset_server.load("models/World.glb"))); -} - -fn animations( - added_animation_players:Query<(Entity, &Name, &AnimationPlayer)>, - added_animation_infos:Query<(Entity, &Name, &AnimationInfos),(Added)>, - - animtest: Res, - - mut commands: Commands, - assets_gltf: Res>, - - parents: Query<&Parent>, - names: Query<&Name>, -) { - for (entity, name, animation_infos) in added_animation_infos.iter() { - //println!("animated stuf {:?} on entity {}", animation_infos, name); - let gltf = assets_gltf.get(&animtest.0).unwrap(); - let mut matching_data = true; - for animation_info in &animation_infos.animations { - if !gltf.named_animations.contains_key(&animation_info.name){ - matching_data = false; - break; - } - } - if matching_data { - println!("inserting Animations components into {} ({:?})", name, entity); - println!("Found match {:?}", gltf.named_animations); - commands.entity(entity).insert( - InstanceAnimations { - named_animations: gltf.named_animations.clone(), - }, - ); - for ancestor in parents.iter_ancestors(entity) { - if added_animation_players.contains(ancestor) { - // println!("found match with animationPlayer !! {:?}",names.get(ancestor)); - commands.entity(entity).insert(InstanceAnimationPlayerLink(ancestor)); - } - // info!("{:?} is an ancestor of {:?}", ancestor, player); - } - } - println!(""); - } -} - -fn play_animations( - animated_marker1: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With, With)>, - animated_marker2: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations), (With, With)>, - animated_marker3: Query<(&InstanceAnimationPlayerLink, &InstanceAnimations, &BlueprintAnimationPlayerLink, &BlueprintAnimations), (With, With)>, - - mut animation_players: Query<&mut AnimationPlayer>, - keycode: Res>, - -) { - if keycode.just_pressed(KeyCode::KeyM) { - for (link, animations) in animated_marker1.iter() { - println!("animations {:?}", animations.named_animations); - let mut animation_player = animation_players.get_mut(link.0).unwrap(); - let anim_name = "Blueprint1_move"; - animation_player - .play_with_transition( - animations - .named_animations - .get(anim_name) - .expect("animation name should be in the list") - .clone(), - Duration::from_secs(5), - ) - .repeat(); - } - } - if keycode.just_pressed(KeyCode::KeyJ) { - for (link, animations) in animated_marker1.iter() { - println!("animations {:?}", animations.named_animations); - let mut animation_player = animation_players.get_mut(link.0).unwrap(); - let anim_name = "Blueprint1_jump"; - animation_player - .play_with_transition( - animations - .named_animations - .get(anim_name) - .expect("animation name should be in the list") - .clone(), - Duration::from_secs(5), - ) - .repeat(); - } - } - - if keycode.just_pressed(KeyCode::KeyA) { - for (link, animations) in animated_marker2.iter() { - println!("animations {:?}", animations.named_animations); - let mut animation_player = animation_players.get_mut(link.0).unwrap(); - let anim_name = "Blueprint1_move"; - animation_player - .play_with_transition( - animations - .named_animations - .get(anim_name) - .expect("animation name should be in the list") - .clone(), - Duration::from_secs(5), - ) - .repeat(); - } - } - if keycode.just_pressed(KeyCode::KeyB) { - for (link, animations) in animated_marker2.iter() { - println!("animations {:?}", animations.named_animations); - let mut animation_player = animation_players.get_mut(link.0).unwrap(); - let anim_name = "Blueprint1_jump"; - animation_player - .play_with_transition( - animations - .named_animations - .get(anim_name) - .expect("animation name should be in the list") - .clone(), - Duration::from_secs(5), - ) - .repeat(); - } - } - - // play instance animation - if keycode.just_pressed(KeyCode::KeyW) { - for (link, animations, _, _) in animated_marker3.iter() { - println!("animations {:?}", animations.named_animations); - let mut animation_player = animation_players.get_mut(link.0).unwrap(); - let anim_name = "Blueprint8_move"; - animation_player - .play_with_transition( - animations - .named_animations - .get(anim_name) - .expect("animation name should be in the list") - .clone(), - Duration::from_secs(5), - ) - .repeat(); - } - } - // play blueprint animation - if keycode.just_pressed(KeyCode::KeyX) { - for (_, _, link, animations) in animated_marker3.iter() { - println!("animations {:?}", animations.named_animations); - let mut animation_player = animation_players.get_mut(link.0).unwrap(); - let anim_name = "Walk"; - animation_player - .play_with_transition( - animations - .named_animations - .get(anim_name) - .expect("animation name should be in the list") - .clone(), - Duration::from_secs(5), - ) - .repeat(); - } - } -} - -fn trigger_event_based_on_animation_marker( - animation_infos: Query<(Entity, &AnimationMarkers, &InstanceAnimationPlayerLink, &InstanceAnimations, &AnimationInfos)>, - animation_players: Query<&AnimationPlayer>, - animation_clips: Res>, - mut animation_marker_events: EventWriter -) { - for (entity, markers, link, animations, animation_infos) in animation_infos.iter() { - let animation_player = animation_players.get(link.0).unwrap(); - let animation_clip = animation_clips.get(animation_player.animation_clip()); - - if animation_clip.is_some(){ - // if marker_trackers.0.contains_key(k) - // marker_trackers.0 - // println!("Entity {:?} markers {:?}", entity, markers); - // println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions()); - - // FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing - let animation_name = animations.named_animations.iter().find_map(|(key, value)| if value == animation_player.animation_clip(){ Some(key) }else { None}); - if animation_name.is_some() { - let animation_name = animation_name.unwrap(); - - let animation_length_seconds = animation_clip.unwrap().duration(); - let animation_length_frames = animation_infos.animations.iter().find(|anim| &anim.name == animation_name).unwrap().frames_length; - // TODO: we also need to take playback speed into account - let time_in_animation = animation_player.elapsed() - (animation_player.completions() as f32) * animation_length_seconds; - let frame_seconds = (animation_length_frames as f32 / animation_length_seconds) * time_in_animation ; - let frame = frame_seconds as u32; - - - let matching_animation_marker = &markers.0[animation_name]; - if matching_animation_marker.contains_key(&frame) { - let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap(); - // println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame); - //emit an event , something like AnimationMarkerReached(entity, animation_name, frame, marker_name) - // FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc - for marker_name in matching_markers_per_frame { - animation_marker_events.send(AnimationMarkerReached { - entity: entity, - animation_name: animation_name.clone(), - frame: frame, - marker_name: marker_name.clone() - }); - } - } - } - - } - } -} - -fn react_to_animation_markers( - mut animation_marker_events: EventReader -) -{ - for event in animation_marker_events.read() { - println!("animation marker event {:?}", event) - } -} - -#[derive(Component, Reflect, Default, Debug)] -#[reflect(Component)] -/// flag component for testing -pub struct Marker1; - -#[derive(Component, Reflect, Default, Debug)] -#[reflect(Component)] -/// flag component for testing -pub struct Marker2; - -#[derive(Component, Reflect, Default, Debug)] -#[reflect(Component)] -/// flag component for testing -pub struct Marker3; - pub struct GamePlugin; impl Plugin for GamePlugin { fn build(&self, app: &mut App) {