feat(Blenvy): continued restructuring & upgrade of blueprint spawning logic

* updated simple test to include deep nested blueprint instance within blueprint instance case
 * restructured materials handling to use the new logic, removed clutter
 * experimenting with solutions to the deeply nested blueprint instances problem
 * moved out all obsolete code to "old" folder, keeeping around until all problems are solved
 * added hack-ish solution to the "aabb system as post process" implementation
 * various minor boilerprate changes
This commit is contained in:
kaosat.dev 2024-07-05 12:56:51 +02:00
parent 6dde9823ed
commit fbcd025dc1
12 changed files with 314 additions and 188 deletions

View File

@ -0,0 +1,190 @@
use std::path::Path;
use bevy::{
asset::{AssetServer, Assets, Handle},
ecs::{
component::Component,
entity::Entity,
query::{Added, With},
reflect::ReflectComponent,
system::{Commands, Query, Res, ResMut},
},
gltf::Gltf,
hierarchy::{Children, Parent},
log::debug,
pbr::StandardMaterial,
reflect::Reflect,
render::mesh::Mesh,
};
use crate::{AssetLoadTracker, BlueprintAssetsLoadState, BlenvyConfig, BlueprintInstanceReady};
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// struct containing the name & path of the material to apply
pub struct MaterialInfo {
pub name: String,
pub path: String,
}
/// flag component
#[derive(Component)]
pub(crate) struct BlueprintMaterialAssetsLoaded;
/// flag component
#[derive(Component)]
pub(crate) struct BlueprintMaterialAssetsNotLoaded;
/// system that injects / replaces materials from material library
pub(crate) fn materials_inject(
blenvy_config: ResMut<BlenvyConfig>,
material_infos: Query<(Entity, &MaterialInfo), Added<MaterialInfo>>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, material_info) in material_infos.iter() {
println!("Entity with material info {:?} {:?}", entity, material_info);
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
if blenvy_config
.materials_cache
.contains_key(&material_full_path)
{
debug!("material is cached, retrieving");
blenvy_config
.materials_cache
.get(&material_full_path)
.expect("we should have the material available");
commands
.entity(entity)
.insert(BlueprintMaterialAssetsLoaded);
} else {
let material_file_handle = asset_server.load_untyped(&material_info.path.clone()); // : Handle<Gltf>
let material_file_id = material_file_handle.id();
let asset_infos: Vec<AssetLoadTracker> = vec![AssetLoadTracker {
name: material_info.name.clone(),
path: material_info.path.clone(),
id: material_file_id,
loaded: false,
handle: material_file_handle.clone(),
}];
commands
.entity(entity)
.insert(BlueprintAssetsLoadState {
all_loaded: false,
asset_infos,
..Default::default()
})
.insert(BlueprintMaterialAssetsNotLoaded);
}
}
}
// TODO, merge with blueprints_check_assets_loading, make generic ?
pub(crate) fn check_for_material_loaded(
mut blueprint_assets_to_load: Query<
(Entity, &mut BlueprintAssetsLoadState),
With<BlueprintMaterialAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
let mut all_loaded = true;
let mut loaded_amount = 0;
let total = assets_to_load.asset_infos.len();
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);
tracker.loaded = loaded;
if loaded {
loaded_amount += 1;
} else {
all_loaded = false;
}
}
let progress: f32 = loaded_amount as f32 / total as f32;
assets_to_load.progress = progress;
if all_loaded {
assets_to_load.all_loaded = true;
commands
.entity(entity)
.insert(BlueprintMaterialAssetsLoaded)
.remove::<BlueprintMaterialAssetsNotLoaded>();
}
}
}
/// system that injects / replaces materials from material library
pub(crate) fn materials_inject2(
mut blenvy_config: ResMut<BlenvyConfig>,
material_infos: Query<
(&MaterialInfo, &Children),
(
Added<BlueprintMaterialAssetsLoaded>,
With<BlueprintMaterialAssetsLoaded>,
),
>,
with_materials_and_meshes: Query<
(),
(
With<Parent>,
With<Handle<StandardMaterial>>,
With<Handle<Mesh>>,
),
>,
assets_gltf: Res<Assets<Gltf>>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (material_info, children) in material_infos.iter() {
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
let mut material_found: Option<&Handle<StandardMaterial>> = None;
if blenvy_config
.materials_cache
.contains_key(&material_full_path)
{
debug!("material is cached, retrieving");
let material = blenvy_config
.materials_cache
.get(&material_full_path)
.expect("we should have the material available");
material_found = Some(material);
} else {
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
let mat_gltf = assets_gltf
.get(model_handle.id())
.expect("material should have been preloaded");
if mat_gltf.named_materials.contains_key(&material_info.name as &str) {
let material = mat_gltf
.named_materials
.get(&material_info.name as &str)
.expect("this material should have been loaded");
blenvy_config
.materials_cache
.insert(material_full_path, material.clone());
material_found = Some(material);
}
}
if let Some(material) = material_found {
for child in children.iter() {
if with_materials_and_meshes.contains(*child) {
debug!(
"injecting material {}, path: {:?}",
material_info.name,
material_info.path.clone()
);
commands.entity(*child).insert(material.clone());
}
}
}
}
}

