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")]
127#[reflect(derived_type = "UiNode")]
128pub struct AnimationBlendingStateMachine {
129 widget: Widget,
130 #[component(include)]
131 machine: InheritableVariable<Machine>,
132 #[component(include)]
133 animation_player: InheritableVariable<Handle<UiNode>>,
134}
135
136impl ConstructorProvider<UiNode, UserInterface> for AnimationBlendingStateMachine {
137 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
138 GraphNodeConstructor::new::<Self>()
139 .with_variant("Animation Blending State Machine", |ui| {
140 AnimationBlendingStateMachineBuilder::new(
141 WidgetBuilder::new().with_name("Animation Blending State Machine"),
142 )
143 .build(&mut ui.build_ctx())
144 .into()
145 })
146 .with_group("Animation")
147 }
148}
149
150impl AnimationBlendingStateMachine {
151 pub fn set_machine(&mut self, machine: Machine) {
153 self.machine.set_value_and_mark_modified(machine);
154 }
155
156 pub fn machine(&self) -> &InheritableVariable<Machine> {
158 &self.machine
159 }
160
161 pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
163 &mut self.machine
164 }
165
166 pub fn set_animation_player(&mut self, animation_player: Handle<UiNode>) {
169 self.animation_player
170 .set_value_and_mark_modified(animation_player);
171 }
172
173 pub fn animation_player(&self) -> Handle<UiNode> {
175 *self.animation_player
176 }
177}
178
179define_widget_deref!(AnimationBlendingStateMachine);
180
181impl Control for AnimationBlendingStateMachine {
182 fn update(&mut self, dt: f32, ui: &mut UserInterface) {
183 if let Some(animation_player) = ui
184 .nodes
185 .try_borrow_mut(*self.animation_player)
186 .and_then(|n| n.component_mut::<AnimationPlayer>())
187 {
188 animation_player.set_auto_apply(false);
191
192 let pose = self
193 .machine
194 .get_value_mut_silent()
195 .evaluate_pose(animation_player.animations.get_value_mut_silent(), dt);
196
197 pose.apply(ui);
198 }
199 }
200
201 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
202 self.widget.handle_routed_message(ui, message)
203 }
204}
205
206pub struct AnimationBlendingStateMachineBuilder {
208 widget_builder: WidgetBuilder,
209 machine: Machine,
210 animation_player: Handle<UiNode>,
211}
212
213impl AnimationBlendingStateMachineBuilder {
214 pub fn new(widget_builder: WidgetBuilder) -> Self {
216 Self {
217 widget_builder,
218 machine: Default::default(),
219 animation_player: Default::default(),
220 }
221 }
222
223 pub fn with_machine(mut self, machine: Machine) -> Self {
225 self.machine = machine;
226 self
227 }
228
229 pub fn with_animation_player(mut self, animation_player: Handle<UiNode>) -> Self {
231 self.animation_player = animation_player;
232 self
233 }
234
235 pub fn build_node(self, ctx: &BuildContext) -> UiNode {
237 UiNode::new(AnimationBlendingStateMachine {
238 widget: self.widget_builder.with_need_update(true).build(ctx),
239 machine: self.machine.into(),
240 animation_player: self.animation_player.into(),
241 })
242 }
243
244 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
246 ctx.add_node(self.build_node(ctx))
247 }
248}
249
250#[derive(
251 Visit,
252 Reflect,
253 Clone,
254 Debug,
255 Default,
256 PartialEq,
257 TypeUuidProvider,
258 AsRefStr,
259 EnumString,
260 VariantNames,
261)]
262#[type_uuid(id = "291e8734-47df-408e-8b2c-57bfb941d8ec")]
263pub enum EventKind {
264 #[default]
265 MouseEnter,
266 MouseLeave,
267 MouseMove,
268 MouseDown(MouseButton),
269 MouseUp(MouseButton),
270 MouseWheel,
271 KeyDown(KeyCode),
272 KeyUp(KeyCode),
273 Focus,
274 Unfocus,
275 TouchStarted,
276 TouchEnded,
277 TouchMoved,
278 TouchCancelled,
279 DoubleTap,
280}
281
282#[derive(Visit, Reflect, Clone, Debug, Default, PartialEq, TypeUuidProvider)]
283#[type_uuid(id = "15f306b8-3bb8-4b35-87bd-6e9e5d748454")]
284pub struct EventAction {
285 kind: EventKind,
286 parameter_name: String,
287 parameter_value: Parameter,
288}
289
290#[derive(Visit, Reflect, Clone, Debug, Default, ComponentProvider, TypeUuidProvider)]
292#[type_uuid(id = "15f306b8-3bb8-4b35-87bd-6e9e5d748455")]
293#[reflect(derived_type = "UiNode")]
294pub struct AbsmEventProvider {
295 widget: Widget,
296 actions: InheritableVariable<Vec<EventAction>>,
297 absm: InheritableVariable<Handle<UiNode>>,
298}
299
300impl ConstructorProvider<UiNode, UserInterface> for AbsmEventProvider {
301 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
302 GraphNodeConstructor::new::<Self>()
303 .with_variant("Absm Event Provider", |ui| {
304 AbsmEventProviderBuilder::new(WidgetBuilder::new().with_name("Absm Event Provider"))
305 .build(&mut ui.build_ctx())
306 .into()
307 })
308 .with_group("Animation")
309 }
310}
311
312define_widget_deref!(AbsmEventProvider);
313
314impl AbsmEventProvider {
315 fn on_event(&self, ui: &mut UserInterface, kind: EventKind) {
316 let Some(action) = self.actions.iter().find(|a| a.kind == kind) else {
317 return;
318 };
319
320 let Some(absm) = ui.try_get_mut_of_type::<AnimationBlendingStateMachine>(*self.absm) else {
321 return;
322 };
323
324 absm.machine_mut()
325 .set_parameter(&action.parameter_name, action.parameter_value);
326 }
327}
328
329impl Control for AbsmEventProvider {
330 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
331 self.widget.handle_routed_message(ui, message);
332
333 let Some(msg) = message.data::<WidgetMessage>() else {
334 return;
335 };
336
337 use WidgetMessage as Msg;
338 match msg {
339 Msg::MouseDown { button, .. } => self.on_event(ui, EventKind::MouseDown(*button)),
340 Msg::MouseUp { button, .. } => self.on_event(ui, EventKind::MouseUp(*button)),
341 Msg::MouseMove { .. } => self.on_event(ui, EventKind::MouseMove),
342 Msg::MouseWheel { .. } => self.on_event(ui, EventKind::MouseWheel),
343 Msg::MouseLeave => self.on_event(ui, EventKind::MouseLeave),
344 Msg::MouseEnter => self.on_event(ui, EventKind::MouseEnter),
345 Msg::Focus => self.on_event(ui, EventKind::Focus),
346 Msg::Unfocus => self.on_event(ui, EventKind::Unfocus),
347 Msg::TouchStarted { .. } => self.on_event(ui, EventKind::TouchStarted),
348 Msg::TouchEnded { .. } => self.on_event(ui, EventKind::TouchEnded),
349 Msg::TouchMoved { .. } => self.on_event(ui, EventKind::TouchMoved),
350 Msg::TouchCancelled { .. } => self.on_event(ui, EventKind::TouchCancelled),
351 Msg::DoubleTap { .. } => self.on_event(ui, EventKind::DoubleTap),
352 Msg::KeyUp(key) => self.on_event(ui, EventKind::KeyUp(*key)),
353 Msg::KeyDown(key) => self.on_event(ui, EventKind::KeyDown(*key)),
354 _ => (),
355 }
356 }
357}
358
359pub struct AbsmEventProviderBuilder {
360 widget_builder: WidgetBuilder,
361 actions: Vec<EventAction>,
362 absm: Handle<UiNode>,
363}
364
365impl AbsmEventProviderBuilder {
366 pub fn new(widget_builder: WidgetBuilder) -> Self {
367 Self {
368 widget_builder,
369 actions: Default::default(),
370 absm: Default::default(),
371 }
372 }
373
374 pub fn with_actions(mut self, actions: Vec<EventAction>) -> Self {
375 self.actions = actions;
376 self
377 }
378
379 pub fn with_absm(mut self, absm: Handle<UiNode>) -> Self {
380 self.absm = absm;
381 self
382 }
383
384 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
385 let provider = AbsmEventProvider {
386 widget: self.widget_builder.build(ctx),
387 actions: self.actions.into(),
388 absm: self.absm.into(),
389 };
390
391 ctx.add_node(UiNode::new(provider))
392 }
393}
394
395#[cfg(test)]
396mod test {
397 use crate::absm::AnimationBlendingStateMachineBuilder;
398 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
399
400 #[test]
401 fn test_deletion() {
402 test_widget_deletion(|ctx| {
403 AnimationBlendingStateMachineBuilder::new(WidgetBuilder::new()).build(ctx)
404 });
405 }
406}