add support for loading animations in scene_viewer example (#4403)

The `scene_viewer` example can be used to load and display glTF scenes. This PR also adds the ability to play animations in the file and cycle through them.

https://user-images.githubusercontent.com/22177966/161443011-0365c7de-c49e-44c4-b0f7-44330a015a4f.mp4
This commit is contained in:
Jakob Hellermann 2022-04-03 19:35:05 +00:00
parent f90da74e32
commit ac29cbecf7

View file

@ -1,5 +1,6 @@
use bevy::{ use bevy::{
asset::{AssetServerSettings, LoadState}, asset::{AssetServerSettings, LoadState},
gltf::Gltf,
input::mouse::MouseMotion, input::mouse::MouseMotion,
math::Vec3A, math::Vec3A,
prelude::*, prelude::*,
@ -26,6 +27,9 @@ Controls:
5/6 - decrease/increase shadow projection width 5/6 - decrease/increase shadow projection width
7/8 - decrease/increase shadow projection height 7/8 - decrease/increase shadow projection height
9/0 - decrease/increase shadow projection near/far 9/0 - decrease/increase shadow projection near/far
Space - Play/Pause animation
Enter - Cycle through animations
" "
); );
App::new() App::new()
@ -48,11 +52,14 @@ Controls:
.add_system(check_camera_controller) .add_system(check_camera_controller)
.add_system(update_lights) .add_system(update_lights)
.add_system(camera_controller.after(check_camera_controller)) .add_system(camera_controller.after(check_camera_controller))
.add_system(start_animation)
.add_system(keyboard_animation_control)
.run(); .run();
} }
struct SceneHandle { struct SceneHandle {
handle: Handle<Scene>, handle: Handle<Scene>,
animations: Vec<Handle<AnimationClip>>,
instance_id: Option<InstanceId>, instance_id: Option<InstanceId>,
is_loaded: bool, is_loaded: bool,
has_camera: bool, has_camera: bool,
@ -60,21 +67,13 @@ struct SceneHandle {
} }
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let scene_path = std::env::args().nth(1).map_or_else( let scene_path = std::env::args()
|| "assets/models/FlightHelmet/FlightHelmet.gltf#Scene0".to_string(), .nth(1)
|s| { .unwrap_or_else(|| "assets/models/FlightHelmet/FlightHelmet.gltf".to_string());
if let Some(index) = s.find("#Scene") {
if index + 6 < s.len() && s[index + 6..].chars().all(char::is_numeric) {
return s;
}
return format!("{}#Scene0", &s[..index]);
}
format!("{}#Scene0", s)
},
);
info!("Loading {}", scene_path); info!("Loading {}", scene_path);
commands.insert_resource(SceneHandle { commands.insert_resource(SceneHandle {
handle: asset_server.load(&scene_path), handle: asset_server.load(&scene_path),
animations: Vec::new(),
instance_id: None, instance_id: None,
is_loaded: false, is_loaded: false,
has_camera: false, has_camera: false,
@ -85,12 +84,17 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
fn scene_load_check( fn scene_load_check(
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
mut scenes: ResMut<Assets<Scene>>, mut scenes: ResMut<Assets<Scene>>,
gltf_assets: ResMut<Assets<Gltf>>,
mut scene_handle: ResMut<SceneHandle>, mut scene_handle: ResMut<SceneHandle>,
mut scene_spawner: ResMut<SceneSpawner>, mut scene_spawner: ResMut<SceneSpawner>,
) { ) {
match scene_handle.instance_id { match scene_handle.instance_id {
None if asset_server.get_load_state(&scene_handle.handle) == LoadState::Loaded => { None => {
if let Some(scene) = scenes.get_mut(&scene_handle.handle) { if asset_server.get_load_state(&scene_handle.handle) == LoadState::Loaded {
let gltf = gltf_assets.get(&scene_handle.handle).unwrap();
let gltf_scene_handle = gltf.scenes.first().expect("glTF file contains no scenes!");
let scene = scenes.get_mut(gltf_scene_handle).unwrap();
let mut query = scene let mut query = scene
.world .world
.query::<(Option<&Camera2d>, Option<&Camera3d>)>(); .query::<(Option<&Camera2d>, Option<&Camera3d>)>();
@ -111,7 +115,21 @@ fn scene_load_check(
}); });
scene_handle.instance_id = scene_handle.instance_id =
Some(scene_spawner.spawn(scene_handle.handle.clone_weak())); Some(scene_spawner.spawn(gltf_scene_handle.clone_weak()));
scene_handle.animations = gltf.animations.clone();
if !scene_handle.animations.is_empty() {
info!(
"Found {} animation{}",
scene_handle.animations.len(),
if scene_handle.animations.len() == 1 {
""
} else {
"s"
}
);
}
info!("Spawning scene..."); info!("Spawning scene...");
} }
} }
@ -121,7 +139,49 @@ fn scene_load_check(
scene_handle.is_loaded = true; scene_handle.is_loaded = true;
} }
} }
_ => {} Some(_) => {}
}
}
fn start_animation(
mut player: Query<&mut AnimationPlayer>,
mut done: Local<bool>,
scene_handle: Res<SceneHandle>,
) {
if !*done {
if let Ok(mut player) = player.get_single_mut() {
if let Some(animation) = scene_handle.animations.first() {
player.play(animation.clone_weak()).repeat();
*done = true;
}
}
}
}
fn keyboard_animation_control(
keyboard_input: Res<Input<KeyCode>>,
mut animation_player: Query<&mut AnimationPlayer>,
scene_handle: Res<SceneHandle>,
mut current_animation: Local<usize>,
) {
if scene_handle.animations.is_empty() {
return;
}
if let Ok(mut player) = animation_player.get_single_mut() {
if keyboard_input.just_pressed(KeyCode::Space) {
if player.is_paused() {
player.resume();
} else {
player.pause();
}
}
if keyboard_input.just_pressed(KeyCode::Return) {
*current_animation = (*current_animation + 1) % scene_handle.animations.len();
player
.play(scene_handle.animations[*current_animation].clone_weak())
.repeat();
}
} }
} }