|
|
|
@ -1,4 +1,4 @@
|
|
|
|
use bevy::{asset::AssetEvents, gltf::Gltf, prelude::*, utils::HashMap};
|
|
|
|
use bevy::{gltf::Gltf, prelude::*, utils::HashMap};
|
|
|
|
use monologue_trees::debug::*;
|
|
|
|
use monologue_trees::debug::*;
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
|
@ -18,6 +18,7 @@ fn main() {
|
|
|
|
.add_event::<SpawnScene>()
|
|
|
|
.add_event::<SpawnScene>()
|
|
|
|
.init_resource::<Current>()
|
|
|
|
.init_resource::<Current>()
|
|
|
|
.add_systems(Startup, spawn_ui)
|
|
|
|
.add_systems(Startup, spawn_ui)
|
|
|
|
|
|
|
|
.add_systems(PreUpdate, (add_camera_ui, add_scene_ui, add_animation_ui))
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
(
|
|
|
|
@ -25,13 +26,19 @@ fn main() {
|
|
|
|
loading,
|
|
|
|
loading,
|
|
|
|
reset_scene.before(spawn_scene),
|
|
|
|
reset_scene.before(spawn_scene),
|
|
|
|
spawn_scene.after(reset_scene),
|
|
|
|
spawn_scene.after(reset_scene),
|
|
|
|
wire_up_scenes,
|
|
|
|
|
|
|
|
control_scene,
|
|
|
|
control_scene,
|
|
|
|
wire_up_animations,
|
|
|
|
|
|
|
|
control_animation,
|
|
|
|
control_animation,
|
|
|
|
wire_up_cameras,
|
|
|
|
|
|
|
|
control_camera,
|
|
|
|
control_camera,
|
|
|
|
control_default_camera,
|
|
|
|
control_default_camera,
|
|
|
|
|
|
|
|
control_default_light,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.add_systems(
|
|
|
|
|
|
|
|
PostUpdate,
|
|
|
|
|
|
|
|
(
|
|
|
|
|
|
|
|
clean_ui::<SceneButton>,
|
|
|
|
|
|
|
|
clean_ui::<AnimationButton>,
|
|
|
|
|
|
|
|
clean_ui::<CameraButton>,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.run();
|
|
|
|
.run();
|
|
|
|
@ -41,7 +48,10 @@ fn main() {
|
|
|
|
struct SceneMarker;
|
|
|
|
struct SceneMarker;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct MainCamera;
|
|
|
|
struct DefaultCamera;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
|
|
|
struct DefaultLight;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct MainUi;
|
|
|
|
struct MainUi;
|
|
|
|
@ -58,6 +68,9 @@ struct SceneSelectUi;
|
|
|
|
#[derive(Component, PartialEq, Debug)]
|
|
|
|
#[derive(Component, PartialEq, Debug)]
|
|
|
|
struct SceneButton(Handle<Scene>);
|
|
|
|
struct SceneButton(Handle<Scene>);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Event)]
|
|
|
|
|
|
|
|
struct SpawnScene(Handle<Scene>);
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct AnimationSelectUi;
|
|
|
|
struct AnimationSelectUi;
|
|
|
|
|
|
|
|
|
|
|
|
@ -80,8 +93,8 @@ struct Current {
|
|
|
|
#[derive(Event)]
|
|
|
|
#[derive(Event)]
|
|
|
|
struct ResetScene;
|
|
|
|
struct ResetScene;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Event)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct SpawnScene(Handle<Scene>);
|
|
|
|
struct ResetAnimation;
|
|
|
|
|
|
|
|
|
|
|
|
fn drag_and_drop(
|
|
|
|
fn drag_and_drop(
|
|
|
|
mut events: EventReader<FileDragAndDrop>,
|
|
|
|
mut events: EventReader<FileDragAndDrop>,
|
|
|
|
@ -90,13 +103,9 @@ fn drag_and_drop(
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events
|
|
|
|
events
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.filter_map(|event| {
|
|
|
|
.filter_map(|event| match event {
|
|
|
|
if let FileDragAndDrop::DroppedFile { path_buf, .. } = event {
|
|
|
|
FileDragAndDrop::DroppedFile { path_buf, .. } => Some(path_buf),
|
|
|
|
info!("Drag+Drop file: {:?}", path_buf);
|
|
|
|
_ => None,
|
|
|
|
Some(path_buf)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.map(|path_buf| server.load(path_buf.to_str().expect("PathBuf to str")))
|
|
|
|
.map(|path_buf| server.load(path_buf.to_str().expect("PathBuf to str")))
|
|
|
|
.for_each(|handle| current.gltf = handle.clone());
|
|
|
|
.for_each(|handle| current.gltf = handle.clone());
|
|
|
|
@ -107,9 +116,19 @@ fn spawn_ui(mut commands: Commands) {
|
|
|
|
// TODO: Scene select container
|
|
|
|
// TODO: Scene select container
|
|
|
|
// TODO: Animation Play/Pause Placeholder
|
|
|
|
// TODO: Animation Play/Pause Placeholder
|
|
|
|
commands.spawn((
|
|
|
|
commands.spawn((
|
|
|
|
Camera3dBundle { ..default() },
|
|
|
|
Camera3dBundle {
|
|
|
|
|
|
|
|
transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
UiCameraConfig { show_ui: true },
|
|
|
|
UiCameraConfig { show_ui: true },
|
|
|
|
MainCamera,
|
|
|
|
DefaultCamera,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
|
|
|
|
DirectionalLightBundle {
|
|
|
|
|
|
|
|
transform: Transform::default().looking_at(Vec3::new(1.0, -1.0, -1.0), Vec3::Y),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
DefaultLight,
|
|
|
|
));
|
|
|
|
));
|
|
|
|
commands
|
|
|
|
commands
|
|
|
|
.spawn((
|
|
|
|
.spawn((
|
|
|
|
@ -127,23 +146,46 @@ fn spawn_ui(mut commands: Commands) {
|
|
|
|
MainUi,
|
|
|
|
MainUi,
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
// TOOD: Prompt to drag+drop Gltf/Gltf file
|
|
|
|
parent
|
|
|
|
parent.spawn((
|
|
|
|
.spawn(NodeBundle {
|
|
|
|
TextBundle {
|
|
|
|
style: Style {
|
|
|
|
text: Text {
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
sections: vec![TextSection {
|
|
|
|
|
|
|
|
value: String::from("Drag and Drop .gltf/.glb file"),
|
|
|
|
|
|
|
|
style: TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
})
|
|
|
|
InstructionsUi,
|
|
|
|
.with_children(|parent| {
|
|
|
|
));
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
TextBundle::from_section(
|
|
|
|
|
|
|
|
"Drag and Drop .gltf/.glb file",
|
|
|
|
|
|
|
|
TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
InstructionsUi,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
TextBundle::from_section(
|
|
|
|
|
|
|
|
"Using default camera",
|
|
|
|
|
|
|
|
TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
DefaultCamera,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
TextBundle::from_section(
|
|
|
|
|
|
|
|
"Using default light",
|
|
|
|
|
|
|
|
TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
DefaultLight,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
});
|
|
|
|
parent
|
|
|
|
parent
|
|
|
|
.spawn((
|
|
|
|
.spawn((
|
|
|
|
NodeBundle {
|
|
|
|
NodeBundle {
|
|
|
|
@ -170,11 +212,11 @@ fn spawn_ui(mut commands: Commands) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
AnimationSelectUi,
|
|
|
|
CameraSelectUi,
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn((
|
|
|
|
parent.spawn((
|
|
|
|
TextBundle::from_section("Animations", TextStyle { ..default() }),
|
|
|
|
TextBundle::from_section("Cameras", TextStyle { ..default() }),
|
|
|
|
UiTitle,
|
|
|
|
UiTitle,
|
|
|
|
));
|
|
|
|
));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@ -187,91 +229,35 @@ fn spawn_ui(mut commands: Commands) {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
CameraSelectUi,
|
|
|
|
AnimationSelectUi,
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn((
|
|
|
|
parent.spawn((
|
|
|
|
TextBundle::from_section("Cameras", TextStyle { ..default() }),
|
|
|
|
TextBundle::from_section("Animations", TextStyle { ..default() }),
|
|
|
|
UiTitle,
|
|
|
|
UiTitle,
|
|
|
|
));
|
|
|
|
));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn wire_up_scenes(
|
|
|
|
/// When a new camera is loaded, clear the camera buttons
|
|
|
|
mut events: EventReader<AssetEvent<Scene>>,
|
|
|
|
fn clean_ui<T: Component>(
|
|
|
|
root: Query<Entity, With<SceneSelectUi>>,
|
|
|
|
mut spawn_events: EventReader<SpawnScene>,
|
|
|
|
root_children: Query<&Children, With<SceneSelectUi>>,
|
|
|
|
mut clear_events: EventReader<ResetScene>,
|
|
|
|
|
|
|
|
cameras: Query<Entity, With<T>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
|
|
|
current: Res<Current>,
|
|
|
|
|
|
|
|
existing: Query<&SceneButton, Without<UiTitle>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
// We don't care about the content of these events, just that we want to clear the UI after
|
|
|
|
// Cleanup unused buttons
|
|
|
|
// each spawn or reset event, so we map each event to () and chain the two event streams.
|
|
|
|
root_children.iter().for_each(|children| {
|
|
|
|
spawn_events
|
|
|
|
// Iterate over UI children elements
|
|
|
|
.iter()
|
|
|
|
children
|
|
|
|
.map(|_| ())
|
|
|
|
.iter()
|
|
|
|
.chain(clear_events.iter().map(|_| ()))
|
|
|
|
// Filter to just the buttons we want to despawn
|
|
|
|
.for_each(|_| {
|
|
|
|
.filter(|&child| {
|
|
|
|
cameras.iter().for_each(|entity| {
|
|
|
|
// Check each child entity against the currently loaded scenes
|
|
|
|
commands.entity(entity).despawn_recursive();
|
|
|
|
// Note: !(_.any(...)) (not-any)
|
|
|
|
});
|
|
|
|
!((*current).scenes.iter().any(|(_, handle)| {
|
|
|
|
|
|
|
|
// If the current entitie's scene button corresponds to one of the scenes
|
|
|
|
|
|
|
|
// loaded, continue, otherwise
|
|
|
|
|
|
|
|
existing.get(*child).map_or(false, |scene_button| {
|
|
|
|
|
|
|
|
*scene_button == SceneButton(handle.cast_weak::<Scene>())
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.for_each(|&e| {
|
|
|
|
|
|
|
|
commands.entity(e).despawn_recursive();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Add buttons
|
|
|
|
|
|
|
|
match event {
|
|
|
|
|
|
|
|
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
|
|
|
|
|
|
|
|
let (name, _) = gltfs
|
|
|
|
|
|
|
|
.get(¤t.gltf)
|
|
|
|
|
|
|
|
.expect("Load current gltf")
|
|
|
|
|
|
|
|
.named_scenes
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.find(|(_, h)| h == &handle)
|
|
|
|
|
|
|
|
.expect("Find this scene");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut ui = commands.entity(root.single());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ui.with_children(|parent| {
|
|
|
|
|
|
|
|
parent
|
|
|
|
|
|
|
|
.spawn((
|
|
|
|
|
|
|
|
ButtonBundle {
|
|
|
|
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
SceneButton(handle.clone()),
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn(TextBundle {
|
|
|
|
|
|
|
|
text: Text {
|
|
|
|
|
|
|
|
sections: vec![TextSection {
|
|
|
|
|
|
|
|
value: name.clone(),
|
|
|
|
|
|
|
|
style: TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetEvent::Removed { .. } => warn!("IGNORED Remove scene in list"),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Translate UI button presses into scene spawn events
|
|
|
|
/// Translate UI button presses into scene spawn events
|
|
|
|
@ -288,150 +274,88 @@ fn control_scene(
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn wire_up_animations(
|
|
|
|
/// Add scene buttons when a new scene is added/gltf is loaded
|
|
|
|
mut events: EventReader<AssetEvent<AnimationClip>>,
|
|
|
|
fn add_scene_ui(
|
|
|
|
root: Query<Entity, With<AnimationSelectUi>>,
|
|
|
|
mut events: EventReader<SpawnScene>,
|
|
|
|
root_children: Query<&Children, With<AnimationSelectUi>>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
|
|
|
current: Res<Current>,
|
|
|
|
current: Res<Current>,
|
|
|
|
existing: Query<&AnimationButton, Without<UiTitle>>,
|
|
|
|
root: Query<Entity, With<SceneSelectUi>>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
events.iter().for_each(|_| {
|
|
|
|
// Cleanup unused buttons
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
root_children.iter().for_each(|children| {
|
|
|
|
current.scenes.iter().for_each(|(name, handle)| {
|
|
|
|
// Iterate over UI children elements
|
|
|
|
parent
|
|
|
|
children
|
|
|
|
.spawn((
|
|
|
|
.iter()
|
|
|
|
ButtonBundle {
|
|
|
|
// Filter to just the buttons we want to despawn
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
.filter(|&child| {
|
|
|
|
..default()
|
|
|
|
// Check each child entity against the currently loaded animations
|
|
|
|
},
|
|
|
|
// Note: !(_.any(...)) (not-any)
|
|
|
|
SceneButton(handle.clone()),
|
|
|
|
!((*current).animations.iter().any(|(_, handle)| {
|
|
|
|
))
|
|
|
|
// If the current entitie's animation button corresponds to one of
|
|
|
|
.with_children(|parent| {
|
|
|
|
// the animations loaded, continue, otherwise
|
|
|
|
parent.spawn(TextBundle::from_section(name, TextStyle { ..default() }));
|
|
|
|
existing.get(*child).map_or(false, |scene_button| {
|
|
|
|
});
|
|
|
|
*scene_button == AnimationButton(handle.cast_weak::<AnimationClip>())
|
|
|
|
});
|
|
|
|
})
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.for_each(|&e| {
|
|
|
|
|
|
|
|
commands.entity(e).despawn_recursive();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
match event {
|
|
|
|
|
|
|
|
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
|
|
|
|
|
|
|
|
let (name, _) = gltfs
|
|
|
|
|
|
|
|
.get(¤t.gltf)
|
|
|
|
|
|
|
|
.expect("Load current gltf")
|
|
|
|
|
|
|
|
.named_animations
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.find(|(_, h)| h == &handle)
|
|
|
|
|
|
|
|
.expect("Find this animation");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
|
|
|
|
parent
|
|
|
|
|
|
|
|
.spawn((
|
|
|
|
|
|
|
|
ButtonBundle {
|
|
|
|
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
AnimationButton(handle.clone()),
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn(TextBundle {
|
|
|
|
|
|
|
|
text: Text {
|
|
|
|
|
|
|
|
sections: vec![TextSection {
|
|
|
|
|
|
|
|
value: name.clone(),
|
|
|
|
|
|
|
|
style: TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetEvent::Removed { .. } => warn!("Ignoring asset removal"),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn control_animation(
|
|
|
|
fn control_animation(
|
|
|
|
interactions: Query<(&Interaction, &AnimationButton), (Changed<Interaction>, With<Button>)>,
|
|
|
|
interactions: Query<(&Interaction, &AnimationButton), (Changed<Interaction>, With<Button>)>,
|
|
|
|
|
|
|
|
mut players: Query<(&mut AnimationPlayer, &Name)>,
|
|
|
|
|
|
|
|
clips: Res<Assets<AnimationClip>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
// For each button interaction
|
|
|
|
interactions
|
|
|
|
interactions
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.for_each(|(interaction, AnimationButton(handle))| match interaction {
|
|
|
|
.for_each(|(interaction, AnimationButton(handle))| match interaction {
|
|
|
|
Interaction::Pressed => {
|
|
|
|
// If this is a butotn press
|
|
|
|
info!("TODO: Play animation {:?}", handle);
|
|
|
|
Interaction::Pressed => players
|
|
|
|
}
|
|
|
|
.iter_mut()
|
|
|
|
|
|
|
|
// Find all entities compatible with this animation
|
|
|
|
|
|
|
|
.filter(|(_, name)| {
|
|
|
|
|
|
|
|
clips
|
|
|
|
|
|
|
|
.get(handle)
|
|
|
|
|
|
|
|
.expect("Check animation clip compatability")
|
|
|
|
|
|
|
|
.compatible_with(name)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
// Play the given (checked compatible) animation
|
|
|
|
|
|
|
|
.for_each(|(mut player, _)| {
|
|
|
|
|
|
|
|
player.play(handle.clone()).repeat();
|
|
|
|
|
|
|
|
}),
|
|
|
|
_ => (),
|
|
|
|
_ => (),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Wire up the camera UI
|
|
|
|
/// Add animation buttons when a new animation is added/gltf is loaded
|
|
|
|
fn wire_up_cameras(
|
|
|
|
fn add_animation_ui(
|
|
|
|
added_cam: Query<(Entity, &Name), (Without<MainCamera>, Added<Camera>)>,
|
|
|
|
mut events: EventReader<SpawnScene>,
|
|
|
|
mut removed_cam: RemovedComponents<Camera>,
|
|
|
|
current: Res<Current>,
|
|
|
|
|
|
|
|
root: Query<Entity, With<AnimationSelectUi>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
root: Query<Entity, With<CameraSelectUi>>,
|
|
|
|
|
|
|
|
root_children: Query<&Children, With<CameraSelectUi>>,
|
|
|
|
|
|
|
|
existing: Query<&CameraButton, Without<UiTitle>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
// Add new camera buttons
|
|
|
|
events.iter().for_each(|_| {
|
|
|
|
added_cam.iter().for_each(|(entity, name)| {
|
|
|
|
|
|
|
|
info!("Adding camera button");
|
|
|
|
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
parent
|
|
|
|
current.animations.iter().for_each(|(name, handle)| {
|
|
|
|
.spawn((
|
|
|
|
parent
|
|
|
|
ButtonBundle {
|
|
|
|
.spawn((
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
ButtonBundle {
|
|
|
|
..default()
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
},
|
|
|
|
|
|
|
|
CameraButton(entity),
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn(TextBundle {
|
|
|
|
|
|
|
|
text: Text {
|
|
|
|
|
|
|
|
sections: vec![TextSection {
|
|
|
|
|
|
|
|
value: name.as_str().into(),
|
|
|
|
|
|
|
|
style: TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
AnimationButton(handle.clone()),
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn(TextBundle::from_section(name, TextStyle { ..default() }));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Remove deleted cameras
|
|
|
|
|
|
|
|
removed_cam.iter().for_each(|entity| {
|
|
|
|
|
|
|
|
info!("Removing camera button");
|
|
|
|
|
|
|
|
root_children
|
|
|
|
|
|
|
|
.single()
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.filter(|&child| {
|
|
|
|
|
|
|
|
if let Ok(button) = existing.get(*child) {
|
|
|
|
|
|
|
|
*button == CameraButton(entity)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.for_each(|child| commands.entity(*child).despawn_recursive());
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn control_camera(
|
|
|
|
fn control_camera(
|
|
|
|
interactions: Query<(&Interaction, &CameraButton), (Changed<Interaction>, With<Button>)>,
|
|
|
|
interactions: Query<(&Interaction, &CameraButton), (Changed<Interaction>, With<Button>)>,
|
|
|
|
mut cameras: Query<(Entity, &mut Camera), Without<MainCamera>>,
|
|
|
|
mut cameras: Query<(Entity, &mut Camera), Without<DefaultCamera>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
interactions
|
|
|
|
interactions
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
@ -443,12 +367,70 @@ fn control_camera(
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// If there are no other cameras, use default camera
|
|
|
|
fn control_default_camera(
|
|
|
|
fn control_default_camera(
|
|
|
|
mut main_camera: Query<&mut Camera, With<MainCamera>>,
|
|
|
|
mut main_camera: Query<&mut Camera, With<DefaultCamera>>,
|
|
|
|
other_cameras: Query<&mut Camera, Without<MainCamera>>,
|
|
|
|
other_cameras: Query<Entity, (With<Camera>, Without<DefaultCamera>)>,
|
|
|
|
|
|
|
|
mut text_indicators: Query<&mut Visibility, With<DefaultCamera>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
// PERF: Probably don't need to do this every frame
|
|
|
|
// Figure out if we should use default camera
|
|
|
|
main_camera.single_mut().is_active = other_cameras.is_empty();
|
|
|
|
let state = other_cameras.is_empty();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Toggle camera
|
|
|
|
|
|
|
|
main_camera
|
|
|
|
|
|
|
|
.iter_mut()
|
|
|
|
|
|
|
|
.for_each(|mut cam| cam.is_active = state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update UI indicator
|
|
|
|
|
|
|
|
text_indicators.iter_mut().for_each(|mut vis| {
|
|
|
|
|
|
|
|
*vis = match state {
|
|
|
|
|
|
|
|
true => Visibility::Visible,
|
|
|
|
|
|
|
|
false => Visibility::Hidden,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// If there are no other lights, use default light
|
|
|
|
|
|
|
|
fn control_default_light(
|
|
|
|
|
|
|
|
mut toggle: Query<&mut Visibility, With<DefaultLight>>,
|
|
|
|
|
|
|
|
other_lights: Query<
|
|
|
|
|
|
|
|
Entity,
|
|
|
|
|
|
|
|
(
|
|
|
|
|
|
|
|
Or<(With<SpotLight>, With<DirectionalLight>, With<PointLight>)>,
|
|
|
|
|
|
|
|
Without<DefaultLight>,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
toggle.iter_mut().for_each(|mut vis| {
|
|
|
|
|
|
|
|
*vis = if other_lights.is_empty() {
|
|
|
|
|
|
|
|
Visibility::Visible
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Visibility::Hidden
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Add camera buttons when a new camera is added/gltf is loaded
|
|
|
|
|
|
|
|
fn add_camera_ui(
|
|
|
|
|
|
|
|
events: Query<(Entity, &Name), Added<Camera>>,
|
|
|
|
|
|
|
|
ui_root: Query<Entity, With<CameraSelectUi>>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events.iter().for_each(|(entity, name)| {
|
|
|
|
|
|
|
|
commands.entity(ui_root.single()).with_children(|parent| {
|
|
|
|
|
|
|
|
parent
|
|
|
|
|
|
|
|
.spawn((
|
|
|
|
|
|
|
|
ButtonBundle {
|
|
|
|
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
CameraButton(entity),
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn(TextBundle::from_section(name, TextStyle { ..default() }));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Load gltfs and spawn default scene
|
|
|
|
/// Load gltfs and spawn default scene
|
|
|
|
@ -459,21 +441,19 @@ fn loading(
|
|
|
|
mut current: ResMut<Current>,
|
|
|
|
mut current: ResMut<Current>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
info!("Event!: {:?}", event);
|
|
|
|
match event {
|
|
|
|
if let AssetEvent::<Gltf>::Created { handle, .. } = event {
|
|
|
|
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
|
|
|
|
// Despawn existing scene
|
|
|
|
let gltf = gltfs.get(handle).expect("Loaded Gltf");
|
|
|
|
let gltf = gltfs.get(handle).expect("Loaded Gltf");
|
|
|
|
|
|
|
|
let default_scene = gltf.default_scene.clone().expect("Default scene");
|
|
|
|
// Save active scenes
|
|
|
|
spawn.send(SpawnScene(default_scene));
|
|
|
|
current.scenes = gltf.named_scenes.clone();
|
|
|
|
|
|
|
|
current.animations = gltf.named_animations.clone();
|
|
|
|
// Save active scenes
|
|
|
|
|
|
|
|
current.scenes = gltf.named_scenes.clone();
|
|
|
|
// Despawn existing scene
|
|
|
|
current.animations = gltf.named_animations.clone();
|
|
|
|
let default_scene = gltf.default_scene.clone().expect("Default scene");
|
|
|
|
|
|
|
|
spawn.send(SpawnScene(default_scene));
|
|
|
|
// TOOD: Spawn default camera
|
|
|
|
}
|
|
|
|
// TODO: Handle multiple cameras
|
|
|
|
AssetEvent::Removed { .. } => warn!("Ignoring asset removal"),
|
|
|
|
// TODO: Scene Selection scenes in one Gltf
|
|
|
|
|
|
|
|
// TODO: Handle animation
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|