Support Warning
WebGPU is currently only supported on Chrome starting with version 113, and only on desktop. If they don't work on your configuration, you can check the WebGL2 examples
here.
Support for WebGPU in Bevy hasn't been released yet, this example has been compiled using the main branch.
use std::f32::consts::PI;
use std::time::Duration;
use bevy::pbr::CascadeShadowConfigBuilder;
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0,
})
.add_systems(Startup, setup)
.add_systems(
Update,
(setup_scene_once_loaded, keyboard_animation_control),
)
.run();
}
#[derive(Resource)]
struct Animations(Vec<Handle<AnimationClip>>);
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.insert_resource(Animations(vec![
asset_server.load("models/animated/Fox.glb#Animation2"),
asset_server.load("models/animated/Fox.glb#Animation1"),
asset_server.load("models/animated/Fox.glb#Animation0"),
]));
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(100.0, 100.0, 150.0)
.looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
..default()
});
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()
});
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()
});
commands.spawn(SceneBundle {
scene: asset_server.load("models/animated/Fox.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!(" - return: change animation");
}
fn setup_scene_once_loaded(
animations: Res<Animations>,
mut player: Query<&mut AnimationPlayer>,
mut done: Local<bool>,
) {
if !*done {
if let Ok(mut player) = player.get_single_mut() {
player.play(animations.0[0].clone_weak()).repeat();
*done = true;
}
}
}
fn keyboard_animation_control(
keyboard_input: Res<Input<KeyCode>>,
mut animation_player: Query<&mut AnimationPlayer>,
animations: Res<Animations>,
mut current_animation: Local<usize>,
) {
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::Up) {
let speed = player.speed();
player.set_speed(speed * 1.2);
}
if keyboard_input.just_pressed(KeyCode::Down) {
let speed = player.speed();
player.set_speed(speed * 0.8);
}
if keyboard_input.just_pressed(KeyCode::Left) {
let elapsed = player.elapsed();
player.set_elapsed(elapsed - 0.1);
}
if keyboard_input.just_pressed(KeyCode::Right) {
let elapsed = player.elapsed();
player.set_elapsed(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();
}
}
}