Only-move-active implemented

For some reason the camera is not moving with the scene, so that's a
bug.
main
Elijah Voigt 2 years ago
parent 3ea19020ae
commit 8b257b1914

@ -0,0 +1,13 @@
# GLTF Inspector
## Staging area
I want to have parallel "world" for each of my scenes so they're all in the same location in space, but not overlapping in the viewport.
For example, we load scene A and B and we want to show independent previews of A and B.
The way this tends to be done is by moving the staged assets "out of the way" to some far off part of the world that the player can't see.
This works, but makes the game world grounded in reality more than it has to be.
In principle (these are just 1s and 0s) we can spawn these all in their own worlds and toggle between worlds in the "multiverse".
There is [at least one] RFC for Bevy to support a "Universe" with multiple worlds, but nothing implemented.

BIN
assets/models/monkey-nod.blend (Stored with Git LFS)

Binary file not shown.

BIN
assets/models/monkey-nod.glb (Stored with Git LFS)

Binary file not shown.

BIN
assets/models/torus-spin.blend (Stored with Git LFS)

Binary file not shown.

BIN
assets/models/torus-spin.blend1 (Stored with Git LFS)

Binary file not shown.

BIN
assets/models/torus-spin.glb (Stored with Git LFS)

Binary file not shown.

@ -13,7 +13,6 @@ use bevy::{
render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
view::RenderLayers,
},
};
@ -27,16 +26,17 @@ fn main() {
}),
..default()
}))
// Only run when window is active to reduce cpu cycles
// .insert_resource(WinitSettings::desktop_app())
.add_event::<ManageActive>()
.add_startup_system(load_models)
.add_startup_system(spawn_base_scene)
.add_startup_system(spawn_base_ui)
.add_system(spawn_models)
// .add_system(control_ui)
.add_system(control_camera)
.add_system(spawn_ui)
.add_system(rotate_model)
.add_system(zoom_model)
.add_system(scroll)
.add_system(select)
.add_system(manage_active)
.run();
}
@ -61,6 +61,18 @@ struct PreviewCamera;
#[derive(Component)]
struct ScrollingList;
#[derive(Component)]
struct Preview(Handle<Image>);
#[derive(Component)]
struct Container;
#[derive(Component)]
struct Active;
// Event
struct ManageActive(Option<Entity>);
///
/// Load all GLTF models on startup
fn load_models(mut commands: Commands, ass: Res<AssetServer>) {
@ -88,41 +100,36 @@ fn spawn_base_scene(mut commands: Commands) {
));
}
fn control_camera(
mut keyboard_evr: EventReader<KeyboardInput>,
mut ui_camera: Query<&mut Camera, (With<SelectionUI>, Without<Inspect>)>,
mut scene_camera: Query<&mut Camera, (With<Inspect>, Without<SelectionUI>)>,
) {
for ev in keyboard_evr.iter() {
match ev {
KeyboardInput {
state: ButtonState::Pressed,
key_code: Some(KeyCode::Space),
..
} => {
// Disable UI camera
let mut ui_cam = ui_camera.single_mut();
info!("Toggling UI camera {}", !ui_cam.is_active);
ui_cam.is_active = !ui_cam.is_active;
// Enable scene camera
let mut scene_cam = scene_camera
.iter_mut()
.nth(0)
.expect("Failed to get Scene camera");
info!("Toggling Scene camera {}", !scene_cam.is_active);
scene_cam.is_active = !scene_cam.is_active;
}
KeyboardInput {
state: ButtonState::Released,
key_code: Some(KeyCode::Space),
..
} => {
// No-Op
}
_ => {} // No-Op
}
}
fn spawn_base_ui(mut commands: Commands) {
commands
.spawn((
NodeBundle {
style: Style {
justify_content: JustifyContent::Center,
size: Size::all(Val::Percent(90.0)),
overflow: Overflow::Hidden,
..default()
},
..default()
},
SelectionUI,
))
.with_children(|parent| {
parent.spawn((
NodeBundle {
style: Style {
flex_wrap: FlexWrap::Wrap,
flex_direction: FlexDirection::Row,
justify_content: JustifyContent::SpaceAround,
size: Size::AUTO,
max_size: Size::UNDEFINED,
..default()
},
..default()
},
ScrollingList,
));
});
}
///
@ -136,13 +143,14 @@ fn spawn_models(
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 {
@ -173,8 +181,35 @@ fn spawn_models(
let image_handle = images.add(image);
image_handle
};
let local = {
// 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
commands
.spawn((
SpatialBundle {
transform: local,
..default()
},
Inspect,
Container,
Preview(preview_image_handle.clone()),
))
.with_children(|builder| {
let camera_location =
Transform::from_xyz(0.0, 0.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y);
// Spawn preview camera
commands.spawn((
builder.spawn((
Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::Custom(Color::WHITE),
@ -182,83 +217,17 @@ fn spawn_models(
},
camera: Camera {
order: -1,
target: RenderTarget::Image(image_handle.clone()),
target: RenderTarget::Image(preview_image_handle.clone()),
..default()
},
transform: Transform::from_xyz(5.0, 5.0, 5.0)
.looking_at(Vec3::ZERO, Vec3::Y),
transform: camera_location,
..default()
},
UiCameraConfig { show_ui: false },
PreviewCamera,
));
// UI container
commands
.spawn(NodeBundle {
style: Style t
{
justify_content: JustifyContent::Center,
size: Size::all(Val::Percent(90.0)),
overflow: Overflow::Hidden,
..default()
},
..default()
})
.with_children(|parent| {
parent
.spawn((
NodeBundle {
style: Style {
flex_wrap: FlexWrap::Wrap,
flex_direction: FlexDirection::Row,
justify_content: JustifyContent::SpaceAround,
size: Size::AUTO,
max_size: Size::UNDEFINED,
..default()
},
..default()
},
ScrollingList,
))
.with_children(|parent| {
// Preview Image
let colors = [
Color::RED,
Color::GREEN,
Color::BLUE,
Color::YELLOW,
Color::PURPLE,
];
for i in 0..5 {
info!("Spawning image preview");
parent.spawn(ImageBundle {
style: Style {
size: Size::all(Val::Px(256.0)),
padding: UiRect::all(Val::Px(5.0)),
..default()
},
background_color: BackgroundColor(colors[i]),
image: UiImage {
texture: image_handle.clone(),
..default()
},
..default()
});
}
});
});
}
// Spawn the actual scene
commands
.spawn((
Inspect,
VisibilityBundle::default(),
TransformBundle::default(), // TODO: Move to staging area
RenderLayers::layer(1), // SUS
))
.with_children(|builder| {
// Spawn window camera
builder.spawn((
Camera3dBundle {
camera_3d: Camera3d {
@ -269,12 +238,11 @@ fn spawn_models(
is_active: false,
..default()
},
transform: Transform::from_xyz(0.0, 0.0, 5.0)
.looking_at(Vec3::ZERO, Vec3::Y),
transform: camera_location,
..default()
},
Inspect,
RenderLayers::layer(1), // SUS
Preview(preview_image_handle.clone()),
));
builder.spawn((
@ -292,7 +260,6 @@ fn spawn_models(
..default()
},
Inspect,
RenderLayers::layer(1), // SUS
));
builder.spawn((
@ -301,7 +268,6 @@ fn spawn_models(
..default()
},
Inspect,
RenderLayers::layer(1), // SUS
));
});
}
@ -315,16 +281,50 @@ fn spawn_models(
}
}
// fn control_ui(mut commands: Commands, cameras: Query<Camera, With<Inspect>>) {
// todo!()
// }
fn spawn_ui(
mut commands: Commands,
query: Query<Entity, With<ScrollingList>>,
previews: Query<&Preview, (Added<Preview>, Without<Camera>, Without<Interaction>)>,
) {
// UI container
if let Ok(scrolling_list_container) = query.get_single() {
let mut entity_commands = commands.entity(scrolling_list_container);
entity_commands.with_children(|parent| {
for Preview(image_handle) in previews.iter() {
// Preview Image
info!("Spawning image preview");
parent
.spawn((
ButtonBundle { ..default() },
SelectionUI,
Preview(image_handle.clone()),
))
.with_children(|parent| {
parent.spawn(ImageBundle {
style: Style {
size: Size::all(Val::Px(256.0)),
padding: UiRect::all(Val::Px(5.0)),
..default()
},
image: UiImage {
texture: image_handle.clone(),
..default()
},
..default()
});
});
}
});
}
}
///
/// Rotate a model as part of inspection
/// TODO: Only modify selected entities
fn rotate_model(
buttons: Res<Input<MouseButton>>,
mut mouse_evr: EventReader<MouseMotion>,
mut transforms: Query<&mut Transform, (With<Inspect>, Without<Camera>)>,
mut transforms: Query<&mut Transform, (With<Inspect>, With<Active>, Without<Camera>)>,
) {
if buttons.pressed(MouseButton::Left) {
for MouseMotion { delta } in mouse_evr.iter() {
@ -340,10 +340,11 @@ fn rotate_model(
///
/// Zoom in and out of the model
/// TODO: Only modify selected entities
fn zoom_model(
keys: Res<Input<KeyCode>>,
mut wheel_evr: EventReader<MouseWheel>,
mut transforms: Query<&mut Transform, With<Inspect>>,
mut transforms: Query<&mut Transform, (With<Inspect>, With<Active>, Without<Camera>)>,
) {
if keys.pressed(KeyCode::LShift) {
for ev in wheel_evr.iter() {
@ -368,3 +369,109 @@ fn scroll(
}
}
}
///
/// 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>,
parents: Query<Entity, (With<Children>, Without<Parent>)>, // TODO: Constrain
mut events: EventWriter<ManageActive>,
) {
for (interaction, selected_preview) in query.iter() {
if interaction == &Interaction::Clicked {
// 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()
.find(|(_, _, preview)| selected_preview.0 == preview.0)
.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(
mut commands: Commands,
mut events: EventReader<ManageActive>,
query: Query<&Children>,
current: Query<Entity, With<Active>>,
) {
for event in events.iter() {
match event {
ManageActive(None) => {
for entity in current.iter() {
if let Some(mut entity_commands) = commands.get_entity(entity) {
entity_commands.remove::<Active>();
}
}
}
ManageActive(Some(entity)) => {
for child in query.iter_descendants(*entity) {
if let Some(mut entity_commands) = commands.get_entity(child) {
info!("Setting active: {:?}", child);
entity_commands.log_components();
entity_commands.insert(Active);
}
}
}
}
}
}

Loading…
Cancel
Save