Skip to main content

fyrox_impl/scene/animation/
absm.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Animation blending state machine is a node that takes multiple animations from an animation player and
22//! mixes them in arbitrary way into one animation. See [`AnimationBlendingStateMachine`] docs for more info.
23
24use crate::scene::node::constructor::NodeConstructor;
25use crate::{
26    core::{
27        math::aabb::AxisAlignedBoundingBox,
28        pool::Handle,
29        reflect::prelude::*,
30        type_traits::prelude::*,
31        uuid::{uuid, Uuid},
32        variable::InheritableVariable,
33        visitor::prelude::*,
34    },
35    scene::{
36        animation::prelude::*,
37        base::{Base, BaseBuilder},
38        graph::Graph,
39        node::{Node, NodeTrait, UpdateContext},
40        Scene,
41    },
42};
43use fyrox_graph::constructor::ConstructorProvider;
44use fyrox_graph::SceneGraph;
45use std::ops::{Deref, DerefMut};
46
47/// Scene specific root motion settings.
48pub type RootMotionSettings = crate::generic_animation::RootMotionSettings<Handle<Node>>;
49/// Scene specific animation pose node.
50pub type PoseNode = crate::generic_animation::machine::PoseNode<Handle<Node>>;
51/// Scene specific animation pose node.
52pub type PlayAnimation = crate::generic_animation::machine::node::play::PlayAnimation<Handle<Node>>;
53/// Scene specific animation blending state machine BlendAnimations node.
54pub type BlendAnimations =
55    crate::generic_animation::machine::node::blend::BlendAnimations<Handle<Node>>;
56/// Scene specific animation blending state machine BlendAnimationsByIndex node.
57pub type BlendAnimationsByIndex =
58    crate::generic_animation::machine::node::blend::BlendAnimationsByIndex<Handle<Node>>;
59/// Scene specific animation blending state machine BlendPose node.
60pub type BlendPose = crate::generic_animation::machine::node::blend::BlendPose<Handle<Node>>;
61/// Scene specific animation blending state machine IndexedBlendInput node.
62pub type IndexedBlendInput =
63    crate::generic_animation::machine::node::blend::IndexedBlendInput<Handle<Node>>;
64/// Scene specific animation blending state machine BlendSpace node.
65pub type BlendSpace = crate::generic_animation::machine::node::blendspace::BlendSpace<Handle<Node>>;
66/// Scene specific animation blending state machine blend space point.
67pub type BlendSpacePoint =
68    crate::generic_animation::machine::node::blendspace::BlendSpacePoint<Handle<Node>>;
69/// Scene specific animation blending state machine layer mask.
70pub type LayerMask = crate::generic_animation::machine::mask::LayerMask<Handle<Node>>;
71/// Scene specific animation blending state machine layer mask.
72pub type Event = crate::generic_animation::machine::event::Event<Handle<Node>>;
73/// Scene specific animation blending state machine.
74pub type Machine = crate::generic_animation::machine::Machine<Handle<Node>>;
75/// Scene specific animation blending state machine layer.
76pub type MachineLayer = crate::generic_animation::machine::MachineLayer<Handle<Node>>;
77/// Scene specific animation blending state machine transition.
78pub type Transition = crate::generic_animation::machine::transition::Transition<Handle<Node>>;
79/// Scene specific animation blending state machine state.
80pub type State = crate::generic_animation::machine::state::State<Handle<Node>>;
81/// Scene specific animation blending state machine base pose node.
82pub type BasePoseNode = crate::generic_animation::machine::node::BasePoseNode<Handle<Node>>;
83/// Scene specific animation blending state machine state action.
84pub type StateAction = crate::generic_animation::machine::state::StateAction<Handle<Node>>;
85/// Scene specific animation blending state machine state action wrapper.
86pub type StateActionWrapper =
87    crate::generic_animation::machine::state::StateActionWrapper<Handle<Node>>;
88/// Scene specific animation blending state machine logic node.
89pub type LogicNode = crate::generic_animation::machine::transition::LogicNode<Handle<Node>>;
90/// Scene specific animation blending state machine And logic node.
91pub type AndNode = crate::generic_animation::machine::transition::AndNode<Handle<Node>>;
92/// Scene specific animation blending state machine Xor logic nde.
93pub type XorNode = crate::generic_animation::machine::transition::XorNode<Handle<Node>>;
94/// Scene specific animation blending state machine Or logic node.
95pub type OrNode = crate::generic_animation::machine::transition::OrNode<Handle<Node>>;
96/// Scene specific animation blending state machine Not logic node.
97pub type NotNode = crate::generic_animation::machine::transition::NotNode<Handle<Node>>;
98/// Scene specific animation blending state machine layer animation events collection.
99pub type LayerAnimationEventsCollection =
100    crate::generic_animation::machine::layer::LayerAnimationEventsCollection<Handle<Node>>;
101/// Scene specific animation blending state machine animation events source.
102pub type AnimationEventsSource =
103    crate::generic_animation::machine::layer::AnimationEventsSource<Handle<Node>>;
104
105/// Standard prelude for animation blending state machine, that contains all most commonly used types and traits.
106pub mod prelude {
107    pub use super::{
108        AndNode, AnimationBlendingStateMachine, AnimationBlendingStateMachineBuilder,
109        AnimationEventsSource, BasePoseNode, BlendAnimations, BlendAnimationsByIndex, BlendPose,
110        BlendSpace, BlendSpacePoint, Event, IndexedBlendInput, LayerAnimationEventsCollection,
111        LayerMask, LogicNode, Machine, MachineLayer, NotNode, OrNode, PlayAnimation, PoseNode,
112        RootMotionSettings, State, StateAction, StateActionWrapper, Transition, XorNode,
113    };
114    pub use crate::generic_animation::machine::{
115        node::AnimationEventCollectionStrategy,
116        parameter::{Parameter, ParameterContainer, ParameterDefinition, PoseWeight},
117    };
118}
119
120/// Extension trait for [`LayerMask`].
121pub trait LayerMaskExt {
122    /// Creates a layer mask for every descendant node starting from specified `root` (included). It could
123    /// be useful if you have an entire node hierarchy (for example, lower part of a body) that needs to
124    /// be filtered out.
125    fn from_hierarchy(graph: &Graph, root: Handle<Node>) -> Self;
126}
127
128impl LayerMaskExt for LayerMask {
129    fn from_hierarchy(graph: &Graph, root: Handle<Node>) -> Self {
130        Self::from(
131            graph
132                .traverse_iter(root)
133                .map(|(handle, _)| handle)
134                .collect::<Vec<_>>(),
135        )
136    }
137}
138
139/// Animation blending state machine (ABSM) is a node that takes multiple animations from an animation player and
140/// mixes them in arbitrary way into one animation. Usually, ABSMs are used to animate humanoid characters in games,
141/// by blending multiple states with one or more animations. More info about state machines can be found in
142/// [`Machine`] docs.
143///
144/// # Important notes
145///
146/// The node does **not** contain any animations, instead it just takes animations from an animation
147/// player node and mixes them.
148///
149/// # Example
150///
151/// You should always prefer using the editor (FyroxEd) to create animation blending state machines, for many cases
152/// creating machines by code is quite slow and hard to debug. The editor shows all the states, nodes, transitions and
153/// helps you to quickly debug your ABSMs. However, if you need to create a state machine from code (for example, for
154/// procedural animations), then the following example is for you.
155///
156/// ```rust
157/// # use fyrox_impl::{
158/// #     core::pool::Handle,
159/// #     scene::{
160/// #         animation::{absm::prelude::*, prelude::*},
161/// #         base::BaseBuilder,
162/// #         graph::Graph,
163/// #         node::Node,
164/// #     },
165/// # };
166/// # use fyrox_graph::SceneGraph;
167///
168/// fn create_walk_idle_state_machine(
169///     animation_player_handle: Handle<AnimationPlayer>,
170///     graph: &mut Graph,
171/// ) -> Handle<AnimationBlendingStateMachine> {
172///     // Find idle and run animations first.
173///     let animation_player = graph
174///         .try_get(animation_player_handle)
175///         .unwrap();
176///     let idle_animation = animation_player
177///         .animations()
178///         .find_by_name_ref("Idle")
179///         .unwrap()
180///         .0;
181///     let run_animation = animation_player
182///         .animations()
183///         .find_by_name_ref("Run")
184///         .unwrap()
185///         .0;
186///
187///     // Create state machine.
188///     let mut machine = Machine::new();
189///
190///     let root_layer = machine.layers_mut().first_mut().unwrap();
191///
192///     let idle_pose = root_layer.add_node(PoseNode::make_play_animation(idle_animation));
193///     let idle_state = root_layer.add_state(State::new("Idle", idle_pose));
194///
195///     let run_pose = root_layer.add_node(PoseNode::make_play_animation(run_animation));
196///     let run_state = root_layer.add_state(State::new("Idle", run_pose));
197///
198///     root_layer.add_transition(Transition::new(
199///         "Idle -> Run",
200///         idle_state,
201///         run_state,
202///         0.3,
203///         "Run",
204///     ));
205///     root_layer.add_transition(Transition::new(
206///         "Run -> Idle",
207///         idle_state,
208///         run_state,
209///         0.3,
210///         "Idle",
211///     ));
212///
213///     // Make the node.
214///     AnimationBlendingStateMachineBuilder::new(BaseBuilder::new())
215///         .with_machine(machine)
216///         .with_animation_player(animation_player_handle)
217///         .build(graph)
218/// }
219/// ```
220#[derive(Visit, Reflect, Clone, Debug, Default, ComponentProvider)]
221#[reflect(derived_type = "Node")]
222pub struct AnimationBlendingStateMachine {
223    base: Base,
224    #[component(include)]
225    machine: InheritableVariable<Machine>,
226    #[component(include)]
227    animation_player: InheritableVariable<Handle<AnimationPlayer>>,
228}
229
230impl AnimationBlendingStateMachine {
231    /// Sets new state machine to the node.
232    pub fn set_machine(&mut self, machine: Machine) {
233        self.machine.set_value_and_mark_modified(machine);
234    }
235
236    /// Returns a reference to the state machine used by the node.
237    pub fn machine(&self) -> &InheritableVariable<Machine> {
238        &self.machine
239    }
240
241    /// Returns a mutable reference to the state machine used by the node.
242    pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
243        &mut self.machine
244    }
245
246    /// Sets new animation player of the node. The animation player is a source of animations for blending, the state
247    /// machine node must have the animation player specified, otherwise it won't have any effect.
248    pub fn set_animation_player(&mut self, animation_player: Handle<AnimationPlayer>) {
249        self.animation_player
250            .set_value_and_mark_modified(animation_player);
251    }
252
253    /// Returns an animation player used by the node.
254    pub fn animation_player(&self) -> Handle<AnimationPlayer> {
255        *self.animation_player
256    }
257}
258
259impl TypeUuidProvider for AnimationBlendingStateMachine {
260    fn type_uuid() -> Uuid {
261        uuid!("4b08c753-2a10-41e3-8fb2-4fd0517e86bc")
262    }
263}
264
265impl Deref for AnimationBlendingStateMachine {
266    type Target = Base;
267
268    fn deref(&self) -> &Self::Target {
269        &self.base
270    }
271}
272
273impl DerefMut for AnimationBlendingStateMachine {
274    fn deref_mut(&mut self) -> &mut Self::Target {
275        &mut self.base
276    }
277}
278
279impl ConstructorProvider<Node, Graph> for AnimationBlendingStateMachine {
280    fn constructor() -> NodeConstructor {
281        NodeConstructor::new::<Self>()
282            .with_variant("Animation Blending State Machine", |_| {
283                let mut machine = Machine::default();
284
285                let mut layer = MachineLayer::new();
286                layer.set_name("Base Layer");
287
288                machine.add_layer(layer);
289
290                AnimationBlendingStateMachineBuilder::new(
291                    BaseBuilder::new().with_name("Animation Blending State Machine"),
292                )
293                .with_machine(machine)
294                .build_node()
295                .into()
296            })
297            .with_group("Animation")
298    }
299}
300
301impl NodeTrait for AnimationBlendingStateMachine {
302    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
303        self.base.local_bounding_box()
304    }
305
306    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
307        self.base.world_bounding_box()
308    }
309
310    fn id(&self) -> Uuid {
311        Self::type_uuid()
312    }
313
314    fn update(&mut self, context: &mut UpdateContext) {
315        if let Ok(animation_player) = context.nodes.try_get_mut(*self.animation_player) {
316            // Prevent animation player to apply animation to scene nodes. The animation will
317            // do than instead.
318            animation_player.set_auto_apply(false);
319
320            let pose = self.machine.get_value_mut_silent().evaluate_pose(
321                animation_player.animations.get_value_mut_silent(),
322                context.dt,
323            );
324
325            pose.apply_internal(context.nodes);
326        }
327    }
328
329    fn validate(&self, scene: &Scene) -> Result<(), String> {
330        if scene.graph.try_get(*self.animation_player).is_err() {
331            Err(
332                "Animation player is not set or invalid! Animation blending state \
333            machine won't operate! Set the animation player handle in the Inspector."
334                    .to_string(),
335            )
336        } else {
337            Ok(())
338        }
339    }
340}
341
342/// Animation blending state machine builder allows you to create state machines in declarative manner.
343pub struct AnimationBlendingStateMachineBuilder {
344    base_builder: BaseBuilder,
345    machine: Machine,
346    animation_player: Handle<AnimationPlayer>,
347}
348
349impl AnimationBlendingStateMachineBuilder {
350    /// Creates new builder instance.
351    pub fn new(base_builder: BaseBuilder) -> Self {
352        Self {
353            base_builder,
354            machine: Default::default(),
355            animation_player: Default::default(),
356        }
357    }
358
359    /// Sets the desired state machine.
360    pub fn with_machine(mut self, machine: Machine) -> Self {
361        self.machine = machine;
362        self
363    }
364
365    /// Sets the animation player as a source of animations.
366    pub fn with_animation_player(mut self, animation_player: Handle<AnimationPlayer>) -> Self {
367        self.animation_player = animation_player;
368        self
369    }
370
371    /// Creates new node.
372    pub fn build_node(self) -> Node {
373        Node::new(AnimationBlendingStateMachine {
374            base: self.base_builder.build_base(),
375            machine: self.machine.into(),
376            animation_player: self.animation_player.into(),
377        })
378    }
379
380    /// Creates new node and adds it to the graph.
381    pub fn build(self, graph: &mut Graph) -> Handle<AnimationBlendingStateMachine> {
382        graph.add_node(self.build_node()).to_variant()
383    }
384}