View File

@ -5,7 +5,7 @@ use bevy::prelude::*;
use bevy::scene::SceneInstance; use bevy::scene::SceneInstance;
// use bevy::utils::hashbrown::HashSet; // use bevy::utils::hashbrown::HashSet;
use crate::{BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo, BlueprintReadyForPostProcess, BlueprintSpawned, BlueprintSpawning, SpawnTrackRoot, SubBlueprintsSpawnTracker}; use crate::{BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo, BlueprintReadyForPostProcess, BlueprintInstanceReady, BlueprintSpawning, SpawnTrackRoot, SubBlueprintsSpawnTracker};
use crate::{SpawnHere, Spawned}; use crate::{SpawnHere, Spawned};
use crate::{ use crate::{
BlueprintEvent, CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren BlueprintEvent, CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren
@ -113,7 +113,7 @@ pub(crate) fn spawned_blueprint_post_process( // rename to '
); );
commands.entity(original) commands.entity(original)
.insert(BlueprintSpawned) .insert(BlueprintInstanceReady)
.remove::<BlueprintSpawning>() .remove::<BlueprintSpawning>()

View File

@ -5,6 +5,7 @@ use crate::{BlenvyConfig, BlueprintReadyForFinalizing, BlueprintReadyForPostProc
/// helper system that computes the compound aabbs of the scenes/blueprints /// helper system that computes the compound aabbs of the scenes/blueprints
pub fn compute_scene_aabbs( pub fn compute_scene_aabbs(
root_entities: Query<(Entity, &Name), (With<BlueprintReadyForPostProcess>, Without<Aabb>)>, root_entities: Query<(Entity, &Name), (With<BlueprintReadyForPostProcess>, Without<Aabb>)>,
other_entities: Query<Entity, (With<BlueprintReadyForPostProcess>, With<Aabb>)>,
children: Query<&Children>, children: Query<&Children>,
existing_aabbs: Query<&Aabb>, existing_aabbs: Query<&Aabb>,
@ -24,10 +25,16 @@ pub fn compute_scene_aabbs(
commands.entity(root_entity).insert(*aabb).insert(BlueprintReadyForFinalizing); commands.entity(root_entity).insert(*aabb).insert(BlueprintReadyForFinalizing);
} else { } else {
let aabb = compute_descendant_aabb(root_entity, &children, &existing_aabbs); let aabb = compute_descendant_aabb(root_entity, &children, &existing_aabbs);
commands.entity(root_entity).insert(aabb).insert(BlueprintReadyForFinalizing);
blenvy_config.aabb_cache.insert(name.to_string(), aabb); blenvy_config.aabb_cache.insert(name.to_string(), aabb);
info!("generating aabb for {:?}", name);
commands.entity(root_entity).insert(aabb).insert(BlueprintReadyForFinalizing);
} }
} }
for entity in other_entities.iter() {
println!("stuff with AABB");
commands.entity(entity).insert(BlueprintReadyForFinalizing); // FIXME ! Yikes !!
}
} }
pub fn compute_descendant_aabb( pub fn compute_descendant_aabb(

View File

@ -1,23 +1,6 @@
use std::path::Path; use bevy::prelude::*;
use bevy::{ use crate::{BlenvyConfig, BlueprintReadyForPostProcess};
asset::{AssetServer, Assets, Handle},
ecs::{
component::Component,
entity::Entity,
query::{Added, With},
reflect::ReflectComponent,
system::{Commands, Query, Res, ResMut},
},
gltf::Gltf,
hierarchy::{Children, Parent},
log::debug,
pbr::StandardMaterial,
reflect::Reflect,
render::mesh::Mesh,
};
use crate::{AssetLoadTracker, BlueprintAssetsLoadState, BlenvyConfig, BlueprintInstanceReady};
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
@ -27,107 +10,21 @@ pub struct MaterialInfo {
pub path: String, pub path: String,
} }
/// flag component #[derive(Component, Default, Debug)]
#[derive(Component)] pub struct MaterialProcessed;
pub(crate) struct BlueprintMaterialAssetsLoaded;
/// flag component
#[derive(Component)]
pub(crate) struct BlueprintMaterialAssetsNotLoaded;
/// system that injects / replaces materials from material library /// system that injects / replaces materials from material library
pub(crate) fn materials_inject( pub(crate) fn inject_materials(
blenvy_config: ResMut<BlenvyConfig>,
material_infos: Query<(Entity, &MaterialInfo), Added<MaterialInfo>>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, material_info) in material_infos.iter() {
println!("Entity with material info {:?} {:?}", entity, material_info);
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
if blenvy_config
.materials_cache
.contains_key(&material_full_path)
{
debug!("material is cached, retrieving");
blenvy_config
.materials_cache
.get(&material_full_path)
.expect("we should have the material available");
commands
.entity(entity)
.insert(BlueprintMaterialAssetsLoaded);
} else {
let material_file_handle = asset_server.load_untyped(&material_info.path.clone()); // : Handle<Gltf>
let material_file_id = material_file_handle.id();
let asset_infos: Vec<AssetLoadTracker> = vec![AssetLoadTracker {
name: material_info.name.clone(),
path: material_info.path.clone(),
id: material_file_id,
loaded: false,
handle: material_file_handle.clone(),
}];
commands
.entity(entity)
.insert(BlueprintAssetsLoadState {
all_loaded: false,
asset_infos,
..Default::default()
})
.insert(BlueprintMaterialAssetsNotLoaded);
}
}
}
// TODO, merge with blueprints_check_assets_loading, make generic ?
pub(crate) fn check_for_material_loaded(
mut blueprint_assets_to_load: Query<
(Entity, &mut BlueprintAssetsLoadState),
With<BlueprintMaterialAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
let mut all_loaded = true;
let mut loaded_amount = 0;
let total = assets_to_load.asset_infos.len();
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);
tracker.loaded = loaded;
if loaded {
loaded_amount += 1;
} else {
all_loaded = false;
}
}
let progress: f32 = loaded_amount as f32 / total as f32;
assets_to_load.progress = progress;
if all_loaded {
assets_to_load.all_loaded = true;
commands
.entity(entity)
.insert(BlueprintMaterialAssetsLoaded)
.remove::<BlueprintMaterialAssetsNotLoaded>();
}
}
}
/// system that injects / replaces materials from material library
pub(crate) fn materials_inject2(
mut blenvy_config: ResMut<BlenvyConfig>, mut blenvy_config: ResMut<BlenvyConfig>,
material_infos: Query< material_infos: Query<
(&MaterialInfo, &Children), (Entity, &MaterialInfo, &Children),
( Without<MaterialProcessed>
// (With<BlueprintReadyForPostProcess>)
/*(
Added<BlueprintMaterialAssetsLoaded>, Added<BlueprintMaterialAssetsLoaded>,
With<BlueprintMaterialAssetsLoaded>, With<BlueprintMaterialAssetsLoaded>,
), ),*/
>, >,
with_materials_and_meshes: Query< with_materials_and_meshes: Query<
(), (),
@ -142,7 +39,7 @@ pub(crate) fn materials_inject2(
mut commands: Commands, mut commands: Commands,
) { ) {
for (material_info, children) in material_infos.iter() { for (entity, material_info, children) in material_infos.iter() {
let material_full_path = format!("{}#{}", material_info.path, material_info.name); let material_full_path = format!("{}#{}", material_info.path, material_info.name);
let mut material_found: Option<&Handle<StandardMaterial>> = None; let mut material_found: Option<&Handle<StandardMaterial>> = None;
@ -165,7 +62,7 @@ pub(crate) fn materials_inject2(
let material = mat_gltf let material = mat_gltf
.named_materials .named_materials
.get(&material_info.name as &str) .get(&material_info.name as &str)
.expect("this material should have been loaded"); .expect("this material should have been loaded at this stage, please make sure you are correctly preloading them");
blenvy_config blenvy_config
.materials_cache .materials_cache
.insert(material_full_path, material.clone()); .insert(material_full_path, material.clone());
@ -173,10 +70,12 @@ pub(crate) fn materials_inject2(
} }
} }
commands.entity(entity).insert(MaterialProcessed);
if let Some(material) = material_found { if let Some(material) = material_found {
for child in children.iter() { for child in children.iter() {
if with_materials_and_meshes.contains(*child) { if with_materials_and_meshes.contains(*child) {
debug!( info!(
"injecting material {}, path: {:?}", "injecting material {}, path: {:?}",
material_info.name, material_info.name,
material_info.path.clone() material_info.path.clone()
@ -187,4 +86,4 @@ pub(crate) fn materials_inject2(
} }
} }
} }
} }

View File

@ -1,9 +1,6 @@
pub mod spawn_from_blueprints; pub mod spawn_from_blueprints;
pub use spawn_from_blueprints::*; pub use spawn_from_blueprints::*;
pub mod spawn_post_process;
pub(crate) use spawn_post_process::*;
pub mod animation; pub mod animation;
pub use animation::*; pub use animation::*;
@ -70,10 +67,38 @@ fn aabbs_enabled(blenvy_config: Res<BlenvyConfig>) -> bool {
blenvy_config.aabbs blenvy_config.aabbs
} }
fn hot_reload(watching_for_changes: Res<WatchingForChanges>) -> bool {
watching_for_changes.0
}
trait BlenvyApp {
fn register_watching_for_changes(&mut self) -> &mut Self;
}
impl BlenvyApp for App {
fn register_watching_for_changes(&mut self) -> &mut Self {
let asset_server = self
.world()
.get_resource::<AssetServer>()
.expect(ASSET_ERROR);
let watching_for_changes = asset_server.watching_for_changes();
self.insert_resource(WatchingForChanges(watching_for_changes))
}
}
#[derive(Debug, Clone, Resource, Default)]
pub(crate) struct WatchingForChanges(pub(crate) bool);
const ASSET_ERROR: &str = ""; // TODO
impl Plugin for BlueprintsPlugin { impl Plugin for BlueprintsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app app
.register_watching_for_changes()
.register_type::<BlueprintInfo>() .register_type::<BlueprintInfo>()
.register_type::<MaterialInfo>() .register_type::<MaterialInfo>()
.register_type::<SpawnHere>() .register_type::<SpawnHere>()
@ -94,7 +119,6 @@ impl Plugin for BlueprintsPlugin {
.add_event::<BlueprintEvent>() .add_event::<BlueprintEvent>()
.register_type::<HashMap<String, Vec<String>>>() .register_type::<HashMap<String, Vec<String>>>()
.configure_sets( .configure_sets(
Update, Update,
@ -111,32 +135,16 @@ impl Plugin for BlueprintsPlugin {
blueprints_scenes_spawned, blueprints_scenes_spawned,
blueprints_transfer_components, blueprints_transfer_components,
// post process
(compute_scene_aabbs, apply_deferred) inject_materials,
.chain(), compute_scene_aabbs,// .run_if(aabbs_enabled),
// .run_if(aabbs_enabled),
apply_deferred,
blueprints_finalize_instances, blueprints_finalize_instances,
/*(
materials_inject,
check_for_material_loaded,
materials_inject2,
)
.chain()*/
) )
.chain() .chain()
.in_set(GltfBlueprintsSet::Spawn), .in_set(GltfBlueprintsSet::Spawn),
) )
/* .add_systems(
Update,
(spawned_blueprint_post_process, apply_deferred)
.chain()
.in_set(GltfBlueprintsSet::AfterSpawn),
)*/
/* .add_systems( /* .add_systems(
Update, Update,
( (
@ -144,9 +152,8 @@ impl Plugin for BlueprintsPlugin {
trigger_blueprint_animation_markers_events, trigger_blueprint_animation_markers_events,
), ),
)*/ )*/
// hot reload
.add_systems(Update, react_to_asset_changes) .add_systems(Update, react_to_asset_changes.run_if(hot_reload))
// .add_systems(Update, track_sub_blueprints)
; ;
} }
} }

