mirror of
https://github.com/bevyengine/bevy
synced 2024-11-14 00:47:32 +00:00
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:
parent
f90da74e32
commit
ac29cbecf7
1 changed files with 76 additions and 16 deletions
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue