You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
13 KiB
Rust

use bevy::{
core_pipeline::clear_color::ClearColorConfig,
gltf::Gltf,
input::{
keyboard::KeyboardInput,
mouse::{MouseMotion, MouseWheel},
ButtonState,
},
pbr::CascadeShadowConfigBuilder,
prelude::*,
render::{
camera::RenderTarget,
render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
view::RenderLayers,
},
};
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "GLTF Inspector".into(),
resolution: (640., 480.).into(),
..default()
}),
..default()
}))
// Only run when window is active to reduce cpu cycles
// .insert_resource(WinitSettings::desktop_app())
.add_startup_system(load_models)
.add_startup_system(spawn_base_scene)
.add_system(spawn_models)
// .add_system(control_ui)
.add_system(control_camera)
.add_system(rotate_model)
.add_system(zoom_model)
.add_system(scroll)
.run();
}
///
/// Stores GLTF handles for later use
#[derive(Resource)]
struct Models {
_handles: Vec<Handle<Gltf>>,
}
///
/// Marks GLTF model as inspectable
#[derive(Component)]
struct Inspect;
#[derive(Component)]
struct SelectionUI;
#[derive(Component)]
struct PreviewCamera;
#[derive(Component)]
struct ScrollingList;
///
/// Load all GLTF models on startup
fn load_models(mut commands: Commands, ass: Res<AssetServer>) {
let weak_handles = ass.load_folder("models").expect("Load gltfs");
let _handles: Vec<Handle<Gltf>> = weak_handles
.iter()
.map(|weak| weak.clone().typed::<Gltf>())
.collect();
info!("Scene Handles: {:#?}", _handles);
commands.insert_resource(Models { _handles });
}
///
/// Spawn base scene
fn spawn_base_scene(mut commands: Commands) {
commands.spawn((
Camera2dBundle {
camera_2d: Camera2d {
clear_color: ClearColorConfig::Custom(Color::BLACK),
},
..default()
},
UiCameraConfig { ..default() },
SelectionUI,
));
}
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
}
}
}
///
/// Spawn a loaded scene for inspection
/// TODO: Update/add/delete when files are updated
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
{
info!("Creating preview");
let size = Extent3d {
width: 256,
height: 256,
..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()
};
// Fill with zeroes
image.resize(size);
let image_handle = images.add(image);
// Spawn preview camera
commands.spawn((
Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::Custom(Color::WHITE),
..default()
},
camera: Camera {
order: -1,
target: RenderTarget::Image(image_handle.clone()),
..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
commands
.spawn((
Inspect,
VisibilityBundle::default(),
TransformBundle::default(), // TODO: Move to staging area
RenderLayers::layer(1), // SUS
))
.with_children(|builder| {
builder.spawn((
Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::Custom(Color::WHITE),
..default()
},
camera: Camera {
is_active: false,
..default()
},
transform: Transform::from_xyz(0.0, 0.0, 5.0)
.looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
Inspect,
RenderLayers::layer(1), // SUS
));
builder.spawn((
DirectionalLightBundle {
directional_light: DirectionalLight {
shadows_enabled: true,
..default()
},
cascade_shadow_config: CascadeShadowConfigBuilder {
num_cascades: 1,
maximum_distance: 1.6,
..default()
}
.into(),
..default()
},
Inspect,
RenderLayers::layer(1), // SUS
));
builder.spawn((
SceneBundle {
scene: handle.clone(),
..default()
},
Inspect,
RenderLayers::layer(1), // SUS
));
});
}
AssetEvent::Removed { handle: _handle } => {
todo!("Remove deleted scene")
}
AssetEvent::Modified { handle: _handle } => {
todo!("Update modified scene")
}
}
}
}
// fn control_ui(mut commands: Commands, cameras: Query<Camera, With<Inspect>>) {
// todo!()
// }
///
/// Rotate a model as part of inspection
fn rotate_model(
buttons: Res<Input<MouseButton>>,
mut mouse_evr: EventReader<MouseMotion>,
mut transforms: Query<&mut Transform, (With<Inspect>, Without<Camera>)>,
) {
if buttons.pressed(MouseButton::Left) {
for MouseMotion { delta } in mouse_evr.iter() {
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);
}
}
}
}
///
/// Zoom in and out of the model
fn zoom_model(
keys: Res<Input<KeyCode>>,
mut wheel_evr: EventReader<MouseWheel>,
mut transforms: Query<&mut Transform, With<Inspect>>,
) {
if keys.pressed(KeyCode::LShift) {
for ev in wheel_evr.iter() {
for mut transform in transforms.iter_mut() {
let scale = (Vec3::ONE * ev.y) / 100.0;
transform.scale += scale;
}
}
}
}
fn scroll(
mut scroll_evr: EventReader<MouseWheel>,
mut query: Query<&mut Style, With<ScrollingList>>,
) {
for ev in scroll_evr.iter() {
for mut s in query.iter_mut() {
s.position.top = match s.position.top {
Val::Px(current) => Val::Px(current + (ev.y * 5.0)),
_ => Val::Px(0.0),
};
}
}
}