View File

@ -80,8 +80,9 @@ pub enum BlueprintEvent {
}, },
/// ///
Ready { InstanceReady {
entity: Entity, entity: Entity,
blueprint_name: String,
blueprint_path: String, blueprint_path: String,
} }
@ -102,12 +103,6 @@ pub struct DynamicBlueprintInstance;
pub struct BlueprintSpawning; pub struct BlueprintSpawning;
#[derive(Component, Reflect, Debug, Default)]
#[reflect(Component)]
/// component gets added when a blueprint spawning is done
pub struct BlueprintSpawned;
use gltf::Gltf as RawGltf; use gltf::Gltf as RawGltf;
pub(crate) fn blueprints_prepare_spawn( pub(crate) fn blueprints_prepare_spawn(
@ -388,26 +383,44 @@ pub struct BlueprintChildrenReady;
pub struct BlueprintReadyForPostProcess; pub struct BlueprintReadyForPostProcess;
// TODO: disregard blueprints that have been spawned WAIT , we already have BlueprintSpawning
pub(crate) fn blueprints_scenes_spawned( pub(crate) fn blueprints_scenes_spawned(
spawned_blueprint_scene_instances: Query<(Entity, Option<&Name>, Option<&Children>, Option<&SpawnTrackRoot>), (With<BlueprintSpawning>, Added<SceneInstance>)>, spawned_blueprint_scene_instances: Query<(Entity, Option<&Name>, Option<&Children>, Option<&SpawnTrackRoot>), (With<BlueprintSpawning>, Added<SceneInstance>)>,
with_blueprint_infos : Query<(Entity, Option<&Name>), With<BlueprintInfo>>, with_blueprint_infos : Query<(Entity, Option<&Name>), With<BlueprintInfo>>,
all_children: Query<&Children>, all_children: Query<&Children>,
all_parents: Query<&Parent>,
mut sub_blueprint_trackers: Query<(Entity, &mut SubBlueprintsSpawnTracker, &BlueprintInfo)>, mut sub_blueprint_trackers: Query<(Entity, &mut SubBlueprintsSpawnTracker, &BlueprintInfo)>,
mut commands: Commands, mut commands: Commands,
all_names: Query<&Name>
) { ) {
for (entity, name, children, track_root) in spawned_blueprint_scene_instances.iter(){ for (entity, name, children, track_root) in spawned_blueprint_scene_instances.iter(){
info!("Done spawning blueprint scene for entity named {:?} (track root: {:?})", name, track_root); info!("Done spawning blueprint scene for entity named {:?} (track root: {:?})", name, track_root);
let mut sub_blueprint_instances: Vec<Entity> = vec![]; let mut sub_blueprint_instances: Vec<Entity> = vec![];
let mut tracker_data: HashMap<Entity, bool> = HashMap::new(); let mut sub_blueprint_instance_names: Vec<Name> = vec![];
let mut tracker_data: HashMap<Entity, bool> = HashMap::new();
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));
break;
}
}
if children.is_some() { if children.is_some() {
for child in all_children.iter_descendants(entity) { for child in all_children.iter_descendants(entity) {
if with_blueprint_infos.get(child).is_ok() { if with_blueprint_infos.get(child).is_ok() {
sub_blueprint_instances.push(child); sub_blueprint_instances.push(child);
if let Ok(nname) = all_names.get(child) {
sub_blueprint_instance_names.push(nname.clone());
}
tracker_data.insert(child, false); tracker_data.insert(child, false);
if track_root.is_some() { if track_root.is_some() {
@ -424,7 +437,9 @@ pub(crate) fn blueprints_scenes_spawned(
} }
} }
println!("sub blueprint instances {:?}", sub_blueprint_instances);
println!("sub blueprint instances {:?}", sub_blueprint_instance_names);
// TODO: how about when no sub blueprints are present // TODO: how about when no sub blueprints are present
if tracker_data.keys().len() > 0 { if tracker_data.keys().len() > 0 {
@ -439,9 +454,7 @@ pub(crate) fn blueprints_scenes_spawned(
// could be done differently, by notifying each parent of a spawning blueprint that this child is done spawning ? // could be done differently, by notifying each parent of a spawning blueprint that this child is done spawning ?
// perhaps using component hooks or observers (ie , if a ComponentSpawning + Parent) // perhaps using component hooks or observers (ie , if a ComponentSpawning + Parent)
use crate::{ use crate:: CopyComponents;
CopyComponents,
};
use std::any::TypeId; use std::any::TypeId;
@ -501,7 +514,6 @@ pub(crate) fn blueprints_transfer_components(
} }
} }
commands.entity(original) commands.entity(original)
.insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can now it is now their "turn" .insert(BlueprintReadyForPostProcess); // Tag the entity so any systems dealing with post processing can now it is now their "turn"
// commands.entity(original).remove::<Handle<Scene>>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want // commands.entity(original).remove::<Handle<Scene>>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want
@ -513,7 +525,8 @@ pub(crate) fn blueprints_transfer_components(
// 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
if let Some(track_root) = track_root { if let Some(track_root) = track_root {
// println!("got some root {:?}", root_name); let root_name = all_names.get(track_root.0);
println!("got some root {:?}", root_name);
if let Ok((s_entity, mut tracker, bp_info)) = sub_blueprint_trackers.get_mut(track_root.0) { if let Ok((s_entity, mut tracker, bp_info)) = sub_blueprint_trackers.get_mut(track_root.0) {
tracker.sub_blueprint_instances.entry(original).or_insert(true); tracker.sub_blueprint_instances.entry(original).or_insert(true);
tracker.sub_blueprint_instances.insert(original, true); tracker.sub_blueprint_instances.insert(original, true);
@ -535,44 +548,49 @@ pub(crate) fn blueprints_transfer_components(
} }
} }
#[derive(Component, Reflect, Debug)] #[derive(Component, Reflect, Debug)]
#[reflect(Component)] #[reflect(Component)]
pub struct BlueprintReadyForFinalizing; pub struct BlueprintReadyForFinalizing;
pub(crate) fn blueprints_finalize_instances( pub(crate) fn blueprints_finalize_instances(
blueprint_instances: Query<(Entity, Option<&Name>), (With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>)>, blueprint_instances: Query<(Entity, Option<&Name>, &BlueprintInfo), (With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>)>,
mut blueprint_events: EventWriter<BlueprintEvent>,
mut commands: Commands, mut commands: Commands,
) { ) {
for (entity, name) in blueprint_instances.iter() { for (entity, name, blueprint_info) in blueprint_instances.iter() {
info!("Finalizing blueprint instance {:?}", name); info!("Finalizing blueprint instance {:?}", name);
commands.entity(entity) commands.entity(entity)
.remove::<SpawnHere>() .remove::<SpawnHere>()
.remove::<BlueprintSpawning>() .remove::<BlueprintSpawning>()
.insert(BlueprintSpawned) .remove::<BlueprintReadyForPostProcess>()
.insert(BlueprintInstanceReady)
.insert(Visibility::Visible) .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 BlueprintSpawning
- Blueprint Load Assets - Blueprint Load Assets
- Blueprint Assets Ready: spawn Blueprint's scene - Blueprint Assets Ready: spawn Blueprint's scene
- Blueprint Scene Ready: - Blueprint Scene Ready (SceneInstance component is present):
- get list of sub Blueprints if any, inject blueprints spawn tracker - get list of sub Blueprints if any, inject sub blueprints spawn tracker
=> annoying issue with the "nested" useless root node created by blender - Blueprint copy components to original entity, remove useless nodes
=> distinguish between blueprint instances inside blueprint instances vs blueprint instances inside blueprints ?? - Blueprint post process
- Blueprint sub_blueprints Ready
if all children are ready
- Blueprints post process
- generate aabb (need full hierarchy in its final form) - generate aabb (need full hierarchy in its final form)
- materials ? - 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
*/ */
// HOT RELOAD // HOT RELOAD

View File

@ -215,12 +215,17 @@ Blender side:
Bevy Side: Bevy Side:
- [x] deprecate BlueprintName & BlueprintPath & use BlueprintInfo instead - [x] deprecate BlueprintName & BlueprintPath & use BlueprintInfo instead
- [ ] make blueprint instances invisible until spawning is done to avoid "spawn flash"? - [ ] make blueprint instances invisible until spawning is done to avoid "spawn flash"?
- [ ] should "blueprint spawned" only be triggered after all its sub blueprints have spawned ? - [ ] restructure blueprint spawning
- [ ] "blueprintInstance ready"/finished - [x] "blueprint ready" only be triggered after all its sub blueprints are ready
BlueprintAssetsLoaded - [x] "blueprintInstance ready"/finished
BlueprintSceneSpawned BlueprintAssetsLoaded
BlueprintChildrenReady BlueprintSceneSpawned
BlueprintReadyForPostProcess BlueprintChildrenReady
BlueprintReadyForPostProcess
- [ ] fix issues with deeply nested blueprints
- perhaps reverse logic by using iter_ascendants
- [x] fix materials handling
- [ ] fix animations handling
- [ ] 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 ?)

View File

@ -122,9 +122,9 @@ def test_export_complex(setup_data):
user_asset.path = "audio/fake.mp3" user_asset.path = "audio/fake.mp3"
# we have to cheat, since we cannot rely on the data injected when saving the library file (since we are not saving it as part of the tests) # we have to cheat, since we cannot rely on the data injected when saving the library file (since we are not saving it as part of the tests)
bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb" #bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb"
bpy.data.collections["External_blueprint2"]["export_path"] = "blueprints/External_blueprint2.glb" #bpy.data.collections["External_blueprint2"]["export_path"] = "blueprints/External_blueprint2.glb"
bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb" #bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb"
# do the actual exporting # do the actual exporting
prepare_and_export() prepare_and_export()

View File

@ -106,7 +106,7 @@ def test_export_complex(setup_data):
blenvy.auto_export.auto_export = True blenvy.auto_export.auto_export = True
blenvy.auto_export.export_scene_settings = True blenvy.auto_export.export_scene_settings = True
blenvy.auto_export.export_blueprints = True blenvy.auto_export.export_blueprints = True
#blenvy.auto_export.export_materials_library = True blenvy.auto_export.export_materials_library = True
bpy.data.scenes['World'].blenvy_scene_type = 'Level' # set scene as main/level scene bpy.data.scenes['World'].blenvy_scene_type = 'Level' # set scene as main/level scene
bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene