use bevy::{ gltf::Gltf, input::{ keyboard::KeyboardInput, mouse::{MouseMotion, MouseWheel}, ButtonState, }, pbr::CascadeShadowConfigBuilder, prelude::*, }; fn main() { App::new() .add_plugins(DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { title: "GLTF Inspector".into(), resolution: (640., 480.).into(), ..default() }), ..default() })) .add_startup_system(load_models) .add_startup_system(spawn_base_scene) .add_system(spawn_models) .add_system(control_camera) .add_system(rotate_model) .add_system(zoom_model) .run(); } /// /// Stores GLTF handles for later use #[derive(Resource)] struct Models { handles: Vec>, } /// /// Marks GLTF model as inspectable #[derive(Component)] struct Inspect; #[derive(Component)] struct SelectionUI; /// /// Load all GLTF models on startup fn load_models(mut commands: Commands, ass: Res) { let weak_handles = ass.load_folder("models").expect("Load gltfs"); let handles: Vec> = weak_handles .iter() .map(|weak| weak.clone().typed::()) .collect(); info!("Scene Handles: {:#?}", handles); commands.insert_resource(Models { handles }); } /// /// Spawn base scene fn spawn_base_scene(mut commands: Commands) { commands.spawn(( Camera2dBundle { transform: Transform::from_xyz(0.0, 0.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() }, UiCameraConfig { ..default() }, SelectionUI, )); } fn control_camera( mut keyboard_evr: EventReader, mut ui_camera: Query<&mut Camera, (With, Without)>, mut scene_camera: Query<&mut Camera, (With, Without)>, mut scene_objects: Query<&mut ComputedVisibility, With>, ) { 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, // TODO: Need to iter over gltf not scenes? mut scene_evr: EventReader>, ) { if !scene_evr.is_empty() { info!("Spawning scenes"); } for ev in scene_evr.iter() { match ev { AssetEvent::Created { handle } => { info!("Creating scene {:#?}", handle); commands .spawn(( Inspect, VisibilityBundle::default(), TransformBundle::default(), )) .with_children(|builder| { builder.spawn(( Camera3dBundle { camera: Camera { is_active: false, ..default() }, transform: Transform::from_xyz(0.0, 0.0, 5.0) .looking_at(Vec3::ZERO, Vec3::Y), ..default() }, Inspect, )); 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, )); builder.spawn(( SceneBundle { scene: handle.clone(), ..default() }, Inspect, )); }); } AssetEvent::Removed { handle: _handle } => { todo!("Remove deleted scene") } AssetEvent::Modified { handle: _handle } => { todo!("Update modified scene") } } } } /// /// Rotate a model as part of inspection fn rotate_model( buttons: Res>, mut mouse_evr: EventReader, mut transforms: Query<&mut Transform, (With, Without)>, ) { 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( mut wheel_evr: EventReader, mut transforms: Query<&mut Transform, With>, ) { for ev in wheel_evr.iter() { for mut transform in transforms.iter_mut() { let scale = (Vec3::ONE * ev.y) / 100.0; transform.scale += scale; } } }