feat(Blenvy:Bevy): big improvements to hot reload:
* does not try to respawn parents & their children at the same time anymore * does not try to respawn already spawning blueprint instances anymore * "batches" respawning per set of simultaneous changes to assets to avoid chaotic respawning * minor other tweaks & improvements
This commit is contained in:
parent
f0cca65128
commit
33cddda7a5
|
@ -37,7 +37,7 @@ pub fn compute_scene_aabbs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for entity in other_entities.iter() {
|
for entity in other_entities.iter() {
|
||||||
println!("stuff with AABB");
|
println!("already got AABB");
|
||||||
commands.entity(entity).insert(BlueprintReadyForFinalizing); // FIXME ! Yikes !!
|
commands.entity(entity).insert(BlueprintReadyForFinalizing); // FIXME ! Yikes !!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub struct BlueprintAsset {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// helper component, is used to store the list of sub blueprints to enable automatic loading of dependend blueprints
|
/// helper component, is used to store the list of sub blueprints to enable automatic loading of dependend blueprints
|
||||||
|
/// these are only the DIRECT dependencies of a blueprint, does not contain the indirect assets (ie assets of sub blueprints, etc)
|
||||||
#[derive(Component, Reflect, Default, Debug, Deserialize)]
|
#[derive(Component, Reflect, Default, Debug, Deserialize)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct BlueprintAssets {
|
pub struct BlueprintAssets {
|
||||||
|
@ -29,6 +30,14 @@ pub struct BlueprintAssets {
|
||||||
}
|
}
|
||||||
//(pub Vec<BlueprintAsset>);
|
//(pub Vec<BlueprintAsset>);
|
||||||
|
|
||||||
|
/// helper component, is used to store the list of sub blueprints to enable automatic loading of dependend blueprints
|
||||||
|
#[derive(Component, Reflect, Default, Debug, Deserialize)]
|
||||||
|
pub struct BlueprintAllAssets {
|
||||||
|
/// only this field should get filled in from the Blender side
|
||||||
|
pub assets: Vec<BlueprintAsset>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
///
|
///
|
||||||
/// flag component, usually added when a blueprint is loaded
|
/// flag component, usually added when a blueprint is loaded
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintChildrenReady, BlueprintInfo, BlueprintInstanceReady, InBlueprint, SpawnBlueprint, SubBlueprintsSpawnTracker};
|
use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintChildrenReady, BlueprintInfo, BlueprintInstanceReady, BlueprintSpawning, InBlueprint, SpawnBlueprint, SubBlueprintsSpawnTracker};
|
||||||
use bevy::asset::{AssetEvent, UntypedAssetId};
|
use bevy::asset::{AssetEvent, UntypedAssetId};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::scene::SceneInstance;
|
use bevy::scene::SceneInstance;
|
||||||
|
@ -23,11 +23,15 @@ pub(crate) fn react_to_asset_changes(
|
||||||
)>,
|
)>,
|
||||||
// blueprint_children_entities: Query<&InBlueprint>, => can only be used if the entites are tagged, right now that is optional...perhaps do not make it optional
|
// blueprint_children_entities: Query<&InBlueprint>, => can only be used if the entites are tagged, right now that is optional...perhaps do not make it optional
|
||||||
assets_to_blueprint_instances: Res<AssetToBlueprintInstancesMapper>,
|
assets_to_blueprint_instances: Res<AssetToBlueprintInstancesMapper>,
|
||||||
|
all_parents: Query<&Parent>,
|
||||||
|
spawning_blueprints: Query<&BlueprintSpawning>,
|
||||||
|
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
let mut respawn_candidates: Vec<&Entity> = vec![];
|
||||||
|
|
||||||
for event in gltf_events.read() {
|
for event in gltf_events.read() {
|
||||||
// LoadedUntypedAsset
|
// LoadedUntypedAsset
|
||||||
match event {
|
match event {
|
||||||
|
@ -39,27 +43,16 @@ pub(crate) fn react_to_asset_changes(
|
||||||
// println!("matching untyped handle {:?}", untyped);
|
// println!("matching untyped handle {:?}", untyped);
|
||||||
// let bla = untyped.unwrap().id();
|
// let bla = untyped.unwrap().id();
|
||||||
// asset_server.get
|
// asset_server.get
|
||||||
|
// in order to avoid respawn both a parent & a child , which would crash Bevy, we do things in two steps
|
||||||
if let Some(entities) = assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids.get(&asset_path.to_string()) {
|
if let Some(entities) = assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids.get(&asset_path.to_string()) {
|
||||||
for entity in entities.iter() {
|
for entity in entities.iter() {
|
||||||
println!("matching blueprint instance {}", entity);
|
println!("matching blueprint instance {}", entity);
|
||||||
if let Ok((entity, entity_name, _blueprint_info, children)) = blueprint_assets.get(*entity) {
|
// disregard entities that are already (re) spawning
|
||||||
println!("HOLY MOLY IT DETECTS !!, now respawn {:?}", entity_name);
|
if !respawn_candidates.contains(&entity) && blueprint_assets.get(*entity).is_ok() && spawning_blueprints.get(*entity).is_err()
|
||||||
|
{
|
||||||
// TODO: only remove those that are "in blueprint"
|
respawn_candidates.push(entity);
|
||||||
if children.is_some() {
|
|
||||||
for child in children.unwrap().iter() {
|
|
||||||
commands.entity(*child).despawn_recursive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.remove::<BlueprintInstanceReady>()
|
|
||||||
.remove::<BlueprintAssetsLoaded>()
|
|
||||||
.remove::<SceneInstance>()
|
|
||||||
.remove::<BlueprintAssetsLoadState>()
|
|
||||||
.remove::<SubBlueprintsSpawnTracker>()
|
|
||||||
.insert(SpawnBlueprint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,4 +60,48 @@ pub(crate) fn react_to_asset_changes(
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// we process all candidates here to deal with the case where multiple assets have changed in a single frame, which could cause respawn chaos
|
||||||
|
// now find hierarchy of changes and only set the uppermost parent up for respawning
|
||||||
|
// TODO: improve this, very inneficient
|
||||||
|
let mut retained_candidates: Vec<Entity> = vec![];
|
||||||
|
'outer: for entity in respawn_candidates.iter() {
|
||||||
|
for parent in all_parents.iter_ancestors(**entity){
|
||||||
|
for ent in respawn_candidates.iter() {
|
||||||
|
if **ent == parent {
|
||||||
|
if ! retained_candidates.contains(&parent) {
|
||||||
|
retained_candidates.push(parent);
|
||||||
|
}
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ! retained_candidates.contains(&entity) {
|
||||||
|
retained_candidates.push(**entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("respawn candidates {:?}", respawn_candidates);
|
||||||
|
for retained in retained_candidates.iter() {
|
||||||
|
println!("retained {}", retained);
|
||||||
|
|
||||||
|
if let Ok((entity, entity_name, _blueprint_info, children)) = blueprint_assets.get(*retained) {
|
||||||
|
println!("HOLY MOLY IT DETECTS !!, now respawn {:?}", entity_name);
|
||||||
|
|
||||||
|
// TODO: only remove those that are "in blueprint"
|
||||||
|
if children.is_some() {
|
||||||
|
for child in children.unwrap().iter() {
|
||||||
|
commands.entity(*child).despawn_recursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.remove::<BlueprintInstanceReady>()
|
||||||
|
.remove::<BlueprintAssetsLoaded>()
|
||||||
|
.remove::<SceneInstance>()
|
||||||
|
.remove::<BlueprintAssetsLoadState>()
|
||||||
|
.remove::<SubBlueprintsSpawnTracker>()
|
||||||
|
.insert(SpawnBlueprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("done with asset updates");
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,9 @@ pub(crate) fn blueprints_prepare_spawn(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
// for hot reload
|
// for hot reload
|
||||||
mut assets_to_blueprint_instances: ResMut<AssetToBlueprintInstancesMapper>
|
mut assets_to_blueprint_instances: ResMut<AssetToBlueprintInstancesMapper>,
|
||||||
|
// for debug
|
||||||
|
all_names: Query<&Name>
|
||||||
) {
|
) {
|
||||||
for (entity, blueprint_info, entity_name) in blueprint_instances_to_spawn.iter() {
|
for (entity, blueprint_info, entity_name) in blueprint_instances_to_spawn.iter() {
|
||||||
info!(
|
info!(
|
||||||
|
@ -173,6 +175,7 @@ pub(crate) fn blueprints_prepare_spawn(
|
||||||
// println!("all_assets {:?}", all_assets);
|
// println!("all_assets {:?}", all_assets);
|
||||||
|
|
||||||
for asset in all_assets.assets.iter() {
|
for asset in all_assets.assets.iter() {
|
||||||
|
println!("ASSET {}",asset.path);
|
||||||
let untyped_handle = asset_server.load_untyped(&asset.path);
|
let untyped_handle = asset_server.load_untyped(&asset.path);
|
||||||
let asset_id = untyped_handle.id();
|
let asset_id = untyped_handle.id();
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
||||||
|
@ -195,6 +198,7 @@ pub(crate) fn blueprints_prepare_spawn(
|
||||||
}
|
}
|
||||||
// only insert if not already present in mapping
|
// only insert if not already present in mapping
|
||||||
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids[&path_id].contains(&entity) {
|
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids[&path_id].contains(&entity) {
|
||||||
|
println!("adding mapping between {} and entity {:?}", path_id, all_names.get(entity));
|
||||||
assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids.get_mut(&path_id).unwrap().push(entity);
|
assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids.get_mut(&path_id).unwrap().push(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -699,6 +703,7 @@ pub(crate) fn blueprints_finalize_instances(
|
||||||
(With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>),
|
(With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>),
|
||||||
>,
|
>,
|
||||||
mut sub_blueprint_trackers: Query<&mut SubBlueprintsSpawnTracker, With<BlueprintInfo>>,
|
mut sub_blueprint_trackers: Query<&mut SubBlueprintsSpawnTracker, With<BlueprintInfo>>,
|
||||||
|
spawning_blueprints: Query<&BlueprintSpawning>,
|
||||||
all_children: Query<&Children>,
|
all_children: Query<&Children>,
|
||||||
mut blueprint_events: EventWriter<BlueprintEvent>,
|
mut blueprint_events: EventWriter<BlueprintEvent>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
@ -725,25 +730,30 @@ pub(crate) fn blueprints_finalize_instances(
|
||||||
// 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) = parent_blueprint {
|
if let Some(track_root) = parent_blueprint {
|
||||||
if let Ok(mut tracker) = sub_blueprint_trackers.get_mut(track_root.0) {
|
// only propagate sub_blueprint spawning if the parent blueprint instance ist actually in spawning mode
|
||||||
tracker
|
if spawning_blueprints.get(track_root.0).is_ok() {
|
||||||
.sub_blueprint_instances
|
if let Ok(mut tracker) = sub_blueprint_trackers.get_mut(track_root.0) {
|
||||||
.entry(entity)
|
tracker
|
||||||
.or_insert(true);
|
.sub_blueprint_instances
|
||||||
tracker.sub_blueprint_instances.insert(entity, true);
|
.entry(entity)
|
||||||
|
.or_insert(true);
|
||||||
|
tracker.sub_blueprint_instances.insert(entity, true);
|
||||||
|
|
||||||
|
// TODO: ugh, my limited rust knowledge, this is bad code
|
||||||
|
let mut all_spawned = true;
|
||||||
|
for val in tracker.sub_blueprint_instances.values() {
|
||||||
|
if !val {
|
||||||
|
all_spawned = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if all_spawned {
|
||||||
|
|
||||||
|
let root_name = all_names.get(track_root.0);
|
||||||
|
println!("ALLLLL SPAAAAWNED for {} named {:?}", track_root.0, root_name);
|
||||||
|
commands.entity(track_root.0).insert(BlueprintChildrenReady);
|
||||||
|
|
||||||
// TODO: ugh, my limited rust knowledge, this is bad code
|
|
||||||
let mut all_spawned = true;
|
|
||||||
for val in tracker.sub_blueprint_instances.values() {
|
|
||||||
if !val {
|
|
||||||
all_spawned = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if all_spawned {
|
|
||||||
let root_name = all_names.get(track_root.0);
|
|
||||||
println!("ALLLLL SPAAAAWNED for {} named {:?}", track_root.0, root_name);
|
|
||||||
commands.entity(track_root.0).insert(BlueprintChildrenReady);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue