diff --git a/crates/bevy_animation_graph_editor/src/asset_saving.rs b/crates/bevy_animation_graph_editor/src/asset_saving.rs index b547b07..d84389a 100644 --- a/crates/bevy_animation_graph_editor/src/asset_saving.rs +++ b/crates/bevy_animation_graph_editor/src/asset_saving.rs @@ -1,4 +1,4 @@ -use crate::{ui::UiState, PersistedAssetHandles}; +use crate::{scanner::PersistedAssetHandles, ui::UiState}; use bevy::{ asset::io::{file::FileAssetReader, AssetReader, AssetSourceId}, prelude::*, diff --git a/crates/bevy_animation_graph_editor/src/main.rs b/crates/bevy_animation_graph_editor/src/main.rs index 9fc6a9f..0a0c85a 100644 --- a/crates/bevy_animation_graph_editor/src/main.rs +++ b/crates/bevy_animation_graph_editor/src/main.rs @@ -5,19 +5,18 @@ mod egui_nodes; mod fsm_show; mod graph_show; mod graph_update; +mod scanner; mod tree; mod ui; use asset_saving::AssetSavingPlugin; -use bevy::{asset::LoadedFolder, prelude::*, utils::HashSet}; -use bevy_animation_graph::core::{ - animation_graph::AnimationGraph, plugin::AnimationGraphPlugin, - state_machine::high_level::StateMachine, -}; +use bevy::prelude::*; +use bevy_animation_graph::core::plugin::AnimationGraphPlugin; use bevy_egui::EguiPlugin; use bevy_inspector_egui::{bevy_egui, DefaultInspectorConfigPlugin}; use clap::Parser; use egui_inspector_impls::BetterInspectorPlugin; +use scanner::ScannerPlugin; use std::path::PathBuf; use ui::{graph_debug_draw_bone_system, UiState}; @@ -27,15 +26,6 @@ struct Cli { asset_source: PathBuf, } -/// Keeps a handle to the folder so that it does not get unloaded -#[derive(Resource)] -struct PersistedAssetHandles { - #[allow(dead_code)] - folder: Handle, - unsaved_graphs: HashSet>, - unsaved_fsms: HashSet>, -} - fn main() { let cli = Cli::parse(); @@ -56,9 +46,10 @@ fn main() { .add_plugins(DefaultInspectorConfigPlugin) .add_plugins(BetterInspectorPlugin) .add_plugins(AssetSavingPlugin) + .add_plugins(ScannerPlugin) .insert_resource(UiState::new()) .insert_resource(cli) - .add_systems(Startup, (core_setup, ui::setup_system)) + .add_systems(Startup, ui::setup_system) .add_systems( Update, ( @@ -72,18 +63,3 @@ fn main() { app.run(); } - -fn core_setup( - mut commands: Commands, - asset_server: Res, - mut gizmo_config: ResMut, -) { - commands.insert_resource(PersistedAssetHandles { - folder: asset_server.load_folder(""), - unsaved_graphs: HashSet::default(), - unsaved_fsms: HashSet::default(), - }); - - let config = gizmo_config.config_mut::().0; - config.depth_bias = -1.; -} diff --git a/crates/bevy_animation_graph_editor/src/scanner.rs b/crates/bevy_animation_graph_editor/src/scanner.rs new file mode 100644 index 0000000..deae10d --- /dev/null +++ b/crates/bevy_animation_graph_editor/src/scanner.rs @@ -0,0 +1,84 @@ +use crate::Cli; +use bevy::{asset::LoadedUntypedAsset, prelude::*, utils::HashSet}; +use bevy_animation_graph::{ + core::state_machine::high_level::StateMachine, prelude::AnimationGraph, +}; +use std::{ + fs, io, + path::{Path, PathBuf}, +}; + +pub struct ScannerPlugin; +impl Plugin for ScannerPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + app.insert_resource(PersistedAssetHandles { + unsaved_graphs: HashSet::default(), + unsaved_fsms: HashSet::default(), + loaded_paths: HashSet::default(), + }) + .add_event::() + .add_systems(Startup, core_setup) + .add_systems(Update, asset_reload); + } +} + +/// Keeps a handle to the folder so that it does not get unloaded +#[derive(Resource)] +pub struct PersistedAssetHandles { + #[allow(dead_code)] + pub loaded_paths: HashSet>, + pub unsaved_graphs: HashSet>, + pub unsaved_fsms: HashSet>, +} + +#[derive(Event)] +pub struct RescanAssets; + +pub fn core_setup( + mut evw_rescan_events: EventWriter, + mut gizmo_config: ResMut, +) { + evw_rescan_events.send(RescanAssets); + + let config = gizmo_config.config_mut::().0; + config.depth_bias = -1.; +} + +pub fn asset_reload( + mut reload_events: EventReader, + asset_server: Res, + mut persisted_asset_handles: ResMut, + cli: Res, +) { + if reload_events.read().next().is_some() { + visit_dirs(&cli.asset_source, &mut |path| { + let relative_path = path.strip_prefix(&cli.asset_source).unwrap().to_owned(); + let loaded = asset_server.load_untyped(relative_path); + persisted_asset_handles.loaded_paths.insert(loaded); + }) + .unwrap_or_else(|err| { + panic!( + "Failed to load asset path {:?}: {:?}", + cli.asset_source, err + ) + }); + } +} + +// one possible implementation of walking a directory only visiting files +// taken from https://doc.rust-lang.org/nightly/std/fs/fn.read_dir.html#examples +fn visit_dirs(dir: &Path, cb: &mut dyn FnMut(PathBuf)) -> io::Result<()> { + if dir.is_dir() { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + visit_dirs(&path, cb)?; + } else { + info!("Loading {path:?}"); + cb(path); + } + } + } + Ok(()) +} diff --git a/crates/bevy_animation_graph_editor/src/ui.rs b/crates/bevy_animation_graph_editor/src/ui.rs index b746b38..2a1c10a 100644 --- a/crates/bevy_animation_graph_editor/src/ui.rs +++ b/crates/bevy_animation_graph_editor/src/ui.rs @@ -10,8 +10,8 @@ use crate::graph_update::{ apply_global_changes, convert_fsm_change, convert_graph_change, update_graph, Change, FsmChange, FsmPropertiesChange, GlobalChange, GraphChange, }; +use crate::scanner::PersistedAssetHandles; use crate::tree::{Tree, TreeInternal, TreeResult}; -use crate::PersistedAssetHandles; use bevy::asset::UntypedAssetId; use bevy::ecs::system::CommandQueue; use bevy::prelude::*;