|
|
|
|
@ -7,12 +7,13 @@
|
|
|
|
|
// * Multi-GLTF UX is bad.
|
|
|
|
|
// * Consider GLTF hierarchy (GLTF1 > Scene1a/Scene1b, GlTF2 > Scene2a/Scene2b, etc)
|
|
|
|
|
// * Easy despawn when de-selecting gltf
|
|
|
|
|
// * Load new GLTF -> Despawn all level entities
|
|
|
|
|
//
|
|
|
|
|
// TODO:
|
|
|
|
|
// * Disable auto start/load
|
|
|
|
|
// * Make GLTF nested menu
|
|
|
|
|
// * Better handle hide/close monologue
|
|
|
|
|
// * (hard) Harden Active Camera
|
|
|
|
|
// * (medium) Despawn entire scene when GLTF changed?
|
|
|
|
|
// * (medium) Select Font -> "Default Font" Resource
|
|
|
|
|
// * (medium) Pre-compute animation target entities
|
|
|
|
|
// * (medium) Animation buttons only visible when playable
|
|
|
|
|
@ -76,16 +77,16 @@ fn main() {
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
(
|
|
|
|
|
manage_gltf_animation_ui,
|
|
|
|
|
init_animations_ui,
|
|
|
|
|
animations_ui,
|
|
|
|
|
remove_animations_ui,
|
|
|
|
|
add_animations_ui,
|
|
|
|
|
play_all_animations,
|
|
|
|
|
play_animation,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
(manage_gltf_scene_ui, scenes_ui, control_active_scenes),
|
|
|
|
|
(remove_scenes_ui, add_scenes_ui, control_active_scenes),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
@ -640,103 +641,6 @@ mod gltf {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn manage_gltf_animation_ui(
|
|
|
|
|
added: Query<Entity, (With<Button>, Added<ui::Active>)>,
|
|
|
|
|
mut removed: RemovedComponents<ui::Active>,
|
|
|
|
|
targets_gltf: Query<&ui::TargetAsset<Gltf>>,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
mut animation_clip_events: EventWriter<CustomAssetEvent<AnimationClip>>,
|
|
|
|
|
) {
|
|
|
|
|
removed
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|entity| {
|
|
|
|
|
if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) {
|
|
|
|
|
gltfs.get(handle)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.for_each(|gltf| {
|
|
|
|
|
gltf.named_animations
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|(animation_name, animation_handle)| {
|
|
|
|
|
info!("Named animation: {:?}", animation_name);
|
|
|
|
|
animation_clip_events.send(CustomAssetEvent::Remove {
|
|
|
|
|
handle: animation_handle.clone(),
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
added
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|entity| {
|
|
|
|
|
if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) {
|
|
|
|
|
gltfs.get(handle)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.for_each(|gltf| {
|
|
|
|
|
// Populate animations tab
|
|
|
|
|
gltf.named_animations
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|(animation_name, animation_handle)| {
|
|
|
|
|
animation_clip_events.send(CustomAssetEvent::Add {
|
|
|
|
|
name: animation_name.clone(),
|
|
|
|
|
handle: animation_handle.clone(),
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn manage_gltf_scene_ui(
|
|
|
|
|
added: Query<Entity, (With<Button>, Added<ui::Active>)>,
|
|
|
|
|
mut removed: RemovedComponents<ui::Active>,
|
|
|
|
|
targets_gltf: Query<&ui::TargetAsset<Gltf>>,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
mut scene_events: EventWriter<CustomAssetEvent<Scene>>,
|
|
|
|
|
) {
|
|
|
|
|
removed
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|entity| {
|
|
|
|
|
if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) {
|
|
|
|
|
gltfs.get(handle)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.for_each(|gltf| {
|
|
|
|
|
gltf.named_scenes
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|(scene_name, scene_handle)| {
|
|
|
|
|
info!("Named scene: {:?}", scene_name);
|
|
|
|
|
scene_events.send(CustomAssetEvent::Remove {
|
|
|
|
|
handle: scene_handle.clone(),
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
added
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|entity| {
|
|
|
|
|
if let Ok(ui::TargetAsset { handle }) = targets_gltf.get(entity) {
|
|
|
|
|
gltfs.get(handle)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.for_each(|gltf| {
|
|
|
|
|
// Populate scenes tab
|
|
|
|
|
gltf.named_scenes
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|(scene_name, scene_handle)| {
|
|
|
|
|
info!("Named scene: {:?}", scene_name);
|
|
|
|
|
scene_events.send(CustomAssetEvent::Add {
|
|
|
|
|
name: scene_name.clone(),
|
|
|
|
|
handle: scene_handle.clone(),
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Mark loaded animation as active
|
|
|
|
|
@ -747,18 +651,16 @@ mod scenes {
|
|
|
|
|
#[derive(Debug, Component, Default)]
|
|
|
|
|
pub struct SceneWidget;
|
|
|
|
|
|
|
|
|
|
pub fn scenes_ui(
|
|
|
|
|
mut events: EventReader<CustomAssetEvent<Scene>>,
|
|
|
|
|
pub fn add_scenes_ui(
|
|
|
|
|
gltf_selected: Query<&ui::TargetAsset<Gltf>, Added<ui::Active>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
widget: Query<Entity, With<SceneWidget>>,
|
|
|
|
|
current: Query<(Entity, &ui::TargetAsset<Scene>)>,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
|
match event {
|
|
|
|
|
CustomAssetEvent::Add { name, handle } => {
|
|
|
|
|
info!("Asset loading! {:?}({:?})", name, handle);
|
|
|
|
|
// Spawn new tree
|
|
|
|
|
let e = create_asset_button(
|
|
|
|
|
gltf_selected.iter().for_each(|ui::TargetAsset { handle }| {
|
|
|
|
|
if let Some(gltf) = gltfs.get(&handle.clone()) {
|
|
|
|
|
gltf.named_scenes.iter().for_each(|(name, handle)| {
|
|
|
|
|
create_asset_button(
|
|
|
|
|
&widget,
|
|
|
|
|
&mut commands,
|
|
|
|
|
ui::TargetAsset {
|
|
|
|
|
@ -767,8 +669,24 @@ mod scenes {
|
|
|
|
|
name.clone(),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
CustomAssetEvent::Remove { handle } => {
|
|
|
|
|
|
|
|
|
|
pub fn remove_scenes_ui(
|
|
|
|
|
mut gltf_unselected: RemovedComponents<ui::Active>,
|
|
|
|
|
target_assets: Query<&ui::TargetAsset<Gltf>>,
|
|
|
|
|
current: Query<(Entity, &ui::TargetAsset<Scene>)>,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
gltf_unselected
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|entity| target_assets.get(entity).ok())
|
|
|
|
|
.filter_map(|ui::TargetAsset { handle }| gltfs.get(handle))
|
|
|
|
|
.for_each(|gltf| {
|
|
|
|
|
gltf.scenes.iter().for_each(|handle| {
|
|
|
|
|
destroy_asset_button(
|
|
|
|
|
¤t,
|
|
|
|
|
&mut commands,
|
|
|
|
|
@ -776,11 +694,7 @@ mod scenes {
|
|
|
|
|
handle: handle.clone(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
CustomAssetEvent::Clear => {
|
|
|
|
|
commands.entity(widget.single()).despawn_descendants();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -862,28 +776,57 @@ mod animations {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn animations_ui(
|
|
|
|
|
mut events: EventReader<CustomAssetEvent<AnimationClip>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
/// When a new scene is loaded, add any newly compatible animations
|
|
|
|
|
pub fn add_animations_ui(
|
|
|
|
|
player_spawned: Query<&Name, Added<AnimationPlayer>>,
|
|
|
|
|
widget: Query<Entity, With<AnimationWidget>>,
|
|
|
|
|
current: Query<(Entity, &ui::TargetAsset<AnimationClip>)>,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
clips: Res<Assets<AnimationClip>>,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
|
match event {
|
|
|
|
|
CustomAssetEvent::Add { name, handle } => {
|
|
|
|
|
info!("Asset loading! {:?}({:?})", name, handle);
|
|
|
|
|
// Spawn new tree
|
|
|
|
|
player_spawned.iter().for_each(|player_name| {
|
|
|
|
|
gltfs.iter().for_each(|(_, gltf)| {
|
|
|
|
|
gltf.named_animations
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|(clip_name, handle)| {
|
|
|
|
|
info!("Checking clip {:?}", clip_name);
|
|
|
|
|
let clip = clips.get(&handle).expect("load animation clip");
|
|
|
|
|
clip.compatible_with(player_name).then(|| {
|
|
|
|
|
create_asset_button(
|
|
|
|
|
&widget,
|
|
|
|
|
&mut commands,
|
|
|
|
|
ui::TargetAsset {
|
|
|
|
|
handle: handle.clone(),
|
|
|
|
|
},
|
|
|
|
|
name.clone(),
|
|
|
|
|
clip_name.clone(),
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
CustomAssetEvent::Remove { handle } => {
|
|
|
|
|
|
|
|
|
|
// When a scene is de-selected, remove any outdated animation options
|
|
|
|
|
pub fn remove_animations_ui(
|
|
|
|
|
mut events: RemovedComponents<Handle<Scene>>,
|
|
|
|
|
current: Query<(Entity, &ui::TargetAsset<AnimationClip>)>,
|
|
|
|
|
clips: Res<Assets<AnimationClip>>,
|
|
|
|
|
targets: Query<(&AnimationPlayer, &Name)>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
// For each removed scene
|
|
|
|
|
events.iter().for_each(|_| {
|
|
|
|
|
info!("Scene despawn!");
|
|
|
|
|
// Iterate over the current animation buttons
|
|
|
|
|
current.iter().for_each(|(_, ui::TargetAsset { handle })| {
|
|
|
|
|
info!("Checking button for {:?}", handle);
|
|
|
|
|
// If the button points to a valid clip
|
|
|
|
|
if let Some(clip) = clips.get(handle) {
|
|
|
|
|
// Check if any active animation players are compatible with this clip
|
|
|
|
|
let compatible = targets.iter().any(|(_, name)| clip.compatible_with(name));
|
|
|
|
|
// If not, despawn the button
|
|
|
|
|
if !compatible {
|
|
|
|
|
destroy_asset_button(
|
|
|
|
|
¤t,
|
|
|
|
|
&mut commands,
|
|
|
|
|
@ -892,11 +835,9 @@ mod animations {
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
CustomAssetEvent::Clear => {
|
|
|
|
|
commands.entity(widget.single()).despawn_descendants();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn play_all_animations(
|
|
|
|
|
@ -1082,7 +1023,6 @@ mod monologues {
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Load .txt files for monologues
|
|
|
|
|
pub fn texts_ui(
|
|
|
|
|
mut events: EventReader<AssetEvent<Monologue>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
@ -1215,7 +1155,6 @@ mod cameras {
|
|
|
|
|
#[derive(Debug, Component, Default)]
|
|
|
|
|
pub struct CameraWidget;
|
|
|
|
|
|
|
|
|
|
// TODO: Despawn camera button when camera removed
|
|
|
|
|
pub fn cameras_ui(
|
|
|
|
|
mut added: Query<(Entity, &mut Camera, &Name), (Added<Camera>, Without<EditorCamera>)>,
|
|
|
|
|
mut removed: RemovedComponents<Camera>,
|
|
|
|
|
@ -1229,7 +1168,7 @@ mod cameras {
|
|
|
|
|
});
|
|
|
|
|
added.iter_mut().for_each(|(entity, mut camera, name)| {
|
|
|
|
|
info!("Camera added {:?} {:?}", entity, name);
|
|
|
|
|
let e = create_entity_button(
|
|
|
|
|
create_entity_button(
|
|
|
|
|
&widget,
|
|
|
|
|
&mut commands,
|
|
|
|
|
ui::TargetEntity { entity },
|
|
|
|
|
@ -1268,13 +1207,11 @@ mod cameras {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|