Skip to main content

fyrox_animation/machine/
parameter.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//! Parameter is a name variable of a fixed type. See [`Parameter`] docs for more info.
22
23use crate::core::{algebra::Vector2, reflect::prelude::*, visitor::prelude::*};
24use fxhash::FxHashMap;
25use fyrox_core::uuid_provider;
26use std::{
27    cell::{Cell, RefCell},
28    ops::{Deref, DerefMut},
29};
30use strum_macros::{AsRefStr, EnumString, VariantNames};
31
32/// Machine parameter is a named variable of a fixed type. Machine uses various parameters for specific actions. For example
33/// Rule parameter is used to check where transition from a state to state is possible, `Weight` parameters are used to be
34/// a source real numbers that are used to calculate blend weights, etc.
35#[derive(Copy, Clone, Debug, PartialEq, Reflect, Visit, VariantNames, EnumString, AsRefStr)]
36pub enum Parameter {
37    /// Weight parameter is used to control blend weight in animation blending nodes.
38    Weight(f32),
39
40    /// Rule parameter is used to check where transition from a state to state is possible.
41    Rule(bool),
42
43    /// An index of a pose.
44    Index(u32),
45
46    /// A sampling point. Usually it is used together with BlendSpace nodes.
47    SamplingPoint(Vector2<f32>),
48}
49
50uuid_provider!(Parameter = "ace1b8ea-15ee-444d-97be-1682cd9e4245");
51
52impl Default for Parameter {
53    fn default() -> Self {
54        Self::Weight(0.0)
55    }
56}
57
58/// Specific animation pose weight.
59#[derive(Debug, Visit, Clone, PartialEq, Reflect, VariantNames, EnumString, AsRefStr)]
60pub enum PoseWeight {
61    /// Fixed scalar value. Should not be negative, negative numbers will probably result in weird visual artifacts.
62    Constant(f32),
63
64    /// Reference to Weight parameter with given name.
65    Parameter(String),
66}
67
68uuid_provider!(PoseWeight = "46af44ea-eae9-4f3e-803c-06306caed23f");
69
70impl PoseWeight {
71    /// Calculates the actual pose weight value.
72    pub fn value(&self, params: &ParameterContainer) -> Option<f32> {
73        match self {
74            PoseWeight::Constant(val) => Some(*val),
75            PoseWeight::Parameter(name) => params.get(name).and_then(|p| {
76                if let Parameter::Weight(weight) = p {
77                    Some(*weight)
78                } else {
79                    None
80                }
81            }),
82        }
83    }
84}
85
86impl Default for PoseWeight {
87    fn default() -> Self {
88        Self::Constant(0.0)
89    }
90}
91
92/// A parameter value with its name.
93#[derive(Reflect, Visit, Default, Debug, Clone, PartialEq)]
94pub struct ParameterDefinition {
95    /// Name of the parameter.
96    pub name: String,
97
98    /// Value of the parameter.
99    pub value: Parameter,
100}
101
102uuid_provider!(ParameterDefinition = "6cbba5c9-8daf-4f4c-a920-9716ed233d89");
103
104#[derive(Default, Debug, Clone)]
105struct Wrapper {
106    parameters: Vec<ParameterDefinition>,
107    dirty: Cell<bool>,
108}
109
110impl PartialEq for Wrapper {
111    fn eq(&self, other: &Self) -> bool {
112        self.parameters == other.parameters
113    }
114}
115
116impl Visit for Wrapper {
117    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
118        self.parameters.visit(name, visitor)
119    }
120}
121
122impl Deref for Wrapper {
123    type Target = Vec<ParameterDefinition>;
124
125    fn deref(&self) -> &Self::Target {
126        &self.parameters
127    }
128}
129
130impl DerefMut for Wrapper {
131    fn deref_mut(&mut self) -> &mut Self::Target {
132        self.dirty.set(true);
133        &mut self.parameters
134    }
135}
136
137/// A container for all parameters used by a state machine. Parameters are shared across multiple animation layers.
138#[derive(Reflect, Visit, Default, Debug)]
139pub struct ParameterContainer {
140    #[reflect(deref)]
141    parameters: Wrapper,
142
143    #[reflect(hidden)]
144    #[visit(skip)]
145    lookup: RefCell<FxHashMap<String, usize>>,
146}
147
148impl PartialEq for ParameterContainer {
149    fn eq(&self, other: &Self) -> bool {
150        self.parameters == other.parameters
151    }
152}
153
154impl Clone for ParameterContainer {
155    fn clone(&self) -> Self {
156        Self {
157            parameters: self.parameters.clone(),
158            lookup: RefCell::new(self.lookup.borrow().clone()),
159        }
160    }
161}
162
163impl ParameterContainer {
164    fn update_index(&self) {
165        if self.parameters.dirty.get() {
166            *self.lookup.borrow_mut() = self
167                .parameters
168                .parameters
169                .iter()
170                .enumerate()
171                .map(|(i, p)| (p.name.clone(), i))
172                .collect();
173            self.parameters.dirty.set(false);
174        }
175    }
176
177    /// Adds a new parameter with a given name and value to the container.
178    pub fn add(&mut self, name: &str, value: Parameter) {
179        self.parameters.push(ParameterDefinition {
180            name: name.to_string(),
181            value,
182        })
183    }
184
185    /// Tries to borrow a parameter by its name. The method has O(1) complexity.
186    pub fn get(&self, name: &str) -> Option<&Parameter> {
187        self.update_index();
188        self.lookup
189            .borrow()
190            .get(name)
191            .and_then(|i| self.parameters.parameters.get(*i).map(|d| &d.value))
192    }
193
194    /// Tries to borrow a parameter by its name. The method has O(1) complexity.
195    pub fn get_mut(&mut self, name: &str) -> Option<&mut Parameter> {
196        self.update_index();
197        self.lookup
198            .borrow()
199            .get(name)
200            .and_then(|i| self.parameters.parameters.get_mut(*i).map(|d| &mut d.value))
201    }
202}