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
Elijah Voigt 2 years ago
commit c82a38dbc8

4
.gitattributes vendored

@ -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

1
.gitignore vendored

@ -0,0 +1 @@
/target

3447
Cargo.lock generated

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.

BIN
assets/models/ico-sphere-dance.blend (Stored with Git LFS)

Binary file not shown.

BIN
assets/models/ico-sphere-dance.blend1 (Stored with Git LFS)

Binary file not shown.

BIN
assets/models/ico-sphere-dance.gltf (Stored with Git LFS)

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…
Cancel
Save