feat(animation): added basic handling of animation markers
* added additional component on the Bevy side + experimented with handling the data in the testing setup * added parsing of animation markers on the Blender side + injection of custom property/ component * rough around the edges, but the basics work !
This commit is contained in:
parent
1f959c51bd
commit
c44d82e7dc
|
@ -34,4 +34,14 @@ pub struct InstanceAnimations {
|
|||
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
|
||||
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
|
||||
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
|
||||
pub struct InstanceAnimationPlayerLink(pub Entity);
|
||||
pub struct InstanceAnimationPlayerLink(pub Entity);
|
||||
|
||||
|
||||
pub struct AnimationMarker{
|
||||
pub frame:u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct AnimationMarkers(pub HashMap<String, HashMap<u32, Vec<String> >>);
|
|
@ -123,6 +123,10 @@ impl Plugin for BlueprintsPlugin {
|
|||
.register_type::<BlueprintAnimations>()
|
||||
.register_type::<InstanceAnimations>()
|
||||
.register_type::<Animated>()
|
||||
.register_type::<AnimationMarkers>()
|
||||
.register_type::<HashMap<u32, Vec<String>>>()
|
||||
.register_type::<HashMap<String, HashMap<u32, Vec<String> >>>()
|
||||
|
||||
|
||||
.register_type::<BlueprintsList>()
|
||||
.register_type::<Vec<String>>()
|
||||
|
|
Binary file not shown.
|
@ -3,7 +3,7 @@ use std::{
|
|||
collections::HashMap, fs, time::Duration
|
||||
};
|
||||
|
||||
use bevy_gltf_blueprints::{Animated, BlueprintAnimationPlayerLink, BlueprintAnimations, InstanceAnimationPlayerLink, InstanceAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet};
|
||||
use bevy_gltf_blueprints::{Animated, AnimationMarkers, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, BlueprintsList, GltfBlueprintsSet, InstanceAnimationPlayerLink, InstanceAnimations};
|
||||
pub use in_game::*;
|
||||
|
||||
use bevy::{
|
||||
|
@ -308,6 +308,39 @@ fn play_animations(
|
|||
}
|
||||
}
|
||||
|
||||
fn trigger_event_based_on_animation_marker(
|
||||
bla: Query<(Entity, &AnimationMarkers, &InstanceAnimationPlayerLink, &InstanceAnimations)>,
|
||||
animation_players: Query<&AnimationPlayer>,
|
||||
animation_clips: Res<Assets<AnimationClip>>
|
||||
) {
|
||||
for (entity, markers, link, animations) in bla.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(){
|
||||
// println!("Entity {:?} markers {:?}", entity, markers);
|
||||
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
|
||||
|
||||
let animation_total_length = animation_clip.unwrap().duration();
|
||||
let animation_total_frames = 80; // FIXME just for testing
|
||||
// TODO: we also need to take playback speed into account
|
||||
let time_in_animation = animation_player.elapsed() - (animation_player.completions() as f32) * animation_total_length;//(animation_player.elapsed() / (animation_player.completions() as f32 + 1.0)) ;// / animation_total_length;
|
||||
let time_bla = (animation_total_frames as f32 / animation_total_length) * time_in_animation ;
|
||||
let frame = time_bla as u32;
|
||||
// println!("time_in_animation {} out of {}, completions {}, // frame {}",time_in_animation, animation_total_length, animation_player.completions(), frame);
|
||||
//animation_player.animation_clip()
|
||||
|
||||
let matching_animation_marker = &markers.0[&"Blueprint1_jump".to_string()];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
/// flag component for testing
|
||||
|
@ -336,7 +369,7 @@ impl Plugin for GamePlugin {
|
|||
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
||||
|
||||
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
|
||||
.add_systems(Update, animations
|
||||
.add_systems(Update, (animations, trigger_event_based_on_animation_marker)
|
||||
.run_if(in_state(AppState::AppRunning))
|
||||
.after(GltfBlueprintsSet::AfterSpawn)
|
||||
)
|
||||
|
|
|
@ -56,8 +56,22 @@ def copy_animation_data(source, target):
|
|||
# sort animations alphabetically (case insensitive) so they have a defined order and match Blender's Action list
|
||||
blender_actions.sort(key = lambda a: a.name.lower())
|
||||
|
||||
markers_per_animation = {}
|
||||
for action in blender_actions:
|
||||
animations.append(blender_tracks[action.name])
|
||||
animation_name = blender_tracks[action.name]
|
||||
animations.append(animation_name)
|
||||
|
||||
markers_per_animation[animation_name] = {}
|
||||
|
||||
print("markers", action.pose_markers, "for", action.name)
|
||||
for marker in action.pose_markers:
|
||||
|
||||
if marker.frame not in markers_per_animation[animation_name]:
|
||||
markers_per_animation[animation_name][marker.frame] = []
|
||||
print(" marker", marker.name, marker.frame)
|
||||
|
||||
markers_per_animation[animation_name][marker.frame].append(marker.name)
|
||||
|
||||
print("animations", animations)
|
||||
|
||||
"""if target.animation_data == None:
|
||||
|
@ -67,13 +81,29 @@ def copy_animation_data(source, target):
|
|||
with bpy.context.temp_override(active_object=source, selected_editable_objects=[target]):
|
||||
bpy.ops.object.make_links_data(type='ANIMATION')
|
||||
# we add an "animated" flag component
|
||||
target['Animated'] = f'(animations: {animations})'.replace("'", '"') #'(animations: [])' #
|
||||
target['Animated'] = f'(animations: {animations})'.replace("'", '"')
|
||||
|
||||
markers_formated = '{'
|
||||
for animation in markers_per_animation.keys():
|
||||
markers_formated += f'"{animation}":'
|
||||
markers_formated += "{"
|
||||
for frame in markers_per_animation[animation].keys():
|
||||
markers = markers_per_animation[animation][frame]
|
||||
markers_formated += f"{frame}:{markers}, ".replace("'", '"')
|
||||
markers_formated += '}, '
|
||||
markers_formated += '}'
|
||||
print("markers_formated", markers_formated)
|
||||
target["AnimationMarkers"] = f'( {markers_formated} )'
|
||||
#'({"animation_name": {5: ["Marker_1"]} })'
|
||||
#f'({json.dumps(markers_per_animation)})'
|
||||
|
||||
"""print("copying animation data for", source.name, target.animation_data)
|
||||
properties = [p.identifier for p in source.animation_data.bl_rna.properties if not p.is_readonly]
|
||||
for prop in properties:
|
||||
print("copying stuff", prop)
|
||||
setattr(target.animation_data, prop, getattr(source.animation_data, prop))"""
|
||||
|
||||
|
||||
|
||||
def duplicate_object(object, parent, combine_mode, destination_collection, library_collections, legacy_mode, nester=""):
|
||||
copy = None
|
||||
|
@ -95,6 +125,10 @@ def duplicate_object(object, parent, combine_mode, destination_collection, libra
|
|||
children_per_collection = {}
|
||||
get_sub_collections([object.instance_collection], root_node, children_per_collection)
|
||||
empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
|
||||
|
||||
# empty_obj["AnimationMarkers"] = '({"animation_name": {5: "Marker_1"} })'
|
||||
|
||||
#'({5: "sdf"})'#.replace('"',"'") #f"({json.dumps(dict(animation_foo))})"
|
||||
#empty_obj["Assets"] = {"Animations": [], "Materials": [], "Models":[], "Textures":[], "Audio":[], "Other":[]}
|
||||
|
||||
# we copy custom properties over from our original object to our empty
|
||||
|
@ -109,10 +143,7 @@ def duplicate_object(object, parent, combine_mode, destination_collection, libra
|
|||
object.name = original_name + "____bak"
|
||||
copy = object.copy()
|
||||
copy.name = original_name
|
||||
# FIXME: orphan data comes from this one, not even sure if this copying is needed at all
|
||||
"""if object.data:
|
||||
data = object.data.copy()
|
||||
obj_copy.data = data"""
|
||||
|
||||
|
||||
destination_collection.objects.link(copy)
|
||||
|
||||
|
|
Loading…
Reference in New Issue