//! Plays animations from a skinned glTF.
use std::f32::consts::PI;
use std::time::Duration;
use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
use bevy::window::{CursorGrabMode, PresentMode, WindowLevel, WindowTheme};
use bevy::{animation::RepeatAnimation, pbr::CascadeShadowConfigBuilder, prelude::*};
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "I am a window!".into(),
resolution: (1024., 576.).into(),
present_mode: PresentMode::AutoVsync,
canvas: Some("#bevy-portal".to_string()),
// Tells wasm to resize the window according to the available canvas
fit_canvas_to_parent: true,
// Tells wasm not to override default event handling, like F5, Ctrl+R etc.
prevent_default_event_handling: false,
window_theme: Some(WindowTheme::Dark),
enabled_buttons: bevy::window::EnabledButtons {
maximize: false,
..Default::default()
},
// This will spawn an invisible window
// The window will be made visible in the make_visible() system after 3 frames.
// This is useful when you want to avoid the white window that shows up before the GPU is ready to render the app.
visible: true,
..default()
}),
..default()
}),
//LogDiagnosticsPlugin::default(),
//FrameTimeDiagnosticsPlugin,
))
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0,
})
.add_systems(Startup, setup)
.add_systems(
Update,
(setup_scene_once_loaded, keyboard_animation_control),
)
.add_systems(Update, text_update_system)
.run();
}
#[derive(Resource)]
struct Animations(Vec>);
#[derive(Resource)]
struct CurrentAnimation {
id: usize,
}
fn setup(
mut commands: Commands,
asset_server: Res,
mut meshes: ResMut>,
mut materials: ResMut>,
) {
// Insert a resource with the current scene information
commands.insert_resource(Animations(vec![
asset_server.load("idle/idle.glb#Animation0"),
asset_server.load("idle/idle.glb#Animation1"),
asset_server.load("idle/idle.glb#Animation2"),
]));
commands.insert_resource(CurrentAnimation { id: 0_usize });
// Camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(100.0, 150.0, 350.0)
.looking_at(Vec3::new(0.0, 50.0, 0.0), Vec3::Y),
..default()
});
commands.spawn((
// Create a TextBundle that has a Text with a list of sections.
TextBundle::from_sections([TextSection::new(
"current animation: ",
TextStyle {
// This font is loaded and will be used instead of the default font.
font_size: 20.0,
..default()
},
)])
.with_text_alignment(TextAlignment::Right)
.with_style(Style {
position_type: PositionType::Absolute,
bottom: Val::Px(5.0),
right: Val::Px(5.0),
..default()
}),
));
// Plane
//commands.spawn(PbrBundle {
// mesh: meshes.add(shape::Plane::from_size(500000.0).into()),
// material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
// ..default()
//});
// Light
commands.spawn(DirectionalLightBundle {
transform: Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
directional_light: DirectionalLight {
shadows_enabled: true,
..default()
},
cascade_shadow_config: CascadeShadowConfigBuilder {
first_cascade_far_bound: 200.0,
maximum_distance: 400.0,
..default()
}
.into(),
..default()
});
// Fox
commands.spawn(SceneBundle {
scene: asset_server.load("idle/idle.glb#Scene0"),
..default()
});
/*
println!("Animation controls:");
println!(" - spacebar: play / pause");
println!(" - arrow up / down: speed up / slow down animation playback");
println!(" - arrow left / right: seek backward / forward");
println!(" - digit 1 / 3 / 5: play the animation times");
println!(" - L: loop the animation forever");
println!(" - return: change animation");
*/
}
// Once the scene is loaded, start the animation
fn setup_scene_once_loaded(
animations: Res,
mut players: Query<&mut AnimationPlayer, Added>,
) {
for mut player in &mut players {
player.play(animations.0[0].clone_weak()).repeat();
}
}
fn keyboard_animation_control(
keyboard_input: Res>,
mut animation_players: Query<&mut AnimationPlayer>,
animations: Res,
mut current_animation: ResMut,
mut query: Query<&mut Text>,
) {
for mut player in &mut animation_players {
if keyboard_input.just_pressed(KeyCode::Space) {
if player.is_paused() {
player.resume();
} else {
player.pause();
}
}
let one_second = Duration::new(0, 200000000);
if keyboard_input.just_pressed(KeyCode::Up) && current_animation.id != 1_usize {
let speed = player.speed();
//player.set_speed(speed * 1.2);
//current_animation = Res<1_usize>;
player
.start_with_transition(animations.0[1].clone_weak(), one_second)
.repeat();
current_animation.id = 1;
}
if keyboard_input.just_pressed(KeyCode::Down) && current_animation.id != 2_usize {
let speed = player.speed();
//player.set_speed(speed * 0.8);
//*current_animation = 2_usize;
player
.start_with_transition(animations.0[2].clone_weak(), one_second)
.repeat();
current_animation.id = 2;
}
if keyboard_input.just_pressed(KeyCode::Left) && current_animation.id != 0_usize {
let elapsed = player.seek_time();
//player.seek_to(elapsed - 0.1);
player
.start_with_transition(animations.0[0].clone_weak(), one_second)
.repeat();
//*current_animation = 0_usize;
current_animation.id = 0;
}
/*
if keyboard_input.just_pressed(KeyCode::Right) {
let elapsed = player.seek_time();
player.seek_to(elapsed + 0.1);
}
if keyboard_input.just_pressed(KeyCode::Return) {
*current_animation = (*current_animation + 1) % animations.0.len();
player
.play_with_transition(
animations.0[*current_animation].clone_weak(),
Duration::from_millis(250),
)
.repeat();
}
if keyboard_input.just_pressed(KeyCode::Key1) {
player.set_repeat(RepeatAnimation::Count(1));
player.replay();
}
if keyboard_input.just_pressed(KeyCode::Key3) {
player.set_repeat(RepeatAnimation::Count(3));
player.replay();
}
if keyboard_input.just_pressed(KeyCode::Key5) {
player.set_repeat(RepeatAnimation::Count(5));
player.replay();
}
if keyboard_input.just_pressed(KeyCode::L) {
player.set_repeat(RepeatAnimation::Forever);
player.replay();
}
*/
}
}
fn text_update_system(
mut commands: Commands,
current_animation: Res,
mut query: Query<&mut Text>,
) {
for mut text in &mut query {
//println!("{:?}", current_animation.id);
let id = current_animation.id.to_string();
text.sections[0].value = format!("current animation {}", id);
}
}