diff --git a/crates/blenvy/README.md b/crates/blenvy/README.md index f586818..3b3a8fc 100644 --- a/crates/blenvy/README.md +++ b/crates/blenvy/README.md @@ -52,7 +52,7 @@ fn spawn_blueprint( if keycode.just_pressed(KeyCode::S) { let new_entity = commands.spawn(( BlueprintInfo(name: "Health_Pickup".to_string(), path:""), // mandatory !! - SpawnHere, // mandatory !! + SpawnBlueprint, // mandatory !! TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), // VERY important !! // any other component you want to insert )); @@ -115,7 +115,7 @@ You can spawn entities from blueprints like this: ```rust no_run commands.spawn(( BlueprintInfo("Health_Pickup".to_string()), // mandatory !! - SpawnHere, // mandatory !! + SpawnBlueprint, // mandatory !! TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), // optional // any other component you want to insert @@ -135,7 +135,7 @@ you can just add any additional components you need when spawning : ```rust no_run commands.spawn(( BlueprintInfo("Health_Pickup".to_string()), - SpawnHere, + SpawnBlueprint, TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), // from Rapier/bevy_xpbd: this means the entity will also have a velocity component when inserted into the world Velocity { @@ -153,7 +153,7 @@ for example ```rust no_run commands.spawn(( BlueprintInfo("Health_Pickup".to_string()), - SpawnHere, + SpawnBlueprint, TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), HealthPowerUp(20)// if this is component is also present inside the "Health_Pickup" blueprint, that one will be replaced with this component during spawning )) @@ -164,7 +164,7 @@ commands.spawn(( There is also a ```BluePrintBundle``` for convenience , which just has * a ```BlueprintInfo``` component - * a ```SpawnHere``` component + * a ```SpawnBlueprint``` component ## Additional information diff --git a/crates/blenvy/old/old.rs b/crates/blenvy/old/old.rs index 20724e5..0de0911 100644 --- a/crates/blenvy/old/old.rs +++ b/crates/blenvy/old/old.rs @@ -49,7 +49,7 @@ pub(crate) fn prepare_blueprints( Option<&Name>, Option<&BlueprintsList>, ), - (Added, Added, Without), + (Added, Added, Without), >, mut commands: Commands, diff --git a/crates/blenvy/old/spawn_from_blueprints copy.rs b/crates/blenvy/old/spawn_from_blueprints copy.rs index 4f18e55..cc48648 100644 --- a/crates/blenvy/old/spawn_from_blueprints copy.rs +++ b/crates/blenvy/old/spawn_from_blueprints copy.rs @@ -22,7 +22,7 @@ pub struct BlueprintPath(pub String); /// flag component needed to signify the intent to spawn a Blueprint #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] -pub struct SpawnHere; +pub struct SpawnBlueprint; #[derive(Component)] /// flag component for dynamically spawned scenes @@ -93,7 +93,7 @@ pub(crate) fn blueprints_prepare_spawn( Entity, &BlueprintPath, ), - (Added, Without, Without)>, + (Added, Without, Without)>, // before 0.14 we have to use a seperate query, after migrating we can query at the root level entities_with_assets: Query< diff --git a/crates/blenvy/old/spawn_post_process.rs b/crates/blenvy/old/spawn_post_process.rs index 71a4bbb..2479634 100644 --- a/crates/blenvy/old/spawn_post_process.rs +++ b/crates/blenvy/old/spawn_post_process.rs @@ -6,7 +6,7 @@ use bevy::scene::SceneInstance; // use bevy::utils::hashbrown::HashSet; use crate::{BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo, BlueprintReadyForPostProcess, BlueprintInstanceReady, BlueprintSpawning, SubBlueprintSpawnRoot, SubBlueprintsSpawnTracker}; -use crate::{SpawnHere, Spawned}; +use crate::{SpawnBlueprint, Spawned}; use crate::{ BlueprintEvent, CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren }; @@ -34,7 +34,7 @@ pub(crate) fn spawned_blueprint_post_process( // rename to ' // sub blueprint instances tracker Option<&SubBlueprintSpawnRoot> ), - (With, With, Added), + (With, With, Added), >, added_animation_players: Query<(Entity, &Parent), Added>, all_children: Query<&Children>, @@ -103,7 +103,7 @@ pub(crate) fn spawned_blueprint_post_process( // rename to ' } } - commands.entity(original).remove::(); + commands.entity(original).remove::(); commands.entity(original).remove::(); // commands.entity(original).remove::>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want //commands.entity(original).remove::(); // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ? diff --git a/crates/blenvy/src/blueprints/animation.rs b/crates/blenvy/src/blueprints/animation.rs index 6a61a65..380d754 100644 --- a/crates/blenvy/src/blueprints/animation.rs +++ b/crates/blenvy/src/blueprints/animation.rs @@ -6,6 +6,8 @@ use bevy::utils::HashMap; /// storage for animations for a given entity's BLUEPRINT (ie for example a characters animations), essentially a clone of gltf's `named_animations` pub struct BlueprintAnimations { pub named_animations: HashMap>, + pub named_indices: HashMap, + pub graph: Handle } #[derive(Component, Debug)] @@ -20,6 +22,7 @@ pub struct BlueprintAnimationPlayerLink(pub Entity); /// storage for scene level animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations` pub struct SceneAnimations { pub named_animations: HashMap>, + pub named_indices: HashMap } #[derive(Component, Debug)] diff --git a/crates/blenvy/src/blueprints/hot_reload.rs b/crates/blenvy/src/blueprints/hot_reload.rs index 38688d1..10f3e3c 100644 --- a/crates/blenvy/src/blueprints/hot_reload.rs +++ b/crates/blenvy/src/blueprints/hot_reload.rs @@ -2,7 +2,7 @@ use bevy::prelude::*; use bevy::asset::{AssetEvent, LoadedUntypedAsset}; use bevy::scene::SceneInstance; -use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintInfo, SpawnHere}; +use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintInfo, SpawnBlueprint}; pub(crate) fn react_to_asset_changes( mut gltf_events: EventReader>, @@ -33,7 +33,7 @@ pub(crate) fn react_to_asset_changes( .remove::() .remove::() .remove::() - .insert(SpawnHere); + .insert(SpawnBlueprint); break; } diff --git a/crates/blenvy/src/blueprints/mod.rs b/crates/blenvy/src/blueprints/mod.rs index f157b6a..cd13cd7 100644 --- a/crates/blenvy/src/blueprints/mod.rs +++ b/crates/blenvy/src/blueprints/mod.rs @@ -36,13 +36,13 @@ pub enum GltfBlueprintsSet { #[derive(Bundle)] pub struct BluePrintBundle { pub blueprint: BlueprintInfo, - pub spawn_here: SpawnHere, + pub spawn_here: SpawnBlueprint, } impl Default for BluePrintBundle { fn default() -> Self { BluePrintBundle { blueprint: BlueprintInfo{ name: "default".into(), path:"".into()}, - spawn_here: SpawnHere, + spawn_here: SpawnBlueprint, } } } @@ -105,7 +105,7 @@ impl Plugin for BlueprintsPlugin { .register_type::() .register_type::() - .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() @@ -136,7 +136,7 @@ impl Plugin for BlueprintsPlugin { blueprints_check_assets_loading, blueprints_assets_ready, blueprints_scenes_spawned, - blueprints_transfer_components, + blueprints_cleanup_spawned_scene, // post process inject_materials, diff --git a/crates/blenvy/src/blueprints/spawn_from_blueprints.rs b/crates/blenvy/src/blueprints/spawn_from_blueprints.rs index 8d0199e..f9cf745 100644 --- a/crates/blenvy/src/blueprints/spawn_from_blueprints.rs +++ b/crates/blenvy/src/blueprints/spawn_from_blueprints.rs @@ -1,9 +1,9 @@ use std::path::{Path, PathBuf}; -use bevy::{gltf::Gltf, prelude::*, render::view::visibility, scene::SceneInstance, transform::commands, utils::hashbrown::HashMap}; +use bevy::{gltf::Gltf, prelude::*, scene::SceneInstance, utils::hashbrown::HashMap}; use serde_json::Value; -use crate::{BlueprintAssets, BlueprintAssetsLoadState, AssetLoadTracker, BlenvyConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded}; +use crate::{AssetLoadTracker, BlenvyConfig, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded}; /// this is a flag component for our levels/game world #[derive(Component)] @@ -22,12 +22,7 @@ pub struct BlueprintInfo { /// flag component needed to signify the intent to spawn a Blueprint #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] -pub struct SpawnHere; - -#[derive(Component)] -/// flag component for dynamically spawned scenes -pub struct Spawned; - +pub struct SpawnBlueprint; #[derive(Component, Debug)] /// flag component added when a Blueprint instance ist Ready : ie : @@ -103,12 +98,28 @@ pub struct BlueprintSpawning; use gltf::Gltf as RawGltf; +/* +Overview of the Blueprint Spawning process + - Blueprint Load Assets + - Blueprint Assets Ready: spawn Blueprint's scene + - Blueprint Scene Ready (SceneInstance component is present): + - get list of sub Blueprints if any, inject sub blueprints spawn tracker + - Blueprint copy components to original entity, remove useless nodes + - Blueprint post process + - generate aabb (need full hierarchy in its final form) + - inject materials from library if needed + - Blueprint Ready + - bubble information up to parent blueprint instance + - if all sub_blueprints are ready => Parent blueprint Instance is ready + => distinguish between blueprint instances inside blueprint instances vs blueprint instances inside blueprints ?? +*/ + pub(crate) fn blueprints_prepare_spawn( blueprint_instances_to_spawn : Query< ( Entity, &BlueprintInfo, - ),(Added) + ),(Added) >, mut commands: Commands, asset_server: Res, @@ -118,7 +129,6 @@ asset_server: Res, info!("BLUEPRINT: to spawn detected: {:?} path:{:?}", blueprint_info.name, blueprint_info.path); //println!("all assets {:?}", all_assets); ////////////// - // we add the asset of the blueprint itself // TODO: add detection of already loaded data let untyped_handle = asset_server.load_untyped(&blueprint_info.path); @@ -140,13 +150,9 @@ asset_server: Res, /* prefetch attempt */ let gltf = RawGltf::open(format!("assets/{}", blueprint_info.path)).unwrap(); for scene in gltf.scenes() { - let foo_extras = scene.extras().clone().unwrap(); - - let lookup: HashMap = serde_json::from_str(&foo_extras.get()).unwrap(); - /*for (key, value) in lookup.clone().into_iter() { - println!("{} / {}", key, value); - }*/ - + let scene_extras = scene.extras().clone().unwrap(); + let lookup: HashMap = serde_json::from_str(&scene_extras.get()).unwrap(); + if lookup.contains_key("BlueprintAssets"){ let assets_raw = &lookup["BlueprintAssets"]; //println!("ASSETS RAW {}", assets_raw); @@ -155,12 +161,8 @@ asset_server: Res, for asset in all_assets.assets.iter() { let untyped_handle = asset_server.load_untyped(&asset.path); - //println!("untyped handle {:?}", untyped_handle); - //asset_server.load(asset.path); let asset_id = untyped_handle.id(); - //println!("ID {:?}", asset_id); let loaded = asset_server.is_loaded_with_dependencies(asset_id); - //println!("Loaded ? {:?}", loaded); if !loaded { asset_infos.push(AssetLoadTracker { name: asset.name.clone(), @@ -175,25 +177,28 @@ asset_server: Res, } // now insert load tracker + // if there are assets to load if !asset_infos.is_empty() { commands .entity(entity) - .insert(BlueprintAssetsLoadState { - all_loaded: false, - asset_infos, - ..Default::default() - }) - .insert(BlueprintAssetsNotLoaded) + .insert(( + BlueprintAssetsLoadState { + all_loaded: false, + asset_infos, + ..Default::default() + }, + BlueprintAssetsNotLoaded + )) ; } else { commands.entity(entity).insert(BlueprintAssetsLoaded); } - - + // add the blueprint spawning marker commands.entity(entity).insert(BlueprintSpawning); } } +/// This system tracks & updates the loading state of all blueprints assets pub(crate) fn blueprints_check_assets_loading( mut blueprint_assets_to_load: Query< (Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState), @@ -211,7 +216,6 @@ pub(crate) fn blueprints_check_assets_loading( for tracker in assets_to_load.asset_infos.iter_mut() { let asset_id = tracker.id; let loaded = asset_server.is_loaded_with_dependencies(asset_id); - // println!("loading {}: // load state: {:?}", tracker.name, asset_server.load_state(asset_id)); // FIXME: hack for now let mut failed = false;// asset_server.load_state(asset_id) == bevy::asset::LoadState::Failed(_error); @@ -230,6 +234,7 @@ pub(crate) fn blueprints_check_assets_loading( } let progress: f32 = loaded_amount as f32 / total as f32; assets_to_load.progress = progress; + // println!("LOADING: in progress for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0); if all_loaded { assets_to_load.all_loaded = true; @@ -240,10 +245,8 @@ pub(crate) fn blueprints_check_assets_loading( .entity(entity) .insert(BlueprintAssetsLoaded) .remove::() - //.remove::() //REMOVE it in release mode/ when hot reload is off, keep it for dev/hot reload + //.remove::() //REMOVE this component in release mode/ when hot reload is off, keep it for dev/hot reload ; - }else { - // println!("LOADING: in progress for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0); } } } @@ -265,13 +268,15 @@ pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query< Without, ), >, - - mut commands: Commands, + all_children: Query<&Children>, mut game_world: Query>, - assets_gltf: Res>, asset_server: Res, - children: Query<&Children>,) + + mut graphs: ResMut>, + + mut commands: Commands, + ) { for ( entity, @@ -319,17 +324,24 @@ pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query< } let mut original_children: Vec = vec![]; - if let Ok(c) = children.get(entity) { + if let Ok(c) = all_children.get(entity) { for child in c.iter() { original_children.push(*child); } } // TODO: not a fan of this + // prepare data for animations + let mut graph = AnimationGraph::new(); let mut named_animations:HashMap> = HashMap::new() ; - for (key, value) in gltf.named_animations.iter() { - named_animations.insert(key.to_string(), value.clone()); + let mut animation_indices:HashMap = HashMap::new(); + + for (key, clip) in gltf.named_animations.iter() { + named_animations.insert(key.to_string(), clip.clone()); + let animation_index = graph.add_clip(clip.clone(), 1.0, graph.root); + animation_indices.insert(key.to_string(), animation_index); } + let graph = graphs.add(graph); commands.entity(entity).insert(( SceneBundle { @@ -338,9 +350,12 @@ pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query< ..Default::default() }, OriginalChildren(original_children), + BlueprintAnimations { // these are animations specific to the inside of the blueprint - named_animations: named_animations//gltf.named_animations.clone(), + named_animations: named_animations, //gltf.named_animations.clone(), + named_indices: animation_indices, + graph }, )); @@ -358,7 +373,6 @@ pub(crate) fn blueprints_assets_ready(spawn_placeholders: Query< commands.entity(world).add_child(entity); } } - } } @@ -399,16 +413,13 @@ pub(crate) fn blueprints_scenes_spawned( info!("Done spawning blueprint scene for entity named {:?} (track root: {:?})", name, track_root); let mut sub_blueprint_instances: Vec = vec![]; let mut sub_blueprint_instance_names: Vec = vec![]; - let mut tracker_data: HashMap = HashMap::new(); if track_root.is_none() { for parent in all_parents.iter_ancestors(entity) { if with_blueprint_infos.get(parent).is_ok() { - println!("found a parent with blueprint_info {:?} for {:?}", all_names.get(parent), all_names.get(entity)); commands.entity(entity).insert(SubBlueprintSpawnRoot(parent));// Injecting to know which entity is the root - break; } } @@ -451,9 +462,9 @@ pub(crate) fn blueprints_scenes_spawned( println!("sub blueprint instances {:?}", sub_blueprint_instance_names); - // TODO: how about when no sub blueprints are present if tracker_data.keys().len() > 0 { - commands.entity(entity) + commands + .entity(entity) .insert(SubBlueprintsSpawnTracker{sub_blueprint_instances: tracker_data.clone()}); }else { commands.entity(entity).insert(BlueprintChildrenReady); @@ -475,16 +486,21 @@ pub struct BlueprintReadyForPostProcess; /// - it removes one level of useless nesting /// - it copies the blueprint's root components to the entity it was spawned on (original entity) /// - it copies the children of the blueprint scene into the original entity -/// - it add `AnimationLink` components so that animations can be controlled from the original entity -pub(crate) fn blueprints_transfer_components( +/// - 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( foo: Query<( Entity, &Children, &OriginalChildren, Option<&Name>, - Option<&SubBlueprintSpawnRoot>), + Option<&SubBlueprintSpawnRoot>, + &BlueprintAnimations, + Option<&NoInBlueprint> + ), Added >, + added_animation_players: Query<(Entity, &Parent), Added>, + mut sub_blueprint_trackers: Query<(Entity, &mut SubBlueprintsSpawnTracker, &BlueprintInfo)>, all_children: Query<&Children>, @@ -493,7 +509,7 @@ pub(crate) fn blueprints_transfer_components( all_names: Query<&Name> ) { - for (original, children, original_children, name, track_root) in foo.iter() { + for (original, children, original_children, name, track_root, animations, no_inblueprint) in foo.iter() { info!("YOOO ready !! removing empty nodes {:?}", name); if children.len() == 0 { @@ -511,6 +527,14 @@ pub(crate) fn blueprints_transfer_components( } } + // we flag all children of the blueprint instance with 'InBlueprint' + // can be usefull to filter out anything that came from blueprints vs normal children + if no_inblueprint.is_none() { + 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 + } + } + // copy components into from blueprint instance's blueprint_root_entity to original entity commands.add(CopyComponents { source: blueprint_root_entity, @@ -527,6 +551,31 @@ pub(crate) fn blueprints_transfer_components( } } + + if animations.named_animations.keys().len() > 0 { + for (added, parent) in added_animation_players.iter() { + if parent.get() == blueprint_root_entity { + // 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), + )); + + // since v0.14 you need both AnimationTransitions and AnimationGraph components/handle on the same entity as the animationPlayer + let transitions = AnimationTransitions::new(); + commands + .entity(added) + .insert(( + transitions, + animations.graph.clone() + )); + } + } + } + commands.entity(original) .insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can know it is now their "turn" @@ -574,7 +623,7 @@ pub(crate) fn blueprints_finalize_instances( for (entity, name, blueprint_info, hide_until_ready) in blueprint_instances.iter() { info!("Finalizing blueprint instance {:?}", name); commands.entity(entity) - .remove::() + .remove::() .remove::() .remove::() // .remove::>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want @@ -587,25 +636,8 @@ pub(crate) fn blueprints_finalize_instances( println!("REVEAAAL"); commands.entity(entity).insert(Visibility::Visible); } - - + blueprint_events.send(BlueprintEvent::InstanceReady {entity: entity, blueprint_name: blueprint_info.name.clone(), blueprint_path: blueprint_info.path.clone()}); } } -/* -=> annoying issue with the "nested" useless root node created by blender - => distinguish between blueprint instances inside blueprint instances vs blueprint instances inside blueprints ?? -BlueprintSpawning - - Blueprint Load Assets - - Blueprint Assets Ready: spawn Blueprint's scene - - Blueprint Scene Ready (SceneInstance component is present): - - get list of sub Blueprints if any, inject sub blueprints spawn tracker - - Blueprint copy components to original entity, remove useless nodes - - Blueprint post process - - generate aabb (need full hierarchy in its final form) - - inject materials from library if needed - - Blueprint Ready - - bubble information up to parent blueprint instance - - if all sub_blueprints are ready => Parent blueprint Instance is ready -*/ diff --git a/testing/bevy_example/art/testing.blend b/testing/bevy_example/art/testing.blend index 24db140..51f1c26 100644 Binary files a/testing/bevy_example/art/testing.blend and b/testing/bevy_example/art/testing.blend differ diff --git a/testing/bevy_example/assets/registry.json b/testing/bevy_example/assets/registry.json index 3075ec8..54a49df 100644 --- a/testing/bevy_example/assets/registry.json +++ b/testing/bevy_example/assets/registry.json @@ -12003,6 +12003,24 @@ } } }, + "bevy_utils::hashbrown::HashMap": { + "isComponent": false, + "isResource": false, + "keyType": { + "type": { + "$ref": "#/$defs/alloc::string::String" + } + }, + "long_name": "bevy_utils::hashbrown::HashMap", + "short_name": "HashMap", + "type": "object", + "typeInfo": "Map", + "valueType": { + "type": { + "$ref": "#/$defs/petgraph::graph::NodeIndex" + } + } + }, "bevy_utils::hashbrown::HashMap, bevy_utils::NoOpHash>": { "isComponent": false, "isResource": false, @@ -13286,14 +13304,26 @@ "isResource": false, "long_name": "blenvy::blueprints::animation::BlueprintAnimations", "properties": { + "graph": { + "type": { + "$ref": "#/$defs/bevy_asset::handle::Handle" + } + }, "named_animations": { "type": { "$ref": "#/$defs/bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>" } + }, + "named_indices": { + "type": { + "$ref": "#/$defs/bevy_utils::hashbrown::HashMap" + } } }, "required": [ - "named_animations" + "named_animations", + "named_indices", + "graph" ], "short_name": "BlueprintAnimations", "type": "object", @@ -13309,10 +13339,16 @@ "type": { "$ref": "#/$defs/bevy_utils::hashbrown::HashMap, bevy_utils::hashbrown::hash_map::DefaultHashBuilder>" } + }, + "named_indices": { + "type": { + "$ref": "#/$defs/bevy_utils::hashbrown::HashMap" + } } }, "required": [ - "named_animations" + "named_animations", + "named_indices" ], "short_name": "SceneAnimations", "type": "object", @@ -13435,14 +13471,14 @@ "type": "object", "typeInfo": "Struct" }, - "blenvy::blueprints::spawn_from_blueprints::SpawnHere": { + "blenvy::blueprints::spawn_from_blueprints::SpawnBlueprint": { "additionalProperties": false, "isComponent": true, "isResource": false, - "long_name": "blenvy::blueprints::spawn_from_blueprints::SpawnHere", + "long_name": "blenvy::blueprints::spawn_from_blueprints::SpawnBlueprint", "properties": {}, "required": [], - "short_name": "SpawnHere", + "short_name": "SpawnBlueprint", "type": "object", "typeInfo": "Struct" }, diff --git a/testing/bevy_example/src/game/animation.rs b/testing/bevy_example/src/game/animation.rs index 6ff29d9..a03dec9 100644 --- a/testing/bevy_example/src/game/animation.rs +++ b/testing/bevy_example/src/game/animation.rs @@ -5,7 +5,7 @@ use std::time::Duration; SceneAnimationPlayerLink, SceneAnimations, };*/ -use bevy::{gltf::Gltf, prelude::*}; +use bevy::{animation::RepeatAnimation, gltf::Gltf, prelude::*}; use blenvy::{ AnimationInfos, AnimationMarkerReached, BlueprintAnimationPlayerLink, BlueprintAnimations, @@ -79,11 +79,13 @@ pub fn animations( } } } -} +}*/ #[allow(clippy::type_complexity)] pub fn play_animations( - animated_marker1: Query< + animated_fox: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With>, + + /*animated_marker1: Query< (&SceneAnimationPlayerLink, &SceneAnimations), (With, With), >, @@ -99,32 +101,43 @@ pub fn play_animations( &BlueprintAnimations, ), (With, With), - >, + >, */ - animated_fox: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With>, - - mut animation_players: Query<&mut AnimationPlayer>, + mut animation_players: Query<(&mut AnimationPlayer, &mut AnimationTransitions)>, keycode: Res>, ) { if keycode.just_pressed(KeyCode::KeyP) { + println!("playing fox animation requested"); for (link, animations) in animated_fox.iter() { println!("animations {:?}", animations.named_animations); - let mut animation_player = animation_players.get_mut(link.0).unwrap(); - let anim_name = "Run"; - animation_player - .play_with_transition( - animations - .named_animations - .get(anim_name) - .expect("animation name should be in the list") - .clone(), - Duration::from_secs(5), + println!("LINK target {}", link.0); + let (mut animation_player, mut animation_transitions) = animation_players.get_mut(link.0).unwrap(); + let anim_name = "Survey"; + 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(); + + /*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);*/ + } } - if keycode.just_pressed(KeyCode::KeyM) { + /*if keycode.just_pressed(KeyCode::KeyM) { for (link, animations) in animated_marker1.iter() { println!("animations {:?}", animations.named_animations); let mut animation_player = animation_players.get_mut(link.0).unwrap(); @@ -229,9 +242,12 @@ pub fn play_animations( ) .repeat(); } - } + }*/ } -*/ + + + + pub fn react_to_animation_markers( mut animation_marker_events: EventReader, ) { diff --git a/testing/bevy_example/src/game/in_game.rs b/testing/bevy_example/src/game/in_game.rs index 8b235c6..5af07b5 100644 --- a/testing/bevy_example/src/game/in_game.rs +++ b/testing/bevy_example/src/game/in_game.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use blenvy::{AddToGameWorld, BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, SpawnHere}; +use blenvy::{AddToGameWorld, BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, SpawnBlueprint}; use crate::{GameState, InAppRunning}; //use bevy_rapier3d::prelude::Velocity; @@ -25,7 +25,7 @@ pub fn setup_game( BlueprintInfo{name: "World".into(), path: "levels/World.glb".into()}, HideUntilReady, bevy::prelude::Name::from("world"), //FIXME: not really needed ? could be infered from blueprint's name/ path - SpawnHere, + SpawnBlueprint, GameWorldTag, InAppRunning, )); @@ -43,7 +43,7 @@ pub fn spawn_test( mut game_world: Query<(Entity, &Children), With>, ) { - if keycode.just_pressed(KeyCode::KeyT) { + if keycode.just_pressed(KeyCode::KeyS) { let world = game_world.single_mut(); let world = world.1[0]; diff --git a/testing/bevy_example/src/game/mod.rs b/testing/bevy_example/src/game/mod.rs index a62181c..783e017 100644 --- a/testing/bevy_example/src/game/mod.rs +++ b/testing/bevy_example/src/game/mod.rs @@ -167,9 +167,9 @@ impl Plugin for GamePlugin { /* .add_systems(Update, (animations) .run_if(in_state(AppState::AppRunning)) .after(GltfBlueprintsSet::AfterSpawn) - ) + )*/ .add_systems(Update, play_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( diff --git a/tools/blenvy/README_auto_export.md b/tools/blenvy/README_auto_export.md index d3a3869..179dcbd 100644 --- a/tools/blenvy/README_auto_export.md +++ b/tools/blenvy/README_auto_export.md @@ -133,7 +133,7 @@ This issue has been resolved in v0.9. You can enable this option to automatically replace all the **collection instances** inside your main scene with blueprints - whenever you change your main scene (or your library scene , if that option is enabled), all your collection instances * will be replaced with empties (this will not be visible to you) - * those empties will have additional custom properties / components : ```BlueprintInfo``` & ```SpawnHere``` + * those empties will have additional custom properties / components : ```BlueprintInfo``` & ```SpawnBlueprint``` * your main scene/ level will be exported to a much more trimmed down gltf file (see next point) * all the original collections (that you used to create the instances) will be exported as **seperate gltf files** into the "library" folder diff --git a/tools/blenvy/TODO.md b/tools/blenvy/TODO.md index e2f1e0e..26a38f6 100644 --- a/tools/blenvy/TODO.md +++ b/tools/blenvy/TODO.md @@ -218,7 +218,7 @@ Bevy Side: - [x] make blueprint instances invisible until spawning is done to avoid "spawn flash"? - [x] make this controlable via an additional "HideUntilReady" component - [x] register "HideUntilReady" so users can set this on their blueprints in Blender directly -- [ ] restructure blueprint spawning +- [x] restructure blueprint spawning - [x] "blueprint ready" only be triggered after all its sub blueprints are ready - [x] "blueprintInstance ready"/finished BlueprintAssetsLoaded @@ -228,7 +228,7 @@ Bevy Side: - [x] fix issues with deeply nested blueprints - perhaps reverse logic by using iter_ascendants - [x] fix materials handling - - [ ] fix animations handling + - [x] fix animations handling - [ ] simplify testing example: - [x] remove use of rapier physics (or even the whole common boilerplate ?) diff --git a/tools/blenvy/add_ons/auto_export/common/duplicate_object.py b/tools/blenvy/add_ons/auto_export/common/duplicate_object.py index a3a6ddb..7157118 100644 --- a/tools/blenvy/add_ons/auto_export/common/duplicate_object.py +++ b/tools/blenvy/add_ons/auto_export/common/duplicate_object.py @@ -98,7 +98,7 @@ def duplicate_object(object, parent, combine_mode, destination_collection, bluep empty_obj = make_empty(original_name, object.location, object.rotation_euler, object.scale, destination_collection) """we inject the collection/blueprint name & path, as a component called 'BlueprintInfo', but we only do this in the empty, not the original object""" - empty_obj['SpawnHere'] = '()' + empty_obj['SpawnBlueprint'] = '()' empty_obj['BlueprintInfo'] = f'(name: "{blueprint_name}", path: "{blueprint_path}")' # we copy custom properties over from our original object to our empty diff --git a/tools/blenvy/tests/expected_component_values.py b/tools/blenvy/tests/expected_component_values.py index 84c7b1f..6164a74 100644 --- a/tools/blenvy/tests/expected_component_values.py +++ b/tools/blenvy/tests/expected_component_values.py @@ -89,7 +89,7 @@ expected_custom_property_values = {'bevy_animation::AnimationPlayer': '(animatio 'bevy_gltf_blueprints::animation::SceneAnimations': '(named_animations: "")', 'bevy_gltf_blueprints::materials::MaterialInfo': '(name: " ", source: " ")', 'bevy_gltf_blueprints::spawn_from_blueprints::BlueprintsList': '({})', - 'bevy_gltf_blueprints::spawn_from_blueprints::SpawnHere': '()', + 'bevy_gltf_blueprints::spawn_from_blueprints::SpawnBlueprint': '()', 'bevy_gltf_components::GltfProcessed': '()', 'bevy_gltf_components::blender_settings::lighting::BlenderBackgroundShader': '(color: Rgba(red:1.0, green:1.0, ' 'blue:0.0, alpha:1.0), strength: 0.0)', @@ -347,7 +347,7 @@ expected_custom_property_values_randomized = {'bevy_animation::AnimationPlayer': 'bevy_gltf_blueprints::animation::SceneAnimations': '(named_animations: "")', 'bevy_gltf_blueprints::materials::MaterialInfo': '(name: "sbnpsago", source: "piuzfbqp")', 'bevy_gltf_blueprints::spawn_from_blueprints::BlueprintsList': '({})', - 'bevy_gltf_blueprints::spawn_from_blueprints::SpawnHere': '()', + 'bevy_gltf_blueprints::spawn_from_blueprints::SpawnBlueprint': '()', 'bevy_gltf_components::GltfProcessed': '()', 'bevy_gltf_components::blender_settings::lighting::BlenderBackgroundShader': '(color: Rgba(red:0.5714026093482971, ' 'green:0.42888906598091125, '