feat(Blenvy:Bevy): fixed & upgraded hot reloading
* added mapping of assets to blueprint instance ids so we know which blueprint instances need to be de/respawned when a given asset changes * simplified & streamlined internals of hot reloading using the above * related cleanups & minor improvements
This commit is contained in:
parent
8602383445
commit
f0cca65128
|
@ -1,34 +1,51 @@
|
|||
use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintInfo, SpawnBlueprint};
|
||||
use bevy::asset::AssetEvent;
|
||||
use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintChildrenReady, BlueprintInfo, BlueprintInstanceReady, InBlueprint, SpawnBlueprint, SubBlueprintsSpawnTracker};
|
||||
use bevy::asset::{AssetEvent, UntypedAssetId};
|
||||
use bevy::prelude::*;
|
||||
use bevy::scene::SceneInstance;
|
||||
use bevy::utils::hashbrown::HashMap;
|
||||
|
||||
|
||||
/// Resource mapping asset paths (ideally untyped ids, but more complex) to a list of blueprint instance entity ids
|
||||
#[derive(Debug, Clone, Resource, Default)]
|
||||
pub(crate) struct AssetToBlueprintInstancesMapper{
|
||||
// pub(crate) untyped_id_to_blueprint_entity_ids: HashMap<UntypedAssetId, Vec<Entity>>
|
||||
pub(crate) untyped_id_to_blueprint_entity_ids: HashMap<String, Vec<Entity>>
|
||||
}
|
||||
|
||||
pub(crate) fn react_to_asset_changes(
|
||||
mut gltf_events: EventReader<AssetEvent<Gltf>>,
|
||||
mut gltf_events: EventReader<AssetEvent<Gltf>>, // FIXME: Problem: we need to react to any asset change, not just gltf files !
|
||||
// mut untyped_events: EventReader<AssetEvent<LoadedUntypedAsset>>,
|
||||
mut blueprint_assets: Query<(
|
||||
blueprint_assets: Query<(
|
||||
Entity,
|
||||
Option<&Name>,
|
||||
&BlueprintInfo,
|
||||
&mut BlueprintAssetsLoadState,
|
||||
Option<&Children>,
|
||||
)>,
|
||||
// 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>,
|
||||
|
||||
asset_server: Res<AssetServer>,
|
||||
mut commands: Commands,
|
||||
|
||||
) {
|
||||
for event in gltf_events.read() {
|
||||
// LoadedUntypedAsset
|
||||
match event {
|
||||
AssetEvent::Modified { id } => {
|
||||
// React to the image being modified
|
||||
// React to the gltf file being modified
|
||||
// println!("Modified gltf {:?}", asset_server.get_path(*id));
|
||||
for (entity, entity_name, _blueprint_info, mut assets_to_load, children) in
|
||||
blueprint_assets.iter_mut()
|
||||
{
|
||||
for tracker in assets_to_load.asset_infos.iter_mut() {
|
||||
if asset_server.get_path(*id).is_some() {
|
||||
if tracker.path == asset_server.get_path(*id).unwrap().to_string() {
|
||||
if let Some(asset_path) = asset_server.get_path(*id) {
|
||||
// let untyped = asset_server.get_handle_untyped(asset_path.clone());
|
||||
// println!("matching untyped handle {:?}", untyped);
|
||||
// let bla = untyped.unwrap().id();
|
||||
// asset_server.get
|
||||
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();
|
||||
|
@ -36,12 +53,12 @@ pub(crate) fn react_to_asset_changes(
|
|||
}
|
||||
commands
|
||||
.entity(entity)
|
||||
.remove::<BlueprintInstanceReady>()
|
||||
.remove::<BlueprintAssetsLoaded>()
|
||||
.remove::<SceneInstance>()
|
||||
.remove::<BlueprintAssetsLoadState>()
|
||||
.remove::<SubBlueprintsSpawnTracker>()
|
||||
.insert(SpawnBlueprint);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ pub use copy_components::*;
|
|||
pub(crate) mod hot_reload;
|
||||
pub(crate) use hot_reload::*;
|
||||
|
||||
use bevy::{prelude::*, utils::HashMap};
|
||||
use bevy::{prelude::*, utils::hashbrown::HashMap};
|
||||
|
||||
use crate::{BlenvyConfig, GltfComponentsSet};
|
||||
|
||||
|
@ -65,7 +65,8 @@ fn aabbs_enabled(blenvy_config: Res<BlenvyConfig>) -> bool {
|
|||
}
|
||||
|
||||
fn hot_reload(watching_for_changes: Res<WatchingForChanges>) -> bool {
|
||||
watching_for_changes.0
|
||||
// println!("hot reload ? {}", watching_for_changes.0);
|
||||
return watching_for_changes.0
|
||||
}
|
||||
|
||||
trait BlenvyApp {
|
||||
|
@ -91,6 +92,9 @@ const ASSET_ERROR: &str = ""; // TODO
|
|||
impl Plugin for BlueprintsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.register_watching_for_changes()
|
||||
.insert_resource(AssetToBlueprintInstancesMapper {
|
||||
untyped_id_to_blueprint_entity_ids: HashMap::new(),
|
||||
})
|
||||
.add_event::<BlueprintEvent>()
|
||||
.register_type::<BlueprintInfo>()
|
||||
.register_type::<MaterialInfo>()
|
||||
|
|
|
@ -4,9 +4,7 @@ use bevy::{gltf::Gltf, prelude::*, scene::SceneInstance, utils::hashbrown::HashM
|
|||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
AnimationInfos, AssetLoadTracker, BlenvyConfig, BlueprintAnimationPlayerLink,
|
||||
BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded,
|
||||
BlueprintAssetsNotLoaded, SceneAnimationPlayerLink, SceneAnimations,
|
||||
AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlenvyConfig, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, SceneAnimationPlayerLink, SceneAnimations
|
||||
};
|
||||
|
||||
/// this is a flag component for our levels/game world
|
||||
|
@ -39,11 +37,7 @@ impl BlueprintInfo {
|
|||
#[reflect(Component)]
|
||||
pub struct SpawnBlueprint;
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
/// flag component added when a Blueprint instance ist Ready : ie :
|
||||
/// - its assets have loaded
|
||||
/// - it has finished spawning
|
||||
pub struct BlueprintInstanceReady;
|
||||
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
|
@ -112,6 +106,9 @@ pub struct BlueprintSpawning;
|
|||
|
||||
use gltf::Gltf as RawGltf;
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Overview of the Blueprint Spawning process
|
||||
- Blueprint Load Assets
|
||||
|
@ -135,6 +132,8 @@ pub(crate) fn blueprints_prepare_spawn(
|
|||
>,
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
// for hot reload
|
||||
mut assets_to_blueprint_instances: ResMut<AssetToBlueprintInstancesMapper>
|
||||
) {
|
||||
for (entity, blueprint_info, entity_name) in blueprint_instances_to_spawn.iter() {
|
||||
info!(
|
||||
|
@ -186,6 +185,18 @@ pub(crate) fn blueprints_prepare_spawn(
|
|||
handle: untyped_handle.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME: dang, too early, asset server has not yet started loading yet
|
||||
// let path_id = asset_server.get_path_id(&asset.path).expect("we should have alread checked for this asset");
|
||||
let path_id = asset.path.clone();
|
||||
// TODO: make this dependant on if hot reload is enabled or not
|
||||
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids.contains_key(&path_id) {
|
||||
assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids.insert(path_id.clone(), vec![]);
|
||||
}
|
||||
// only insert if not already present in mapping
|
||||
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids[&path_id].contains(&entity) {
|
||||
assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids.get_mut(&path_id).unwrap().push(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -659,6 +670,7 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
|
|||
|
||||
commands
|
||||
.entity(original)
|
||||
.remove::<BlueprintChildrenReady>() // we are done with this step, we can remove the `BlueprintChildrenReady` tag component
|
||||
.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
|
||||
|
@ -669,6 +681,12 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
|
|||
#[reflect(Component)]
|
||||
pub struct BlueprintReadyForFinalizing;
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
/// flag component added when a Blueprint instance ist Ready : ie :
|
||||
/// - its assets have loaded
|
||||
/// - it has finished spawning
|
||||
pub struct BlueprintInstanceReady;
|
||||
|
||||
pub(crate) fn blueprints_finalize_instances(
|
||||
blueprint_instances: Query<
|
||||
(
|
||||
|
@ -684,6 +702,8 @@ pub(crate) fn blueprints_finalize_instances(
|
|||
all_children: Query<&Children>,
|
||||
mut blueprint_events: EventWriter<BlueprintEvent>,
|
||||
mut commands: Commands,
|
||||
|
||||
all_names: Query<&Name>
|
||||
) {
|
||||
for (entity, name, blueprint_info, parent_blueprint, hide_until_ready) in
|
||||
blueprint_instances.iter()
|
||||
|
@ -691,6 +711,7 @@ pub(crate) fn blueprints_finalize_instances(
|
|||
info!("Finalizing blueprint instance {:?}", name);
|
||||
commands
|
||||
.entity(entity)
|
||||
.remove::<BlueprintReadyForFinalizing>()
|
||||
.remove::<BlueprintReadyForPostProcess>()
|
||||
.remove::<BlueprintSpawning>()
|
||||
.remove::<SpawnBlueprint>()
|
||||
|
@ -720,7 +741,8 @@ pub(crate) fn blueprints_finalize_instances(
|
|||
}
|
||||
}
|
||||
if all_spawned {
|
||||
// println!("ALLLLL SPAAAAWNED for {} named {:?}", track_root.0, root_name);
|
||||
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