diff --git a/assets/models/Monologue Trees 7-21-23.glb.bak b/assets/models/Monologue Trees 7-21-23.glb similarity index 100% rename from assets/models/Monologue Trees 7-21-23.glb.bak rename to assets/models/Monologue Trees 7-21-23.glb diff --git a/assets/models/ico-sphere-dance.gltf.bak b/assets/models/ico-sphere-dance.gltf similarity index 100% rename from assets/models/ico-sphere-dance.gltf.bak rename to assets/models/ico-sphere-dance.gltf diff --git a/assets/models/inspect.glb.bak b/assets/models/inspect.glb similarity index 100% rename from assets/models/inspect.glb.bak rename to assets/models/inspect.glb diff --git a/assets/models/materials.blend b/assets/models/materials.blend index 144b13d..25ad295 100644 --- a/assets/models/materials.blend +++ b/assets/models/materials.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:034f798a326c865905ef61c3099a44bf8e4d541d34246be2a02b0c28bf9d7bd4 +oid sha256:1e90293cb3ed77a245603b9f1ef74514973a9472fa30a1339af28f4d9cd2e8b6 size 1320856 diff --git a/assets/models/materials.blend1 b/assets/models/materials.blend1 index a4f9a8a..144b13d 100644 --- a/assets/models/materials.blend1 +++ b/assets/models/materials.blend1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:541d68c95e4b5dd42d65d3bc33228a468a558c90464fe24ffc88ffa434cfef8b -size 1309552 +oid sha256:034f798a326c865905ef61c3099a44bf8e4d541d34246be2a02b0c28bf9d7bd4 +size 1320856 diff --git a/assets/models/materials.glb.bak b/assets/models/materials.glb similarity index 100% rename from assets/models/materials.glb.bak rename to assets/models/materials.glb diff --git a/assets/models/monkey-nod.glb.bak b/assets/models/monkey-nod.glb similarity index 100% rename from assets/models/monkey-nod.glb.bak rename to assets/models/monkey-nod.glb diff --git a/assets/models/torus-spin.glb.bak b/assets/models/torus-spin.glb similarity index 100% rename from assets/models/torus-spin.glb.bak rename to assets/models/torus-spin.glb diff --git a/bin/gltf-inspect.rs b/bin/gltf-inspect.rs index 4d30e95..64ca5b3 100644 --- a/bin/gltf-inspect.rs +++ b/bin/gltf-inspect.rs @@ -1,24 +1,5 @@ -use bevy::{ - core_pipeline::clear_color::ClearColorConfig, - gltf::{Gltf, GltfNode}, - input::{ - keyboard::KeyboardInput, - mouse::{MouseMotion, MouseWheel}, - ButtonState, - }, - pbr::CascadeShadowConfigBuilder, - prelude::*, - render::{ - camera::RenderTarget, - render_resource::{ - Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, - }, - view::VisibleEntities, - }, - window::PrimaryWindow, -}; -use bevy_rapier3d::{prelude::*, rapier::prelude::RigidBodyType}; -use monologue_trees::{debug::*, text::*}; +use bevy::{asset::AssetEvents, gltf::Gltf, prelude::*, utils::HashMap}; +use monologue_trees::debug::*; fn main() { App::new() @@ -32,586 +13,506 @@ fn main() { ..default() }), DebugInfoPlugin, - RapierPhysicsPlugin::::default(), - RapierDebugRenderPlugin::default(), )) - .add_event::() - .add_event::() - .add_systems(PreStartup, load_models) - .add_systems(Startup, (spawn_base_scene, spawn_base_ui)) + .add_event::() + .add_event::() + .init_resource::() + .add_systems(Startup, spawn_ui) .add_systems( Update, ( - spawn_models, - spawn_ui, + drag_and_drop, + loading, + reset_scene.before(spawn_scene), + spawn_scene.after(reset_scene), + wire_up_scenes, + control_scene, + wire_up_animations, control_animation, - rotate_model, - zoom_model, - scroll, - select, - manage_active, - inspect_nodes, - selection, + wire_up_cameras, + control_camera, + control_default_camera, ), ) .run(); } -/// -/// Stores GLTF handles for later use -#[derive(Resource)] -struct Models { - handles: Vec>, -} +#[derive(Component)] +struct SceneMarker; -/// -/// Marks GLTF model as inspectable #[derive(Component)] -struct Inspect; +struct MainCamera; #[derive(Component)] -struct SelectionUI; +struct MainUi; #[derive(Component)] -struct PreviewCamera; +struct UiTitle; #[derive(Component)] -struct ScrollingList; +struct InstructionsUi; #[derive(Component)] -struct Preview(Handle); +struct SceneSelectUi; + +#[derive(Component, PartialEq, Debug)] +struct SceneButton(Handle); #[derive(Component)] -struct Container; +struct AnimationSelectUi; + +#[derive(Component, PartialEq, Debug)] +struct AnimationButton(Handle); #[derive(Component)] -struct Active; - -/// -/// Event for managing which entities are tagged "Active" -#[derive(Event, Debug)] -struct ManageActive(Option); - -/// -/// Event for tracking which entities are selected -#[derive(Event, Debug)] -enum Selected { - Hovered(Entity), - Selected(Entity), +struct CameraSelectUi; + +#[derive(Component, PartialEq, Debug)] +struct CameraButton(Entity); + +#[derive(Resource, Default)] +struct Current { + gltf: Handle, + scenes: HashMap>, + animations: HashMap>, } -/// -/// 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 +#[derive(Event)] +struct ResetScene; + +#[derive(Event)] +struct SpawnScene(Handle); + +fn drag_and_drop( + mut events: EventReader, + server: Res, + mut current: ResMut, +) { + events .iter() - .map(|weak| weak.clone().typed::()) - .collect(); - // info!("Scene Handles: {:#?}", handles); - commands.insert_resource(Models { handles }); + .filter_map(|event| { + if let FileDragAndDrop::DroppedFile { path_buf, .. } = event { + info!("Drag+Drop file: {:?}", path_buf); + Some(path_buf) + } else { + None + } + }) + .map(|path_buf| server.load(path_buf.to_str().expect("PathBuf to str"))) + .for_each(|handle| current.gltf = handle.clone()); } -/// -/// Spawn base scene -fn spawn_base_scene(mut commands: Commands) { +fn spawn_ui(mut commands: Commands) { + // TODO: Warn no camera (hidden) + // TODO: Scene select container + // TODO: Animation Play/Pause Placeholder commands.spawn(( - Camera2dBundle { - camera_2d: Camera2d { - clear_color: ClearColorConfig::Custom(Color::BLACK), - }, - ..default() - }, - UiCameraConfig { ..default() }, - SelectionUI, + Camera3dBundle { ..default() }, + UiCameraConfig { show_ui: true }, + MainCamera, )); -} - -fn spawn_base_ui(mut commands: Commands) { commands .spawn(( NodeBundle { style: Style { - justify_content: JustifyContent::Center, - width: Val::Percent(90.0), - height: Val::Percent(90.0), - overflow: Overflow::clip(), + flex_wrap: FlexWrap::Wrap, + flex_direction: FlexDirection::Row, + justify_content: JustifyContent::SpaceAround, + width: Val::Percent(100.0), + height: Val::Percent(100.0), ..default() }, ..default() }, - SelectionUI, + MainUi, )) .with_children(|parent| { + // TOOD: Prompt to drag+drop Gltf/Gltf file parent.spawn(( - NodeBundle { - style: Style { - flex_wrap: FlexWrap::Wrap, - flex_direction: FlexDirection::Row, - justify_content: JustifyContent::SpaceAround, - width: Val::Auto, - height: Val::Auto, + TextBundle { + text: Text { + sections: vec![TextSection { + value: String::from("Drag and Drop .gltf/.glb file"), + style: TextStyle { + color: Color::WHITE, + ..default() + }, + }], ..default() }, ..default() }, - ScrollingList, + InstructionsUi, )); - }); -} - -/// -/// Spawn a loaded scene for inspection -/// TODO: Update/add/delete when files are updated -fn spawn_models( - mut commands: Commands, - mut scene_evr: EventReader>, - mut images: ResMut>, -) { - 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 { - width: 256, - height: 256, + parent + .spawn(( + NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, + ..default() + }, ..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: &[], + }, + SceneSelectUi, + )) + .with_children(|parent| { + parent.spawn(( + TextBundle::from_section("Scenes", TextStyle { ..default() }), + UiTitle, + )); + }); + parent + .spawn(( + NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, + ..default() }, ..default() - }; - - // Fill with zeroes - image.resize(size); - - 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, + }, + AnimationSelectUi, + )) + .with_children(|parent| { + parent.spawn(( + TextBundle::from_section("Animations", TextStyle { ..default() }), + UiTitle, + )); + }); + parent + .spawn(( + NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, ..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 - 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() + }, + CameraSelectUi, + )) + .with_children(|parent| { + parent.spawn(( + TextBundle::from_section("Cameras", TextStyle { ..default() }), + UiTitle, + )); + }); + }); +} + +fn wire_up_scenes( + mut events: EventReader>, + root: Query>, + root_children: Query<&Children, With>, + mut commands: Commands, + gltfs: Res>, + current: Res, + existing: Query<&SceneButton, Without>, +) { + events.iter().for_each(|event| { + // Cleanup unused buttons + root_children.iter().for_each(|children| { + // Iterate over UI children elements + children + .iter() + // Filter to just the buttons we want to despawn + .filter(|&child| { + // Check each child entity against the currently loaded scenes + // Note: !(_.any(...)) (not-any) + !((*current).scenes.iter().any(|(_, handle)| { + // If the current entitie's scene button corresponds to one of the scenes + // loaded, continue, otherwise + existing.get(*child).map_or(false, |scene_button| { + *scene_button == SceneButton(handle.cast_weak::()) + }) + })) + }) + .for_each(|&e| { + commands.entity(e).despawn_recursive(); + }); + }); + + // Add buttons + match event { + AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { + let (name, _) = gltfs + .get(¤t.gltf) + .expect("Load current gltf") + .named_scenes + .iter() + .find(|(_, h)| h == &handle) + .expect("Find this scene"); + + let mut ui = commands.entity(root.single()); + + ui.with_children(|parent| { + parent + .spawn(( + ButtonBundle { + background_color: BackgroundColor(Color::NONE), ..default() }, - UiCameraConfig { show_ui: false }, - PreviewCamera, - )); - - // Spawn window camera - builder.spawn(( - Camera3dBundle { - camera_3d: Camera3d { - clear_color: ClearColorConfig::Custom(Color::WHITE), + SceneButton(handle.clone()), + )) + .with_children(|parent| { + parent.spawn(TextBundle { + text: Text { + sections: vec![TextSection { + value: name.clone(), + style: TextStyle { + color: Color::WHITE, + ..default() + }, + }], ..default() }, - camera: Camera { - is_active: false, - ..default() - }, - transform: camera_location, ..default() - }, - Inspect, - Preview(preview_image_handle.clone()), - )); - - builder.spawn(( - SceneBundle { - scene: handle.clone(), - ..default() - }, - RigidBody::KinematicPositionBased, - Collider::cylinder(1.0, 1.0), - Inspect, - )); - }); - } - AssetEvent::Removed { .. } => { - todo!("Remove deleted scene") - } - AssetEvent::Modified { .. } => { - todo!("Update modified scene") + }); + }); + }); } + AssetEvent::Removed { .. } => warn!("IGNORED Remove scene in list"), } - } + }); +} + +/// Translate UI button presses into scene spawn events +fn control_scene( + mut events: EventWriter, + interactions: Query<(&Interaction, &SceneButton), (Changed, With