|
|
|
@ -1,24 +1,5 @@
|
|
|
|
use bevy::{
|
|
|
|
use bevy::{asset::AssetEvents, gltf::Gltf, prelude::*, utils::HashMap};
|
|
|
|
core_pipeline::clear_color::ClearColorConfig,
|
|
|
|
use monologue_trees::debug::*;
|
|
|
|
gltf::{Gltf, GltfNode},
|
|
|
|
|
|
|
|
input::{
|
|
|
|
|
|
|
|
keyboard::KeyboardInput,
|
|
|
|
|
|
|
|
mouse::{MouseMotion, MouseWheel},
|
|
|
|
|
|
|
|
ButtonState,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
pbr::CascadeShadowConfigBuilder,
|
|
|
|
|
|
|
|
prelude::*,
|
|
|
|
|
|
|
|
render::{
|
|
|
|
|
|
|
|
camera::RenderTarget,
|
|
|
|
|
|
|
|
render_resource::{
|
|
|
|
|
|
|
|
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
view::VisibleEntities,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
window::PrimaryWindow,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
use bevy_rapier3d::{prelude::*, rapier::prelude::RigidBodyType};
|
|
|
|
|
|
|
|
use monologue_trees::{debug::*, text::*};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
|
App::new()
|
|
|
|
App::new()
|
|
|
|
@ -32,586 +13,506 @@ fn main() {
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
DebugInfoPlugin,
|
|
|
|
DebugInfoPlugin,
|
|
|
|
RapierPhysicsPlugin::<NoUserData>::default(),
|
|
|
|
|
|
|
|
RapierDebugRenderPlugin::default(),
|
|
|
|
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.add_event::<ManageActive>()
|
|
|
|
.add_event::<ResetScene>()
|
|
|
|
.add_event::<Selected>()
|
|
|
|
.add_event::<SpawnScene>()
|
|
|
|
.add_systems(PreStartup, load_models)
|
|
|
|
.init_resource::<Current>()
|
|
|
|
.add_systems(Startup, (spawn_base_scene, spawn_base_ui))
|
|
|
|
.add_systems(Startup, spawn_ui)
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
(
|
|
|
|
spawn_models,
|
|
|
|
drag_and_drop,
|
|
|
|
spawn_ui,
|
|
|
|
loading,
|
|
|
|
|
|
|
|
reset_scene.before(spawn_scene),
|
|
|
|
|
|
|
|
spawn_scene.after(reset_scene),
|
|
|
|
|
|
|
|
wire_up_scenes,
|
|
|
|
|
|
|
|
control_scene,
|
|
|
|
|
|
|
|
wire_up_animations,
|
|
|
|
control_animation,
|
|
|
|
control_animation,
|
|
|
|
rotate_model,
|
|
|
|
wire_up_cameras,
|
|
|
|
zoom_model,
|
|
|
|
control_camera,
|
|
|
|
scroll,
|
|
|
|
control_default_camera,
|
|
|
|
select,
|
|
|
|
|
|
|
|
manage_active,
|
|
|
|
|
|
|
|
inspect_nodes,
|
|
|
|
|
|
|
|
selection,
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.run();
|
|
|
|
.run();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
#[derive(Component)]
|
|
|
|
/// Stores GLTF handles for later use
|
|
|
|
struct SceneMarker;
|
|
|
|
#[derive(Resource)]
|
|
|
|
|
|
|
|
struct Models {
|
|
|
|
|
|
|
|
handles: Vec<Handle<Gltf>>,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Marks GLTF model as inspectable
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct Inspect;
|
|
|
|
struct MainCamera;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct SelectionUI;
|
|
|
|
struct MainUi;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct PreviewCamera;
|
|
|
|
struct UiTitle;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct ScrollingList;
|
|
|
|
struct InstructionsUi;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct Preview(Handle<Image>);
|
|
|
|
struct SceneSelectUi;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component, PartialEq, Debug)]
|
|
|
|
|
|
|
|
struct SceneButton(Handle<Scene>);
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct Container;
|
|
|
|
struct AnimationSelectUi;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component, PartialEq, Debug)]
|
|
|
|
|
|
|
|
struct AnimationButton(Handle<AnimationClip>);
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
struct Active;
|
|
|
|
struct CameraSelectUi;
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
#[derive(Component, PartialEq, Debug)]
|
|
|
|
/// Event for managing which entities are tagged "Active"
|
|
|
|
struct CameraButton(Entity);
|
|
|
|
#[derive(Event, Debug)]
|
|
|
|
|
|
|
|
struct ManageActive(Option<Entity>);
|
|
|
|
#[derive(Resource, Default)]
|
|
|
|
|
|
|
|
struct Current {
|
|
|
|
///
|
|
|
|
gltf: Handle<Gltf>,
|
|
|
|
/// Event for tracking which entities are selected
|
|
|
|
scenes: HashMap<String, Handle<Scene>>,
|
|
|
|
#[derive(Event, Debug)]
|
|
|
|
animations: HashMap<String, Handle<AnimationClip>>,
|
|
|
|
enum Selected {
|
|
|
|
|
|
|
|
Hovered(Entity),
|
|
|
|
|
|
|
|
Selected(Entity),
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
#[derive(Event)]
|
|
|
|
/// Load all GLTF models on startup
|
|
|
|
struct ResetScene;
|
|
|
|
fn load_models(mut commands: Commands, ass: Res<AssetServer>) {
|
|
|
|
|
|
|
|
let weak_handles = ass.load_folder("models").expect("Load gltfs");
|
|
|
|
#[derive(Event)]
|
|
|
|
let handles: Vec<Handle<Gltf>> = weak_handles
|
|
|
|
struct SpawnScene(Handle<Scene>);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn drag_and_drop(
|
|
|
|
|
|
|
|
mut events: EventReader<FileDragAndDrop>,
|
|
|
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
|
|
|
mut current: ResMut<Current>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.map(|weak| weak.clone().typed::<Gltf>())
|
|
|
|
.filter_map(|event| {
|
|
|
|
.collect();
|
|
|
|
if let FileDragAndDrop::DroppedFile { path_buf, .. } = event {
|
|
|
|
// info!("Scene Handles: {:#?}", handles);
|
|
|
|
info!("Drag+Drop file: {:?}", path_buf);
|
|
|
|
commands.insert_resource(Models { handles });
|
|
|
|
Some(path_buf)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.map(|path_buf| server.load(path_buf.to_str().expect("PathBuf to str")))
|
|
|
|
|
|
|
|
.for_each(|handle| current.gltf = handle.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
fn spawn_ui(mut commands: Commands) {
|
|
|
|
/// Spawn base scene
|
|
|
|
// TODO: Warn no camera (hidden)
|
|
|
|
fn spawn_base_scene(mut commands: Commands) {
|
|
|
|
// TODO: Scene select container
|
|
|
|
|
|
|
|
// TODO: Animation Play/Pause Placeholder
|
|
|
|
commands.spawn((
|
|
|
|
commands.spawn((
|
|
|
|
Camera2dBundle {
|
|
|
|
Camera3dBundle { ..default() },
|
|
|
|
camera_2d: Camera2d {
|
|
|
|
UiCameraConfig { show_ui: true },
|
|
|
|
clear_color: ClearColorConfig::Custom(Color::BLACK),
|
|
|
|
MainCamera,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
commands
|
|
|
|
|
|
|
|
.spawn((
|
|
|
|
|
|
|
|
NodeBundle {
|
|
|
|
|
|
|
|
style: Style {
|
|
|
|
|
|
|
|
flex_wrap: FlexWrap::Wrap,
|
|
|
|
|
|
|
|
flex_direction: FlexDirection::Row,
|
|
|
|
|
|
|
|
justify_content: JustifyContent::SpaceAround,
|
|
|
|
|
|
|
|
width: Val::Percent(100.0),
|
|
|
|
|
|
|
|
height: Val::Percent(100.0),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
UiCameraConfig { ..default() },
|
|
|
|
MainUi,
|
|
|
|
SelectionUI,
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
// TOOD: Prompt to drag+drop Gltf/Gltf file
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
TextBundle {
|
|
|
|
|
|
|
|
text: Text {
|
|
|
|
|
|
|
|
sections: vec![TextSection {
|
|
|
|
|
|
|
|
value: String::from("Drag and Drop .gltf/.glb file"),
|
|
|
|
|
|
|
|
style: TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
InstructionsUi,
|
|
|
|
));
|
|
|
|
));
|
|
|
|
}
|
|
|
|
parent
|
|
|
|
|
|
|
|
|
|
|
|
fn spawn_base_ui(mut commands: Commands) {
|
|
|
|
|
|
|
|
commands
|
|
|
|
|
|
|
|
.spawn((
|
|
|
|
.spawn((
|
|
|
|
NodeBundle {
|
|
|
|
NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
style: Style {
|
|
|
|
justify_content: JustifyContent::Center,
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
width: Val::Percent(90.0),
|
|
|
|
|
|
|
|
height: Val::Percent(90.0),
|
|
|
|
|
|
|
|
overflow: Overflow::clip(),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
SelectionUI,
|
|
|
|
SceneSelectUi,
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn((
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
TextBundle::from_section("Scenes", TextStyle { ..default() }),
|
|
|
|
|
|
|
|
UiTitle,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
parent
|
|
|
|
|
|
|
|
.spawn((
|
|
|
|
NodeBundle {
|
|
|
|
NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
style: Style {
|
|
|
|
flex_wrap: FlexWrap::Wrap,
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
flex_direction: FlexDirection::Row,
|
|
|
|
|
|
|
|
justify_content: JustifyContent::SpaceAround,
|
|
|
|
|
|
|
|
width: Val::Auto,
|
|
|
|
|
|
|
|
height: Val::Auto,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ScrollingList,
|
|
|
|
AnimationSelectUi,
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
TextBundle::from_section("Animations", TextStyle { ..default() }),
|
|
|
|
|
|
|
|
UiTitle,
|
|
|
|
));
|
|
|
|
));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
parent
|
|
|
|
|
|
|
|
.spawn((
|
|
|
|
///
|
|
|
|
NodeBundle {
|
|
|
|
/// Spawn a loaded scene for inspection
|
|
|
|
style: Style {
|
|
|
|
/// TODO: Update/add/delete when files are updated
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
fn spawn_models(
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
mut scene_evr: EventReader<AssetEvent<Scene>>,
|
|
|
|
|
|
|
|
mut images: ResMut<Assets<Image>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
if !scene_evr.is_empty() {
|
|
|
|
|
|
|
|
info!("Spawning scenes");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for ev in scene_evr.iter() {
|
|
|
|
|
|
|
|
match ev {
|
|
|
|
|
|
|
|
AssetEvent::Created { handle } => {
|
|
|
|
|
|
|
|
info!("Creating scene {:#?}", handle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Preview image
|
|
|
|
|
|
|
|
let preview_image_handle = {
|
|
|
|
|
|
|
|
info!("Creating preview");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let size = Extent3d {
|
|
|
|
|
|
|
|
width: 256,
|
|
|
|
|
|
|
|
height: 256,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create render target image for the preview camera
|
|
|
|
|
|
|
|
let mut image = Image {
|
|
|
|
|
|
|
|
texture_descriptor: TextureDescriptor {
|
|
|
|
|
|
|
|
label: None,
|
|
|
|
|
|
|
|
size,
|
|
|
|
|
|
|
|
dimension: TextureDimension::D2,
|
|
|
|
|
|
|
|
format: TextureFormat::Bgra8UnormSrgb,
|
|
|
|
|
|
|
|
mip_level_count: 1,
|
|
|
|
|
|
|
|
sample_count: 1,
|
|
|
|
|
|
|
|
usage: TextureUsages::TEXTURE_BINDING
|
|
|
|
|
|
|
|
| TextureUsages::COPY_DST
|
|
|
|
|
|
|
|
| TextureUsages::RENDER_ATTACHMENT,
|
|
|
|
|
|
|
|
view_formats: &[],
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
CameraSelectUi,
|
|
|
|
// Fill with zeroes
|
|
|
|
))
|
|
|
|
image.resize(size);
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
TextBundle::from_section("Cameras", TextStyle { ..default() }),
|
|
|
|
|
|
|
|
UiTitle,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let image_handle = images.add(image);
|
|
|
|
fn wire_up_scenes(
|
|
|
|
|
|
|
|
mut events: EventReader<AssetEvent<Scene>>,
|
|
|
|
|
|
|
|
root: Query<Entity, With<SceneSelectUi>>,
|
|
|
|
|
|
|
|
root_children: Query<&Children, With<SceneSelectUi>>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
|
|
|
current: Res<Current>,
|
|
|
|
|
|
|
|
existing: Query<&SceneButton, Without<UiTitle>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
|
|
|
|
// Cleanup unused buttons
|
|
|
|
|
|
|
|
root_children.iter().for_each(|children| {
|
|
|
|
|
|
|
|
// Iterate over UI children elements
|
|
|
|
|
|
|
|
children
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
// Filter to just the buttons we want to despawn
|
|
|
|
|
|
|
|
.filter(|&child| {
|
|
|
|
|
|
|
|
// Check each child entity against the currently loaded scenes
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
image_handle
|
|
|
|
// 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 local = {
|
|
|
|
let mut ui = commands.entity(root.single());
|
|
|
|
// Get a unique number for this resource from the handle
|
|
|
|
|
|
|
|
let idx = handle.id().reflect_hash().unwrap();
|
|
|
|
|
|
|
|
// Set the origin of this scene to [idx, idx, idx];
|
|
|
|
|
|
|
|
let origin = Vec3::ONE * ((idx % 1000) as f32);
|
|
|
|
|
|
|
|
// Transform pointing at origin
|
|
|
|
|
|
|
|
Transform::from_translation(origin)
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Spawn the actual scene
|
|
|
|
ui.with_children(|parent| {
|
|
|
|
commands
|
|
|
|
parent
|
|
|
|
.spawn((
|
|
|
|
.spawn((
|
|
|
|
SpatialBundle {
|
|
|
|
ButtonBundle {
|
|
|
|
transform: local,
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Inspect,
|
|
|
|
SceneButton(handle.clone()),
|
|
|
|
Container,
|
|
|
|
|
|
|
|
Preview(preview_image_handle.clone()),
|
|
|
|
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.with_children(|builder| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
let camera_location =
|
|
|
|
parent.spawn(TextBundle {
|
|
|
|
Transform::from_xyz(0.0, 0.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y);
|
|
|
|
text: Text {
|
|
|
|
|
|
|
|
sections: vec![TextSection {
|
|
|
|
// Spawn preview camera
|
|
|
|
value: name.clone(),
|
|
|
|
builder.spawn((
|
|
|
|
style: TextStyle {
|
|
|
|
Camera3dBundle {
|
|
|
|
color: Color::WHITE,
|
|
|
|
camera_3d: Camera3d {
|
|
|
|
|
|
|
|
clear_color: ClearColorConfig::Custom(Color::WHITE),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
camera: Camera {
|
|
|
|
}],
|
|
|
|
order: -1,
|
|
|
|
|
|
|
|
target: RenderTarget::Image(preview_image_handle.clone()),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
transform: camera_location,
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
});
|
|
|
|
UiCameraConfig { show_ui: false },
|
|
|
|
});
|
|
|
|
PreviewCamera,
|
|
|
|
});
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetEvent::Removed { .. } => warn!("IGNORED Remove scene in list"),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Translate UI button presses into scene spawn events
|
|
|
|
|
|
|
|
fn control_scene(
|
|
|
|
|
|
|
|
mut events: EventWriter<SpawnScene>,
|
|
|
|
|
|
|
|
interactions: Query<(&Interaction, &SceneButton), (Changed<Interaction>, With<Button>)>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Handle UI buttons
|
|
|
|
|
|
|
|
interactions
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.for_each(|(interaction, SceneButton(handle))| match interaction {
|
|
|
|
|
|
|
|
Interaction::Pressed => events.send(SpawnScene(handle.clone())),
|
|
|
|
|
|
|
|
_ => (),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn wire_up_animations(
|
|
|
|
|
|
|
|
mut events: EventReader<AssetEvent<AnimationClip>>,
|
|
|
|
|
|
|
|
root: Query<Entity, With<AnimationSelectUi>>,
|
|
|
|
|
|
|
|
root_children: Query<&Children, With<AnimationSelectUi>>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
|
|
|
current: Res<Current>,
|
|
|
|
|
|
|
|
existing: Query<&AnimationButton, Without<UiTitle>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
|
|
|
|
// Cleanup unused buttons
|
|
|
|
|
|
|
|
root_children.iter().for_each(|children| {
|
|
|
|
|
|
|
|
// Iterate over UI children elements
|
|
|
|
|
|
|
|
children
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
// Filter to just the buttons we want to despawn
|
|
|
|
|
|
|
|
.filter(|&child| {
|
|
|
|
|
|
|
|
// Check each child entity against the currently loaded animations
|
|
|
|
|
|
|
|
// Note: !(_.any(...)) (not-any)
|
|
|
|
|
|
|
|
!((*current).animations.iter().any(|(_, handle)| {
|
|
|
|
|
|
|
|
// If the current entitie's animation button corresponds to one of
|
|
|
|
|
|
|
|
// the animations loaded, continue, otherwise
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
|
|
|
|
// Spawn window camera
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
builder.spawn((
|
|
|
|
parent
|
|
|
|
Camera3dBundle {
|
|
|
|
.spawn((
|
|
|
|
camera_3d: Camera3d {
|
|
|
|
ButtonBundle {
|
|
|
|
clear_color: ClearColorConfig::Custom(Color::WHITE),
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
camera: Camera {
|
|
|
|
AnimationButton(handle.clone()),
|
|
|
|
is_active: false,
|
|
|
|
))
|
|
|
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
|
|
|
parent.spawn(TextBundle {
|
|
|
|
|
|
|
|
text: Text {
|
|
|
|
|
|
|
|
sections: vec![TextSection {
|
|
|
|
|
|
|
|
value: name.clone(),
|
|
|
|
|
|
|
|
style: TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
transform: camera_location,
|
|
|
|
}],
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Inspect,
|
|
|
|
|
|
|
|
Preview(preview_image_handle.clone()),
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
builder.spawn((
|
|
|
|
|
|
|
|
SceneBundle {
|
|
|
|
|
|
|
|
scene: handle.clone(),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
});
|
|
|
|
RigidBody::KinematicPositionBased,
|
|
|
|
});
|
|
|
|
Collider::cylinder(1.0, 1.0),
|
|
|
|
|
|
|
|
Inspect,
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AssetEvent::Removed { .. } => {
|
|
|
|
AssetEvent::Removed { .. } => warn!("Ignoring asset removal"),
|
|
|
|
todo!("Remove deleted scene")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetEvent::Modified { .. } => {
|
|
|
|
|
|
|
|
todo!("Update modified scene")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn control_animation(
|
|
|
|
|
|
|
|
interactions: Query<(&Interaction, &AnimationButton), (Changed<Interaction>, With<Button>)>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
interactions
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.for_each(|(interaction, AnimationButton(handle))| match interaction {
|
|
|
|
|
|
|
|
Interaction::Pressed => {
|
|
|
|
|
|
|
|
info!("TODO: Play animation {:?}", handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => (),
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn spawn_ui(
|
|
|
|
/// Wire up the camera UI
|
|
|
|
|
|
|
|
fn wire_up_cameras(
|
|
|
|
|
|
|
|
added_cam: Query<(Entity, &Name), (Without<MainCamera>, Added<Camera>)>,
|
|
|
|
|
|
|
|
mut removed_cam: RemovedComponents<Camera>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
query: Query<Entity, With<ScrollingList>>,
|
|
|
|
root: Query<Entity, With<CameraSelectUi>>,
|
|
|
|
previews: Query<&Preview, (Added<Preview>, Without<Camera>, Without<Interaction>)>,
|
|
|
|
root_children: Query<&Children, With<CameraSelectUi>>,
|
|
|
|
|
|
|
|
existing: Query<&CameraButton, Without<UiTitle>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
// UI container
|
|
|
|
// Add new camera buttons
|
|
|
|
if let Ok(scrolling_list_container) = query.get_single() {
|
|
|
|
added_cam.iter().for_each(|(entity, name)| {
|
|
|
|
let mut entity_commands = commands.entity(scrolling_list_container);
|
|
|
|
info!("Adding camera button");
|
|
|
|
entity_commands.with_children(|parent| {
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
for Preview(image_handle) in previews.iter() {
|
|
|
|
|
|
|
|
// Preview Image
|
|
|
|
|
|
|
|
info!("Spawning image preview");
|
|
|
|
|
|
|
|
parent
|
|
|
|
parent
|
|
|
|
.spawn((
|
|
|
|
.spawn((
|
|
|
|
ButtonBundle { ..default() },
|
|
|
|
ButtonBundle {
|
|
|
|
SelectionUI,
|
|
|
|
background_color: BackgroundColor(Color::NONE),
|
|
|
|
Preview(image_handle.clone()),
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
CameraButton(entity),
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn(ImageBundle {
|
|
|
|
parent.spawn(TextBundle {
|
|
|
|
style: Style {
|
|
|
|
text: Text {
|
|
|
|
width: Val::Px(256.0),
|
|
|
|
sections: vec![TextSection {
|
|
|
|
height: Val::Px(256.0),
|
|
|
|
value: name.as_str().into(),
|
|
|
|
padding: UiRect::all(Val::Px(5.0)),
|
|
|
|
style: TextStyle {
|
|
|
|
|
|
|
|
color: Color::WHITE,
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
image: UiImage {
|
|
|
|
}],
|
|
|
|
texture: image_handle.clone(),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn inspect_nodes(gltfs: Res<Assets<Gltf>>, nodes: Res<Assets<GltfNode>>, mut done: Local<bool>) {
|
|
|
|
// Remove deleted cameras
|
|
|
|
if !(*done) {
|
|
|
|
removed_cam.iter().for_each(|entity| {
|
|
|
|
for gltf in gltfs.iter() {
|
|
|
|
info!("Removing camera button");
|
|
|
|
// info!("Gltf {:#?}", gltf);
|
|
|
|
root_children
|
|
|
|
*done = true;
|
|
|
|
.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_animation(
|
|
|
|
fn control_camera(
|
|
|
|
mut key_evr: EventReader<KeyboardInput>,
|
|
|
|
interactions: Query<(&Interaction, &CameraButton), (Changed<Interaction>, With<Button>)>,
|
|
|
|
mut active_evr: EventReader<ManageActive>,
|
|
|
|
mut cameras: Query<(Entity, &mut Camera), Without<MainCamera>>,
|
|
|
|
mut players: Query<&mut AnimationPlayer>,
|
|
|
|
|
|
|
|
children: Query<&Children>,
|
|
|
|
|
|
|
|
models: Res<Models>,
|
|
|
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
|
|
|
mut active: Local<Vec<Entity>>,
|
|
|
|
|
|
|
|
mut playing: Local<bool>,
|
|
|
|
|
|
|
|
mut dirty: Local<bool>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
for event in active_evr.iter() {
|
|
|
|
interactions
|
|
|
|
match event {
|
|
|
|
.iter()
|
|
|
|
ManageActive(None) => {
|
|
|
|
.for_each(|(interaction, CameraButton(entity))| match interaction {
|
|
|
|
// world state must be updated
|
|
|
|
Interaction::Pressed => cameras
|
|
|
|
*dirty = true;
|
|
|
|
.iter_mut()
|
|
|
|
// Stop playing
|
|
|
|
.for_each(|(e, mut camera)| camera.is_active = e == *entity),
|
|
|
|
*playing = false;
|
|
|
|
|
|
|
|
// Add this to a list of acive entities
|
|
|
|
|
|
|
|
(*active).clear();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ManageActive(Some(entity)) => {
|
|
|
|
|
|
|
|
// world state must be updated
|
|
|
|
|
|
|
|
*dirty = true;
|
|
|
|
|
|
|
|
// Start playing
|
|
|
|
|
|
|
|
*playing = true;
|
|
|
|
|
|
|
|
// Add this to a list of acive entities
|
|
|
|
|
|
|
|
(*active).push(*entity);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for event in key_evr.iter() {
|
|
|
|
|
|
|
|
match event {
|
|
|
|
|
|
|
|
KeyboardInput {
|
|
|
|
|
|
|
|
key_code: Some(KeyCode::Space),
|
|
|
|
|
|
|
|
state: ButtonState::Pressed,
|
|
|
|
|
|
|
|
..
|
|
|
|
|
|
|
|
} => {
|
|
|
|
|
|
|
|
// World state needs to be updated to deisred state
|
|
|
|
|
|
|
|
*dirty = true;
|
|
|
|
|
|
|
|
// Toggle playing
|
|
|
|
|
|
|
|
*playing = !(*playing);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => (),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// If world needs to be updated
|
|
|
|
|
|
|
|
if *dirty {
|
|
|
|
|
|
|
|
if *playing {
|
|
|
|
|
|
|
|
for entity in active.iter() {
|
|
|
|
|
|
|
|
for child in children.iter_descendants(*entity) {
|
|
|
|
|
|
|
|
if let Ok(mut player) = players.get_mut(child) {
|
|
|
|
|
|
|
|
for gltf_handle in models.handles.iter() {
|
|
|
|
|
|
|
|
if let Some(gltf) = gltfs.get(gltf_handle) {
|
|
|
|
|
|
|
|
for animation_handle in gltf.animations.iter() {
|
|
|
|
|
|
|
|
player.start(animation_handle.clone()).repeat();
|
|
|
|
|
|
|
|
player.resume();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
info!("Failed to get GLTF handle");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
for mut player in players.iter_mut() {
|
|
|
|
|
|
|
|
player.pause();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done making updates
|
|
|
|
|
|
|
|
*dirty = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
fn control_default_camera(
|
|
|
|
/// Rotate a model as part of inspection
|
|
|
|
mut main_camera: Query<&mut Camera, With<MainCamera>>,
|
|
|
|
/// TODO: move light with model (want to see shadows)
|
|
|
|
other_cameras: Query<&mut Camera, Without<MainCamera>>,
|
|
|
|
fn rotate_model(
|
|
|
|
|
|
|
|
buttons: Res<Input<MouseButton>>,
|
|
|
|
|
|
|
|
mut mouse_evr: EventReader<MouseMotion>,
|
|
|
|
|
|
|
|
mut transforms: Query<&mut Transform, (With<Inspect>, With<Active>, Without<Camera>)>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if buttons.pressed(MouseButton::Left) {
|
|
|
|
// PERF: Probably don't need to do this every frame
|
|
|
|
for MouseMotion { delta } in mouse_evr.iter() {
|
|
|
|
main_camera.single_mut().is_active = other_cameras.is_empty();
|
|
|
|
for mut transform in transforms.iter_mut() {
|
|
|
|
|
|
|
|
let rot_y = delta.x / 1000.0;
|
|
|
|
|
|
|
|
let rot_x = delta.y / 1000.0;
|
|
|
|
|
|
|
|
transform.rotate_y(rot_y);
|
|
|
|
|
|
|
|
transform.rotate_x(rot_x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Load gltfs and spawn default scene
|
|
|
|
/// Zoom in and out of the model
|
|
|
|
fn loading(
|
|
|
|
/// TODO: Only modify selected entities
|
|
|
|
mut events: EventReader<AssetEvent<Gltf>>,
|
|
|
|
fn zoom_model(
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
keys: Res<Input<KeyCode>>,
|
|
|
|
mut spawn: EventWriter<SpawnScene>,
|
|
|
|
mut wheel_evr: EventReader<MouseWheel>,
|
|
|
|
mut current: ResMut<Current>,
|
|
|
|
mut transforms: Query<&mut Transform, (With<Inspect>, With<Active>, Without<Camera>)>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if keys.pressed(KeyCode::ShiftLeft) {
|
|
|
|
events.iter().for_each(|event| {
|
|
|
|
for ev in wheel_evr.iter() {
|
|
|
|
info!("Event!: {:?}", event);
|
|
|
|
for mut transform in transforms.iter_mut() {
|
|
|
|
if let AssetEvent::<Gltf>::Created { handle, .. } = event {
|
|
|
|
let scale = (Vec3::ONE * ev.y) / 100.0;
|
|
|
|
// Despawn existing scene
|
|
|
|
transform.scale += scale;
|
|
|
|
let gltf = gltfs.get(handle).expect("Loaded Gltf");
|
|
|
|
}
|
|
|
|
let default_scene = gltf.default_scene.clone().expect("Default scene");
|
|
|
|
}
|
|
|
|
spawn.send(SpawnScene(default_scene));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Save active scenes
|
|
|
|
|
|
|
|
current.scenes = gltf.named_scenes.clone();
|
|
|
|
|
|
|
|
current.animations = gltf.named_animations.clone();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TOOD: Spawn default camera
|
|
|
|
|
|
|
|
// TODO: Handle multiple cameras
|
|
|
|
|
|
|
|
// TODO: Scene Selection scenes in one Gltf
|
|
|
|
|
|
|
|
// TODO: Handle animation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn scroll(
|
|
|
|
/// Reset the current scene
|
|
|
|
mut scroll_evr: EventReader<MouseWheel>,
|
|
|
|
fn reset_scene(
|
|
|
|
mut query: Query<&mut Style, With<ScrollingList>>,
|
|
|
|
mut events: EventReader<ResetScene>,
|
|
|
|
active: Query<Entity, With<Active>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
current: Query<Entity, With<SceneMarker>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
for ev in scroll_evr.iter() {
|
|
|
|
events.iter().for_each(|_| {
|
|
|
|
// Only scroll if scene not selected
|
|
|
|
info!("Reset scene");
|
|
|
|
if active.is_empty() {
|
|
|
|
|
|
|
|
for mut s in query.iter_mut() {
|
|
|
|
|
|
|
|
s.top = match s.top {
|
|
|
|
|
|
|
|
Val::Px(current) => Val::Px(current + (ev.y * 5.0)),
|
|
|
|
|
|
|
|
_ => Val::Px(0.0),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
current
|
|
|
|
/// Click a UI element to select
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This is a really ugly implementation. I'm not really happy with how we have to navigate the ECS
|
|
|
|
|
|
|
|
/// parent/child hierarchy. There should be a more direct way to correlate the scene with the
|
|
|
|
|
|
|
|
/// button.
|
|
|
|
|
|
|
|
fn select(
|
|
|
|
|
|
|
|
query: Query<(&Interaction, &Preview), (With<SelectionUI>, Changed<Interaction>)>,
|
|
|
|
|
|
|
|
mut selection_ui: Query<&mut Visibility, (With<SelectionUI>, Without<Parent>)>,
|
|
|
|
|
|
|
|
mut ui_camera: Query<&mut Camera, (With<SelectionUI>, Without<Inspect>)>,
|
|
|
|
|
|
|
|
mut scene_camera: Query<(Entity, &mut Camera, &Preview), (With<Inspect>, Without<SelectionUI>)>,
|
|
|
|
|
|
|
|
mut key_evr: EventReader<KeyboardInput>,
|
|
|
|
|
|
|
|
mut selected: Local<Option<Entity>>, // Active camera index
|
|
|
|
|
|
|
|
parent_search: Query<&Children>,
|
|
|
|
|
|
|
|
cameras: Query<&Camera, (Without<SelectionUI>, Without<Inspect>)>,
|
|
|
|
|
|
|
|
parents: Query<Entity, (With<Children>, Without<Parent>)>, // TODO: Constrain
|
|
|
|
|
|
|
|
mut events: EventWriter<ManageActive>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
for (interaction, selected_preview) in query.iter() {
|
|
|
|
|
|
|
|
if interaction == &Interaction::Pressed {
|
|
|
|
|
|
|
|
// Hide UI
|
|
|
|
|
|
|
|
let mut ui_vis = selection_ui.single_mut();
|
|
|
|
|
|
|
|
*ui_vis = Visibility::Hidden;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Disable UI camera
|
|
|
|
|
|
|
|
let mut ui_cam = ui_camera.single_mut();
|
|
|
|
|
|
|
|
ui_cam.is_active = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Determine selected scene
|
|
|
|
|
|
|
|
*selected = scene_camera
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.find(|(_, _, preview)| selected_preview.0 == preview.0)
|
|
|
|
.for_each(|e| commands.entity(e).despawn_recursive())
|
|
|
|
.map(|(entity, _, _)| entity);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Enable scene camera
|
|
|
|
|
|
|
|
let (_, mut scene_cam, _) = scene_camera
|
|
|
|
|
|
|
|
.get_mut(selected.expect("Selected scene should be set"))
|
|
|
|
|
|
|
|
.expect("Failed to get Scene camera");
|
|
|
|
|
|
|
|
scene_cam.is_active = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set relevant entities active
|
|
|
|
|
|
|
|
let message = parents.iter().find(|&parent| {
|
|
|
|
|
|
|
|
parent_search
|
|
|
|
|
|
|
|
.iter_descendants(parent)
|
|
|
|
|
|
|
|
.find(|&entity| Some(entity) == *selected)
|
|
|
|
|
|
|
|
.is_some()
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
events.send(ManageActive(message));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for ev in key_evr.iter() {
|
|
|
|
|
|
|
|
match ev {
|
|
|
|
|
|
|
|
KeyboardInput {
|
|
|
|
|
|
|
|
state: ButtonState::Pressed,
|
|
|
|
|
|
|
|
key_code: Some(KeyCode::Escape),
|
|
|
|
|
|
|
|
..
|
|
|
|
|
|
|
|
} => {
|
|
|
|
|
|
|
|
if let Some(s) = *selected {
|
|
|
|
|
|
|
|
// Set all inactive
|
|
|
|
|
|
|
|
events.send(ManageActive(None));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Disable scene camera
|
|
|
|
|
|
|
|
let (_, mut scene_cam, _) =
|
|
|
|
|
|
|
|
scene_camera.get_mut(s).expect("Failed to get Scene camera");
|
|
|
|
|
|
|
|
scene_cam.is_active = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Enable UI camera
|
|
|
|
|
|
|
|
let mut ui_cam = ui_camera.single_mut();
|
|
|
|
|
|
|
|
ui_cam.is_active = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make UI visible
|
|
|
|
|
|
|
|
let mut ui_vis = selection_ui.single_mut();
|
|
|
|
|
|
|
|
*ui_vis = Visibility::Inherited;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => (),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn manage_active(
|
|
|
|
/// Spawn a desired scene
|
|
|
|
|
|
|
|
fn spawn_scene(
|
|
|
|
|
|
|
|
mut events: EventReader<SpawnScene>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut events: EventReader<ManageActive>,
|
|
|
|
current: Query<Entity, With<SceneMarker>>,
|
|
|
|
query: Query<&Children>,
|
|
|
|
|
|
|
|
current: Query<Entity, With<Active>>,
|
|
|
|
|
|
|
|
names: Query<&Name>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
for ManageActive(inner) in events.iter() {
|
|
|
|
// Handle SpawnScene events
|
|
|
|
info!("Setting active: {:?}", inner);
|
|
|
|
events.iter().for_each(|SpawnScene(handle)| {
|
|
|
|
match inner {
|
|
|
|
info!("Reset scene (inline)");
|
|
|
|
None => {
|
|
|
|
current
|
|
|
|
for entity in current.iter() {
|
|
|
|
.iter()
|
|
|
|
if let Some(mut entity_commands) = commands.get_entity(entity) {
|
|
|
|
.for_each(|e| commands.entity(e).despawn_recursive());
|
|
|
|
entity_commands.remove::<Active>();
|
|
|
|
|
|
|
|
entity_commands.remove::<ColliderDebugColor>();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(entity) => {
|
|
|
|
|
|
|
|
for child in query.iter_descendants(*entity) {
|
|
|
|
|
|
|
|
if let Some(mut child_commands) = commands.get_entity(child) {
|
|
|
|
|
|
|
|
// info!("Name: {:?}", names.get(child));
|
|
|
|
|
|
|
|
// child_commands.log_components();
|
|
|
|
|
|
|
|
child_commands.insert(Active);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn selection(
|
|
|
|
info!("Spawning scene {:?}", handle);
|
|
|
|
mut commands: Commands,
|
|
|
|
commands.spawn((
|
|
|
|
windows: Query<&Window, With<PrimaryWindow>>,
|
|
|
|
SceneBundle {
|
|
|
|
mut mouse_events: EventReader<MouseMotion>,
|
|
|
|
scene: handle.clone(),
|
|
|
|
camera_q: Query<(&Camera, &GlobalTransform, &VisibleEntities), With<Active>>,
|
|
|
|
..default()
|
|
|
|
rapier_context: Res<RapierContext>,
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
SceneMarker,
|
|
|
|
if !mouse_events.is_empty() {
|
|
|
|
));
|
|
|
|
let window = windows.single();
|
|
|
|
});
|
|
|
|
for _ in mouse_events.iter() {
|
|
|
|
|
|
|
|
if let Some(cursor) = window.cursor_position() {
|
|
|
|
|
|
|
|
for (camera, camera_t, visible_entities) in camera_q.iter() {
|
|
|
|
|
|
|
|
if let Some(ray) = camera.viewport_to_world(camera_t, cursor) {
|
|
|
|
|
|
|
|
if let Some((entity, toi)) = rapier_context.cast_ray(
|
|
|
|
|
|
|
|
ray.origin,
|
|
|
|
|
|
|
|
ray.direction,
|
|
|
|
|
|
|
|
f32::MAX,
|
|
|
|
|
|
|
|
true,
|
|
|
|
|
|
|
|
QueryFilter::new()
|
|
|
|
|
|
|
|
.predicate(&|entity| visible_entities.entities.contains(&entity)),
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
if let Some(mut cmds) = commands.get_entity(entity) {
|
|
|
|
|
|
|
|
info!("hit! ({:?}@{})", entity, toi);
|
|
|
|
|
|
|
|
cmds.insert(ColliderDebugColor(Color::BLUE));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|