fyrox_ui/
animation.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 player is a node that contains multiple animations. It updates and plays all the animations.
22//! See [`AnimationPlayer`] docs for more info.
23
24use crate::MessageDirection;
25use crate::{
26    core::{
27        log::{Log, MessageKind},
28        pool::Handle,
29        reflect::prelude::*,
30        type_traits::prelude::*,
31        variable::InheritableVariable,
32        visitor::prelude::*,
33    },
34    define_constructor, define_widget_deref,
35    generic_animation::value::{BoundValueCollection, TrackValue, ValueBinding},
36    message::UiMessage,
37    widget::{Widget, WidgetBuilder},
38    BuildContext, Control, UiNode, UserInterface,
39};
40use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
41use std::ops::{Deref, DerefMut};
42
43#[derive(Debug, Clone, PartialEq)]
44pub enum AnimationPlayerMessage {
45    EnableAnimation { animation: String, enabled: bool },
46    RewindAnimation { animation: String },
47    TimePosition { animation: String, time: f32 },
48}
49
50impl AnimationPlayerMessage {
51    define_constructor!(
52        /// Creates a new [Self::EnableAnimation] message.
53        AnimationPlayerMessage:EnableAnimation => fn enable_animation(animation: String, enabled: bool), layout: false
54    );
55    define_constructor!(
56        /// Creates a new [Self::RewindAnimation] message.
57        AnimationPlayerMessage:RewindAnimation => fn rewind_animation(animation: String), layout: false
58    );
59    define_constructor!(
60        /// Creates a new [Self::TimePosition] message.
61        AnimationPlayerMessage:TimePosition => fn time_position(animation: String, time: f32), layout: false
62    );
63}
64
65/// UI-specific animation.
66pub type Animation = crate::generic_animation::Animation<Handle<UiNode>>;
67/// UI-specific animation track.
68pub type Track = crate::generic_animation::track::Track;
69/// UI-specific animation container.
70pub type AnimationContainer = crate::generic_animation::AnimationContainer<Handle<UiNode>>;
71/// UI-specific animation pose.
72pub type AnimationPose = crate::generic_animation::AnimationPose<Handle<UiNode>>;
73/// UI-specific animation node pose.
74pub type NodePose = crate::generic_animation::NodePose<Handle<UiNode>>;
75
76/// Standard prelude for animations, that contains all most commonly used types and traits.
77pub mod prelude {
78    pub use super::{
79        Animation, AnimationContainer, AnimationContainerExt, AnimationPlayer,
80        AnimationPlayerBuilder, AnimationPose, AnimationPoseExt, BoundValueCollectionExt, NodePose,
81        Track,
82    };
83    pub use crate::generic_animation::{
84        container::{TrackDataContainer, TrackValueKind},
85        signal::AnimationSignal,
86        value::{BoundValueCollection, TrackValue, ValueBinding, ValueType},
87        AnimationEvent,
88    };
89}
90
91/// Extension trait for [`AnimationContainer`].
92pub trait AnimationContainerExt {
93    /// Updates all animations in the container and applies their poses to respective nodes. This method is intended to
94    /// be used only by the internals of the engine!
95    fn update_animations(&mut self, nodes: &mut UserInterface, dt: f32);
96}
97
98impl AnimationContainerExt for AnimationContainer {
99    fn update_animations(&mut self, ui: &mut UserInterface, dt: f32) {
100        for animation in self.iter_mut().filter(|anim| anim.is_enabled()) {
101            animation.tick(dt);
102            animation.pose().apply(ui);
103        }
104    }
105}
106
107/// Extension trait for [`AnimationPose`].
108pub trait AnimationPoseExt {
109    /// Tries to set each value to the each property from the animation pose to respective widgets.
110    fn apply(&self, ui: &mut UserInterface);
111}
112
113impl AnimationPoseExt for AnimationPose {
114    fn apply(&self, ui: &mut UserInterface) {
115        for (node, local_pose) in self.poses() {
116            if node.is_none() {
117                Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
118            } else if let Some(node) = ui.try_get_node_mut(*node) {
119                node.invalidate_layout();
120
121                local_pose.values.apply(node);
122            }
123        }
124    }
125}
126
127/// Extension trait for [`BoundValueCollection`].
128pub trait BoundValueCollectionExt {
129    /// Tries to set each value from the collection to the respective property (by binding) of the
130    /// given widget.
131    fn apply(&self, node_ref: &mut UiNode);
132}
133
134impl BoundValueCollectionExt for BoundValueCollection {
135    fn apply(&self, node_ref: &mut UiNode) {
136        for bound_value in self.values.iter() {
137            match bound_value.binding {
138                ValueBinding::Position => {
139                    if let TrackValue::Vector2(v) = bound_value.value {
140                        node_ref.set_desired_local_position(v);
141                    } else {
142                        Log::err(
143                            "Unable to apply position, because underlying type is not Vector2!",
144                        )
145                    }
146                }
147                ValueBinding::Scale => Log::warn("Implement me!"),
148                ValueBinding::Rotation => Log::warn("Implement me!"),
149                ValueBinding::Property {
150                    name: ref property_name,
151                    value_type,
152                } => bound_value.apply_to_object(node_ref, property_name, value_type),
153            }
154        }
155    }
156}
157
158/// Animation player is a node that contains multiple animations. It updates and plays all the animations.
159/// The node could be a source of animations for animation blending state machines. To learn more about
160/// animations, see [`Animation`] docs.
161#[derive(Visit, Reflect, Clone, Debug, ComponentProvider)]
162#[reflect(derived_type = "UiNode")]
163pub struct AnimationPlayer {
164    widget: Widget,
165    #[component(include)]
166    pub(crate) animations: InheritableVariable<AnimationContainer>,
167    #[component(include)]
168    auto_apply: bool,
169}
170
171impl ConstructorProvider<UiNode, UserInterface> for AnimationPlayer {
172    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
173        GraphNodeConstructor::new::<Self>()
174            .with_variant("Animation Player", |ui| {
175                AnimationPlayerBuilder::new(WidgetBuilder::new().with_name("Animation Player"))
176                    .build(&mut ui.build_ctx())
177                    .into()
178            })
179            .with_group("Animation")
180    }
181}
182
183impl Default for AnimationPlayer {
184    fn default() -> Self {
185        Self {
186            widget: Default::default(),
187            animations: Default::default(),
188            auto_apply: true,
189        }
190    }
191}
192
193impl AnimationPlayer {
194    /// Enables or disables automatic animations update and pose applying. If auto applying is enabled,
195    /// then every animation in this node is updated first, and then their output pose could be applied
196    /// to the graph, so the animation takes effect. Automatic applying is useful when you need your
197    /// animations to be applied immediately to the graph, but in some cases (if you're using animation
198    /// blending state machines for example) this functionality is undesired.
199    ///
200    /// Animation blending machines hijacks control over the animation container and updates only
201    /// active animations, instead of all available. This is much better for performance than updating
202    /// all at once.
203    pub fn set_auto_apply(&mut self, auto_apply: bool) {
204        self.auto_apply = auto_apply;
205    }
206
207    /// Returns `true` if the node is automatically applying output poses of animations to the graph, `false` -
208    /// otherwise.
209    pub fn is_auto_apply(&self) -> bool {
210        self.auto_apply
211    }
212
213    /// Returns a reference to internal animations container.
214    pub fn animations(&self) -> &InheritableVariable<AnimationContainer> {
215        &self.animations
216    }
217
218    /// Returns a reference to internal animations container. Keep in mind that mutable access to [`InheritableVariable`]
219    /// may have side effects if used inappropriately. Checks docs for [`InheritableVariable`] for more info.
220    pub fn animations_mut(&mut self) -> &mut InheritableVariable<AnimationContainer> {
221        &mut self.animations
222    }
223
224    /// Sets new animations container of the animation player.
225    pub fn set_animations(&mut self, animations: AnimationContainer) {
226        self.animations.set_value_and_mark_modified(animations);
227    }
228
229    fn find_animation(&mut self, name: &str) -> Option<&mut Animation> {
230        self.animations.find_by_name_mut(name).map(|(_, a)| a)
231    }
232}
233
234impl TypeUuidProvider for AnimationPlayer {
235    fn type_uuid() -> Uuid {
236        uuid!("44d1c94e-354f-4f9a-b918-9d31c28aa16a")
237    }
238}
239
240define_widget_deref!(AnimationPlayer);
241
242impl Control for AnimationPlayer {
243    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
244        self.widget.handle_routed_message(ui, message);
245
246        if let Some(msg) = message.data::<AnimationPlayerMessage>() {
247            match msg {
248                AnimationPlayerMessage::EnableAnimation { animation, enabled } => {
249                    if let Some(animation) = self.find_animation(animation) {
250                        animation.set_enabled(*enabled);
251                    }
252                }
253                AnimationPlayerMessage::RewindAnimation { animation } => {
254                    if let Some(animation) = self.find_animation(animation) {
255                        animation.rewind();
256                    }
257                }
258                AnimationPlayerMessage::TimePosition { animation, time } => {
259                    if let Some(animation) = self.find_animation(animation) {
260                        animation.set_time_position(*time);
261                    }
262                }
263            }
264        }
265    }
266
267    fn update(&mut self, dt: f32, ui: &mut UserInterface) {
268        if self.auto_apply {
269            self.animations
270                .get_value_mut_silent()
271                .update_animations(ui, dt);
272        }
273    }
274}
275
276/// A builder for [`AnimationPlayer`] node.
277pub struct AnimationPlayerBuilder {
278    widget_builder: WidgetBuilder,
279    animations: AnimationContainer,
280    auto_apply: bool,
281}
282
283impl AnimationPlayerBuilder {
284    /// Creates new builder instance.
285    pub fn new(widget_builder: WidgetBuilder) -> Self {
286        Self {
287            widget_builder,
288            animations: AnimationContainer::new(),
289            auto_apply: true,
290        }
291    }
292
293    /// Sets a container with desired animations.
294    pub fn with_animations(mut self, animations: AnimationContainer) -> Self {
295        self.animations = animations;
296        self
297    }
298
299    /// Enables or disables automatic pose applying. See [`AnimationPlayer::set_auto_apply`] docs for more info.
300    pub fn with_auto_apply(mut self, auto_apply: bool) -> Self {
301        self.auto_apply = auto_apply;
302        self
303    }
304
305    /// Creates an instance of [`AnimationPlayer`] node.
306    pub fn build_node(self, ctx: &BuildContext) -> UiNode {
307        UiNode::new(AnimationPlayer {
308            widget: self.widget_builder.with_need_update(true).build(ctx),
309            animations: self.animations.into(),
310            auto_apply: self.auto_apply,
311        })
312    }
313
314    /// Creates an instance of [`AnimationPlayer`] node and adds it to the given user interface.
315    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
316        ctx.add_node(self.build_node(ctx))
317    }
318}
319
320#[cfg(test)]
321mod test {
322    use crate::animation::AnimationPlayerBuilder;
323    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
324
325    #[test]
326    fn test_deletion() {
327        test_widget_deletion(|ctx| AnimationPlayerBuilder::new(WidgetBuilder::new()).build(ctx));
328    }
329}