Basic gltf inspector, WIP
It has the basics of a GLTF inspector, in that it gltf files and you can inspect a scene... but only the first scene. Need to add more functionality like previews and selection. Each scene has it's own camera and light, so we can trivially (fingers crossed) preview those with a render target of image and then pipe that to a UI element. That will happen... tomorrow.main
commit
c82a38dbc8
@ -0,0 +1,4 @@
|
|||||||
|
*.blend filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.blend1 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gltf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||||
@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "monologue-trees"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "gltf-inspect"
|
||||||
|
path = "bin/gltf-inspect.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = "0.10"
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
# Inspect Model
|
||||||
|
|
||||||
|
- [ ] Load previews on startup.
|
||||||
|
- [ ] Click to "Open" a model.
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,211 @@
|
|||||||
|
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<Handle<Gltf>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// 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<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 {
|
||||||
|
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<KeyboardInput>,
|
||||||
|
mut ui_camera: Query<&mut Camera, (With<SelectionUI>, Without<Inspect>)>,
|
||||||
|
mut scene_camera: Query<&mut Camera, (With<Inspect>, Without<SelectionUI>)>,
|
||||||
|
mut scene_objects: Query<&mut ComputedVisibility, With<Inspect>>,
|
||||||
|
) {
|
||||||
|
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<AssetEvent<Scene>>,
|
||||||
|
) {
|
||||||
|
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<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(
|
||||||
|
mut wheel_evr: EventReader<MouseWheel>,
|
||||||
|
mut transforms: Query<&mut Transform, With<Inspect>>,
|
||||||
|
) {
|
||||||
|
for ev in wheel_evr.iter() {
|
||||||
|
for mut transform in transforms.iter_mut() {
|
||||||
|
let scale = (Vec3::ONE * ev.y) / 100.0;
|
||||||
|
transform.scale += scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
nightly
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
title: "Monologue Trees".into(),
|
||||||
|
resolution: (640., 480.).into(),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue