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::any::{Any, TypeId};
46use std::ops::{Deref, DerefMut};
47
48/// Scene specific root motion settings.
49pub type RootMotionSettings = crate::generic_animation::RootMotionSettings<Handle<Node>>;
50/// Scene specific animation pose node.
51pub type PoseNode = crate::generic_animation::machine::PoseNode<Handle<Node>>;
52/// Scene specific animation pose node.
53pub type PlayAnimation = crate::generic_animation::machine::node::play::PlayAnimation<Handle<Node>>;
54/// Scene specific animation blending state machine BlendAnimations node.
55pub type BlendAnimations =
56    crate::generic_animation::machine::node::blend::BlendAnimations<Handle<Node>>;
57/// Scene specific animation blending state machine BlendAnimationsByIndex node.
58pub type BlendAnimationsByIndex =
59    crate::generic_animation::machine::node::blend::BlendAnimationsByIndex<Handle<Node>>;
60/// Scene specific animation blending state machine BlendPose node.
61pub type BlendPose = crate::generic_animation::machine::node::blend::BlendPose<Handle<Node>>;
62/// Scene specific animation blending state machine IndexedBlendInput node.
63pub type IndexedBlendInput =
64    crate::generic_animation::machine::node::blend::IndexedBlendInput<Handle<Node>>;
65/// Scene specific animation blending state machine BlendSpace node.
66pub type BlendSpace = crate::generic_animation::machine::node::blendspace::BlendSpace<Handle<Node>>;
67/// Scene specific animation blending state machine blend space point.
68pub type BlendSpacePoint =
69    crate::generic_animation::machine::node::blendspace::BlendSpacePoint<Handle<Node>>;
70/// Scene specific animation blending state machine layer mask.
71pub type LayerMask = crate::generic_animation::machine::mask::LayerMask<Handle<Node>>;
72/// Scene specific animation blending state machine layer mask.
73pub type Event = crate::generic_animation::machine::event::Event<Handle<Node>>;
74/// Scene specific animation blending state machine.
75pub type Machine = crate::generic_animation::machine::Machine<Handle<Node>>;
76/// Scene specific animation blending state machine layer.
77pub type MachineLayer = crate::generic_animation::machine::MachineLayer<Handle<Node>>;
78/// Scene specific animation blending state machine transition.
79pub type Transition = crate::generic_animation::machine::transition::Transition<Handle<Node>>;
80/// Scene specific animation blending state machine state.
81pub type State = crate::generic_animation::machine::state::State<Handle<Node>>;
82/// Scene specific animation blending state machine base pose node.
83pub type BasePoseNode = crate::generic_animation::machine::node::BasePoseNode<Handle<Node>>;
84/// Scene specific animation blending state machine state action.
85pub type StateAction = crate::generic_animation::machine::state::StateAction<Handle<Node>>;
86/// Scene specific animation blending state machine state action wrapper.
87pub type StateActionWrapper =
88    crate::generic_animation::machine::state::StateActionWrapper<Handle<Node>>;
89/// Scene specific animation blending state machine logic node.
90pub type LogicNode = crate::generic_animation::machine::transition::LogicNode<Handle<Node>>;
91/// Scene specific animation blending state machine And logic node.
92pub type AndNode = crate::generic_animation::machine::transition::AndNode<Handle<Node>>;
93/// Scene specific animation blending state machine Xor logic nde.
94pub type XorNode = crate::generic_animation::machine::transition::XorNode<Handle<Node>>;
95/// Scene specific animation blending state machine Or logic node.
96pub type OrNode = crate::generic_animation::machine::transition::OrNode<Handle<Node>>;
97/// Scene specific animation blending state machine Not logic node.
98pub type NotNode = crate::generic_animation::machine::transition::NotNode<Handle<Node>>;
99/// Scene specific animation blending state machine layer animation events collection.
100pub type LayerAnimationEventsCollection =
101    crate::generic_animation::machine::layer::LayerAnimationEventsCollection<Handle<Node>>;
102/// Scene specific animation blending state machine animation events source.
103pub type AnimationEventsSource =
104    crate::generic_animation::machine::layer::AnimationEventsSource<Handle<Node>>;
105
106/// Standard prelude for animation blending state machine, that contains all most commonly used types and traits.
107pub mod prelude {
108    pub use super::{
109        AndNode, AnimationBlendingStateMachine, AnimationBlendingStateMachineBuilder,
110        AnimationEventsSource, BasePoseNode, BlendAnimations, BlendAnimationsByIndex, BlendPose,
111        BlendSpace, BlendSpacePoint, Event, IndexedBlendInput, LayerAnimationEventsCollection,
112        LayerMask, LogicNode, Machine, MachineLayer, NotNode, OrNode, PlayAnimation, PoseNode,
113        RootMotionSettings, State, StateAction, StateActionWrapper, Transition, XorNode,
114    };
115    pub use crate::generic_animation::machine::{
116        node::AnimationEventCollectionStrategy,
117        parameter::{Parameter, ParameterContainer, ParameterDefinition, PoseWeight},
118    };
119}
120
121/// Extension trait for [`LayerMask`].
122pub trait LayerMaskExt {
123    /// Creates a layer mask for every descendant node starting from specified `root` (included). It could
124    /// be useful if you have an entire node hierarchy (for example, lower part of a body) that needs to
125    /// be filtered out.
126    fn from_hierarchy(graph: &Graph, root: Handle<Node>) -> Self;
127}
128
129impl LayerMaskExt for LayerMask {
130    fn from_hierarchy(graph: &Graph, root: Handle<Node>) -> Self {
131        Self::from(
132            graph
133                .traverse_iter(root)
134                .map(|(handle, _)| handle)
135                .collect::<Vec<_>>(),
136        )
137    }
138}
139
140type MachineType = InheritableVariable<Machine>;
141type AnimationPlayerHandle = InheritableVariable<Handle<AnimationPlayer>>;
142type AnimationPlayerUntypedHandle = InheritableVariable<Handle<Node>>;
143
144/// Animation blending state machine (ABSM) is a node that takes multiple animations from an animation player and
145/// mixes them in arbitrary way into one animation. Usually, ABSMs are used to animate humanoid characters in games,
146/// by blending multiple states with one or more animations. More info about state machines can be found in
147/// [`Machine`] docs.
148///
149/// # Important notes
150///
151/// The node does **not** contain any animations, instead it just takes animations from an animation
152/// player node and mixes them.
153///
154/// # Example
155///
156/// You should always prefer using the editor (FyroxEd) to create animation blending state machines, for many cases
157/// creating machines by code is quite slow and hard to debug. The editor shows all the states, nodes, transitions and
158/// helps you to quickly debug your ABSMs. However, if you need to create a state machine from code (for example, for
159/// procedural animations), then the following example is for you.
160///
161/// ```rust
162/// # use fyrox_impl::{
163/// #     core::pool::Handle,
164/// #     scene::{
165/// #         animation::{absm::prelude::*, prelude::*},
166/// #         base::BaseBuilder,
167/// #         graph::Graph,
168/// #         node::Node,
169/// #     },
170/// # };
171/// # use fyrox_graph::SceneGraph;
172///
173/// fn create_walk_idle_state_machine(
174///     animation_player_handle: Handle<AnimationPlayer>,
175///     graph: &mut Graph,
176/// ) -> Handle<AnimationBlendingStateMachine> {
177///     // Find idle and run animations first.
178///     let animation_player = graph
179///         .try_get(animation_player_handle)
180///         .unwrap();
181///     let idle_animation = animation_player
182///         .animations()
183///         .find_by_name_ref("Idle")
184///         .unwrap()
185///         .0;
186///     let run_animation = animation_player
187///         .animations()
188///         .find_by_name_ref("Run")
189///         .unwrap()
190///         .0;
191///
192///     // Create state machine.
193///     let mut machine = Machine::new();
194///
195///     let root_layer = machine.layers_mut().first_mut().unwrap();
196///
197///     let idle_pose = root_layer.add_node(PoseNode::make_play_animation(idle_animation));
198///     let idle_state = root_layer.add_state(State::new("Idle", idle_pose));
199///
200///     let run_pose = root_layer.add_node(PoseNode::make_play_animation(run_animation));
201///     let run_state = root_layer.add_state(State::new("Idle", run_pose));
202///
203///     root_layer.add_transition(Transition::new(
204///         "Idle -> Run",
205///         idle_state,
206///         run_state,
207///         0.3,
208///         "Run",
209///     ));
210///     root_layer.add_transition(Transition::new(
211///         "Run -> Idle",
212///         idle_state,
213///         run_state,
214///         0.3,
215///         "Idle",
216///     ));
217///
218///     // Make the node.
219///     AnimationBlendingStateMachineBuilder::new(BaseBuilder::new())
220///         .with_machine(machine)
221///         .with_animation_player(animation_player_handle)
222///         .build(graph)
223/// }
224/// ```
225#[derive(Visit, Reflect, Clone, Debug, Default)]
226#[reflect(derived_type = "Node")]
227pub struct AnimationBlendingStateMachine {
228    base: Base,
229    machine: MachineType,
230    animation_player: AnimationPlayerHandle,
231}
232
233impl ComponentProvider for AnimationBlendingStateMachine {
234    fn query_component_ref(&self, type_id: TypeId) -> Option<&dyn Any> {
235        if type_id == TypeId::of::<Self>() {
236            Some(self)
237        } else if type_id == TypeId::of::<MachineType>() {
238            Some(&self.machine)
239        } else if type_id == TypeId::of::<AnimationPlayerUntypedHandle>() {
240            Some(unsafe {
241                std::mem::transmute::<&AnimationPlayerHandle, &AnimationPlayerUntypedHandle>(
242                    &self.animation_player,
243                )
244            })
245        } else {
246            None
247        }
248    }
249
250    fn query_component_mut(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
251        if type_id == TypeId::of::<Self>() {
252            Some(self)
253        } else if type_id == TypeId::of::<MachineType>() {
254            Some(&mut self.machine)
255        } else if type_id == TypeId::of::<AnimationPlayerUntypedHandle>() {
256            Some(unsafe {
257                std::mem::transmute::<&mut AnimationPlayerHandle, &mut AnimationPlayerUntypedHandle>(
258                    &mut self.animation_player,
259                )
260            })
261        } else {
262            None
263        }
264    }
265}
266
267impl AnimationBlendingStateMachine {
268    /// Sets new state machine to the node.
269    pub fn set_machine(&mut self, machine: Machine) {
270        self.machine.set_value_and_mark_modified(machine);
271    }
272
273    /// Returns a reference to the state machine used by the node.
274    pub fn machine(&self) -> &InheritableVariable<Machine> {
275        &self.machine
276    }
277
278    /// Returns a mutable reference to the state machine used by the node.
279    pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
280        &mut self.machine
281    }
282
283    /// Sets new animation player of the node. The animation player is a source of animations for blending, the state
284    /// machine node must have the animation player specified, otherwise it won't have any effect.
285    pub fn set_animation_player(&mut self, animation_player: Handle<AnimationPlayer>) {
286        self.animation_player
287            .set_value_and_mark_modified(animation_player);
288    }
289
290    /// Returns an animation player used by the node.
291    pub fn animation_player(&self) -> Handle<AnimationPlayer> {
292        *self.animation_player
293    }
294}
295
296impl TypeUuidProvider for AnimationBlendingStateMachine {
297    fn type_uuid() -> Uuid {
298        uuid!("4b08c753-2a10-41e3-8fb2-4fd0517e86bc")
299    }
300}
301
302impl Deref for AnimationBlendingStateMachine {
303    type Target = Base;
304
305    fn deref(&self) -> &Self::Target {
306        &self.base
307    }
308}
309
310impl DerefMut for AnimationBlendingStateMachine {
311    fn deref_mut(&mut self) -> &mut Self::Target {
312        &mut self.base
313    }
314}
315
316impl ConstructorProvider<Node, Graph> for AnimationBlendingStateMachine {
317    fn constructor() -> NodeConstructor {
318        NodeConstructor::new::<Self>()
319            .with_variant("Animation Blending State Machine", |_| {
320                let mut machine = Machine::default();
321
322                let mut layer = MachineLayer::new();
323                layer.set_name("Base Layer");
324
325                machine.add_layer(layer);
326
327                AnimationBlendingStateMachineBuilder::new(
328                    BaseBuilder::new().with_name("Animation Blending State Machine"),
329                )
330                .with_machine(machine)
331                .build_node()
332                .into()
333            })
334            .with_group("Animation")
335    }
336}
337
338impl NodeTrait for AnimationBlendingStateMachine {
339    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
340        self.base.local_bounding_box()
341    }
342
343    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
344        self.base.world_bounding_box()
345    }
346
347    fn id(&self) -> Uuid {
348        Self::type_uuid()
349    }
350
351    fn update(&mut self, context: &mut UpdateContext) {
352        if let Ok(animation_player) = context.nodes.try_get_mut(*self.animation_player) {
353            // Prevent animation player to apply animation to scene nodes. The animation will
354            // do than instead.
355            animation_player.set_auto_apply(false);
356
357            let pose = self.machine.get_value_mut_silent().evaluate_pose(
358                animation_player.animations.get_value_mut_silent(),
359                context.dt,
360            );
361
362            pose.apply_internal(context.nodes);
363        }
364    }
365
366    fn validate(&self, scene: &Scene) -> Result<(), String> {
367        if scene.graph.try_get(*self.animation_player).is_err() {
368            Err(
369                "Animation player is not set or invalid! Animation blending state \
370            machine won't operate! Set the animation player handle in the Inspector."
371                    .to_string(),
372            )
373        } else {
374            Ok(())
375        }
376    }
377}
378
379/// Animation blending state machine builder allows you to create state machines in declarative manner.
380pub struct AnimationBlendingStateMachineBuilder {
381    base_builder: BaseBuilder,
382    machine: Machine,
383    animation_player: Handle<AnimationPlayer>,
384}
385
386impl AnimationBlendingStateMachineBuilder {
387    /// Creates new builder instance.
388    pub fn new(base_builder: BaseBuilder) -> Self {
389        Self {
390            base_builder,
391            machine: Default::default(),
392            animation_player: Default::default(),
393        }
394    }
395
396    /// Sets the desired state machine.
397    pub fn with_machine(mut self, machine: Machine) -> Self {
398        self.machine = machine;
399        self
400    }
401
402    /// Sets the animation player as a source of animations.
403    pub fn with_animation_player(mut self, animation_player: Handle<AnimationPlayer>) -> Self {
404        self.animation_player = animation_player;
405        self
406    }
407
408    /// Creates new node.
409    pub fn build_node(self) -> Node {
410        Node::new(AnimationBlendingStateMachine {
411            base: self.base_builder.build_base(),
412            machine: self.machine.into(),
413            animation_player: self.animation_player.into(),
414        })
415    }
416
417    /// Creates new node and adds it to the graph.
418    pub fn build(self, graph: &mut Graph) -> Handle<AnimationBlendingStateMachine> {
419        graph.add_node(self.build_node()).to_variant()
420    }
421}