Skip to main content

fyrox_animation/machine/
transition.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//! Transition is a connection between two states with a rule that defines possibility of actual transition with blending.
22
23use crate::{
24    core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},
25    machine::{Parameter, ParameterContainer, State},
26    Animation, AnimationContainer, EntityId,
27};
28use fyrox_core::uuid::{uuid, Uuid};
29use fyrox_core::{NameProvider, TypeUuidProvider};
30use std::any::{type_name, Any, TypeId};
31use strum_macros::{AsRefStr, EnumString, VariantNames};
32
33macro_rules! define_two_args_node {
34    ($(#[$meta:meta])* $name:ident) => {
35        $(#[$meta])*
36        #[derive(Debug, Clone, PartialEq)]
37        pub struct $name <T:EntityId> {
38            /// Left argument.
39            pub lhs: Box<LogicNode<T>>,
40            /// Right argument.
41            pub rhs: Box<LogicNode<T>>,
42        }
43
44        impl<T:EntityId> Default for $name<T> {
45            fn default() -> Self {
46                Self {
47                    lhs: Box::new(LogicNode::Parameter(Default::default())),
48                    rhs: Box::new(LogicNode::Parameter(Default::default())),
49                }
50            }
51        }
52
53        impl<T:EntityId> Visit for $name<T> {
54            fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
55                let mut guard = visitor.enter_region(name)?;
56
57                self.lhs.visit("Lhs", &mut guard)?;
58                self.rhs.visit("Rhs", &mut guard)?;
59
60                Ok(())
61            }
62        }
63
64        impl<T:EntityId> Reflect for $name<T> {
65            fn source_path() -> &'static str {
66                file!()
67            }
68
69            fn derived_types() -> &'static [TypeId] {
70                &[]
71            }
72
73            fn query_derived_types(&self) -> &'static [TypeId] {
74                Self::derived_types()
75            }
76
77            fn try_clone_box(&self) -> Option<Box<dyn Reflect>> {
78                Some(Box::new(self.clone()))
79            }
80
81            fn type_name(&self) -> &'static str {
82                type_name::<Self>()
83            }
84
85            fn doc(&self) -> &'static str {
86                ""
87            }
88
89            fn assembly_name(&self) -> &'static str {
90                env!("CARGO_PKG_NAME")
91            }
92
93            fn type_assembly_name() -> &'static str {
94                env!("CARGO_PKG_NAME")
95            }
96
97            fn fields_ref(&self, func: &mut dyn FnMut(&[FieldRef])) {
98                func(&[
99                    {
100                        static METADATA: FieldMetadata = FieldMetadata {
101                            name: "Lhs",
102                            display_name: "Lhs",
103                            tag: "",
104                            read_only: false,
105                            immutable_collection: false,
106                            min_value: None,
107                            max_value: None,
108                            step: None,
109                            precision: None,
110                            doc: "",
111                        };
112
113                        FieldRef {
114                            metadata: &METADATA,
115                            value: &*self.lhs,
116                        }
117                    },
118                    {
119                        static METADATA: FieldMetadata = FieldMetadata {
120                            name: "Rhs",
121                            display_name: "Rhs",
122                            tag: "",
123                            read_only: false,
124                            immutable_collection: false,
125                            min_value: None,
126                            max_value: None,
127                            step: None,
128                            precision: None,doc: "",
129                        };
130
131                        FieldRef {
132                            metadata: &METADATA,
133                            value: &*self.rhs,
134                        }
135                    },
136                ])
137            }
138
139            fn fields_mut(&mut self, func: &mut dyn FnMut(&mut [FieldMut])) {
140                func(&mut [
141                    {
142                        static METADATA: FieldMetadata = FieldMetadata {
143                            name: "Lhs",
144                            display_name: "Lhs",
145                            tag: "",
146                            read_only: false,
147                            immutable_collection: false,
148                            min_value: None,
149                            max_value: None,
150                            step: None,
151                            precision: None,
152                            doc: "",
153                        };
154
155                        FieldMut {
156                            metadata: &METADATA,
157                            value: &mut *self.lhs,
158                        }
159                    },
160                    {
161                        static METADATA: FieldMetadata = FieldMetadata {
162                            name: "Rhs",
163                            display_name: "Rhs",
164                            tag: "",
165                            read_only: false,
166                            immutable_collection: false,
167                            min_value: None,
168                            max_value: None,
169                            step: None,
170                            precision: None,doc: "",
171                        };
172
173                        FieldMut {
174                            metadata: &METADATA,
175                            value: &mut *self.rhs,
176                        }
177                    },
178                ])
179            }
180
181
182            fn into_any(self: Box<Self>) -> Box<dyn Any> {
183                self
184            }
185
186            fn as_any(&self, func: &mut dyn FnMut(&dyn ::core::any::Any)) {
187                func(self)
188            }
189
190            fn as_any_mut(&mut self, func: &mut dyn FnMut(&mut dyn ::core::any::Any)) {
191                func(self)
192            }
193
194            fn as_reflect(&self, func: &mut dyn FnMut(&dyn Reflect)) {
195                func(self)
196            }
197
198            fn as_reflect_mut(&mut self, func: &mut dyn FnMut(&mut dyn Reflect)) {
199                func(self)
200            }
201
202            fn set(
203                &mut self,
204                value: Box<dyn Reflect>,
205            ) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
206                let this = std::mem::replace(self, value.take()?);
207                Ok(Box::new(this))
208            }
209        }
210    };
211}
212
213define_two_args_node!(
214    /// Calculates logical AND between two arguments. Output value will be `true` iff both of the arguments is `true`.
215    AndNode
216);
217define_two_args_node!(
218    /// Calculates logical OR between two arguments. Output value will be `true` iff any of the arguments is `true`.
219    OrNode
220);
221define_two_args_node!(
222    /// Calculates logical XOR (excluding OR) between two arguments. Output value will be `true` iff the arguments differ.
223    XorNode
224);
225
226/// Calculates logical NOT of an argument. Output value will be `true` if the value of the argument is `false`.
227#[derive(Debug, Clone, PartialEq)]
228pub struct NotNode<T: EntityId> {
229    /// Argument to be negated.
230    pub lhs: Box<LogicNode<T>>,
231}
232
233impl<T: EntityId> Default for NotNode<T> {
234    fn default() -> Self {
235        Self {
236            lhs: Box::new(LogicNode::Parameter(Default::default())),
237        }
238    }
239}
240
241impl<T: EntityId> Visit for NotNode<T> {
242    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
243        let mut guard = visitor.enter_region(name)?;
244
245        self.lhs.visit("Lhs", &mut guard)?;
246
247        Ok(())
248    }
249}
250
251impl<T: EntityId> Reflect for NotNode<T> {
252    fn source_path() -> &'static str {
253        file!()
254    }
255
256    fn derived_types() -> &'static [TypeId] {
257        &[]
258    }
259
260    fn query_derived_types(&self) -> &'static [TypeId] {
261        Self::derived_types()
262    }
263
264    fn type_name(&self) -> &'static str {
265        type_name::<Self>()
266    }
267
268    fn doc(&self) -> &'static str {
269        ""
270    }
271
272    fn assembly_name(&self) -> &'static str {
273        env!("CARGO_PKG_NAME")
274    }
275
276    fn type_assembly_name() -> &'static str {
277        env!("CARGO_PKG_NAME")
278    }
279
280    fn fields_ref(&self, func: &mut dyn FnMut(&[FieldRef])) {
281        func(&[{
282            static METADATA: FieldMetadata = FieldMetadata {
283                name: "Lhs",
284                display_name: "Lhs",
285                tag: "",
286                read_only: false,
287                immutable_collection: false,
288                min_value: None,
289                max_value: None,
290                step: None,
291                precision: None,
292                doc: "",
293            };
294
295            FieldRef {
296                metadata: &METADATA,
297                value: &*self.lhs,
298            }
299        }])
300    }
301
302    fn fields_mut(&mut self, func: &mut dyn FnMut(&mut [FieldMut])) {
303        func(&mut [{
304            static METADATA: FieldMetadata = FieldMetadata {
305                name: "Lhs",
306                display_name: "Lhs",
307                tag: "",
308                read_only: false,
309                immutable_collection: false,
310                min_value: None,
311                max_value: None,
312                step: None,
313                precision: None,
314                doc: "",
315            };
316
317            FieldMut {
318                metadata: &METADATA,
319                value: &mut *self.lhs,
320            }
321        }])
322    }
323
324    fn into_any(self: Box<Self>) -> Box<dyn Any> {
325        self
326    }
327
328    fn as_any(&self, func: &mut dyn FnMut(&dyn ::core::any::Any)) {
329        func(self)
330    }
331
332    fn as_any_mut(&mut self, func: &mut dyn FnMut(&mut dyn ::core::any::Any)) {
333        func(self)
334    }
335
336    fn as_reflect(&self, func: &mut dyn FnMut(&dyn Reflect)) {
337        func(self)
338    }
339
340    fn as_reflect_mut(&mut self, func: &mut dyn FnMut(&mut dyn Reflect)) {
341        func(self)
342    }
343
344    fn set(&mut self, value: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
345        let this = std::mem::replace(self, value.take()?);
346        Ok(Box::new(this))
347    }
348
349    fn try_clone_box(&self) -> Option<Box<dyn Reflect>> {
350        Some(Box::new(self.clone()))
351    }
352}
353
354/// A node responsible for logical operations evaluation. It can have any number of descendant nodes.
355///
356/// # Examples
357///
358/// ```rust
359/// use fyrox_animation::AnimationContainer;
360/// use fyrox_animation::machine::{
361///     transition::{AndNode, LogicNode, NotNode},
362///     Parameter, ParameterContainer,
363/// };
364/// use fyrox_core::pool::ErasedHandle;
365///
366/// let mut parameters = ParameterContainer::default();
367/// parameters.add("Run", Parameter::Rule(false));
368/// parameters.add("Jump", Parameter::Rule(true));
369///
370/// // !Run && Jump
371/// let transition_logic = LogicNode::<ErasedHandle>::And(AndNode {
372///     lhs: Box::new(LogicNode::Not(NotNode {
373///         lhs: Box::new(LogicNode::Parameter("Run".to_string())),
374///     })),
375///     rhs: Box::new(LogicNode::Parameter("Jump".to_string())),
376/// });
377///
378/// assert_eq!(transition_logic.calculate_value(&parameters, &AnimationContainer::default()), true);
379/// ```
380#[derive(Debug, Visit, Clone, Reflect, PartialEq, AsRefStr, EnumString, VariantNames)]
381pub enum LogicNode<T: EntityId> {
382    /// Fetches a value of `Rule` parameter and returns its value. `false` if the parameter is not found.
383    Parameter(String),
384    /// Calculates logical AND between two arguments. Output value will be `true` iff both of the arguments is `true`.
385    And(AndNode<T>),
386    /// Calculates logical OR between two arguments. Output value will be `true` iff any of the arguments is `true`.
387    Or(OrNode<T>),
388    /// Calculates logical XOR (excluding OR) between two arguments. Output value will be `true` iff the arguments differ.
389    Xor(XorNode<T>),
390    /// Calculates logical NOT of an argument. Output value will be `true` if the value of the argument is `false`.
391    Not(NotNode<T>),
392    /// Returns `true` if the animation has ended, `false` - otherwise.
393    IsAnimationEnded(Handle<Animation<T>>),
394}
395
396impl<T: EntityId> TypeUuidProvider for LogicNode<T> {
397    fn type_uuid() -> Uuid {
398        uuid!("98a5b767-5560-4ed7-ad40-1625a8868e39")
399    }
400}
401
402impl<T: EntityId> Default for LogicNode<T> {
403    fn default() -> Self {
404        Self::Parameter(Default::default())
405    }
406}
407
408impl<T: EntityId> LogicNode<T> {
409    /// Calculates final value of the logic node.
410    pub fn calculate_value(
411        &self,
412        parameters: &ParameterContainer,
413        animations: &AnimationContainer<T>,
414    ) -> bool {
415        match self {
416            LogicNode::Parameter(rule_name) => parameters.get(rule_name).is_some_and(|p| {
417                if let Parameter::Rule(rule_value) = p {
418                    *rule_value
419                } else {
420                    false
421                }
422            }),
423            LogicNode::And(and) => {
424                let lhs_value = and.lhs.calculate_value(parameters, animations);
425                let rhs_value = and.rhs.calculate_value(parameters, animations);
426                lhs_value & rhs_value
427            }
428            LogicNode::Or(or) => {
429                let lhs_value = or.lhs.calculate_value(parameters, animations);
430                let rhs_value = or.rhs.calculate_value(parameters, animations);
431                lhs_value | rhs_value
432            }
433            LogicNode::Xor(or) => {
434                let lhs_value = or.lhs.calculate_value(parameters, animations);
435                let rhs_value = or.rhs.calculate_value(parameters, animations);
436                lhs_value ^ rhs_value
437            }
438            LogicNode::Not(node) => !node.lhs.calculate_value(parameters, animations),
439            LogicNode::IsAnimationEnded(animation) => animations
440                .try_get(*animation)
441                .ok()
442                .is_none_or(|a| a.has_ended()),
443        }
444    }
445}
446
447/// Transition is a connection between two states with a rule that defines possibility of actual transition with blending.
448#[derive(Default, Debug, Clone, Reflect, PartialEq)]
449pub struct Transition<T: EntityId> {
450    /// The name of the transition, it is used for debug output.
451    pub(crate) name: String,
452
453    /// Total amount of time (in seconds) to transition from source to destination state.
454    pub(crate) transition_time: f32,
455
456    pub(crate) elapsed_time: f32,
457
458    #[reflect(read_only)]
459    pub(crate) source: Handle<State<T>>,
460
461    #[reflect(read_only)]
462    pub(crate) dest: Handle<State<T>>,
463
464    /// Computational graph that can use any amount of Rule parameters to calculate transition value.
465    pub(crate) condition: LogicNode<T>,
466
467    /// 0 - evaluates `src` pose, 1 - `dest`, 0..1 - blends `src` and `dest`
468    pub(crate) blend_factor: f32,
469}
470
471impl<T: EntityId> Visit for Transition<T> {
472    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
473        let mut guard = visitor.enter_region(name)?;
474
475        self.name.visit("Name", &mut guard)?;
476        self.transition_time.visit("TransitionTime", &mut guard)?;
477        self.source.visit("Source", &mut guard)?;
478        self.dest.visit("Dest", &mut guard)?;
479        self.blend_factor.visit("BlendFactor", &mut guard)?;
480
481        if guard.is_reading() {
482            if self.condition.visit("Condition", &mut guard).is_err() {
483                // Try to convert the old version.
484                let mut invert_rule = false;
485                let mut rule: String = Default::default();
486
487                invert_rule.visit("InvertRule", &mut guard)?;
488                rule.visit("Rule", &mut guard)?;
489
490                if invert_rule {
491                    self.condition = LogicNode::Not(NotNode {
492                        lhs: Box::new(LogicNode::Parameter(rule)),
493                    });
494                } else {
495                    self.condition = LogicNode::Parameter(rule);
496                }
497            }
498        } else {
499            self.condition.visit("Condition", &mut guard)?;
500        }
501
502        Ok(())
503    }
504}
505
506impl<T: EntityId> NameProvider for Transition<T> {
507    fn name(&self) -> &str {
508        &self.name
509    }
510}
511
512impl<T: EntityId> Transition<T> {
513    /// Creates a new named transition between two states with a given time and a name of a parameter that
514    /// will be used to check if it is possible to activate the transition.
515    pub fn new(
516        name: &str,
517        src: Handle<State<T>>,
518        dest: Handle<State<T>>,
519        time: f32,
520        rule: &str,
521    ) -> Transition<T> {
522        Self {
523            name: name.to_owned(),
524            transition_time: time,
525            elapsed_time: 0.0,
526            source: src,
527            dest,
528            blend_factor: 0.0,
529            condition: LogicNode::Parameter(rule.to_owned()),
530        }
531    }
532
533    /// Returns a reference to the name of the transition.
534    #[inline]
535    pub fn name(&self) -> &str {
536        self.name.as_str()
537    }
538
539    /// Returns the amount of time required to perform a transition from source to destination state, in seconds.
540    #[inline]
541    pub fn transition_time(&self) -> f32 {
542        self.transition_time
543    }
544
545    /// Returns a handle to source state.
546    #[inline]
547    pub fn source(&self) -> Handle<State<T>> {
548        self.source
549    }
550
551    /// Returns a handle to destination state.
552    #[inline]
553    pub fn dest(&self) -> Handle<State<T>> {
554        self.dest
555    }
556
557    /// Sets new condition for the transition.
558    pub fn set_condition(&mut self, condition: LogicNode<T>) {
559        self.condition = condition;
560    }
561
562    /// Returns a reference to the current condition of the transition.
563    pub fn condition(&self) -> &LogicNode<T> {
564        &self.condition
565    }
566
567    /// Returns true if the transition from the source to the destination state was finished.
568    #[inline]
569    pub fn is_done(&self) -> bool {
570        (self.transition_time - self.elapsed_time).abs() <= f32::EPSILON
571    }
572
573    /// Returns current blend factor. 0 - evaluates `source` pose, 1 - `destination`, 0..1 - blends `source` and `destination`.
574    #[inline]
575    pub fn blend_factor(&self) -> f32 {
576        self.blend_factor
577    }
578
579    pub(super) fn reset(&mut self) {
580        self.elapsed_time = 0.0;
581        self.blend_factor = 0.0;
582    }
583
584    pub(super) fn update(&mut self, dt: f32) {
585        self.elapsed_time += dt;
586        if self.elapsed_time > self.transition_time {
587            self.elapsed_time = self.transition_time;
588        }
589        self.blend_factor = self.elapsed_time / self.transition_time;
590    }
591}