feat(Blenvy:Bevy): added built in support for Scene/Level Animations

* VERY clunky code so far, needs a massive cleanup, but works !
 * modified relevant code
 * modified testing/example
 * related minor tweaks & changes
 * small changes to blend project to test instance vs blueprint level animation controls
This commit is contained in:
kaosat.dev 2024-07-08 21:43:27 +02:00
parent f8a1482aaa
commit a947d3b7d1
10 changed files with 385 additions and 182 deletions

View File

@ -23,6 +23,7 @@ pub struct BlueprintAnimationPlayerLink(pub Entity);
pub struct SceneAnimations { pub struct SceneAnimations {
pub named_animations: HashMap<String, Handle<AnimationClip>>, pub named_animations: HashMap<String, Handle<AnimationClip>>,
pub named_indices: HashMap<String, AnimationNodeIndex>, pub named_indices: HashMap<String, AnimationNodeIndex>,
pub graph: Handle<AnimationGraph>,
} }
#[derive(Component, Debug)] #[derive(Component, Debug)]

View File

@ -56,9 +56,7 @@ pub struct BlueprintsPlugin {
impl Default for BlueprintsPlugin { impl Default for BlueprintsPlugin {
fn default() -> Self { fn default() -> Self {
Self { Self { aabbs: false }
aabbs: false,
}
} }
} }
@ -123,7 +121,7 @@ impl Plugin for BlueprintsPlugin {
( (
blueprints_prepare_spawn, blueprints_prepare_spawn,
blueprints_check_assets_loading, blueprints_check_assets_loading,
blueprints_assets_ready, blueprints_assets_loaded,
blueprints_scenes_spawned, blueprints_scenes_spawned,
blueprints_cleanup_spawned_scene, blueprints_cleanup_spawned_scene,
// post process // post process

View File

@ -4,8 +4,9 @@ use bevy::{gltf::Gltf, prelude::*, scene::SceneInstance, utils::hashbrown::HashM
use serde_json::Value; use serde_json::Value;
use crate::{ use crate::{
AssetLoadTracker, BlenvyConfig, BlueprintAnimationPlayerLink, BlueprintAnimations, AnimationInfos, AssetLoadTracker, BlenvyConfig, BlueprintAnimationPlayerLink,
BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded,
BlueprintAssetsNotLoaded, SceneAnimationPlayerLink, SceneAnimations,
}; };
/// this is a flag component for our levels/game world /// this is a flag component for our levels/game world
@ -64,6 +65,10 @@ pub(crate) struct OriginalChildren(pub Vec<Entity>);
/// as it would first become invisible before re-appearing again /// as it would first become invisible before re-appearing again
pub struct HideUntilReady; pub struct HideUntilReady;
#[derive(Component)]
/// marker component, gets added to all children of a currently spawning blueprint instance, can be usefull to avoid manipulating still in progress entities
pub struct BlueprintDisabled;
#[derive(Event, Debug)] #[derive(Event, Debug)]
pub enum BlueprintEvent { pub enum BlueprintEvent {
/// event fired when a blueprint has finished loading all of its assets & before it attempts spawning /// event fired when a blueprint has finished loading all of its assets & before it attempts spawning
@ -245,7 +250,7 @@ pub(crate) fn blueprints_check_assets_loading(
} }
} }
pub(crate) fn blueprints_assets_ready( pub(crate) fn blueprints_assets_loaded(
spawn_placeholders: Query< spawn_placeholders: Query<
( (
Entity, Entity,
@ -255,6 +260,7 @@ pub(crate) fn blueprints_assets_ready(
Option<&AddToGameWorld>, Option<&AddToGameWorld>,
Option<&Name>, Option<&Name>,
Option<&HideUntilReady>, Option<&HideUntilReady>,
Option<&AnimationInfos>,
), ),
( (
With<BlueprintAssetsLoaded>, With<BlueprintAssetsLoaded>,
@ -279,6 +285,7 @@ pub(crate) fn blueprints_assets_ready(
add_to_world, add_to_world,
name, name,
hide_until_ready, hide_until_ready,
animation_infos,
) in spawn_placeholders.iter() ) in spawn_placeholders.iter()
{ {
/*info!( /*info!(
@ -294,7 +301,7 @@ pub(crate) fn blueprints_assets_ready(
// info!("attempting to spawn {:?}", model_path); // info!("attempting to spawn {:?}", model_path);
let model_handle: Handle<Gltf> = asset_server.load(blueprint_info.path.clone()); // FIXME: kinda weird now let model_handle: Handle<Gltf> = asset_server.load(blueprint_info.path.clone()); // FIXME: kinda weird now
let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| { let blueprint_gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| {
panic!( panic!(
"gltf file {:?} should have been loaded", "gltf file {:?} should have been loaded",
&blueprint_info.path &blueprint_info.path
@ -302,13 +309,13 @@ pub(crate) fn blueprints_assets_ready(
}); });
// WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one // WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one
let main_scene_name = gltf let main_scene_name = blueprint_gltf
.named_scenes .named_scenes
.keys() .keys()
.next() .next()
.expect("there should be at least one named scene in the gltf file to spawn"); .expect("there should be at least one named scene in the gltf file to spawn");
let scene = &gltf.named_scenes[main_scene_name]; let scene = &blueprint_gltf.named_scenes[main_scene_name];
// transforms are optional, but still deal with them correctly // transforms are optional, but still deal with them correctly
let mut transforms: Transform = Transform::default(); let mut transforms: Transform = Transform::default();
@ -329,13 +336,16 @@ pub(crate) fn blueprints_assets_ready(
let mut named_animations: HashMap<String, Handle<AnimationClip>> = HashMap::new(); let mut named_animations: HashMap<String, Handle<AnimationClip>> = HashMap::new();
let mut animation_indices: HashMap<String, AnimationNodeIndex> = HashMap::new(); let mut animation_indices: HashMap<String, AnimationNodeIndex> = HashMap::new();
for (key, clip) in gltf.named_animations.iter() { for (key, clip) in blueprint_gltf.named_animations.iter() {
named_animations.insert(key.to_string(), clip.clone()); named_animations.insert(key.to_string(), clip.clone());
let animation_index = graph.add_clip(clip.clone(), 1.0, graph.root); let animation_index = graph.add_clip(clip.clone(), 1.0, graph.root);
animation_indices.insert(key.to_string(), animation_index); animation_indices.insert(key.to_string(), animation_index);
} }
let graph = graphs.add(graph); let graph = graphs.add(graph);
println!("Named animations : {:?}", named_animations.keys());
println!("ANIMATION INFOS: {:?}", animation_infos);
commands.entity(entity).insert(( commands.entity(entity).insert((
SceneBundle { SceneBundle {
scene: scene.clone(), scene: scene.clone(),
@ -344,6 +354,7 @@ pub(crate) fn blueprints_assets_ready(
}, },
OriginalChildren(original_children), OriginalChildren(original_children),
BlueprintAnimations { BlueprintAnimations {
// TODO: perhaps swap this out with SceneAnimations depending on whether we are spawning a level or a simple blueprint
// these are animations specific to the blueprint // these are animations specific to the blueprint
named_animations, named_animations,
named_indices: animation_indices, named_indices: animation_indices,
@ -466,6 +477,8 @@ pub(crate) fn blueprints_scenes_spawned(
} }
} }
} }
// Mark all components as "Disabled" (until Bevy gets this as first class feature)
commands.entity(child).insert(BlueprintDisabled);
} }
} }
@ -496,29 +509,30 @@ pub struct BlueprintReadyForPostProcess;
/// - it copies the children of the blueprint scene into the original entity /// - it copies the children of the blueprint scene into the original entity
/// - it adds an `AnimationLink` component containing the entity that has the AnimationPlayer so that animations can be controlled from the original entity /// - it adds an `AnimationLink` component containing the entity that has the AnimationPlayer so that animations can be controlled from the original entity
pub(crate) fn blueprints_cleanup_spawned_scene( pub(crate) fn blueprints_cleanup_spawned_scene(
foo: Query< blueprint_scenes: Query<
( (
Entity, Entity,
&Children, &Children,
&OriginalChildren, &OriginalChildren,
Option<&Name>, Option<&Name>,
Option<&SubBlueprintSpawnRoot>,
&BlueprintAnimations, &BlueprintAnimations,
Option<&NoInBlueprint>, Option<&NoInBlueprint>,
), ),
Added<BlueprintChildrenReady>, Added<BlueprintChildrenReady>,
>, >,
added_animation_players: Query<(Entity, &Parent), Added<AnimationPlayer>>, animation_players: Query<(Entity, &Parent), With<AnimationPlayer>>,
mut sub_blueprint_trackers: Query<&mut SubBlueprintsSpawnTracker, With<BlueprintInfo>>,
all_children: Query<&Children>, all_children: Query<&Children>,
all_parents: Query<&Parent>,
with_animation_infos: Query<&AnimationInfos>,
// FIXME: meh
anims: Query<&BlueprintAnimations>,
mut commands: Commands, mut commands: Commands,
all_names: Query<&Name>, all_names: Query<&Name>,
) { ) {
for (original, children, original_children, name, track_root, animations, no_inblueprint) in for (original, children, original_children, name, animations, no_inblueprint) in
foo.iter() blueprint_scenes.iter()
{ {
info!("YOOO ready !! removing empty nodes {:?}", name); info!("YOOO ready !! removing empty nodes {:?}", name);
@ -562,22 +576,67 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
} }
if animations.named_animations.keys().len() > 0 { if animations.named_animations.keys().len() > 0 {
for (added, parent) in added_animation_players.iter() { for (entity_with_player, parent) in animation_players.iter() {
if parent.get() == blueprint_root_entity { if parent.get() == blueprint_root_entity {
println!(
"FOUND ANIMATION PLAYER FOR {:?} {:?} ",
all_names.get(original),
all_names.get(entity_with_player)
);
// FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level // 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, // 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 // BUT we still want to have some marker/control at the root entity level, we add this
commands commands
.entity(original) .entity(original)
.insert((BlueprintAnimationPlayerLink(added),)); .insert((BlueprintAnimationPlayerLink(entity_with_player),)); // FIXME : this is only valid for per-blueprint logic, no per scene animations
// since v0.14 you need both AnimationTransitions and AnimationGraph components/handle on the same entity as the animationPlayer // since v0.14 you need both AnimationTransitions and AnimationGraph components/handle on the same entity as the animationPlayer
let transitions = AnimationTransitions::new(); let transitions = AnimationTransitions::new();
commands commands
.entity(added) .entity(entity_with_player)
.insert((transitions, animations.graph.clone())); .insert((transitions, animations.graph.clone()));
} }
} }
// VERY convoluted, but it works
for child in all_children.iter_descendants(blueprint_root_entity) {
if with_animation_infos.get(child).is_ok() {
// player is already on the same entity as the animation_infos
if animation_players.get(child).is_ok() {
println!(
"found BLUEPRINT animation player for {:?} at {:?} Root: {:?}",
all_names.get(child),
all_names.get(child),
all_names.get(original)
);
/*commands
.entity(original)
.insert((BlueprintAnimationPlayerLink(bla),)); */
} else {
for parent in all_parents.iter_ancestors(child) {
if animation_players.get(parent).is_ok() {
println!(
"found SCENE animation player for {:?} at {:?} Root: {:?}",
all_names.get(child),
all_names.get(parent),
all_names.get(original)
);
println!("INSERTING SCENE ANIMATIONS INTO");
let original_animations = anims.get(original).unwrap();
commands.entity(child).insert((
SceneAnimationPlayerLink(parent),
SceneAnimations {
named_animations: original_animations
.named_animations
.clone(),
named_indices: original_animations.named_indices.clone(),
graph: original_animations.graph.clone(),
},
));
}
}
}
}
}
} }
commands commands
@ -585,19 +644,54 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
.insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can know it is now their "turn" .insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can know it is now their "turn"
commands.entity(blueprint_root_entity).despawn_recursive(); // Remove the root entity that comes from the spawned-in scene commands.entity(blueprint_root_entity).despawn_recursive(); // Remove the root entity that comes from the spawned-in scene
}
}
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
pub struct BlueprintReadyForFinalizing;
pub(crate) fn blueprints_finalize_instances(
blueprint_instances: Query<
(
Entity,
Option<&Name>,
&BlueprintInfo,
Option<&SubBlueprintSpawnRoot>,
Option<&HideUntilReady>,
),
(With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>),
>,
mut sub_blueprint_trackers: Query<&mut SubBlueprintsSpawnTracker, With<BlueprintInfo>>,
all_children: Query<&Children>,
mut blueprint_events: EventWriter<BlueprintEvent>,
mut commands: Commands,
) {
for (entity, name, blueprint_info, parent_blueprint, hide_until_ready) in
blueprint_instances.iter()
{
info!("Finalizing blueprint instance {:?}", name);
commands
.entity(entity)
.remove::<BlueprintReadyForPostProcess>()
.remove::<BlueprintSpawning>()
.remove::<SpawnBlueprint>()
//.remove::<Handle<Scene>>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want
//.remove::<BlueprintAssetsLoadState>(); // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ?
//.remove::<BlueprintAssetsLoaded>();
.insert(BlueprintInstanceReady);
// Deal with sub blueprints
// now check if the current entity is a child blueprint instance of another entity // now check if the current entity is a child blueprint instance of another entity
// this should always be done last, as children should be finished before the parent can be processed correctly // this should always be done last, as children should be finished before the parent can be processed correctly
// TODO: perhaps use observers for these // TODO: perhaps use observers for these
if let Some(track_root) = track_root { if let Some(track_root) = parent_blueprint {
let root_name = all_names.get(track_root.0);
println!("got some root {:?}", root_name);
if let Ok(mut tracker) = sub_blueprint_trackers.get_mut(track_root.0) { if let Ok(mut tracker) = sub_blueprint_trackers.get_mut(track_root.0) {
tracker tracker
.sub_blueprint_instances .sub_blueprint_instances
.entry(original) .entry(entity)
.or_insert(true); .or_insert(true);
tracker.sub_blueprint_instances.insert(original, true); tracker.sub_blueprint_instances.insert(entity, true);
// TODO: ugh, my limited rust knowledge, this is bad code // TODO: ugh, my limited rust knowledge, this is bad code
let mut all_spawned = true; let mut all_spawned = true;
@ -613,37 +707,10 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
} }
} }
} }
}
}
#[derive(Component, Reflect, Debug)] for child in all_children.iter_descendants(entity) {
#[reflect(Component)] commands.entity(child).remove::<BlueprintDisabled>();
pub struct BlueprintReadyForFinalizing; }
pub(crate) fn blueprints_finalize_instances(
blueprint_instances: Query<
(
Entity,
Option<&Name>,
&BlueprintInfo,
Option<&HideUntilReady>,
),
(With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>),
>,
mut blueprint_events: EventWriter<BlueprintEvent>,
mut commands: Commands,
) {
for (entity, name, blueprint_info, hide_until_ready) in blueprint_instances.iter() {
info!("Finalizing blueprint instance {:?}", name);
commands
.entity(entity)
.remove::<SpawnBlueprint>()
.remove::<BlueprintSpawning>()
.remove::<BlueprintReadyForPostProcess>()
//.remove::<Handle<Scene>>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want
//.remove::<BlueprintAssetsLoadState>(); // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ?
//.remove::<BlueprintAssetsLoaded>();
.insert(BlueprintInstanceReady);
if hide_until_ready.is_some() { if hide_until_ready.is_some() {
commands.entity(entity).insert(Visibility::Visible); commands.entity(entity).insert(Visibility::Visible);

View File

@ -3705,14 +3705,25 @@
"type": "object", "type": "object",
"typeInfo": "Struct" "typeInfo": "Struct"
}, },
"bevy_example::game::animation::MarkerFox": { "bevy_example::game::animation::MarkerAllFoxes": {
"additionalProperties": false, "additionalProperties": false,
"isComponent": true, "isComponent": true,
"isResource": false, "isResource": false,
"long_name": "bevy_example::game::animation::MarkerFox", "long_name": "bevy_example::game::animation::MarkerAllFoxes",
"properties": {}, "properties": {},
"required": [], "required": [],
"short_name": "MarkerFox", "short_name": "MarkerAllFoxes",
"type": "object",
"typeInfo": "Struct"
},
"bevy_example::game::animation::MarkerSpecificFox": {
"additionalProperties": false,
"isComponent": true,
"isResource": false,
"long_name": "bevy_example::game::animation::MarkerSpecificFox",
"properties": {},
"required": [],
"short_name": "MarkerSpecificFox",
"type": "object", "type": "object",
"typeInfo": "Struct" "typeInfo": "Struct"
}, },
@ -13335,6 +13346,11 @@
"isResource": false, "isResource": false,
"long_name": "blenvy::blueprints::animation::SceneAnimations", "long_name": "blenvy::blueprints::animation::SceneAnimations",
"properties": { "properties": {
"graph": {
"type": {
"$ref": "#/$defs/bevy_asset::handle::Handle<bevy_animation::graph::AnimationGraph>"
}
},
"named_animations": { "named_animations": {
"type": { "type": {
"$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_asset::handle::Handle<bevy_animation::AnimationClip>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>" "$ref": "#/$defs/bevy_utils::hashbrown::HashMap<alloc::string::String, bevy_asset::handle::Handle<bevy_animation::AnimationClip>, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>"
@ -13348,7 +13364,8 @@
}, },
"required": [ "required": [
"named_animations", "named_animations",
"named_indices" "named_indices",
"graph"
], ],
"short_name": "SceneAnimations", "short_name": "SceneAnimations",
"type": "object", "type": "object",

View File

@ -9,7 +9,7 @@ use bevy::{animation::RepeatAnimation, gltf::Gltf, prelude::*};
use blenvy::{ use blenvy::{
AnimationInfos, AnimationMarkerReached, BlueprintAnimationPlayerLink, BlueprintAnimations, AnimationInfos, AnimationMarkerReached, BlueprintAnimationPlayerLink, BlueprintAnimations,
SceneAnimationPlayerLink, SceneAnimations, BlueprintDisabled, SceneAnimationPlayerLink, SceneAnimations,
}; };
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
@ -29,15 +29,13 @@ pub struct Marker3;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// flag component for testing /// flag component for testing; this is at the BLUEPRINT level
pub struct MarkerFox; pub struct MarkerAllFoxes;
#[derive(Resource)] #[derive(Component, Reflect, Default, Debug)]
pub struct AnimTest(Handle<Gltf>); #[reflect(Component)]
/// flag component for testing; this is at the INSTANCE level
pub fn setup_main_scene_animations(asset_server: Res<AssetServer>, mut commands: Commands) { pub struct MarkerSpecificFox;
commands.insert_resource(AnimTest(asset_server.load("levels/World.glb")));
}
/* /*
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@ -81,19 +79,90 @@ pub fn animations(
} }
}*/ }*/
pub fn check_animations(
// (&BlueprintAnimationPlayerLink, &BlueprintAnimations)
foxes: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&SceneAnimationPlayerLink>,
),
(With<MarkerAllFoxes>, Without<BlueprintDisabled>),
>,
foo: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&SceneAnimationPlayerLink>,
),
(With<Marker1>, Without<BlueprintDisabled>),
>,
bar: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&SceneAnimationPlayerLink>,
),
(With<Marker2>, Without<BlueprintDisabled>),
>,
baz: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&SceneAnimationPlayerLink>,
),
(With<Marker3>, Without<BlueprintDisabled>),
>,
bli: Query<(Entity, &AnimationInfos)>,
anim_players: Query<(Entity, &AnimationPlayer)>,
all_names: Query<&Name>,
) {
/*for bla in foxes.iter() {
println!("MarkerAllFoxes {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
for bla in foo.iter() {
println!("Marker1 {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
for bla in bar.iter() {
println!("Marker2 {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
for bla in baz.iter() {
println!("Marker3 {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
println!(""); */
/*for blo in bli.iter() {
println!("YOOOOO {:?}", all_names.get(blo.0))
}
for anim in anim_players.iter() {
println!("Players {:?}", all_names.get(anim.0))
}*/
}
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn play_animations( pub fn play_animations(
animated_fox: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With<MarkerFox>>, animated_foxes: Query<
(&BlueprintAnimationPlayerLink, &BlueprintAnimations),
With<MarkerAllFoxes>,
>,
animated_fox: Query<
(&BlueprintAnimationPlayerLink, &BlueprintAnimations),
With<MarkerSpecificFox>,
>,
/*animated_marker1: Query< animated_marker1: Query<
(&SceneAnimationPlayerLink, &SceneAnimations), (&SceneAnimationPlayerLink, &SceneAnimations),
(With<AnimationInfos>, With<Marker1>), (With<AnimationInfos>, With<Marker1>),
>, >,
animated_marker2: Query< animated_marker2: Query<
(&SceneAnimationPlayerLink, &SceneAnimations), (&SceneAnimationPlayerLink, &SceneAnimations),
(With<AnimationInfos>, With<Marker2>), (With<AnimationInfos>, With<Marker2>),
>, >,
animated_marker3: Query<
with_blueprint_and_scene_animations: Query<
( (
&SceneAnimationPlayerLink, &SceneAnimationPlayerLink,
&SceneAnimations, &SceneAnimations,
@ -101,15 +170,16 @@ pub fn play_animations(
&BlueprintAnimations, &BlueprintAnimations,
), ),
(With<AnimationInfos>, With<Marker3>), (With<AnimationInfos>, With<Marker3>),
>, */ >,
mut animation_players: Query<(&mut AnimationPlayer, &mut AnimationTransitions)>, mut animation_players: Query<(&mut AnimationPlayer, &mut AnimationTransitions)>,
keycode: Res<ButtonInput<KeyCode>>, keycode: Res<ButtonInput<KeyCode>>,
) { ) {
if keycode.just_pressed(KeyCode::KeyP) { if keycode.just_pressed(KeyCode::KeyQ) {
println!("playing fox animation requested"); println!("playing fox blueprint animation requested");
for (link, animations) in animated_fox.iter() { for (link, animations) in animated_fox.iter() {
println!("animations {:?}", animations.named_animations); println!("BAR");
println!("LINK target {}", link.0);
// println!("animations {:?}", animations.named_animations);
let (mut animation_player, mut animation_transitions) = let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap(); animation_players.get_mut(link.0).unwrap();
let anim_name = "Survey"; let anim_name = "Survey";
@ -134,114 +204,155 @@ pub fn play_animations(
println!("Playing animation {:?}", playing_animation); println!("Playing animation {:?}", playing_animation);
playing_animation.set_repeat(RepeatAnimation::Forever);*/ playing_animation.set_repeat(RepeatAnimation::Forever);*/
} }
println!("");
} }
/*if keycode.just_pressed(KeyCode::KeyM) { if keycode.just_pressed(KeyCode::KeyP) {
for (link, animations) in animated_marker1.iter() { println!("playing fox blueprint animation requested");
println!("animations {:?}", animations.named_animations); for (link, animations) in animated_foxes.iter() {
let mut animation_player = animation_players.get_mut(link.0).unwrap(); println!("FOO");
let anim_name = "Blueprint1_move";
animation_player // println!("animations {:?}", animations.named_animations);
.play_with_transition( let (mut animation_player, mut animation_transitions) =
animations animation_players.get_mut(link.0).unwrap();
.named_animations let anim_name = "Run";
.get(anim_name) let animation_index = animations
.expect("animation name should be in the list") .named_indices
.clone(), .get(anim_name)
Duration::from_secs(5), .expect("animation name should be in the list")
) .clone();
.repeat();
} animation_transitions
} .play(
if keycode.just_pressed(KeyCode::KeyJ) { &mut animation_player,
for (link, animations) in animated_marker1.iter() { animation_index,
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), Duration::from_secs(5),
) )
.repeat(); .repeat();
/*let Some((&playing_animation_index, _)) = animation_player.playing_animations().next() else {
continue;
};
let playing_animation = animation_player.animation_mut(playing_animation_index).unwrap();
println!("Playing animation {:?}", playing_animation);
playing_animation.set_repeat(RepeatAnimation::Forever);*/
} }
println!("");
} }
if keycode.just_pressed(KeyCode::KeyA) { if keycode.just_pressed(KeyCode::KeyO) {
for (link, animations) in animated_marker2.iter() { println!("playing marker 3 blueprint animation requested");
println!("animations {:?}", animations.named_animations); for (_, _, link, animations) in with_blueprint_and_scene_animations.iter() {
let mut animation_player = animation_players.get_mut(link.0).unwrap(); // This only works for entities that are spawned as part of the level, as scene animations are only there in that case
let anim_name = "Blueprint1_move"; // println!("animations {:?}", animations.named_animations.keys());
animation_player let (mut animation_player, mut animation_transitions) =
.play_with_transition( animation_players.get_mut(link.0).unwrap();
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"; let anim_name = "Walk";
animation_player let animation_index = animations
.play_with_transition( .named_indices
animations .get(anim_name)
.named_animations .expect("animation name should be in the list")
.get(anim_name) .clone();
.expect("animation name should be in the list")
.clone(), animation_transitions
.play(
&mut animation_player,
animation_index,
Duration::from_secs(5), Duration::from_secs(5),
) )
.repeat(); .repeat();
} }
}*/ }
if keycode.just_pressed(KeyCode::KeyI) {
println!("playing marker 3 scene animation requested");
for (link, animations, _, _) in with_blueprint_and_scene_animations.iter() {
//println!("animations {:?}", animations.named_animations.keys());
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint8_move";
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
&mut animation_player,
animation_index,
Duration::from_secs(5),
)
.repeat();
}
}
if keycode.just_pressed(KeyCode::KeyU) {
for (link, animations) in animated_marker1.iter() {
println!("animations {:?}", animations.named_animations);
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint1_move";
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
&mut animation_player,
animation_index,
Duration::from_secs(5),
)
.repeat();
}
}
if keycode.just_pressed(KeyCode::KeyY) {
for (link, animations) in animated_marker1.iter() {
println!("animations {:?}", animations.named_animations);
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint1_jump";
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
&mut animation_player,
animation_index,
Duration::from_secs(5),
)
.repeat();
}
}
if keycode.just_pressed(KeyCode::KeyT) {
for (link, animations) in animated_marker2.iter() {
println!("animations {:?}", animations.named_animations);
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint1_move";
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
&mut animation_player,
animation_index,
Duration::from_secs(5),
)
.repeat();
}
}
} }
pub fn react_to_animation_markers( pub fn react_to_animation_markers(

View File

@ -70,8 +70,8 @@ pub fn spawn_test(
.spawn(( .spawn((
BluePrintBundle { BluePrintBundle {
blueprint: BlueprintInfo { blueprint: BlueprintInfo {
name: "Blueprint1".into(), name: "Blueprint8_animated_no_bones".into(),
path: "blueprints/Blueprint1.glb".into(), path: "blueprints/Blueprint6_animated.glb".into(),
}, // FIXME }, // FIXME
..Default::default() ..Default::default()
}, },

View File

@ -169,7 +169,8 @@ impl Plugin for GamePlugin {
app.register_type::<Marker1>() app.register_type::<Marker1>()
.register_type::<Marker2>() .register_type::<Marker2>()
.register_type::<Marker3>() .register_type::<Marker3>()
.register_type::<MarkerFox>() .register_type::<MarkerAllFoxes>()
.register_type::<MarkerSpecificFox>()
.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame))) .add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
.add_systems(Update, (validate_export, check_for_gltf_events)) .add_systems(Update, (validate_export, check_for_gltf_events))
@ -178,12 +179,11 @@ impl Plugin for GamePlugin {
.add_systems(OnEnter(AppState::MenuRunning), start_game) .add_systems(OnEnter(AppState::MenuRunning), start_game)
.add_systems(OnEnter(AppState::AppRunning), setup_game) .add_systems(OnEnter(AppState::AppRunning), setup_game)
.add_systems(OnEnter(AppState::MenuRunning), setup_main_scene_animations)
/* .add_systems(Update, (animations) /* .add_systems(Update, (animations)
.run_if(in_state(AppState::AppRunning)) .run_if(in_state(AppState::AppRunning))
.after(GltfBlueprintsSet::AfterSpawn) .after(GltfBlueprintsSet::AfterSpawn)
)*/ )*/
.add_systems(Update, play_animations) .add_systems(Update, (play_animations, check_animations))
//.add_systems(Update, react_to_animation_markers) //.add_systems(Update, react_to_animation_markers)
/*.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once /*.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once

View File

@ -228,7 +228,15 @@ Bevy Side:
- [x] fix issues with deeply nested blueprints - [x] fix issues with deeply nested blueprints
- perhaps reverse logic by using iter_ascendants - perhaps reverse logic by using iter_ascendants
- [x] fix materials handling - [x] fix materials handling
- [x] fix animations handling - [ ] fix animations handling
- [x] fix/upgrade blueprint level animations
- [x] fix/upgrade scene level animations
- [ ] rename SceneAnimations to LevelAnimations (more coherent with the rest)
- [x] move sub blueprint handling to blueprints_finalize_instances
- [ ] look into component overriding , it seems broken:
- [ ] blueprint level/ collection level components are now visible in instances in Blender
- [ ] they do not seem to be transfered to the (instance) entity above:
could they be on the "empty node" ?
- [ ] simplify testing example: - [ ] simplify testing example:
- [x] remove use of rapier physics (or even the whole common boilerplate ?) - [x] remove use of rapier physics (or even the whole common boilerplate ?)
@ -238,16 +246,18 @@ Bevy Side:
- [ ] add hot reloading - [ ] add hot reloading
- [x] basics - [x] basics
- [x] make it enabled/disabled based on general flag - [x] make it enabled/disabled based on general flag
- [ ] make
- [ ] cleanup internals - [ ] cleanup internals
- [ ] review & change general component insertion & spawning ordering & logic
- [x] review & change general component insertion & spawning ordering & logic
- GltfComponentsSet::Injection => GltfBlueprintsSet::Spawn => GltfBlueprintsSet::AfterSpawn - GltfComponentsSet::Injection => GltfBlueprintsSet::Spawn => GltfBlueprintsSet::AfterSpawn
Injection => inject lights & co => spawn => afterSpawn Injection => inject lights & co => spawn => afterSpawn
=> Injection => inject lights & co => Injection => inject lights & co
- [ ] add a way of overriding assets for collection instances => doubt this is possible - [ ] add a way of overriding assets for collection instances => doubt this is possible
- [ ] cleanup all the spurious debug messages - [ ] cleanup all the spurious debug messages
- [ ] fix animation handling - [x] fix animation handling
- [ ] how to deal with animation graphs ? - [x] how to deal with animation graphs ?
- [ ] update main docs - [ ] update main docs
- [ ] rename project to Blenvy - [ ] rename project to Blenvy

View File

@ -9,4 +9,3 @@ custom_properties_to_filter_out = [
'Blenvy_scene_type', 'blenvy_scene_type', 'Blenvy_scene_type', 'blenvy_scene_type',
'Materials_path', 'Export_path' 'Materials_path', 'Export_path'
] ]
#['_combine', 'template', 'components_meta', 'Components_meta', 'Blenvy_scene_type']