diff --git a/crates/blenvy/src/blueprints/animation.rs b/crates/blenvy/src/blueprints/animation.rs index 6f381b5..0b4186c 100644 --- a/crates/blenvy/src/blueprints/animation.rs +++ b/crates/blenvy/src/blueprints/animation.rs @@ -17,6 +17,11 @@ pub struct BlueprintAnimations { /// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid pub struct BlueprintAnimationPlayerLink(pub Entity); +#[derive(Component, Debug)] +/// Same as the above but for `AnimationInfos` components which get added (on the Blender side) to the entities that actually have the animations +/// which often is not the Blueprint or blueprint instance entity itself. +pub struct BlueprintAnimationInfosLink(pub Entity); + #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] /// storage for scene level animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations` @@ -33,6 +38,11 @@ pub struct SceneAnimations { /// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid pub struct SceneAnimationPlayerLink(pub Entity); +#[derive(Component, Debug)] +/// Same as the above but for scene's `AnimationInfos` components which get added (on the Blender side) to the entities that actually have the animations +/// which often is not the Blueprint or blueprint instance entity itself. +pub struct SceneAnimationInfosLink(pub Entity); + /// Stores Animation information: name, frame informations etc #[derive(Reflect, Default, Debug)] pub struct AnimationInfo { @@ -77,7 +87,75 @@ pub struct AnimationMarkerReached { ///////////////////// -/* + +/// triggers events when a given animation marker is reached for BLUEPRINT animations +pub fn trigger_blueprint_animation_markers_events( + animation_data: Query<(Entity, &BlueprintAnimationPlayerLink, &BlueprintAnimationInfosLink, &BlueprintAnimations)>, + // FIXME: annoying hiearchy issue yet again: the Markers & AnimationInfos are stored INSIDE the blueprint, so we need to access them differently + animation_infos: Query<(&AnimationInfos, &AnimationMarkers)>, + animation_players: Query<&AnimationPlayer>, + mut animation_marker_events: EventWriter, + + animation_clips: Res>, +) { + for (entity, player_link, infos_link, animations) in animation_data.iter() { + + for (animation_name, node_index) in animations.named_indices.iter() { + + let animation_player = animation_players.get(player_link.0).unwrap(); + let (animation_infos, animation_markers) = animation_infos.get(infos_link.0).unwrap(); + + if animation_player.animation_is_playing(*node_index) { + if let Some(animation) = animation_player.animation(*node_index) { + // animation.speed() + // animation.completions() + if let Some(animation_clip_handle) = animations.named_animations.get(animation_name) { + if let Some(animation_clip) = animation_clips.get(animation_clip_handle) { + let animation_length_seconds = animation_clip.duration(); + let animation_length_frames = animation_infos // FIXME: horribly inneficient + .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.elapsed() + - (animation.completions() as f32) * animation_length_seconds; + let frame_seconds = (animation_length_frames / animation_length_seconds) + * time_in_animation; + // println!("frame seconds {}", frame_seconds); + let frame = frame_seconds.ceil() as u32; // FIXME , bad hack + + let matching_animation_marker = &animation_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); + // FIXME: complete hack-ish solution , otherwise this can fire multiple times in a row, depending on animation length , speed , etc + let diff = frame as f32 - frame_seconds; + if diff < 0.1 { + for marker_name in matching_markers_per_frame { + animation_marker_events.send(AnimationMarkerReached { + entity, + animation_name: animation_name.clone(), + frame, + marker_name: marker_name.clone(), + }); + } + } + } + } + } + } + } + } + } +} + + /// triggers events when a given animation marker is reached for INSTANCE animations pub fn trigger_instance_animation_markers_events( animation_infos: Query<( @@ -87,14 +165,30 @@ pub fn trigger_instance_animation_markers_events( &SceneAnimations, &AnimationInfos, )>, - animation_players: Query<(&AnimationPlayer)>, + animation_players: Query<&AnimationPlayer>, animation_clips: Res>, animation_graphs: 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()); + for (entity, markers, player_link, animations, animation_infos) in animation_infos.iter() { + //let (animation_player, animation_transitions) = animation_players.get(player_link.0).unwrap(); + //let foo = animation_transitions.get_main_animation().unwrap(); + + for (animation_name, node_index) in animations.named_indices.iter() { + let animation_player = animation_players.get(player_link.0).unwrap(); + if animation_player.animation_is_playing(*node_index) { + if let Some(animation) = animation_player.animation(*node_index) { + if let Some(animation_clip_handle) = animations.named_animations.get(animation_name) { + if let Some(animation_clip) = animation_clips.get(animation_clip_handle) { + + println!("helooo") + } + } + } + } + } + + /*let animation_clip = animation_clips.get(animation_player.animation_clip()); // animation_player.play(animation) if animation_clip.is_some() { @@ -144,82 +238,6 @@ pub fn trigger_instance_animation_markers_events( } } } - } + }*/ } } - -/// triggers events when a given animation marker is reached for BLUEPRINT animations -pub fn trigger_blueprint_animation_markers_events( - animation_infos: Query<(Entity, &BlueprintAnimationPlayerLink, &BlueprintAnimations)>, - // FIXME: annoying hiearchy issue yet again: the Markers & AnimationInfos are stored INSIDE the blueprint, so we need to access them differently - all_animation_infos: Query<(Entity, &AnimationMarkers, &AnimationInfos, &Parent)>, - animation_players: Query<&AnimationPlayer>, - animation_clips: Res>, - mut animation_marker_events: EventWriter, -) { - for (entity, link, animations) in animation_infos.iter() { - let animation_player = animation_players.get(link.0).unwrap(); - let animation_clip = animation_clips.get(animation_player.animation_clip()); - - // FIXME: horrible code - for (_, markers, animation_infos, parent) in all_animation_infos.iter() { - if parent.get() == entity { - if animation_clip.is_some() { - // 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 / animation_length_seconds) - * time_in_animation; - // println!("frame seconds {}", frame_seconds); - let frame = frame_seconds.ceil() as u32; // FIXME , bad hack - - 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 AnimationMarkerReached(entity, animation_name, frame, marker_name) - // FIXME: complete hack-ish solution , otherwise this can fire multiple times in a row, depending on animation length , speed , etc - let diff = frame as f32 - frame_seconds; - println!("diff {}", diff); - if diff < 0.1 { - for marker in matching_markers_per_frame { - animation_marker_events.send(AnimationMarkerReached { - entity, - animation_name: animation_name.clone(), - frame, - marker_name: marker.clone(), - }); - } - } - } - } - } - - break; - } - } - } -} -*/ diff --git a/crates/blenvy/src/blueprints/hot_reload.rs b/crates/blenvy/src/blueprints/hot_reload.rs index bac6132..98e42fd 100644 --- a/crates/blenvy/src/blueprints/hot_reload.rs +++ b/crates/blenvy/src/blueprints/hot_reload.rs @@ -1,4 +1,4 @@ -use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintInfo, BlueprintInstanceReady, BlueprintSpawning, SpawnBlueprint, SubBlueprintsSpawnTracker}; +use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintInfo, BlueprintInstanceReady, BlueprintSpawning, FromBlueprint, SpawnBlueprint, SubBlueprintsSpawnTracker}; use bevy::asset::AssetEvent; use bevy::prelude::*; use bevy::scene::SceneInstance; @@ -21,7 +21,7 @@ pub(crate) fn react_to_asset_changes( &BlueprintInfo, Option<&Children>, )>, - // blueprint_children_entities: Query<&InBlueprint>, => can only be used if the entites are tagged, right now that is optional...perhaps do not make it optional + blueprint_children_entities: Query<&FromBlueprint>, //=> can only be used if the entites are tagged assets_to_blueprint_instances: Res, all_parents: Query<&Parent>, spawning_blueprints: Query<&BlueprintSpawning>, diff --git a/crates/blenvy/src/blueprints/mod.rs b/crates/blenvy/src/blueprints/mod.rs index d389696..d7b5ed9 100644 --- a/crates/blenvy/src/blueprints/mod.rs +++ b/crates/blenvy/src/blueprints/mod.rs @@ -136,13 +136,15 @@ impl Plugin for BlueprintsPlugin { .chain() .in_set(GltfBlueprintsSet::Spawn), ) - /* .add_systems( + + // animation + .add_systems( Update, ( - trigger_instance_animation_markers_events, trigger_blueprint_animation_markers_events, + trigger_instance_animation_markers_events ), - )*/ + ) // hot reload .add_systems(Update, react_to_asset_changes.run_if(hot_reload)); } diff --git a/crates/blenvy/src/blueprints/spawn_from_blueprints.rs b/crates/blenvy/src/blueprints/spawn_from_blueprints.rs index f82e04e..239afe0 100644 --- a/crates/blenvy/src/blueprints/spawn_from_blueprints.rs +++ b/crates/blenvy/src/blueprints/spawn_from_blueprints.rs @@ -4,7 +4,7 @@ use bevy::{gltf::Gltf, prelude::*, scene::SceneInstance, utils::hashbrown::HashM use serde_json::Value; use crate::{ - AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlenvyConfig, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, SceneAnimationPlayerLink, SceneAnimations + AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlenvyConfig, BlueprintAnimationInfosLink, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, SceneAnimationInfosLink, SceneAnimationPlayerLink, SceneAnimations }; /// this is a flag component for our levels/game world @@ -40,7 +40,7 @@ pub struct SpawnBlueprint; #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] /// flag component marking any spwaned child of blueprints -pub struct InBlueprint; +pub struct FromBlueprint; #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] @@ -570,10 +570,10 @@ pub(crate) fn blueprints_cleanup_spawned_scene( } } - // we flag all children of the blueprint instance with 'InBlueprint' + // we flag all children of the blueprint instance with 'FromBlueprint' // can be usefull to filter out anything that came from blueprints vs normal children for child in all_children.iter_descendants(blueprint_root_entity) { - commands.entity(child).insert(InBlueprint); // we do this here in order to avoid doing it to normal children + commands.entity(child).insert(FromBlueprint); // we do this here in order to avoid doing it to normal children } @@ -626,9 +626,14 @@ pub(crate) fn blueprints_cleanup_spawned_scene( all_names.get(child), all_names.get(original) ); - /*commands + commands .entity(original) - .insert((BlueprintAnimationPlayerLink(bla),)); */ + .insert(( + //BlueprintAnimationPlayerLink(bla), + BlueprintAnimationInfosLink(child) + )) + ; + } else { for parent in all_parents.iter_ancestors(child) { if animation_players.get(parent).is_ok() { @@ -651,6 +656,10 @@ pub(crate) fn blueprints_cleanup_spawned_scene( }, )); } + if with_animation_infos.get(parent).is_ok() { + commands.entity(child).insert(SceneAnimationInfosLink(parent)); + + } } } }