mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-22 11:50:53 +00:00
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 crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintChildrenReady, BlueprintInfo, BlueprintInstanceReady, InBlueprint, SpawnBlueprint, SubBlueprintsSpawnTracker};
|
||||||
use bevy::asset::AssetEvent;
|
use bevy::asset::{AssetEvent, UntypedAssetId};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::scene::SceneInstance;
|
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(
|
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 untyped_events: EventReader<AssetEvent<LoadedUntypedAsset>>,
|
||||||
mut blueprint_assets: Query<(
|
blueprint_assets: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
Option<&Name>,
|
Option<&Name>,
|
||||||
&BlueprintInfo,
|
&BlueprintInfo,
|
||||||
&mut BlueprintAssetsLoadState,
|
|
||||||
Option<&Children>,
|
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>,
|
asset_server: Res<AssetServer>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
for event in gltf_events.read() {
|
for event in gltf_events.read() {
|
||||||
// LoadedUntypedAsset
|
// LoadedUntypedAsset
|
||||||
match event {
|
match event {
|
||||||
AssetEvent::Modified { id } => {
|
AssetEvent::Modified { id } => {
|
||||||
// React to the image being modified
|
// React to the gltf file being modified
|
||||||
// println!("Modified gltf {:?}", asset_server.get_path(*id));
|
// println!("Modified gltf {:?}", asset_server.get_path(*id));
|
||||||
for (entity, entity_name, _blueprint_info, mut assets_to_load, children) in
|
if let Some(asset_path) = asset_server.get_path(*id) {
|
||||||
blueprint_assets.iter_mut()
|
// let untyped = asset_server.get_handle_untyped(asset_path.clone());
|
||||||
{
|
// println!("matching untyped handle {:?}", untyped);
|
||||||
for tracker in assets_to_load.asset_infos.iter_mut() {
|
// let bla = untyped.unwrap().id();
|
||||||
if asset_server.get_path(*id).is_some() {
|
// asset_server.get
|
||||||
if tracker.path == asset_server.get_path(*id).unwrap().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() {
|
||||||
|
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);
|
println!("HOLY MOLY IT DETECTS !!, now respawn {:?}", entity_name);
|
||||||
|
|
||||||
|
// TODO: only remove those that are "in blueprint"
|
||||||
if children.is_some() {
|
if children.is_some() {
|
||||||
for child in children.unwrap().iter() {
|
for child in children.unwrap().iter() {
|
||||||
commands.entity(*child).despawn_recursive();
|
commands.entity(*child).despawn_recursive();
|
||||||
@ -36,12 +53,12 @@ pub(crate) fn react_to_asset_changes(
|
|||||||
}
|
}
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
|
.remove::<BlueprintInstanceReady>()
|
||||||
.remove::<BlueprintAssetsLoaded>()
|
.remove::<BlueprintAssetsLoaded>()
|
||||||
.remove::<SceneInstance>()
|
.remove::<SceneInstance>()
|
||||||
.remove::<BlueprintAssetsLoadState>()
|
.remove::<BlueprintAssetsLoadState>()
|
||||||
|
.remove::<SubBlueprintsSpawnTracker>()
|
||||||
.insert(SpawnBlueprint);
|
.insert(SpawnBlueprint);
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ pub use copy_components::*;
|
|||||||
pub(crate) mod hot_reload;
|
pub(crate) mod hot_reload;
|
||||||
pub(crate) use hot_reload::*;
|
pub(crate) use hot_reload::*;
|
||||||
|
|
||||||
use bevy::{prelude::*, utils::HashMap};
|
use bevy::{prelude::*, utils::hashbrown::HashMap};
|
||||||
|
|
||||||
use crate::{BlenvyConfig, GltfComponentsSet};
|
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 {
|
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 {
|
trait BlenvyApp {
|
||||||
@ -91,6 +92,9 @@ 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.register_watching_for_changes()
|
app.register_watching_for_changes()
|
||||||
|
.insert_resource(AssetToBlueprintInstancesMapper {
|
||||||
|
untyped_id_to_blueprint_entity_ids: HashMap::new(),
|
||||||
|
})
|
||||||
.add_event::<BlueprintEvent>()
|
.add_event::<BlueprintEvent>()
|
||||||
.register_type::<BlueprintInfo>()
|
.register_type::<BlueprintInfo>()
|
||||||
.register_type::<MaterialInfo>()
|
.register_type::<MaterialInfo>()
|
||||||
|
@ -4,9 +4,7 @@ use bevy::{gltf::Gltf, prelude::*, scene::SceneInstance, utils::hashbrown::HashM
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnimationInfos, AssetLoadTracker, BlenvyConfig, BlueprintAnimationPlayerLink,
|
AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlenvyConfig, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, SceneAnimationPlayerLink, SceneAnimations
|
||||||
BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded,
|
|
||||||
BlueprintAssetsNotLoaded, SceneAnimationPlayerLink, SceneAnimations,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// this is a flag component for our levels/game world
|
/// this is a flag component for our levels/game world
|
||||||
@ -39,11 +37,7 @@ impl BlueprintInfo {
|
|||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct SpawnBlueprint;
|
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)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
@ -112,6 +106,9 @@ pub struct BlueprintSpawning;
|
|||||||
|
|
||||||
use gltf::Gltf as RawGltf;
|
use gltf::Gltf as RawGltf;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Overview of the Blueprint Spawning process
|
Overview of the Blueprint Spawning process
|
||||||
- Blueprint Load Assets
|
- Blueprint Load Assets
|
||||||
@ -135,6 +132,8 @@ pub(crate) fn blueprints_prepare_spawn(
|
|||||||
>,
|
>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
asset_server: Res<AssetServer>,
|
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() {
|
for (entity, blueprint_info, entity_name) in blueprint_instances_to_spawn.iter() {
|
||||||
info!(
|
info!(
|
||||||
@ -186,6 +185,18 @@ pub(crate) fn blueprints_prepare_spawn(
|
|||||||
handle: untyped_handle.clone(),
|
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
|
commands
|
||||||
.entity(original)
|
.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"
|
.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
|
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)]
|
#[reflect(Component)]
|
||||||
pub struct BlueprintReadyForFinalizing;
|
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(
|
pub(crate) fn blueprints_finalize_instances(
|
||||||
blueprint_instances: Query<
|
blueprint_instances: Query<
|
||||||
(
|
(
|
||||||
@ -684,6 +702,8 @@ pub(crate) fn blueprints_finalize_instances(
|
|||||||
all_children: Query<&Children>,
|
all_children: Query<&Children>,
|
||||||
mut blueprint_events: EventWriter<BlueprintEvent>,
|
mut blueprint_events: EventWriter<BlueprintEvent>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|
||||||
|
all_names: Query<&Name>
|
||||||
) {
|
) {
|
||||||
for (entity, name, blueprint_info, parent_blueprint, hide_until_ready) in
|
for (entity, name, blueprint_info, parent_blueprint, hide_until_ready) in
|
||||||
blueprint_instances.iter()
|
blueprint_instances.iter()
|
||||||
@ -691,6 +711,7 @@ pub(crate) fn blueprints_finalize_instances(
|
|||||||
info!("Finalizing blueprint instance {:?}", name);
|
info!("Finalizing blueprint instance {:?}", name);
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
|
.remove::<BlueprintReadyForFinalizing>()
|
||||||
.remove::<BlueprintReadyForPostProcess>()
|
.remove::<BlueprintReadyForPostProcess>()
|
||||||
.remove::<BlueprintSpawning>()
|
.remove::<BlueprintSpawning>()
|
||||||
.remove::<SpawnBlueprint>()
|
.remove::<SpawnBlueprint>()
|
||||||
@ -720,7 +741,8 @@ pub(crate) fn blueprints_finalize_instances(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if all_spawned {
|
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);
|
commands.entity(track_root.0).insert(BlueprintChildrenReady);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user