diff --git a/crates/bevy_gltf_blueprints/src/animation.rs b/crates/bevy_gltf_blueprints/src/animation.rs index ca55507..ee400b3 100644 --- a/crates/bevy_gltf_blueprints/src/animation.rs +++ b/crates/bevy_gltf_blueprints/src/animation.rs @@ -33,23 +33,23 @@ pub struct InstanceAnimationPlayerLink(pub Entity); #[derive(Reflect, Default, Debug)] pub struct AnimationInfo { pub name: String, - pub frame_start:f32, - pub frame_end:f32, - pub frames_length:f32, - pub frame_start_override:f32, - pub frame_end_override:f32 + pub frame_start: f32, + pub frame_end: f32, + pub frames_length: f32, + pub frame_start_override: f32, + pub frame_end_override: f32, } /// Stores information about animations, to make things a bit easier api wise: /// these components are automatically inserted by gltf_auto_export on entities that have animations #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] -pub struct AnimationInfos{ - pub animations: Vec +pub struct AnimationInfos { + pub animations: Vec, } -pub struct AnimationMarker{ - pub frame:u32, +pub struct AnimationMarker { + pub frame: u32, pub name: String, } @@ -57,25 +57,24 @@ pub struct AnimationMarker{ /// it is essentiall a hashmap of AnimationName => HashMap #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] -pub struct AnimationMarkers(pub HashMap >>); - +pub struct AnimationMarkers(pub HashMap>>); // FIXME: ugh, ugly, there has to be a better way to do this ? #[derive(Component, Default, Debug)] -pub struct AnimationMarkerTrackers(pub HashMap >>); +pub struct AnimationMarkerTrackers(pub HashMap>>); #[derive(Default, Debug)] -pub struct AnimationMarkerTracker{ +pub struct AnimationMarkerTracker { // pub frame:u32, // pub name: String, // pub processed_for_cycle: bool, - pub prev_frame: u32 + pub prev_frame: u32, } /// Event that gets triggered once a specific marker inside an animation has been reached (frame based) /// Provides some usefull information about which entity , wich animation, wich frame & which marker got triggered #[derive(Event, Debug)] -pub struct AnimationMarkerReached{ +pub struct AnimationMarkerReached { pub entity: Entity, pub animation_name: String, pub frame: u32, diff --git a/crates/bevy_gltf_blueprints/src/lib.rs b/crates/bevy_gltf_blueprints/src/lib.rs index c06f70b..612a0b0 100644 --- a/crates/bevy_gltf_blueprints/src/lib.rs +++ b/crates/bevy_gltf_blueprints/src/lib.rs @@ -119,18 +119,15 @@ impl Plugin for BlueprintsPlugin { .register_type::() .register_type::() .register_type::() - .register_type::() .register_type::() .register_type::() .register_type::() .register_type::>() - .register_type::() .register_type::>>() - .register_type:: >>>() + .register_type::>>>() .add_event::() - .register_type::() .register_type::>>() .insert_resource(BluePrintsConfig { diff --git a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs index 8931444..16367df 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; -use crate::{BlueprintAnimations, BluePrintsConfig}; +use crate::{BluePrintsConfig, BlueprintAnimations}; /// this is a flag component for our levels/game world #[derive(Component)] @@ -281,11 +281,12 @@ pub(crate) fn spawn_from_blueprints( }, Spawned, OriginalChildren(original_children), - BlueprintAnimations { // these are animations specific to the inside of the blueprint + BlueprintAnimations { + // these are animations specific to the inside of the blueprint named_animations: gltf.named_animations.clone(), - } + }, )); - + if add_to_world.is_some() { let world = game_world .get_single_mut() diff --git a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs index 0da0386..a88e950 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs @@ -85,7 +85,9 @@ pub(crate) fn spawned_blueprint_post_process( // FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level // and we cannot update animation clips so that the EntityPaths point to one level deeper, // BUT we still want to have some marker/control at the root entity level, we add this - commands.entity(original).insert(BlueprintAnimationPlayerLink(added)); + commands + .entity(original) + .insert(BlueprintAnimationPlayerLink(added)); } } } diff --git a/testing/bevy_example/src/game/animation.rs b/testing/bevy_example/src/game/animation.rs index 71cba7e..326b52b 100644 --- a/testing/bevy_example/src/game/animation.rs +++ b/testing/bevy_example/src/game/animation.rs @@ -1,12 +1,13 @@ 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_blueprints::{ + AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, + BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, + GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations, }; -use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState}; +use bevy::{gltf::Gltf, prelude::*}; +use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState}; #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] @@ -26,17 +27,13 @@ pub struct Marker3; #[derive(Resource)] pub struct AnimTest(Handle); - -pub fn setup_main_scene_animations( - asset_server: Res, - mut commands: Commands, -) { +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)>, + added_animation_players: Query<(Entity, &Name, &AnimationPlayer)>, + added_animation_infos: Query<(Entity, &Name, &AnimationInfos), (Added)>, animtest: Res, mut commands: Commands, assets_gltf: Res>, @@ -47,23 +44,26 @@ pub fn animations( 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){ + if !gltf.named_animations.contains_key(&animation_info.name) { matching_data = false; break; } } if matching_data { - println!("inserting Animations components into {} ({:?})", name, entity); + println!( + "inserting Animations components into {} ({:?})", + name, entity + ); println!("Found match {:?}", gltf.named_animations); - commands.entity(entity).insert( - InstanceAnimations { - named_animations: gltf.named_animations.clone(), - }, - ); + 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)); + commands + .entity(entity) + .insert(InstanceAnimationPlayerLink(ancestor)); } // info!("{:?} is an ancestor of {:?}", ancestor, player); } @@ -73,13 +73,26 @@ pub fn animations( } 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)>, + 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() { @@ -190,60 +203,76 @@ pub fn play_animations( } pub fn trigger_event_based_on_animation_marker( - animation_infos: Query<(Entity, &AnimationMarkers, &InstanceAnimationPlayerLink, &InstanceAnimations, &AnimationInfos)>, + animation_infos: Query<( + Entity, + &AnimationMarkers, + &InstanceAnimationPlayerLink, + &InstanceAnimations, + &AnimationInfos, + )>, animation_players: Query<&AnimationPlayer>, animation_clips: Res>, - mut animation_marker_events: EventWriter + 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 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}); + 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; + 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 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]; + 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() + 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 -) -{ + 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 3c55ed1..d6d6d14 100644 --- a/testing/bevy_example/src/game/mod.rs +++ b/testing/bevy_example/src/game/mod.rs @@ -1,16 +1,19 @@ -pub mod in_game; pub mod animation; -pub use in_game::*; +pub mod in_game; pub use animation::*; +pub use in_game::*; -use std::{ - collections::HashMap, fs, time::Duration +use std::{collections::HashMap, fs, time::Duration}; + +use bevy_gltf_blueprints::{ + AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, + BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, + GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations, }; -use bevy_gltf_blueprints::{AnimationInfos, AnimationMarkerReached, AnimationMarkerTrackers, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations}; - use bevy::{ - ecs::query, gltf::Gltf, prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, window::PrimaryWindow + ecs::query, gltf::Gltf, prelude::*, render::view::screenshot::ScreenshotManager, + time::common_conditions::on_timer, window::PrimaryWindow, }; use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState}; @@ -21,11 +24,10 @@ fn start_game(mut next_app_state: ResMut>) { next_app_state.set(AppState::AppLoading); } - // if the export from Blender worked correctly, we should have animations (simplified here by using AnimationPlayerLink) // if the export from Blender worked correctly, we should have an Entity called "Blueprint4_nested" that has a child called "Blueprint3" that has a "BlueprintName" component with value Blueprint3 // if the export from Blender worked correctly, we should have a blueprints_list -// if the export from Blender worked correctly, we should have the correct tree of entities +// if the export from Blender worked correctly, we should have the correct tree of entities #[allow(clippy::too_many_arguments)] fn validate_export( parents: Query<&Parent>, @@ -36,7 +38,7 @@ fn validate_export( empties_candidates: Query<(Entity, &Name, &GlobalTransform)>, blueprints_list: Query<(Entity, &BlueprintsList)>, - root: Query<(Entity, &Name, &Children), (Without, With)> + root: Query<(Entity, &Name, &Children), (Without, With)>, ) { let animations_found = !animation_player_links.is_empty(); @@ -82,22 +84,23 @@ fn validate_export( let mut tree: HashMap> = HashMap::new(); for child in children.iter_descendants(root.0) { - let child_name:String = names.get(child).map_or(String::from("no_name"), |e| e.to_string() ); //|e| e.to_string(), || "no_name".to_string()); - //println!(" child {}", child_name); + let child_name: String = names + .get(child) + .map_or(String::from("no_name"), |e| e.to_string()); //|e| e.to_string(), || "no_name".to_string()); + //println!(" child {}", child_name); let parent = parents.get(child).unwrap(); - let parent_name:String = names.get(parent.get()).map_or(String::from("no_name"), |e| e.to_string() ); //|e| e.to_string(), || "no_name".to_string()); - tree.entry(parent_name).or_default().push(child_name.clone()); - } + let parent_name: String = names + .get(parent.get()) + .map_or(String::from("no_name"), |e| e.to_string()); //|e| e.to_string(), || "no_name".to_string()); + tree.entry(parent_name) + .or_default() + .push(child_name.clone()); + } let hierarchy = to_json_string(&tree); - fs::write( - "bevy_hierarchy.json", - hierarchy - ) - .expect("unable to write hierarchy file") + fs::write("bevy_hierarchy.json", hierarchy).expect("unable to write hierarchy file") } - fs::write( "bevy_diagnostics.json", format!(