Skip to content

Commit

Permalink
feat(Blenvy:Bevy): overhauled & added back blueprint animation marker…
Browse files Browse the repository at this point in the history
…s & co handling

 * still messy, but way better
 * also worked on adding back the same feature for scene/instance animations
  • Loading branch information
kaosat-dev committed Jul 17, 2024
1 parent ec7dc2c commit 5f955c1
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 93 deletions.
182 changes: 100 additions & 82 deletions crates/blenvy/src/blueprints/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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 {
Expand Down Expand Up @@ -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<AnimationMarkerReached>,

animation_clips: Res<Assets<AnimationClip>>,
) {
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<(
Expand All @@ -87,14 +165,30 @@ pub fn trigger_instance_animation_markers_events(
&SceneAnimations,
&AnimationInfos,
)>,
animation_players: Query<(&AnimationPlayer)>,
animation_players: Query<&AnimationPlayer>,
animation_clips: Res<Assets<AnimationClip>>,
animation_graphs: Res<Assets<AnimationGraph>>,
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
) {
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() {
Expand Down Expand Up @@ -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<Assets<AnimationClip>>,
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
) {
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;
}
}
}*/
}
}
*/
4 changes: 2 additions & 2 deletions crates/blenvy/src/blueprints/hot_reload.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<AssetToBlueprintInstancesMapper>,
all_parents: Query<&Parent>,
spawning_blueprints: Query<&BlueprintSpawning>,
Expand Down
8 changes: 5 additions & 3 deletions crates/blenvy/src/blueprints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
21 changes: 15 additions & 6 deletions crates/blenvy/src/blueprints/spawn_from_blueprints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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
}


Expand Down Expand Up @@ -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() {
Expand All @@ -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));

}
}
}
}
Expand Down

0 comments on commit 5f955c1

Please sign in to comment.