|
|
|
|
@ -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::<AssetRegistry>()
|
|
|
|
|
.insert_resource(AssetsDir::new("assets/scratch"))
|
|
|
|
|
.add_event::<ImportAsset>()
|
|
|
|
|
.add_asset::<Monologue>()
|
|
|
|
|
.init_asset_loader::<MonologueLoader>()
|
|
|
|
|
@ -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<AssetsDir>) {
|
|
|
|
|
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<ImportAsset>,
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
mut registry: ResMut<AssetRegistry>,
|
|
|
|
|
) {
|
|
|
|
|
pub fn import_file(mut events: EventReader<ImportAsset>, assets_dir: Res<AssetsDir>) {
|
|
|
|
|
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<ImportAsset>,
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
mut registry: ResMut<AssetRegistry>,
|
|
|
|
|
) {
|
|
|
|
|
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<ImportAsset>, assets_dir: Res<AssetsDir>) {
|
|
|
|
|
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<ImportAsset>,
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
mut registry: ResMut<AssetRegistry>,
|
|
|
|
|
assets_dir: Res<AssetsDir>,
|
|
|
|
|
) {
|
|
|
|
|
// 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<T: Asset>(server: &AssetServer, handle: Handle<T>) -> String {
|
|
|
|
|
if let Some(asset_path) = server.get_handle_path(handle.clone()) {
|
|
|
|
|
if let Some(stem) = asset_path.path().file_stem() {
|
|
|
|
|
|