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::{BaseSceneGraph, SceneGraph, SceneGraphNode};
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<Node>,
170/// graph: &mut Graph,
171/// ) -> Handle<Node> {
172/// // Find idle and run animations first.
173/// let animation_player = graph
174/// .try_get_of_type::<AnimationPlayer>(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)]
221pub struct AnimationBlendingStateMachine {
222 base: Base,
223 #[component(include)]
224 machine: InheritableVariable<Machine>,
225 #[component(include)]
226 animation_player: InheritableVariable<Handle<Node>>,
227}
228
229impl AnimationBlendingStateMachine {
230 /// Sets new state machine to the node.
231 pub fn set_machine(&mut self, machine: Machine) {
232 self.machine.set_value_and_mark_modified(machine);
233 }
234
235 /// Returns a reference to the state machine used by the node.
236 pub fn machine(&self) -> &InheritableVariable<Machine> {
237 &self.machine
238 }
239
240 /// Returns a mutable reference to the state machine used by the node.
241 pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
242 &mut self.machine
243 }
244
245 /// Sets new animation player of the node. The animation player is a source of animations for blending, the state
246 /// machine node must have the animation player specified, otherwise it won't have any effect.
247 pub fn set_animation_player(&mut self, animation_player: Handle<Node>) {
248 self.animation_player
249 .set_value_and_mark_modified(animation_player);
250 }
251
252 /// Returns an animation player used by the node.
253 pub fn animation_player(&self) -> Handle<Node> {
254 *self.animation_player
255 }
256}
257
258impl TypeUuidProvider for AnimationBlendingStateMachine {
259 fn type_uuid() -> Uuid {
260 uuid!("4b08c753-2a10-41e3-8fb2-4fd0517e86bc")
261 }
262}
263
264impl Deref for AnimationBlendingStateMachine {
265 type Target = Base;
266
267 fn deref(&self) -> &Self::Target {
268 &self.base
269 }
270}
271
272impl DerefMut for AnimationBlendingStateMachine {
273 fn deref_mut(&mut self) -> &mut Self::Target {
274 &mut self.base
275 }
276}
277
278impl ConstructorProvider<Node, Graph> for AnimationBlendingStateMachine {
279 fn constructor() -> NodeConstructor {
280 NodeConstructor::new::<Self>()
281 .with_variant("Animation Blending State Machine", |_| {
282 let mut machine = Machine::default();
283
284 let mut layer = MachineLayer::new();
285 layer.set_name("Base Layer");
286
287 machine.add_layer(layer);
288
289 AnimationBlendingStateMachineBuilder::new(
290 BaseBuilder::new().with_name("Animation Blending State Machine"),
291 )
292 .with_machine(machine)
293 .build_node()
294 .into()
295 })
296 .with_group("Animation")
297 }
298}
299
300impl NodeTrait for AnimationBlendingStateMachine {
301 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
302 self.base.local_bounding_box()
303 }
304
305 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
306 self.base.world_bounding_box()
307 }
308
309 fn id(&self) -> Uuid {
310 Self::type_uuid()
311 }
312
313 fn update(&mut self, context: &mut UpdateContext) {
314 if let Some(animation_player) = context
315 .nodes
316 .try_borrow_mut(*self.animation_player)
317 .and_then(|n| n.component_mut::<AnimationPlayer>())
318 {
319 // Prevent animation player to apply animation to scene nodes. The animation will
320 // do than instead.
321 animation_player.set_auto_apply(false);
322
323 let pose = self.machine.get_value_mut_silent().evaluate_pose(
324 animation_player.animations.get_value_mut_silent(),
325 context.dt,
326 );
327
328 pose.apply_internal(context.nodes);
329 }
330 }
331
332 fn validate(&self, scene: &Scene) -> Result<(), String> {
333 if scene
334 .graph
335 .try_get(*self.animation_player)
336 .and_then(|n| n.component_ref::<AnimationPlayer>())
337 .is_none()
338 {
339 Err(
340 "Animation player is not set or invalid! Animation blending state \
341 machine won't operate! Set the animation player handle in the Inspector."
342 .to_string(),
343 )
344 } else {
345 Ok(())
346 }
347 }
348}
349
350/// Animation blending state machine builder allows you to create state machines in declarative manner.
351pub struct AnimationBlendingStateMachineBuilder {
352 base_builder: BaseBuilder,
353 machine: Machine,
354 animation_player: Handle<Node>,
355}
356
357impl AnimationBlendingStateMachineBuilder {
358 /// Creates new builder instance.
359 pub fn new(base_builder: BaseBuilder) -> Self {
360 Self {
361 base_builder,
362 machine: Default::default(),
363 animation_player: Default::default(),
364 }
365 }
366
367 /// Sets the desired state machine.
368 pub fn with_machine(mut self, machine: Machine) -> Self {
369 self.machine = machine;
370 self
371 }
372
373 /// Sets the animation player as a source of animations.
374 pub fn with_animation_player(mut self, animation_player: Handle<Node>) -> Self {
375 self.animation_player = animation_player;
376 self
377 }
378
379 /// Creates new node.
380 pub fn build_node(self) -> Node {
381 Node::new(AnimationBlendingStateMachine {
382 base: self.base_builder.build_base(),
383 machine: self.machine.into(),
384 animation_player: self.animation_player.into(),
385 })
386 }
387
388 /// Creates new node and adds it to the graph.
389 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
390 graph.add_node(self.build_node())
391 }
392}