animated_mesh_control/
animated_mesh_control.rs1use std::{f32::consts::PI, time::Duration};
4
5use bevy::{
6 animation::RepeatAnimation, light::CascadeShadowConfigBuilder, prelude::*,
7 world_serialization::WorldInstanceReady,
8};
9
10const FOX_PATH: &str = "models/animated/Fox.glb";
11
12fn main() {
13 App::new()
14 .insert_resource(GlobalAmbientLight {
15 color: Color::WHITE,
16 brightness: 2000.,
17 ..default()
18 })
19 .add_plugins(DefaultPlugins)
20 .add_systems(Startup, setup)
21 .add_systems(
22 Update,
23 spawn_fox_asset_when_ready.run_if(not(resource_exists::<Animations>)),
24 )
25 .add_systems(
26 Update,
27 keyboard_control.run_if(resource_exists::<Animations>),
28 )
29 .run();
30}
31
32#[derive(Resource)]
33struct Animations {
34 animations: Vec<AnimationNodeIndex>,
35 graph_handle: Handle<AnimationGraph>,
36}
37
38#[derive(Resource)]
39struct Fox(Handle<Gltf>);
40
41fn setup(
42 mut commands: Commands,
43 asset_server: Res<AssetServer>,
44 mut meshes: ResMut<Assets<Mesh>>,
45 mut materials: ResMut<Assets<StandardMaterial>>,
46) {
47 commands.insert_resource(Fox(asset_server.load(FOX_PATH)));
50
51 commands.spawn((
53 Camera3d::default(),
54 Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
55 ));
56
57 commands.spawn((
59 Mesh3d(meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0))),
60 MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
61 ));
62
63 commands.spawn((
65 Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
66 DirectionalLight {
67 shadow_maps_enabled: true,
68 ..default()
69 },
70 CascadeShadowConfigBuilder {
71 first_cascade_far_bound: 200.0,
72 maximum_distance: 400.0,
73 ..default()
74 }
75 .build(),
76 ));
77
78 commands.spawn((
80 Text::new(concat!(
81 "space: play / pause\n",
82 "up / down: playback speed\n",
83 "left / right: seek\n",
84 "1-3: play N times\n",
85 "L: loop forever\n",
86 "return: change animation\n",
87 )),
88 Node {
89 position_type: PositionType::Absolute,
90 top: px(12),
91 left: px(12),
92 ..default()
93 },
94 ));
95}
96
97fn spawn_fox_asset_when_ready(
98 mut commands: Commands,
99 fox_handle: Res<Fox>,
100 asset_server: Res<AssetServer>,
101 gltfs: Res<Assets<Gltf>>,
102 mut graphs: ResMut<Assets<AnimationGraph>>,
103) {
104 if !asset_server.is_loaded_with_dependencies(&fox_handle.0) {
105 return;
107 }
108
109 let fox = gltfs
110 .get(&fox_handle.0)
111 .expect("a loaded asset should exist in the glTF assets collection");
112
113 let (graph, node_indices) = AnimationGraph::from_clips([
115 fox.named_animations["Run"].clone(),
116 fox.named_animations["Walk"].clone(),
117 fox.named_animations["Survey"].clone(),
118 ]);
119
120 let graph_handle = graphs.add(graph);
123 commands.insert_resource(Animations {
124 animations: node_indices,
125 graph_handle,
126 });
127
128 commands
130 .spawn(WorldAssetRoot(
131 fox.default_scene
132 .clone()
133 .expect("a default scene exists in this file"),
134 ))
135 .observe(setup_scene);
136}
137
138fn setup_scene(
143 _ready: On<WorldInstanceReady>,
144 mut commands: Commands,
145 animations: Res<Animations>,
146 player: Single<(Entity, &mut AnimationPlayer)>,
147) {
148 let (entity, mut player) = player.into_inner();
149 let mut transitions = AnimationTransitions::new();
150
151 transitions
156 .play(&mut player, animations.animations[0], Duration::ZERO)
157 .repeat();
158
159 commands
160 .entity(entity)
161 .insert(AnimationGraphHandle(animations.graph_handle.clone()))
162 .insert(transitions);
163}
164
165fn keyboard_control(
166 keyboard_input: Res<ButtonInput<KeyCode>>,
167 mut animation_players: Query<(&mut AnimationPlayer, &mut AnimationTransitions)>,
168 animations: Res<Animations>,
169 mut current_animation: Local<usize>,
170) {
171 for (mut player, mut transitions) in &mut animation_players {
172 let Some((&playing_animation_index, _)) = player.playing_animations().next() else {
173 continue;
174 };
175
176 if keyboard_input.just_pressed(KeyCode::Space) {
177 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
178 if playing_animation.is_paused() {
179 playing_animation.resume();
180 } else {
181 playing_animation.pause();
182 }
183 }
184
185 if keyboard_input.just_pressed(KeyCode::ArrowUp) {
186 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
187 let speed = playing_animation.speed();
188 playing_animation.set_speed(speed * 1.2);
189 }
190
191 if keyboard_input.just_pressed(KeyCode::ArrowDown) {
192 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
193 let speed = playing_animation.speed();
194 playing_animation.set_speed(speed * 0.8);
195 }
196
197 if keyboard_input.just_pressed(KeyCode::ArrowLeft) {
198 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
199 let elapsed = playing_animation.seek_time();
200 playing_animation.seek_to(elapsed - 0.1);
201 }
202
203 if keyboard_input.just_pressed(KeyCode::ArrowRight) {
204 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
205 let elapsed = playing_animation.seek_time();
206 playing_animation.seek_to(elapsed + 0.1);
207 }
208
209 if keyboard_input.just_pressed(KeyCode::Enter) {
210 *current_animation = (*current_animation + 1) % animations.animations.len();
211
212 transitions
213 .play(
214 &mut player,
215 animations.animations[*current_animation],
216 Duration::from_millis(250),
217 )
218 .repeat();
219 }
220
221 if keyboard_input.just_pressed(KeyCode::Digit1) {
222 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
223 playing_animation
224 .set_repeat(RepeatAnimation::Count(1))
225 .replay();
226 }
227
228 if keyboard_input.just_pressed(KeyCode::Digit2) {
229 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
230 playing_animation
231 .set_repeat(RepeatAnimation::Count(2))
232 .replay();
233 }
234
235 if keyboard_input.just_pressed(KeyCode::Digit3) {
236 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
237 playing_animation
238 .set_repeat(RepeatAnimation::Count(3))
239 .replay();
240 }
241
242 if keyboard_input.just_pressed(KeyCode::KeyL) {
243 let playing_animation = player.animation_mut(playing_animation_index).unwrap();
244 playing_animation.set_repeat(RepeatAnimation::Forever);
245 }
246 }
247}