Skip to main content

fyrox_animation/machine/node/
mod.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//! Node is a part of animation blending tree, that backs a state with animation data. See [`PoseNode`] docs for
22//! more info.
23
24use crate::{
25    core::{
26        algebra::Vector2,
27        pool::{Handle, Pool},
28        reflect::prelude::*,
29        visitor::prelude::*,
30    },
31    machine::{
32        node::{blend::BlendAnimations, blendspace::BlendSpace, play::PlayAnimation},
33        BlendAnimationsByIndex, BlendPose, IndexedBlendInput, ParameterContainer, State,
34    },
35    Animation, AnimationContainer, AnimationEvent, AnimationPose, EntityId,
36};
37use fxhash::FxHashSet;
38use std::{
39    cell::Ref,
40    ops::{Deref, DerefMut},
41};
42
43pub mod blend;
44pub mod blendspace;
45pub mod play;
46
47/// A set of common data fields that is used in every node.
48#[derive(Debug, Visit, Clone, Default, Reflect, PartialEq)]
49pub struct BasePoseNode<T: EntityId> {
50    /// Position on the canvas, it is editor-specific data.
51    pub position: Vector2<f32>,
52
53    /// A handle of parent state that "owns" the node.
54    #[reflect(hidden)]
55    pub parent_state: Handle<State<T>>,
56}
57
58/// Specialized node that provides animation pose. See documentation for each variant.
59#[derive(Debug, Visit, Clone, Reflect, PartialEq)]
60pub enum PoseNode<T: EntityId> {
61    /// See docs for [`PlayAnimation`].
62    PlayAnimation(PlayAnimation<T>),
63
64    /// See docs for [`BlendAnimations`].
65    BlendAnimations(BlendAnimations<T>),
66
67    /// See docs for [`BlendAnimationsByIndex`].
68    BlendAnimationsByIndex(BlendAnimationsByIndex<T>),
69
70    /// See doc for [`BlendSpace`]
71    BlendSpace(BlendSpace<T>),
72}
73
74impl<T: EntityId> Default for PoseNode<T> {
75    fn default() -> Self {
76        Self::PlayAnimation(Default::default())
77    }
78}
79
80impl<T: EntityId> PoseNode<T> {
81    /// Creates new node that plays an animation.
82    pub fn make_play_animation(animation: Handle<Animation<T>>) -> Self {
83        Self::PlayAnimation(PlayAnimation::new(animation))
84    }
85
86    /// Creates new node that blends multiple poses into one.
87    pub fn make_blend_animations(poses: Vec<BlendPose<T>>) -> Self {
88        Self::BlendAnimations(BlendAnimations::new(poses))
89    }
90
91    /// Creates new node that switches between given animations using index and smoothly blends from
92    /// one animation to another while switching.
93    pub fn make_blend_animations_by_index(
94        index_parameter: String,
95        inputs: Vec<IndexedBlendInput<T>>,
96    ) -> Self {
97        Self::BlendAnimationsByIndex(BlendAnimationsByIndex::new(index_parameter, inputs))
98    }
99
100    /// Returns a set of handles to children pose nodes.
101    pub fn children(&self) -> Vec<Handle<PoseNode<T>>> {
102        match self {
103            Self::PlayAnimation(_) => {
104                // No children nodes.
105                vec![]
106            }
107            Self::BlendAnimations(blend_animations) => blend_animations.children(),
108            Self::BlendAnimationsByIndex(blend_by_index) => blend_by_index.children(),
109            Self::BlendSpace(blend_space) => blend_space.children(),
110        }
111    }
112
113    /// Collects all animation handles used by this node and its descendants.
114    pub fn collect_animations(
115        &self,
116        nodes: &Pool<PoseNode<T>>,
117        animations: &mut FxHashSet<Handle<Animation<T>>>,
118    ) {
119        match self {
120            PoseNode::PlayAnimation(play_animation) => {
121                animations.insert(play_animation.animation);
122            }
123            PoseNode::BlendAnimations(blend_animations) => {
124                for input in blend_animations.pose_sources.iter() {
125                    if let Ok(source) = nodes.try_borrow(input.pose_source) {
126                        source.collect_animations(nodes, animations)
127                    }
128                }
129            }
130            PoseNode::BlendAnimationsByIndex(blend_animations_by_index) => {
131                for input in blend_animations_by_index.inputs.iter() {
132                    if let Ok(source) = nodes.try_borrow(input.pose_source) {
133                        source.collect_animations(nodes, animations)
134                    }
135                }
136            }
137            PoseNode::BlendSpace(blend_space) => {
138                for point in blend_space.points() {
139                    if let Ok(source) = nodes.try_borrow(point.pose_source) {
140                        source.collect_animations(nodes, animations)
141                    }
142                }
143            }
144        }
145    }
146}
147
148macro_rules! static_dispatch {
149    ($self:ident, $func:ident, $($args:expr),*) => {
150        match $self {
151            PoseNode::PlayAnimation(v) => v.$func($($args),*),
152            PoseNode::BlendAnimations(v) => v.$func($($args),*),
153            PoseNode::BlendAnimationsByIndex(v) => v.$func($($args),*),
154            PoseNode::BlendSpace(v) => v.$func($($args),*),
155        }
156    };
157}
158
159impl<T: EntityId> Deref for PoseNode<T> {
160    type Target = BasePoseNode<T>;
161
162    fn deref(&self) -> &Self::Target {
163        static_dispatch!(self, deref,)
164    }
165}
166
167impl<T: EntityId> DerefMut for PoseNode<T> {
168    fn deref_mut(&mut self) -> &mut Self::Target {
169        static_dispatch!(self, deref_mut,)
170    }
171}
172
173/// A way of animation events collection.
174#[derive(Copy, Clone, Debug)]
175pub enum AnimationEventCollectionStrategy {
176    /// Collect all events.
177    All,
178    /// Blending nodes will only emit events from nodes or states with max weight.
179    MaxWeight,
180    /// Blending nodes will only emit events from nodes or states with min weight.
181    MinWeight,
182}
183
184/// A trait that responsible for animation pose evaluation.
185pub trait AnimationPoseSource<T: EntityId> {
186    /// Evaluates animation pose and returns a reference to it.
187    fn eval_pose(
188        &self,
189        nodes: &Pool<PoseNode<T>>,
190        params: &ParameterContainer,
191        animations: &AnimationContainer<T>,
192        dt: f32,
193    ) -> Ref<AnimationPose<T>>;
194
195    /// Returns a reference to inner pose of a node.
196    fn pose(&self) -> Ref<AnimationPose<T>>;
197
198    /// Collects animation events from internals.
199    fn collect_animation_events(
200        &self,
201        nodes: &Pool<PoseNode<T>>,
202        params: &ParameterContainer,
203        animations: &AnimationContainer<T>,
204        strategy: AnimationEventCollectionStrategy,
205    ) -> Vec<(Handle<Animation<T>>, AnimationEvent)>;
206}
207
208impl<T: EntityId> AnimationPoseSource<T> for PoseNode<T> {
209    fn eval_pose(
210        &self,
211        nodes: &Pool<PoseNode<T>>,
212        params: &ParameterContainer,
213        animations: &AnimationContainer<T>,
214        dt: f32,
215    ) -> Ref<AnimationPose<T>> {
216        static_dispatch!(self, eval_pose, nodes, params, animations, dt)
217    }
218
219    fn pose(&self) -> Ref<AnimationPose<T>> {
220        static_dispatch!(self, pose,)
221    }
222
223    fn collect_animation_events(
224        &self,
225        nodes: &Pool<PoseNode<T>>,
226        params: &ParameterContainer,
227        animations: &AnimationContainer<T>,
228        strategy: AnimationEventCollectionStrategy,
229    ) -> Vec<(Handle<Animation<T>>, AnimationEvent)> {
230        static_dispatch!(
231            self,
232            collect_animation_events,
233            nodes,
234            params,
235            animations,
236            strategy
237        )
238    }
239}