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() {
|
||||
println!("stuff with AABB");
|
||||
println!("already got AABB");
|
||||
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
|
||||
/// 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)]
|
||||
#[reflect(Component)]
|
||||
pub struct BlueprintAssets {
|
||||
|
@ -29,6 +30,14 @@ pub struct BlueprintAssets {
|
|||
}
|
||||
//(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
|
||||
|
|
|
@ -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::prelude::*;
|
||||
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
|
||||
assets_to_blueprint_instances: Res<AssetToBlueprintInstancesMapper>,
|
||||
all_parents: Query<&Parent>,
|
||||
spawning_blueprints: Query<&BlueprintSpawning>,
|
||||
|
||||
asset_server: Res<AssetServer>,
|
||||
mut commands: Commands,
|
||||
|
||||
) {
|
||||
let mut respawn_candidates: Vec<&Entity> = vec![];
|
||||
|
||||
for event in gltf_events.read() {
|
||||
// LoadedUntypedAsset
|
||||
match event {
|
||||
|
@ -39,27 +43,16 @@ pub(crate) fn react_to_asset_changes(
|
|||
// println!("matching untyped handle {:?}", untyped);
|
||||
// let bla = untyped.unwrap().id();
|
||||
// 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()) {
|
||||
for entity in entities.iter() {
|
||||
println!("matching blueprint instance {}", entity);
|
||||
if let Ok((entity, entity_name, _blueprint_info, children)) = blueprint_assets.get(*entity) {
|
||||
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);
|
||||
// disregard entities that are already (re) spawning
|
||||
if !respawn_candidates.contains(&entity) && blueprint_assets.get(*entity).is_ok() && spawning_blueprints.get(*entity).is_err()
|
||||
{
|
||||
respawn_candidates.push(entity);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
asset_server: Res<AssetServer>,
|
||||
// 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() {
|
||||
info!(
|
||||
|
@ -173,6 +175,7 @@ pub(crate) fn blueprints_prepare_spawn(
|
|||
// println!("all_assets {:?}", all_assets);
|
||||
|
||||
for asset in all_assets.assets.iter() {
|
||||
println!("ASSET {}",asset.path);
|
||||
let untyped_handle = asset_server.load_untyped(&asset.path);
|
||||
let asset_id = untyped_handle.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
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -699,6 +703,7 @@ pub(crate) fn blueprints_finalize_instances(
|
|||
(With<BlueprintSpawning>, With<BlueprintReadyForFinalizing>),
|
||||
>,
|
||||
mut sub_blueprint_trackers: Query<&mut SubBlueprintsSpawnTracker, With<BlueprintInfo>>,
|
||||
spawning_blueprints: Query<&BlueprintSpawning>,
|
||||
all_children: Query<&Children>,
|
||||
mut blueprint_events: EventWriter<BlueprintEvent>,
|
||||
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
|
||||
// TODO: perhaps use observers for these
|
||||
if let Some(track_root) = parent_blueprint {
|
||||
if let Ok(mut tracker) = sub_blueprint_trackers.get_mut(track_root.0) {
|
||||
tracker
|
||||
.sub_blueprint_instances
|
||||
.entry(entity)
|
||||
.or_insert(true);
|
||||
tracker.sub_blueprint_instances.insert(entity, true);
|
||||
// only propagate sub_blueprint spawning if the parent blueprint instance ist actually in spawning mode
|
||||
if spawning_blueprints.get(track_root.0).is_ok() {
|
||||
if let Ok(mut tracker) = sub_blueprint_trackers.get_mut(track_root.0) {
|
||||
tracker
|
||||
.sub_blueprint_instances
|
||||
.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