fyrox_impl/scene/animation/absm.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
//! Animation blending state machine is a node that takes multiple animations from an animation player and
//! mixes them in arbitrary way into one animation. See [`AnimationBlendingStateMachine`] docs for more info.
use crate::{
core::{
math::aabb::AxisAlignedBoundingBox,
pool::Handle,
reflect::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
TypeUuidProvider,
},
scene::{
animation::prelude::*,
base::{Base, BaseBuilder},
graph::Graph,
node::{Node, NodeTrait, UpdateContext},
Scene,
},
};
use fyrox_graph::{BaseSceneGraph, SceneGraph};
use std::ops::{Deref, DerefMut};
/// Scene specific root motion settings.
pub type RootMotionSettings = crate::generic_animation::RootMotionSettings<Handle<Node>>;
/// Scene specific animation pose node.
pub type PoseNode = crate::generic_animation::machine::PoseNode<Handle<Node>>;
/// Scene specific animation pose node.
pub type PlayAnimation = crate::generic_animation::machine::node::play::PlayAnimation<Handle<Node>>;
/// Scene specific animation blending state machine BlendAnimations node.
pub type BlendAnimations =
crate::generic_animation::machine::node::blend::BlendAnimations<Handle<Node>>;
/// Scene specific animation blending state machine BlendAnimationsByIndex node.
pub type BlendAnimationsByIndex =
crate::generic_animation::machine::node::blend::BlendAnimationsByIndex<Handle<Node>>;
/// Scene specific animation blending state machine BlendPose node.
pub type BlendPose = crate::generic_animation::machine::node::blend::BlendPose<Handle<Node>>;
/// Scene specific animation blending state machine IndexedBlendInput node.
pub type IndexedBlendInput =
crate::generic_animation::machine::node::blend::IndexedBlendInput<Handle<Node>>;
/// Scene specific animation blending state machine BlendSpace node.
pub type BlendSpace = crate::generic_animation::machine::node::blendspace::BlendSpace<Handle<Node>>;
/// Scene specific animation blending state machine blend space point.
pub type BlendSpacePoint =
crate::generic_animation::machine::node::blendspace::BlendSpacePoint<Handle<Node>>;
/// Scene specific animation blending state machine layer mask.
pub type LayerMask = crate::generic_animation::machine::mask::LayerMask<Handle<Node>>;
/// Scene specific animation blending state machine layer mask.
pub type Event = crate::generic_animation::machine::event::Event<Handle<Node>>;
/// Scene specific animation blending state machine.
pub type Machine = crate::generic_animation::machine::Machine<Handle<Node>>;
/// Scene specific animation blending state machine layer.
pub type MachineLayer = crate::generic_animation::machine::MachineLayer<Handle<Node>>;
/// Scene specific animation blending state machine transition.
pub type Transition = crate::generic_animation::machine::transition::Transition<Handle<Node>>;
/// Scene specific animation blending state machine state.
pub type State = crate::generic_animation::machine::state::State<Handle<Node>>;
/// Scene specific animation blending state machine base pose node.
pub type BasePoseNode = crate::generic_animation::machine::node::BasePoseNode<Handle<Node>>;
/// Scene specific animation blending state machine state action.
pub type StateAction = crate::generic_animation::machine::state::StateAction<Handle<Node>>;
/// Scene specific animation blending state machine state action wrapper.
pub type StateActionWrapper =
crate::generic_animation::machine::state::StateActionWrapper<Handle<Node>>;
/// Scene specific animation blending state machine logic node.
pub type LogicNode = crate::generic_animation::machine::transition::LogicNode<Handle<Node>>;
/// Scene specific animation blending state machine And logic node.
pub type AndNode = crate::generic_animation::machine::transition::AndNode<Handle<Node>>;
/// Scene specific animation blending state machine Xor logic nde.
pub type XorNode = crate::generic_animation::machine::transition::XorNode<Handle<Node>>;
/// Scene specific animation blending state machine Or logic node.
pub type OrNode = crate::generic_animation::machine::transition::OrNode<Handle<Node>>;
/// Scene specific animation blending state machine Not logic node.
pub type NotNode = crate::generic_animation::machine::transition::NotNode<Handle<Node>>;
/// Scene specific animation blending state machine layer animation events collection.
pub type LayerAnimationEventsCollection =
crate::generic_animation::machine::layer::LayerAnimationEventsCollection<Handle<Node>>;
/// Scene specific animation blending state machine animation events source.
pub type AnimationEventsSource =
crate::generic_animation::machine::layer::AnimationEventsSource<Handle<Node>>;
/// Standard prelude for animation blending state machine, that contains all most commonly used types and traits.
pub mod prelude {
pub use super::{
AndNode, AnimationBlendingStateMachine, AnimationBlendingStateMachineBuilder,
AnimationEventsSource, BasePoseNode, BlendAnimations, BlendAnimationsByIndex, BlendPose,
BlendSpace, BlendSpacePoint, Event, IndexedBlendInput, LayerAnimationEventsCollection,
LayerMask, LogicNode, Machine, MachineLayer, NotNode, OrNode, PlayAnimation, PoseNode,
RootMotionSettings, State, StateAction, StateActionWrapper, Transition, XorNode,
};
pub use crate::generic_animation::machine::{
node::AnimationEventCollectionStrategy,
parameter::{Parameter, ParameterContainer, ParameterDefinition, PoseWeight},
};
}
/// Extension trait for [`LayerMask`].
pub trait LayerMaskExt {
/// Creates a layer mask for every descendant node starting from specified `root` (included). It could
/// be useful if you have an entire node hierarchy (for example, lower part of a body) that needs to
/// be filtered out.
fn from_hierarchy(graph: &Graph, root: Handle<Node>) -> Self;
}
impl LayerMaskExt for LayerMask {
fn from_hierarchy(graph: &Graph, root: Handle<Node>) -> Self {
Self::from(graph.traverse_handle_iter(root).collect::<Vec<_>>())
}
}
/// Animation blending state machine (ABSM) is a node that takes multiple animations from an animation player and
/// mixes them in arbitrary way into one animation. Usually, ABSMs are used to animate humanoid characters in games,
/// by blending multiple states with one or more animations. More info about state machines can be found in
/// [`Machine`] docs.
///
/// # Important notes
///
/// The node does **not** contain any animations, instead it just takes animations from an animation
/// player node and mixes them.
///
/// # Example
///
/// You should always prefer using the editor (FyroxEd) to create animation blending state machines, for many cases
/// creating machines by code is quite slow and hard to debug. The editor shows all the states, nodes, transitions and
/// helps you to quickly debug your ABSMs. However, if you need to create a state machine from code (for example, for
/// procedural animations), then the following example is for you.
///
/// ```rust
/// # use fyrox_impl::{
/// # core::pool::Handle,
/// # scene::{
/// # animation::{absm::prelude::*, prelude::*},
/// # base::BaseBuilder,
/// # graph::Graph,
/// # node::Node,
/// # },
/// # };
/// # use fyrox_graph::SceneGraph;
///
/// fn create_walk_idle_state_machine(
/// animation_player_handle: Handle<Node>,
/// graph: &mut Graph,
/// ) -> Handle<Node> {
/// // Find idle and run animations first.
/// let animation_player = graph
/// .try_get_of_type::<AnimationPlayer>(animation_player_handle)
/// .unwrap();
/// let idle_animation = animation_player
/// .animations()
/// .find_by_name_ref("Idle")
/// .unwrap()
/// .0;
/// let run_animation = animation_player
/// .animations()
/// .find_by_name_ref("Run")
/// .unwrap()
/// .0;
///
/// // Create state machine.
/// let mut machine = Machine::new();
///
/// let root_layer = machine.layers_mut().first_mut().unwrap();
///
/// let idle_pose = root_layer.add_node(PoseNode::make_play_animation(idle_animation));
/// let idle_state = root_layer.add_state(State::new("Idle", idle_pose));
///
/// let run_pose = root_layer.add_node(PoseNode::make_play_animation(run_animation));
/// let run_state = root_layer.add_state(State::new("Idle", run_pose));
///
/// root_layer.add_transition(Transition::new(
/// "Idle -> Run",
/// idle_state,
/// run_state,
/// 0.3,
/// "Run",
/// ));
/// root_layer.add_transition(Transition::new(
/// "Run -> Idle",
/// idle_state,
/// run_state,
/// 0.3,
/// "Idle",
/// ));
///
/// // Make the node.
/// AnimationBlendingStateMachineBuilder::new(BaseBuilder::new())
/// .with_machine(machine)
/// .with_animation_player(animation_player_handle)
/// .build(graph)
/// }
/// ```
#[derive(Visit, Reflect, Clone, Debug, Default)]
pub struct AnimationBlendingStateMachine {
base: Base,
machine: InheritableVariable<Machine>,
animation_player: InheritableVariable<Handle<Node>>,
}
impl AnimationBlendingStateMachine {
/// Sets new state machine to the node.
pub fn set_machine(&mut self, machine: Machine) {
self.machine.set_value_and_mark_modified(machine);
}
/// Returns a reference to the state machine used by the node.
pub fn machine(&self) -> &InheritableVariable<Machine> {
&self.machine
}
/// Returns a mutable reference to the state machine used by the node.
pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
&mut self.machine
}
/// Sets new animation player of the node. The animation player is a source of animations for blending, the state
/// machine node must have the animation player specified, otherwise it won't have any effect.
pub fn set_animation_player(&mut self, animation_player: Handle<Node>) {
self.animation_player
.set_value_and_mark_modified(animation_player);
}
/// Returns an animation player used by the node.
pub fn animation_player(&self) -> Handle<Node> {
*self.animation_player
}
}
impl TypeUuidProvider for AnimationBlendingStateMachine {
fn type_uuid() -> Uuid {
uuid!("4b08c753-2a10-41e3-8fb2-4fd0517e86bc")
}
}
impl Deref for AnimationBlendingStateMachine {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for AnimationBlendingStateMachine {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl NodeTrait for AnimationBlendingStateMachine {
crate::impl_query_component!(
machine: InheritableVariable<Machine>,
animation_player: InheritableVariable<Handle<Node>>
);
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.local_bounding_box()
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.world_bounding_box()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn update(&mut self, context: &mut UpdateContext) {
if let Some(animation_player) = context
.nodes
.try_borrow_mut(*self.animation_player)
.and_then(|n| n.query_component_mut::<AnimationPlayer>())
{
// Prevent animation player to apply animation to scene nodes. The animation will
// do than instead.
animation_player.set_auto_apply(false);
let pose = self.machine.get_value_mut_silent().evaluate_pose(
animation_player.animations.get_value_mut_silent(),
context.dt,
);
pose.apply_internal(context.nodes);
}
}
fn validate(&self, scene: &Scene) -> Result<(), String> {
if scene
.graph
.try_get(*self.animation_player)
.and_then(|n| n.query_component_ref::<AnimationPlayer>())
.is_none()
{
Err(
"Animation player is not set or invalid! Animation blending state \
machine won't operate! Set the animation player handle in the Inspector."
.to_string(),
)
} else {
Ok(())
}
}
}
/// Animation blending state machine builder allows you to create state machines in declarative manner.
pub struct AnimationBlendingStateMachineBuilder {
base_builder: BaseBuilder,
machine: Machine,
animation_player: Handle<Node>,
}
impl AnimationBlendingStateMachineBuilder {
/// Creates new builder instance.
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
machine: Default::default(),
animation_player: Default::default(),
}
}
/// Sets the desired state machine.
pub fn with_machine(mut self, machine: Machine) -> Self {
self.machine = machine;
self
}
/// Sets the animation player as a source of animations.
pub fn with_animation_player(mut self, animation_player: Handle<Node>) -> Self {
self.animation_player = animation_player;
self
}
/// Creates new node.
pub fn build_node(self) -> Node {
Node::new(AnimationBlendingStateMachine {
base: self.base_builder.build_base(),
machine: self.machine.into(),
animation_player: self.animation_player.into(),
})
}
/// Creates new node and adds it to the graph.
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}