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::{ render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
}, },
view::RenderLayers,
}, },
}; };
@ -27,16 +26,17 @@ fn main() {
}), }),
..default() ..default()
})) }))
// Only run when window is active to reduce cpu cycles .add_event::<ManageActive>()
// .insert_resource(WinitSettings::desktop_app())
.add_startup_system(load_models) .add_startup_system(load_models)
.add_startup_system(spawn_base_scene) .add_startup_system(spawn_base_scene)
.add_startup_system(spawn_base_ui)
.add_system(spawn_models) .add_system(spawn_models)
// .add_system(control_ui) .add_system(spawn_ui)
.add_system(control_camera)
.add_system(rotate_model) .add_system(rotate_model)
.add_system(zoom_model) .add_system(zoom_model)
.add_system(scroll) .add_system(scroll)
.add_system(select)
.add_system(manage_active)
.run(); .run();
} }
@ -61,6 +61,18 @@ struct PreviewCamera;
#[derive(Component)] #[derive(Component)]
struct ScrollingList; 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 /// Load all GLTF models on startup
fn load_models(mut commands: Commands, ass: Res<AssetServer>) { fn load_models(mut commands: Commands, ass: Res<AssetServer>) {
@ -88,41 +100,36 @@ fn spawn_base_scene(mut commands: Commands) {
)); ));
} }
fn control_camera( fn spawn_base_ui(mut commands: Commands) {
mut keyboard_evr: EventReader<KeyboardInput>, commands
mut ui_camera: Query<&mut Camera, (With<SelectionUI>, Without<Inspect>)>, .spawn((
mut scene_camera: Query<&mut Camera, (With<Inspect>, Without<SelectionUI>)>, NodeBundle {
) { style: Style {
for ev in keyboard_evr.iter() { justify_content: JustifyContent::Center,
match ev { size: Size::all(Val::Percent(90.0)),
KeyboardInput { overflow: Overflow::Hidden,
state: ButtonState::Pressed, ..default()
key_code: Some(KeyCode::Space), },
.. ..default()
} => { },
// Disable UI camera SelectionUI,
let mut ui_cam = ui_camera.single_mut(); ))
info!("Toggling UI camera {}", !ui_cam.is_active); .with_children(|parent| {
ui_cam.is_active = !ui_cam.is_active; parent.spawn((
NodeBundle {
// Enable scene camera style: Style {
let mut scene_cam = scene_camera flex_wrap: FlexWrap::Wrap,
.iter_mut() flex_direction: FlexDirection::Row,
.nth(0) justify_content: JustifyContent::SpaceAround,
.expect("Failed to get Scene camera"); size: Size::AUTO,
info!("Toggling Scene camera {}", !scene_cam.is_active); max_size: Size::UNDEFINED,
scene_cam.is_active = !scene_cam.is_active; ..default()
} },
KeyboardInput { ..default()
state: ButtonState::Released, },
key_code: Some(KeyCode::Space), ScrollingList,
.. ));
} => { });
// No-Op
}
_ => {} // No-Op
}
}
} }
/// ///
@ -136,13 +143,14 @@ fn spawn_models(
if !scene_evr.is_empty() { if !scene_evr.is_empty() {
info!("Spawning scenes"); info!("Spawning scenes");
} }
for ev in scene_evr.iter() { for ev in scene_evr.iter() {
match ev { match ev {
AssetEvent::Created { handle } => { AssetEvent::Created { handle } => {
info!("Creating scene {:#?}", handle); info!("Creating scene {:#?}", handle);
// Preview image // Preview image
{ let preview_image_handle = {
info!("Creating preview"); info!("Creating preview");
let size = Extent3d { let size = Extent3d {
@ -173,92 +181,53 @@ fn spawn_models(
let image_handle = images.add(image); let image_handle = images.add(image);
// Spawn preview camera image_handle
commands.spawn(( };
Camera3dBundle {
camera_3d: Camera3d { let local = {
clear_color: ClearColorConfig::Custom(Color::WHITE), // Get a unique number for this resource from the handle
..default() let idx = handle.id().reflect_hash().unwrap();
}, // Set the origin of this scene to [idx, idx, idx];
camera: Camera { let origin = Vec3::ONE * ((idx % 1000) as f32);
order: -1, // Transform pointing at origin
target: RenderTarget::Image(image_handle.clone()), Transform::from_translation(origin)
..default() };
},
transform: Transform::from_xyz(5.0, 5.0, 5.0)
.looking_at(Vec3::ZERO, Vec3::Y),
..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 // Spawn the actual scene
commands commands
.spawn(( .spawn((
SpatialBundle {
transform: local,
..default()
},
Inspect, Inspect,
VisibilityBundle::default(), Container,
TransformBundle::default(), // TODO: Move to staging area Preview(preview_image_handle.clone()),
RenderLayers::layer(1), // SUS
)) ))
.with_children(|builder| { .with_children(|builder| {
let camera_location =
Transform::from_xyz(0.0, 0.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y);
// Spawn preview camera
builder.spawn((
Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::Custom(Color::WHITE),
..default()
},
camera: Camera {
order: -1,
target: RenderTarget::Image(preview_image_handle.clone()),
..default()
},
transform: camera_location,
..default()
},
UiCameraConfig { show_ui: false },
PreviewCamera,
));
// Spawn window camera
builder.spawn(( builder.spawn((
Camera3dBundle { Camera3dBundle {
camera_3d: Camera3d { camera_3d: Camera3d {
@ -269,12 +238,11 @@ fn spawn_models(
is_active: false, is_active: false,
..default() ..default()
}, },
transform: Transform::from_xyz(0.0, 0.0, 5.0) transform: camera_location,
.looking_at(Vec3::ZERO, Vec3::Y),
..default() ..default()
}, },
Inspect, Inspect,
RenderLayers::layer(1), // SUS Preview(preview_image_handle.clone()),
)); ));
builder.spawn(( builder.spawn((
@ -292,7 +260,6 @@ fn spawn_models(
..default() ..default()
}, },
Inspect, Inspect,
RenderLayers::layer(1), // SUS
)); ));
builder.spawn(( builder.spawn((
@ -301,7 +268,6 @@ fn spawn_models(
..default() ..default()
}, },
Inspect, Inspect,
RenderLayers::layer(1), // SUS
)); ));
}); });
} }
@ -315,16 +281,50 @@ fn spawn_models(
} }
} }
// fn control_ui(mut commands: Commands, cameras: Query<Camera, With<Inspect>>) { fn spawn_ui(
// todo!() 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 /// Rotate a model as part of inspection
/// TODO: Only modify selected entities
fn rotate_model( fn rotate_model(
buttons: Res<Input<MouseButton>>, buttons: Res<Input<MouseButton>>,
mut mouse_evr: EventReader<MouseMotion>, 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) { if buttons.pressed(MouseButton::Left) {
for MouseMotion { delta } in mouse_evr.iter() { for MouseMotion { delta } in mouse_evr.iter() {
@ -340,10 +340,11 @@ fn rotate_model(
/// ///
/// Zoom in and out of the model /// Zoom in and out of the model
/// TODO: Only modify selected entities
fn zoom_model( fn zoom_model(
keys: Res<Input<KeyCode>>, keys: Res<Input<KeyCode>>,
mut wheel_evr: EventReader<MouseWheel>, 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) { if keys.pressed(KeyCode::LShift) {
for ev in wheel_evr.iter() { 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