use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; use monologue_trees::debug::*; fn main() { App::new() .add_plugins(( DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { title: "GLTF Inspector".into(), resolution: (640., 480.).into(), ..default() }), ..default() }), DebugInfoPlugin, )) .add_event::() .add_event::() .init_resource::() .add_systems(Startup, spawn_ui) .add_systems(PreUpdate, (add_camera_ui, add_scene_ui, add_animation_ui)) .add_systems( Update, ( drag_and_drop, loading, reset_scene.before(spawn_scene), spawn_scene.after(reset_scene), control_scene, control_animation, control_camera, control_default_camera, control_default_light, ), ) .add_systems( PostUpdate, ( clean_ui::, clean_ui::, clean_ui::, ), ) .run(); } #[derive(Component)] struct SceneMarker; #[derive(Component)] struct DefaultCamera; #[derive(Component)] struct DefaultLight; #[derive(Component)] struct MainUi; #[derive(Component)] struct UiTitle; #[derive(Component)] struct InstructionsUi; #[derive(Component)] struct SceneSelectUi; #[derive(Component, PartialEq, Debug)] struct SceneButton(Handle); #[derive(Event)] struct SpawnScene(Handle); #[derive(Component)] struct AnimationSelectUi; #[derive(Component, PartialEq, Debug)] struct AnimationButton(Handle); #[derive(Component)] struct CameraSelectUi; #[derive(Component, PartialEq, Debug)] struct CameraButton(Entity); #[derive(Resource, Default)] struct Current { gltf: Handle, scenes: HashMap>, animations: HashMap>, } #[derive(Event)] struct ResetScene; #[derive(Component)] struct ResetAnimation; fn drag_and_drop( mut events: EventReader, server: Res, mut current: ResMut, ) { events .iter() .filter_map(|event| match event { FileDragAndDrop::DroppedFile { path_buf, .. } => Some(path_buf), _ => 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) { // TODO: Warn no camera (hidden) // TODO: Scene select container // TODO: Animation Play/Pause Placeholder commands.spawn(( Camera3dBundle { transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() }, UiCameraConfig { show_ui: true }, DefaultCamera, )); commands.spawn(( DirectionalLightBundle { transform: Transform::default().looking_at(Vec3::new(1.0, -1.0, -1.0), Vec3::Y), ..default() }, DefaultLight, )); 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() }, MainUi, )) .with_children(|parent| { parent .spawn(NodeBundle { style: Style { flex_direction: FlexDirection::Column, ..default() }, ..default() }) .with_children(|parent| { parent.spawn(( TextBundle::from_section( "Drag and Drop .gltf/.glb file", TextStyle { color: Color::WHITE, ..default() }, ), InstructionsUi, )); parent.spawn(( TextBundle::from_section( "Using default camera", TextStyle { color: Color::WHITE, ..default() }, ), DefaultCamera, )); parent.spawn(( TextBundle::from_section( "Using default light", TextStyle { color: Color::WHITE, ..default() }, ), DefaultLight, )); }); parent .spawn(( NodeBundle { style: Style { flex_direction: FlexDirection::Column, ..default() }, ..default() }, SceneSelectUi, )) .with_children(|parent| { parent.spawn(( TextBundle::from_section("Scenes", TextStyle { ..default() }), UiTitle, )); }); parent .spawn(( NodeBundle { style: Style { flex_direction: FlexDirection::Column, ..default() }, ..default() }, CameraSelectUi, )) .with_children(|parent| { parent.spawn(( TextBundle::from_section("Cameras", TextStyle { ..default() }), UiTitle, )); }); parent .spawn(( NodeBundle { style: Style { flex_direction: FlexDirection::Column, ..default() }, ..default() }, AnimationSelectUi, )) .with_children(|parent| { parent.spawn(( TextBundle::from_section("Animations", TextStyle { ..default() }), UiTitle, )); }); }); } /// When a new camera is loaded, clear the camera buttons fn clean_ui( mut spawn_events: EventReader, mut clear_events: EventReader, cameras: Query>, mut commands: Commands, ) { // We don't care about the content of these events, just that we want to clear the UI after // each spawn or reset event, so we map each event to () and chain the two event streams. spawn_events .iter() .map(|_| ()) .chain(clear_events.iter().map(|_| ())) .for_each(|_| { cameras.iter().for_each(|entity| { commands.entity(entity).despawn_recursive(); }); }); } /// Translate UI button presses into scene spawn events fn control_scene( mut events: EventWriter, interactions: Query<(&Interaction, &SceneButton), (Changed, With