fyrox_animation/
pose.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//! Pose is a set of property values of a node ([`NodePose`]) or a set of nodes ([`AnimationPose`]).
22
23use crate::{value::BoundValue, value::BoundValueCollection, EntityId, RootMotion};
24use fxhash::FxHashMap;
25use std::collections::hash_map::Entry;
26
27/// A "captured" state of properties of some animated scene node. The pose can be considered as container of values of some
28/// properties.
29#[derive(Clone, Debug, PartialEq, Default)]
30pub struct NodePose<T: EntityId> {
31    /// A handle of an animated node.
32    pub node: T,
33
34    /// A set of property values.
35    pub values: BoundValueCollection,
36}
37
38impl<T: EntityId> NodePose<T> {
39    /// Performs a blending of the current with some other pose. See [`super::value::TrackValue::blend_with`] docs for more
40    /// info.
41    pub fn blend_with(&mut self, other: &NodePose<T>, weight: f32) {
42        self.values.blend_with(&other.values, weight)
43    }
44}
45
46/// Animations pose is a set of node poses. See [`NodePose`] docs for more info.
47#[derive(Default, Debug, Clone, PartialEq)]
48pub struct AnimationPose<T: EntityId> {
49    poses: FxHashMap<T, NodePose<T>>,
50    root_motion: Option<RootMotion>,
51}
52
53impl<T: EntityId> AnimationPose<T> {
54    /// Clears the set of node poses in the given animation pose and clones poses from the current animation pose to the given.
55    pub fn clone_into(&self, dest: &mut AnimationPose<T>) {
56        dest.reset();
57        for (handle, local_pose) in self.poses.iter() {
58            dest.poses.insert(*handle, local_pose.clone());
59        }
60        dest.root_motion.clone_from(&self.root_motion);
61    }
62
63    /// Sets root motion for the animation pose; the root motion will be blended with other motions
64    /// and the result can be obtained on a final pose.
65    pub fn set_root_motion(&mut self, root_motion: Option<RootMotion>) {
66        self.root_motion = root_motion;
67    }
68
69    /// Returns current root motion (if any).
70    pub fn root_motion(&self) -> Option<&RootMotion> {
71        self.root_motion.as_ref()
72    }
73
74    /// Blends current animation pose with another using a weight coefficient. Missing node poses (from either animation poses)
75    /// will become a simple copies of a respective node pose.
76    pub fn blend_with(&mut self, other: &AnimationPose<T>, weight: f32) {
77        for (handle, other_pose) in other.poses.iter() {
78            if let Some(current_pose) = self.poses.get_mut(handle) {
79                current_pose.blend_with(other_pose, weight);
80            } else {
81                self.add_node_pose(other_pose.clone());
82            }
83        }
84
85        self.root_motion
86            .get_or_insert_with(Default::default)
87            .blend_with(&other.root_motion.clone().unwrap_or_default(), weight);
88    }
89
90    fn add_node_pose(&mut self, local_pose: NodePose<T>) {
91        self.poses.insert(local_pose.node, local_pose);
92    }
93
94    pub(super) fn add_to_node_pose(&mut self, node: T, bound_value: BoundValue) {
95        match self.poses.entry(node) {
96            Entry::Occupied(entry) => {
97                entry.into_mut().values.values.push(bound_value);
98            }
99            Entry::Vacant(entry) => {
100                entry.insert(NodePose {
101                    node,
102                    values: BoundValueCollection {
103                        values: vec![bound_value],
104                    },
105                });
106            }
107        }
108    }
109
110    /// Clears the pose.
111    pub fn reset(&mut self) {
112        self.poses.clear();
113    }
114
115    /// Returns a reference to inner node pose map.
116    pub fn poses(&self) -> &FxHashMap<T, NodePose<T>> {
117        &self.poses
118    }
119
120    /// Returns a reference to inner node pose map.
121    pub fn poses_mut(&mut self) -> &mut FxHashMap<T, NodePose<T>> {
122        &mut self.poses
123    }
124}