morph_targets/
morph_targets.rs

1//! Play an animation with morph targets.
2//!
3//! Also illustrates how to read morph target names in `name_morphs`.
4
5use bevy::{prelude::*, scene::SceneInstanceReady};
6use std::f32::consts::PI;
7
8const GLTF_PATH: &str = "models/animated/MorphStressTest.gltf";
9
10fn main() {
11    App::new()
12        .add_plugins(DefaultPlugins)
13        .insert_resource(AmbientLight {
14            brightness: 150.0,
15            ..default()
16        })
17        .add_systems(Startup, setup)
18        .add_systems(Update, name_morphs)
19        .run();
20}
21
22#[derive(Component)]
23struct AnimationToPlay {
24    graph_handle: Handle<AnimationGraph>,
25    index: AnimationNodeIndex,
26}
27
28fn setup(
29    mut commands: Commands,
30    asset_server: Res<AssetServer>,
31    mut graphs: ResMut<Assets<AnimationGraph>>,
32) {
33    let (graph, index) = AnimationGraph::from_clip(
34        asset_server.load(GltfAssetLabel::Animation(2).from_asset(GLTF_PATH)),
35    );
36
37    commands
38        .spawn((
39            AnimationToPlay {
40                graph_handle: graphs.add(graph),
41                index,
42            },
43            SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(GLTF_PATH))),
44        ))
45        .observe(play_animation_when_ready);
46
47    commands.spawn((
48        DirectionalLight::default(),
49        Transform::from_rotation(Quat::from_rotation_z(PI / 2.0)),
50    ));
51
52    commands.spawn((
53        Camera3d::default(),
54        Transform::from_xyz(3.0, 2.1, 10.2).looking_at(Vec3::ZERO, Vec3::Y),
55    ));
56}
57
58fn play_animation_when_ready(
59    scene_ready: On<SceneInstanceReady>,
60    mut commands: Commands,
61    children: Query<&Children>,
62    animations_to_play: Query<&AnimationToPlay>,
63    mut players: Query<&mut AnimationPlayer>,
64) {
65    if let Ok(animation_to_play) = animations_to_play.get(scene_ready.entity) {
66        for child in children.iter_descendants(scene_ready.entity) {
67            if let Ok(mut player) = players.get_mut(child) {
68                player.play(animation_to_play.index).repeat();
69
70                commands
71                    .entity(child)
72                    .insert(AnimationGraphHandle(animation_to_play.graph_handle.clone()));
73            }
74        }
75    }
76}
77
78/// Whenever a mesh asset is loaded, print the name of the asset and the names
79/// of its morph targets.
80fn name_morphs(
81    asset_server: Res<AssetServer>,
82    mut events: MessageReader<AssetEvent<Mesh>>,
83    meshes: Res<Assets<Mesh>>,
84) {
85    for event in events.read() {
86        if let AssetEvent::<Mesh>::Added { id } = event
87            && let Some(path) = asset_server.get_path(*id)
88            && let Some(mesh) = meshes.get(*id)
89            && let Some(names) = mesh.morph_target_names()
90        {
91            info!("Morph target names for {path:?}:");
92
93            for name in names {
94                info!("  {name}");
95            }
96        }
97    }
98}