|
|
|
|
@ -3,18 +3,12 @@
|
|
|
|
|
// Editor for creating Monologue Trees levels
|
|
|
|
|
//
|
|
|
|
|
// BUGS:
|
|
|
|
|
// * Cannot view scene when selected, WTF
|
|
|
|
|
// * Scene and Animation tabs are whacked
|
|
|
|
|
//
|
|
|
|
|
// TODO:
|
|
|
|
|
// * (medium) Spawn gltf scene
|
|
|
|
|
// * (medium) Load default scene when gltf selected
|
|
|
|
|
// * (medium) Set gltf to active/inactive
|
|
|
|
|
// * (medium) Play individual animation(s)
|
|
|
|
|
// * (hard) Better Colorscheme
|
|
|
|
|
// * (medium) Visual errors for bad GLTFs
|
|
|
|
|
// * (medium) Play clicked animation
|
|
|
|
|
// * Use default camera.
|
|
|
|
|
// * (easy) Play all animations
|
|
|
|
|
// * (medium) Visual errors for GLTFs problems (no cameras)
|
|
|
|
|
//
|
|
|
|
|
// Asset types:
|
|
|
|
|
// * Audios (done)
|
|
|
|
|
@ -36,6 +30,19 @@ use bevy::{
|
|
|
|
|
};
|
|
|
|
|
use monologue_trees::{debug::*, ui};
|
|
|
|
|
|
|
|
|
|
const WELCOME_MESSAGES: &'static [&'static str] = &[
|
|
|
|
|
"Welcome to the Monologue Trees 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",
|
|
|
|
|
"* Font: .ttf, .otf\n",
|
|
|
|
|
"* Monologues: .monologue.txt\n",
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
App::new()
|
|
|
|
|
.add_plugins((
|
|
|
|
|
@ -70,8 +77,10 @@ fn main() {
|
|
|
|
|
manage_gltf_animation_ui,
|
|
|
|
|
scenes_ui,
|
|
|
|
|
animations_ui,
|
|
|
|
|
spawn_scenes,
|
|
|
|
|
manage_camera,
|
|
|
|
|
control_active_scenes,
|
|
|
|
|
manage_active_camera,
|
|
|
|
|
control_active_camera,
|
|
|
|
|
fallback_camera,
|
|
|
|
|
play_animation,
|
|
|
|
|
play_audio,
|
|
|
|
|
show_preview_text,
|
|
|
|
|
@ -105,10 +114,11 @@ fn initialize_ui(mut commands: Commands) {
|
|
|
|
|
commands.spawn((SpatialBundle { ..default() }, LevelRoot));
|
|
|
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
|
Camera3dBundle { ..default() },
|
|
|
|
|
Camera2dBundle { ..default() },
|
|
|
|
|
UiCameraConfig { show_ui: true },
|
|
|
|
|
Name::new("Editor Camera"),
|
|
|
|
|
EditorCamera,
|
|
|
|
|
ui::Active,
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
let base_style = Style {
|
|
|
|
|
@ -198,7 +208,7 @@ fn initialize_ui(mut commands: Commands) {
|
|
|
|
|
content_containers.push(spawn_tab_container::<SceneWidget>(
|
|
|
|
|
"Scene",
|
|
|
|
|
parent,
|
|
|
|
|
ui::Select::Single,
|
|
|
|
|
ui::Select::Multi,
|
|
|
|
|
));
|
|
|
|
|
content_containers.push(spawn_tab_container::<AnimationWidget>(
|
|
|
|
|
"Animation",
|
|
|
|
|
@ -266,21 +276,9 @@ fn initialize_ui(mut commands: Commands) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn welcome_message(mut writer: EventWriter<ui::Alert>) {
|
|
|
|
|
writer.send(ui::Alert::Info(
|
|
|
|
|
"Welcome to the Monologue Trees editor!".into(),
|
|
|
|
|
));
|
|
|
|
|
writer.send(ui::Alert::Info(
|
|
|
|
|
[
|
|
|
|
|
"Import assets by dragging and dropping files into the editor",
|
|
|
|
|
"",
|
|
|
|
|
"Supported file types (for now):",
|
|
|
|
|
"* 3D: .gltf, .glb",
|
|
|
|
|
"* Audio: .ogg",
|
|
|
|
|
"* Font: .ttf, .otf",
|
|
|
|
|
]
|
|
|
|
|
.join("\n")
|
|
|
|
|
.into(),
|
|
|
|
|
));
|
|
|
|
|
WELCOME_MESSAGES
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|&msg| writer.send(ui::Alert::Info(msg.into())))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn spawn_tab_container<T: Default + Component>(
|
|
|
|
|
@ -524,6 +522,22 @@ mod assets {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn destroy_entity_button(
|
|
|
|
|
current: &Query<(Entity, &ui::TargetEntity)>,
|
|
|
|
|
commands: &mut Commands,
|
|
|
|
|
target: &ui::TargetEntity,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(entity) = current.iter().find_map(|(entity, this)| {
|
|
|
|
|
if this.entity == target.entity {
|
|
|
|
|
Some(entity)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}) {
|
|
|
|
|
commands.entity(entity).despawn_recursive();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn has_extensions<T: Asset>(
|
|
|
|
|
server: &AssetServer,
|
|
|
|
|
handle: Handle<T>,
|
|
|
|
|
@ -764,18 +778,31 @@ mod scenes {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn spawn_scenes(
|
|
|
|
|
events: Query<
|
|
|
|
|
(&Interaction, &ui::TargetAsset<Scene>),
|
|
|
|
|
(With<Button>, Changed<Interaction>),
|
|
|
|
|
>,
|
|
|
|
|
pub fn control_active_scenes(
|
|
|
|
|
added: Query<Entity, (With<Button>, Added<ui::Active>)>,
|
|
|
|
|
mut removed: RemovedComponents<ui::Active>,
|
|
|
|
|
scene_refs: Query<&ui::TargetAsset<Scene>>,
|
|
|
|
|
scenes: Query<(Entity, &Handle<Scene>)>,
|
|
|
|
|
level_root: Query<Entity, With<LevelRoot>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
events
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(&interaction, _)| interaction == Interaction::Pressed)
|
|
|
|
|
.for_each(|(_, ui::TargetAsset { handle })| {
|
|
|
|
|
// A scene button was marked inactive
|
|
|
|
|
removed.iter().for_each(|entity| {
|
|
|
|
|
// Get the handle associated with that button
|
|
|
|
|
if let Ok(ui::TargetAsset { handle }) = scene_refs.get(entity) {
|
|
|
|
|
if let Some(entity) = scenes.iter().find_map(|(entity, this_handle)| {
|
|
|
|
|
if this_handle == handle {
|
|
|
|
|
Some(entity)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}) {
|
|
|
|
|
commands.entity(entity).despawn_recursive();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
added.iter().for_each(|entity| {
|
|
|
|
|
if let Ok(ui::TargetAsset { handle }) = scene_refs.get(entity) {
|
|
|
|
|
info!("Spawning Scene {:?}", handle);
|
|
|
|
|
commands
|
|
|
|
|
.entity(level_root.single())
|
|
|
|
|
@ -785,7 +812,8 @@ mod scenes {
|
|
|
|
|
..default()
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1119,11 +1147,17 @@ mod cameras {
|
|
|
|
|
|
|
|
|
|
// TODO: Despawn camera button when camera removed
|
|
|
|
|
pub fn cameras_ui(
|
|
|
|
|
mut events: Query<(Entity, &mut Camera, &Name), (Added<Camera>, Without<EditorCamera>)>,
|
|
|
|
|
mut added: Query<(Entity, &mut Camera, &Name), (Added<Camera>, Without<EditorCamera>)>,
|
|
|
|
|
mut removed: RemovedComponents<Camera>,
|
|
|
|
|
widget: Query<Entity, With<CameraWidget>>,
|
|
|
|
|
current: Query<(Entity, &ui::TargetEntity)>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
events.iter_mut().for_each(|(entity, mut camera, name)| {
|
|
|
|
|
removed.iter().for_each(|entity| {
|
|
|
|
|
info!("Destroy button for {:?}", entity);
|
|
|
|
|
destroy_entity_button(¤t, &mut commands, &ui::TargetEntity { entity });
|
|
|
|
|
});
|
|
|
|
|
added.iter_mut().for_each(|(entity, mut camera, name)| {
|
|
|
|
|
info!("Camera added {:?} {:?}", entity, name);
|
|
|
|
|
create_entity_button(
|
|
|
|
|
&widget,
|
|
|
|
|
@ -1132,20 +1166,65 @@ mod cameras {
|
|
|
|
|
name.as_str().into(),
|
|
|
|
|
);
|
|
|
|
|
camera.is_active = false;
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn manage_camera(
|
|
|
|
|
/// Set the camera active component based on button clicks
|
|
|
|
|
pub fn manage_active_camera(
|
|
|
|
|
events: Query<(&Interaction, &ui::TargetEntity), Changed<Interaction>>,
|
|
|
|
|
mut cameras: Query<(Entity, &mut Camera)>,
|
|
|
|
|
cameras: Query<Entity, With<Camera>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
events
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(&interaction, _)| interaction == Interaction::Pressed)
|
|
|
|
|
.for_each(|(_, ui::TargetEntity { entity })| {
|
|
|
|
|
cameras.iter_mut().for_each(|(this_entity, mut camera)| {
|
|
|
|
|
camera.is_active = this_entity == *entity;
|
|
|
|
|
cameras.iter().for_each(|this_entity| {
|
|
|
|
|
if this_entity == *entity {
|
|
|
|
|
info!("Marking {:?} as active camera", entity);
|
|
|
|
|
commands.entity(this_entity).insert(ui::Active);
|
|
|
|
|
} else {
|
|
|
|
|
info!("Marking {:?} as inactive camera", entity);
|
|
|
|
|
commands.entity(this_entity).remove::<ui::Active>();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the active camera based on the Active marker component
|
|
|
|
|
pub fn control_active_camera(
|
|
|
|
|
added: Query<Entity, (Added<ui::Active>, With<Camera>)>,
|
|
|
|
|
mut removed: RemovedComponents<ui::Active>,
|
|
|
|
|
mut cameras: Query<&mut Camera>,
|
|
|
|
|
) {
|
|
|
|
|
removed.iter().for_each(|entity| {
|
|
|
|
|
info!("Setting {:?} to inactive camera", entity);
|
|
|
|
|
if let Ok(mut camera) = cameras.get_mut(entity) {
|
|
|
|
|
camera.is_active = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
added.iter().for_each(|entity| {
|
|
|
|
|
info!("Setting {:?} to active camera", entity);
|
|
|
|
|
if let Ok(mut camera) = cameras.get_mut(entity) {
|
|
|
|
|
camera.is_active = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// In the event that an active camera is despawned, fall back to the editor camera
|
|
|
|
|
pub fn fallback_camera(
|
|
|
|
|
modified: Query<Entity, (Changed<Camera>, Without<EditorCamera>)>,
|
|
|
|
|
mut removed: RemovedComponents<Camera>,
|
|
|
|
|
other_cameras: Query<&Camera, Without<EditorCamera>>,
|
|
|
|
|
mut editor_camera: Query<&mut Camera, With<EditorCamera>>,
|
|
|
|
|
) {
|
|
|
|
|
// Any time a camera is modified
|
|
|
|
|
modified.iter().chain(removed.iter()).for_each(|_| {
|
|
|
|
|
// If no other cameras are active
|
|
|
|
|
if !other_cameras.iter().any(|camera| camera.is_active) {
|
|
|
|
|
// Make the editor camera active
|
|
|
|
|
editor_camera.single_mut().is_active = true;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|