diff --git a/assets/foo/bar/baz/inspect.glb b/assets/foo/bar/baz/inspect.glb new file mode 100644 index 0000000..b96f116 --- /dev/null +++ b/assets/foo/bar/baz/inspect.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:976d82ec5c0039852a3f7e5991a60536b0c4050f7910bd55ca01e6b73fbdc8c9 +size 1244020 diff --git a/assets/scratch/foo/bar/baz/inspect.glb b/assets/scratch/foo/bar/baz/inspect.glb new file mode 100644 index 0000000..b96f116 --- /dev/null +++ b/assets/scratch/foo/bar/baz/inspect.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:976d82ec5c0039852a3f7e5991a60536b0c4050f7910bd55ca01e6b73fbdc8c9 +size 1244020 diff --git a/assets/scratch/materials.glb b/assets/scratch/materials.glb new file mode 100644 index 0000000..e4262d9 --- /dev/null +++ b/assets/scratch/materials.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8743ab5b1932c7546856cbfb8fd4afda6caaa35fdc4e728a06b86a9b563f6311 +size 265020 diff --git a/bin/editor.rs b/bin/editor.rs index fac4d35..26b7c8a 100644 --- a/bin/editor.rs +++ b/bin/editor.rs @@ -5,6 +5,12 @@ // BUGS: // // TODO: +// * re-load assets dir when new asset is copied +// * copy-and-reload drag-and-drop dirs +// * font resource for monologues +// * edit textbox with actions +// * run_if logic for actions where possible +// * make min/max/close buttons into actions not selects // * (medium) Select Font -> "Default Font" Resource // * (medium) Pre-compute animation target entities // * (hard) Harden Active Camera @@ -23,9 +29,8 @@ use monologue_trees::{debug::*, ui}; const WELCOME_MESSAGES: &'static [&'static str] = &[ "Welcome to the Monologue Trees editor!", + "Import assets by dragging and dropping files or folders into the editor!", concat!( - "Import assets by dragging and dropping files into the editor\n", - "\n", "Supported file types (for now):\n", "* 3D: .gltf, .glb\n", "* Audio: .ogg\n", @@ -56,6 +61,7 @@ fn main() { }, )) .init_resource::() + .insert_resource(AssetsDir::new("assets/scratch")) .add_event::() .add_asset::() .init_asset_loader::() @@ -87,7 +93,17 @@ fn main() { sync_monologue_font, ), ) - .add_systems(Update, (import_assets, import_file, import_folder)) + .add_systems( + Update, + ( + init_assets_dir, + import_assets, + import_file, + import_folder, + reload_assets, + clear_assets, + ), + ) .add_systems( Update, ( @@ -96,7 +112,7 @@ fn main() { directional_light_force_shadows, ), ) - .add_systems(Update, (clear_level, clear_assets)) + .add_systems(Update, clear_level) .run(); } @@ -479,10 +495,45 @@ mod audio { use assets::*; mod assets { - use std::path::PathBuf; + use std::{ + fs::DirBuilder, + fs::{copy, create_dir_all, read_dir}, + path::PathBuf, + }; + + use bevy::tasks::IoTaskPool; use super::*; + #[derive(Debug, Resource)] + pub struct AssetsDir { + pub root: PathBuf, + } + + impl AssetsDir { + pub fn new(root: &str) -> Self { + AssetsDir { + root: PathBuf::from(root) + .canonicalize() + .expect("Setting assets dir"), + } + } + } + + pub fn init_assets_dir(assets_dir: Res) { + if assets_dir.is_added() || assets_dir.is_changed() { + let d = assets_dir.root.clone(); + IoTaskPool::get() + .spawn(async move { + match DirBuilder::new().create(d) { + Ok(_) => info!("Created assets directory"), + Err(e) => warn!("Error creating assets directory {:?}", e), + } + }) + .detach(); + } + } + #[derive(Debug, Event)] pub enum ImportAsset { File(PathBuf), @@ -507,44 +558,82 @@ mod assets { }) } - pub fn import_file( - mut events: EventReader, - server: Res, - mut registry: ResMut, - ) { + pub fn import_file(mut events: EventReader, assets_dir: Res) { events .iter() .filter_map(|event| match event { - ImportAsset::File(path) => Some(path), + ImportAsset::File(path) => Some(path.clone()), _ => None, }) .for_each(|path| { - registry - .0 - .push(server.load_untyped(String::from(path.to_string_lossy()))); + let fname = path.file_name().expect("PathBuf should have file name"); + let newpath = PathBuf::from(assets_dir.root.clone()).join(fname); + IoTaskPool::get() + .spawn(async move { + match copy(path, newpath) { + Ok(_) => info!("Created assets directory"), + Err(e) => warn!("Error creating assets directory {:?}", e), + } + }) + .detach(); }); } - pub fn import_folder( - mut events: EventReader, - server: Res, - mut registry: ResMut, - ) { + fn copy_dir(from: PathBuf, to: PathBuf) -> std::io::Result<()> { + info!("Copy dir {:?} -> {:?}", from, to); + if let Ok(entries) = read_dir(from) { + for entry in entries.filter_map(|e| e.ok()) { + if entry.path().is_file() { + let from_file = entry.path(); + let to_file = to.join(entry.file_name()); + copy(from_file, to_file).map(|_| ())?; + } else if entry.path().is_dir() { + info!("Recursive"); + let target = to.join(entry.file_name()); + create_dir_all(target.clone())?; + copy_dir(entry.path(), target.clone())?; + } + } + } + Ok(()) + } + + pub fn import_folder(mut events: EventReader, assets_dir: Res) { events .iter() .filter_map(|event| match event { - ImportAsset::Dir(path) => Some(path), + ImportAsset::Dir(path) => Some(path.clone()), _ => None, }) .for_each(|path| { - registry.0.extend( - server - .load_folder(String::from(path.to_string_lossy())) - .expect("Loading assets from folder"), - ); + let fname = path.file_name().expect("PathBuf should have file name"); + let newpath = PathBuf::from(assets_dir.root.clone()).join(fname); + info!("copying folder {:?} to {:?}", path.clone(), newpath); + IoTaskPool::get() + .spawn(async move { + match copy_dir(path.clone(), newpath) { + Ok(_) => info!("Created assets directory"), + Err(e) => warn!("Error creating assets directory {:?}", e), + } + }) + .detach(); }); } + pub fn reload_assets( + mut events: EventReader, + server: Res, + mut registry: ResMut, + assets_dir: Res, + ) { + // Reduce all import events in a frame to one "load_folder" action + if events.iter().len() > 0 { + registry.0 = server + .load_folder(assets_dir.root.clone()) + .expect("Reload assets folder"); + } + } + pub fn get_asset_name(server: &AssetServer, handle: Handle) -> String { if let Some(asset_path) = server.get_handle_path(handle.clone()) { if let Some(stem) = asset_path.path().file_stem() { diff --git a/src/ui.rs b/src/ui.rs index f9a307f..7d30120 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -507,7 +507,7 @@ mod scroll { self_hover || child_hover || parent_hover }) .for_each(|(_, _, _, mut style)| { - let factor = 2.0; + let factor = 8.0; let val = match unit { MouseScrollUnit::Line => match style.top { Val::Px(v) => v + (y * factor),