1use crate::{
25 animation::{AnimationPlayer, AnimationPoseExt},
26 core::{
27 pool::Handle, reflect::prelude::*, type_traits::prelude::*, variable::InheritableVariable,
28 visitor::prelude::*,
29 },
30 define_widget_deref,
31 message::{KeyCode, MouseButton, UiMessage},
32 widget::{Widget, WidgetBuilder, WidgetMessage},
33 BuildContext, Control, UiNode, UserInterface,
34};
35use fyrox_animation::machine::Parameter;
36use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
37use fyrox_graph::{SceneGraph, SceneGraphNode};
38use std::ops::{Deref, DerefMut};
39use strum_macros::{AsRefStr, EnumString, VariantNames};
40
41pub type RootMotionSettings = crate::generic_animation::RootMotionSettings<Handle<UiNode>>;
43pub type PoseNode = crate::generic_animation::machine::PoseNode<Handle<UiNode>>;
45pub type PlayAnimation =
47 crate::generic_animation::machine::node::play::PlayAnimation<Handle<UiNode>>;
48pub type BlendAnimations =
50 crate::generic_animation::machine::node::blend::BlendAnimations<Handle<UiNode>>;
51pub type BlendAnimationsByIndex =
53 crate::generic_animation::machine::node::blend::BlendAnimationsByIndex<Handle<UiNode>>;
54pub type BlendPose = crate::generic_animation::machine::node::blend::BlendPose<Handle<UiNode>>;
56pub type IndexedBlendInput =
58 crate::generic_animation::machine::node::blend::IndexedBlendInput<Handle<UiNode>>;
59pub type BlendSpace =
61 crate::generic_animation::machine::node::blendspace::BlendSpace<Handle<UiNode>>;
62pub type BlendSpacePoint =
64 crate::generic_animation::machine::node::blendspace::BlendSpacePoint<Handle<UiNode>>;
65pub type LayerMask = crate::generic_animation::machine::mask::LayerMask<Handle<UiNode>>;
67pub type Event = crate::generic_animation::machine::event::Event<Handle<UiNode>>;
69pub type Machine = crate::generic_animation::machine::Machine<Handle<UiNode>>;
71pub type MachineLayer = crate::generic_animation::machine::MachineLayer<Handle<UiNode>>;
73pub type Transition = crate::generic_animation::machine::transition::Transition<Handle<UiNode>>;
75pub type State = crate::generic_animation::machine::state::State<Handle<UiNode>>;
77pub type BasePoseNode = crate::generic_animation::machine::node::BasePoseNode<Handle<UiNode>>;
79pub type StateAction = crate::generic_animation::machine::state::StateAction<Handle<UiNode>>;
81pub type StateActionWrapper =
83 crate::generic_animation::machine::state::StateActionWrapper<Handle<UiNode>>;
84pub type LogicNode = crate::generic_animation::machine::transition::LogicNode<Handle<UiNode>>;
86pub type AndNode = crate::generic_animation::machine::transition::AndNode<Handle<UiNode>>;
88pub type XorNode = crate::generic_animation::machine::transition::XorNode<Handle<UiNode>>;
90pub type OrNode = crate::generic_animation::machine::transition::OrNode<Handle<UiNode>>;
92pub type NotNode = crate::generic_animation::machine::transition::NotNode<Handle<UiNode>>;
94pub type LayerAnimationEventsCollection =
96 crate::generic_animation::machine::layer::LayerAnimationEventsCollection<Handle<UiNode>>;
97pub type AnimationEventsSource =
99 crate::generic_animation::machine::layer::AnimationEventsSource<Handle<UiNode>>;
100
101pub mod prelude {
103 pub use super::{
104 AndNode, AnimationBlendingStateMachine, AnimationBlendingStateMachineBuilder,
105 AnimationEventsSource, BasePoseNode, BlendAnimations, BlendAnimationsByIndex, BlendPose,
106 BlendSpace, BlendSpacePoint, Event, IndexedBlendInput, LayerAnimationEventsCollection,
107 LayerMask, LogicNode, Machine, MachineLayer, NotNode, OrNode, PlayAnimation, PoseNode,
108 RootMotionSettings, State, StateAction, StateActionWrapper, Transition, XorNode,
109 };
110 pub use crate::generic_animation::machine::{
111 node::AnimationEventCollectionStrategy,
112 parameter::{Parameter, ParameterContainer, ParameterDefinition, PoseWeight},
113 };
114}
115
116#[derive(Visit, Reflect, Clone, Debug, Default, ComponentProvider, TypeUuidProvider)]
126#[type_uuid(id = "4b08c753-2a10-41e3-8fb2-4fd0517e86bc")]
127pub struct AnimationBlendingStateMachine {
128 widget: Widget,
129 #[component(include)]
130 machine: InheritableVariable<Machine>,
131 #[component(include)]
132 animation_player: InheritableVariable<Handle<UiNode>>,
133}
134
135impl ConstructorProvider<UiNode, UserInterface> for AnimationBlendingStateMachine {
136 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
137 GraphNodeConstructor::new::<Self>()
138 .with_variant("Animation Blending State Machine", |ui| {
139 AnimationBlendingStateMachineBuilder::new(
140 WidgetBuilder::new().with_name("Animation Blending State Machine"),
141 )
142 .build(&mut ui.build_ctx())
143 .into()
144 })
145 .with_group("Animation")
146 }
147}
148
149impl AnimationBlendingStateMachine {
150 pub fn set_machine(&mut self, machine: Machine) {
152 self.machine.set_value_and_mark_modified(machine);
153 }
154
155 pub fn machine(&self) -> &InheritableVariable<Machine> {
157 &self.machine
158 }
159
160 pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
162 &mut self.machine
163 }
164
165 pub fn set_animation_player(&mut self, animation_player: Handle<UiNode>) {
168 self.animation_player
169 .set_value_and_mark_modified(animation_player);
170 }
171
172 pub fn animation_player(&self) -> Handle<UiNode> {
174 *self.animation_player
175 }
176}
177
178define_widget_deref!(AnimationBlendingStateMachine);
179
180impl Control for AnimationBlendingStateMachine {
181 fn update(&mut self, dt: f32, ui: &mut UserInterface) {
182 if let Some(animation_player) = ui
183 .nodes
184 .try_borrow_mut(*self.animation_player)
185 .and_then(|n| n.component_mut::<AnimationPlayer>())
186 {
187 animation_player.set_auto_apply(false);
190
191 let pose = self
192 .machine
193 .get_value_mut_silent()
194 .evaluate_pose(animation_player.animations.get_value_mut_silent(), dt);
195
196 pose.apply(ui);
197 }
198 }
199
200 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
201 self.widget.handle_routed_message(ui, message)
202 }
203}
204
205pub struct AnimationBlendingStateMachineBuilder {
207 widget_builder: WidgetBuilder,
208 machine: Machine,
209 animation_player: Handle<UiNode>,
210}
211
212impl AnimationBlendingStateMachineBuilder {
213 pub fn new(widget_builder: WidgetBuilder) -> Self {
215 Self {
216 widget_builder,
217 machine: Default::default(),
218 animation_player: Default::default(),
219 }
220 }
221
222 pub fn with_machine(mut self, machine: Machine) -> Self {
224 self.machine = machine;
225 self
226 }
227
228 pub fn with_animation_player(mut self, animation_player: Handle<UiNode>) -> Self {
230 self.animation_player = animation_player;
231 self
232 }
233
234 pub fn build_node(self, ctx: &BuildContext) -> UiNode {
236 UiNode::new(AnimationBlendingStateMachine {
237 widget: self.widget_builder.with_need_update(true).build(ctx),
238 machine: self.machine.into(),
239 animation_player: self.animation_player.into(),
240 })
241 }
242
243 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
245 ctx.add_node(self.build_node(ctx))
246 }
247}
248
249#[derive(
250 Visit,
251 Reflect,
252 Clone,
253 Debug,
254 Default,
255 PartialEq,
256 TypeUuidProvider,
257 AsRefStr,
258 EnumString,
259 VariantNames,
260)]
261#[type_uuid(id = "291e8734-47df-408e-8b2c-57bfb941d8ec")]
262pub enum EventKind {
263 #[default]
264 MouseEnter,
265 MouseLeave,
266 MouseMove,
267 MouseDown(MouseButton),
268 MouseUp(MouseButton),
269 MouseWheel,
270 KeyDown(KeyCode),
271 KeyUp(KeyCode),
272 Focus,
273 Unfocus,
274 TouchStarted,
275 TouchEnded,
276 TouchMoved,
277 TouchCancelled,
278 DoubleTap,
279}
280
281#[derive(Visit, Reflect, Clone, Debug, Default, PartialEq, TypeUuidProvider)]
282#[type_uuid(id = "15f306b8-3bb8-4b35-87bd-6e9e5d748454")]
283pub struct EventAction {
284 kind: EventKind,
285 parameter_name: String,
286 parameter_value: Parameter,
287}
288
289#[derive(Visit, Reflect, Clone, Debug, Default, ComponentProvider, TypeUuidProvider)]
291#[type_uuid(id = "15f306b8-3bb8-4b35-87bd-6e9e5d748455")]
292pub struct AbsmEventProvider {
293 widget: Widget,
294 actions: InheritableVariable<Vec<EventAction>>,
295 absm: InheritableVariable<Handle<UiNode>>,
296}
297
298impl ConstructorProvider<UiNode, UserInterface> for AbsmEventProvider {
299 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
300 GraphNodeConstructor::new::<Self>()
301 .with_variant("Absm Event Provider", |ui| {
302 AbsmEventProviderBuilder::new(WidgetBuilder::new().with_name("Absm Event Provider"))
303 .build(&mut ui.build_ctx())
304 .into()
305 })
306 .with_group("Animation")
307 }
308}
309
310define_widget_deref!(AbsmEventProvider);
311
312impl AbsmEventProvider {
313 fn on_event(&self, ui: &mut UserInterface, kind: EventKind) {
314 let Some(action) = self.actions.iter().find(|a| a.kind == kind) else {
315 return;
316 };
317
318 let Some(absm) = ui.try_get_mut_of_type::<AnimationBlendingStateMachine>(*self.absm) else {
319 return;
320 };
321
322 absm.machine_mut()
323 .set_parameter(&action.parameter_name, action.parameter_value);
324 }
325}
326
327impl Control for AbsmEventProvider {
328 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
329 self.widget.handle_routed_message(ui, message);
330
331 let Some(msg) = message.data::<WidgetMessage>() else {
332 return;
333 };
334
335 use WidgetMessage as Msg;
336 match msg {
337 Msg::MouseDown { button, .. } => self.on_event(ui, EventKind::MouseDown(*button)),
338 Msg::MouseUp { button, .. } => self.on_event(ui, EventKind::MouseUp(*button)),
339 Msg::MouseMove { .. } => self.on_event(ui, EventKind::MouseMove),
340 Msg::MouseWheel { .. } => self.on_event(ui, EventKind::MouseWheel),
341 Msg::MouseLeave => self.on_event(ui, EventKind::MouseLeave),
342 Msg::MouseEnter => self.on_event(ui, EventKind::MouseEnter),
343 Msg::Focus => self.on_event(ui, EventKind::Focus),
344 Msg::Unfocus => self.on_event(ui, EventKind::Unfocus),
345 Msg::TouchStarted { .. } => self.on_event(ui, EventKind::TouchStarted),
346 Msg::TouchEnded { .. } => self.on_event(ui, EventKind::TouchEnded),
347 Msg::TouchMoved { .. } => self.on_event(ui, EventKind::TouchMoved),
348 Msg::TouchCancelled { .. } => self.on_event(ui, EventKind::TouchCancelled),
349 Msg::DoubleTap { .. } => self.on_event(ui, EventKind::DoubleTap),
350 Msg::KeyUp(key) => self.on_event(ui, EventKind::KeyUp(*key)),
351 Msg::KeyDown(key) => self.on_event(ui, EventKind::KeyDown(*key)),
352 _ => (),
353 }
354 }
355}
356
357pub struct AbsmEventProviderBuilder {
358 widget_builder: WidgetBuilder,
359 actions: Vec<EventAction>,
360 absm: Handle<UiNode>,
361}
362
363impl AbsmEventProviderBuilder {
364 pub fn new(widget_builder: WidgetBuilder) -> Self {
365 Self {
366 widget_builder,
367 actions: Default::default(),
368 absm: Default::default(),
369 }
370 }
371
372 pub fn with_actions(mut self, actions: Vec<EventAction>) -> Self {
373 self.actions = actions;
374 self
375 }
376
377 pub fn with_absm(mut self, absm: Handle<UiNode>) -> Self {
378 self.absm = absm;
379 self
380 }
381
382 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
383 let provider = AbsmEventProvider {
384 widget: self.widget_builder.build(ctx),
385 actions: self.actions.into(),
386 absm: self.absm.into(),
387 };
388
389 ctx.add_node(UiNode::new(provider))
390 }
391}
392
393#[cfg(test)]
394mod test {
395 use crate::absm::AnimationBlendingStateMachineBuilder;
396 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
397
398 #[test]
399 fn test_deletion() {
400 test_widget_deletion(|ctx| {
401 AnimationBlendingStateMachineBuilder::new(WidgetBuilder::new()).build(ctx)
402 });
403 }
404}