You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
6.5 KiB
Rust
212 lines
6.5 KiB
Rust
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;
|
|
}
|
|
}
|
|
}
|