Merge pull request #580 from FyroxEngine/animation

Animation refactoring
This commit is contained in:
Dmitry Stepanov
2023-12-26 21:49:22 +03:00
committed by GitHub
54 changed files with 1050 additions and 937 deletions
+1
View File
@@ -31,6 +31,7 @@
<sourceFolder url="file://$MODULE_DIR$/editor-standalone/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/template/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/fyrox-scripts/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/fyrox-animation/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/examples/wasm/target" />
</content>
+2 -1
View File
@@ -23,6 +23,7 @@ members = [
"fyrox-ui",
"fyrox-resource",
"fyrox-scripts",
"fyrox-animation",
"editor",
"editor-standalone",
"template"
@@ -41,6 +42,7 @@ fyrox-core = { path = "fyrox-core", version = "0.26.0", features = ["serde"] }
fyrox-sound = { path = "fyrox-sound", version = "0.33.0" }
fyrox-ui = { path = "fyrox-ui", version = "0.23.0" }
fyrox-resource = { path = "fyrox-resource", version = "0.10.0" }
fyrox-animation = { path = "fyrox-animation", version = "0.1.0" }
rapier2d = { version = "0.17", features = ["debug-render"] }
rapier3d = { version = "0.17", features = ["debug-render"] }
image = { version = "0.24.3", default-features = false, features = ["gif", "jpeg", "png", "tga", "tiff", "bmp"] }
@@ -58,7 +60,6 @@ fxhash = "0.2.1"
strum = "0.25.0"
strum_macros = "0.25.0"
clap = { version = "4", features = ["derive"] }
spade = "2.1.0"
winit = { version = "0.29.2", features = ["serde"] }
half = "2.2.1"
fast_image_resize = "2.7.0"
+3 -2
View File
@@ -11,6 +11,7 @@ use crate::{
send_sync_message,
};
use fyrox::core::uuid_provider;
use fyrox::scene::node::Node;
use fyrox::{
animation::machine::{
node::blendspace::BlendSpacePoint, Machine, MachineLayer, Parameter, ParameterContainer,
@@ -811,7 +812,7 @@ impl BlendSpaceEditor {
pub fn sync_to_model(
&mut self,
parameters: &ParameterContainer,
layer: &MachineLayer,
layer: &MachineLayer<Handle<Node>>,
selection: &AbsmSelection,
ui: &mut UserInterface,
) {
@@ -893,7 +894,7 @@ impl BlendSpaceEditor {
selection: &AbsmSelection,
message: &UiMessage,
sender: &MessageSender,
machine: &mut Machine,
machine: &mut Machine<Handle<Node>>,
is_preview_mode_active: bool,
) {
if let Some(SelectedEntity::PoseNode(first)) = selection.entities.first() {
+9 -9
View File
@@ -13,7 +13,7 @@ use fyrox::{
scene::node::Node,
};
define_push_element_to_collection_command!(AddInputCommand<Handle<PoseNode>, IndexedBlendInput>(self, context) {
define_push_element_to_collection_command!(AddInputCommand<Handle<PoseNode<Handle<Node>>>, IndexedBlendInput<Handle<Node>>>(self, context) {
let machine = fetch_machine(context, self.node_handle);
match &mut machine.layers_mut()[self.layer_index].nodes_mut()[self.handle] {
PoseNode::BlendAnimationsByIndex(definition) => &mut definition.inputs,
@@ -21,7 +21,7 @@ define_push_element_to_collection_command!(AddInputCommand<Handle<PoseNode>, Ind
}
});
define_push_element_to_collection_command!(AddPoseSourceCommand<Handle<PoseNode>, BlendPose>(self, context) {
define_push_element_to_collection_command!(AddPoseSourceCommand<Handle<PoseNode<Handle<Node>>>, BlendPose<Handle<Node>>>(self, context) {
let machine = fetch_machine(context, self.node_handle);
match &mut machine.layers_mut()[self.layer_index].nodes_mut()[self.handle] {
PoseNode::BlendAnimations(definition) => &mut definition.pose_sources,
@@ -29,7 +29,7 @@ define_push_element_to_collection_command!(AddPoseSourceCommand<Handle<PoseNode>
}
});
define_push_element_to_collection_command!(AddBlendSpacePointCommand<Handle<PoseNode>, BlendSpacePoint>(self, context) {
define_push_element_to_collection_command!(AddBlendSpacePointCommand<Handle<PoseNode<Handle<Node>>>, BlendSpacePoint<Handle<Node>>>(self, context) {
let machine = fetch_machine(context, self.node_handle);
match &mut machine.layers_mut()[self.layer_index].nodes_mut()[self.handle] {
PoseNode::BlendSpace(definition) => definition.points_mut(),
@@ -38,7 +38,7 @@ define_push_element_to_collection_command!(AddBlendSpacePointCommand<Handle<Pose
});
define_set_collection_element_command!(
SetBlendAnimationByIndexInputPoseSourceCommand<Handle<PoseNode>, Handle<PoseNode>>(self, context) {
SetBlendAnimationByIndexInputPoseSourceCommand<Handle<PoseNode<Handle<Node>>>, Handle<PoseNode<Handle<Node>>>>(self, context) {
let machine = fetch_machine(context, self.node_handle);
if let PoseNode::BlendAnimationsByIndex(ref mut definition) = machine.layers_mut()[self.layer_index].nodes_mut()[self.handle] {
std::mem::swap(&mut definition.inputs[self.index].pose_source, &mut self.value)
@@ -47,7 +47,7 @@ define_set_collection_element_command!(
);
define_set_collection_element_command!(
SetBlendAnimationsPoseSourceCommand<Handle<PoseNode>, Handle<PoseNode>>(self, context) {
SetBlendAnimationsPoseSourceCommand<Handle<PoseNode<Handle<Node>>>, Handle<PoseNode<Handle<Node>>>>(self, context) {
let machine = fetch_machine(context, self.node_handle);
if let PoseNode::BlendAnimations(ref mut definition) = machine.layers_mut()[self.layer_index].nodes_mut()[self.handle] {
std::mem::swap(&mut definition.pose_sources[self.index].pose_source, &mut self.value);
@@ -56,7 +56,7 @@ define_set_collection_element_command!(
);
define_set_collection_element_command!(
SetBlendSpacePoseSourceCommand<Handle<PoseNode>, Handle<PoseNode>>(self, context) {
SetBlendSpacePoseSourceCommand<Handle<PoseNode<Handle<Node>>>, Handle<PoseNode<Handle<Node>>>>(self, context) {
let machine = fetch_machine(context, self.node_handle);
if let PoseNode::BlendSpace(ref mut definition) = machine.layers_mut()[self.layer_index].nodes_mut()[self.handle] {
std::mem::swap( &mut definition.points_mut()[self.index].pose_source, &mut self.value);
@@ -65,7 +65,7 @@ define_set_collection_element_command!(
);
define_set_collection_element_command!(
SetBlendSpacePointPositionCommand<Handle<PoseNode>, Vector2<f32>>(self, context) {
SetBlendSpacePointPositionCommand<Handle<PoseNode<Handle<Node>>>, Vector2<f32>>(self, context) {
let machine = fetch_machine(context, self.node_handle);
if let PoseNode::BlendSpace(ref mut definition) = machine.layers_mut()[self.layer_index].nodes_mut()[self.handle] {
std::mem::swap( &mut definition.points_mut()[self.index].position, &mut self.value);
@@ -78,9 +78,9 @@ define_set_collection_element_command!(
pub struct RemoveBlendSpacePointCommand {
pub scene_node_handle: Handle<Node>,
pub layer_index: usize,
pub node_handle: Handle<PoseNode>,
pub node_handle: Handle<PoseNode<Handle<Node>>>,
pub point_index: usize,
pub point: Option<BlendSpacePoint>,
pub point: Option<BlendSpacePoint<Handle<Node>>>,
}
impl GameSceneCommandTrait for RemoveBlendSpacePointCommand {
+36 -24
View File
@@ -126,7 +126,11 @@ macro_rules! define_spawn_command {
};
}
define_spawn_command!(AddTransitionCommand, Transition, transitions_mut);
define_spawn_command!(
AddTransitionCommand,
Transition<Handle<Node>>,
transitions_mut
);
#[derive(Debug)]
pub enum AddStateCommand {
@@ -134,24 +138,24 @@ pub enum AddStateCommand {
NonExecuted {
node_handle: Handle<Node>,
layer_index: usize,
state: State,
state: State<Handle<Node>>,
},
Executed {
node_handle: Handle<Node>,
layer_index: usize,
handle: Handle<State>,
prev_entry_state: Handle<State>,
handle: Handle<State<Handle<Node>>>,
prev_entry_state: Handle<State<Handle<Node>>>,
},
Reverted {
node_handle: Handle<Node>,
layer_index: usize,
ticket: Ticket<State>,
state: State,
ticket: Ticket<State<Handle<Node>>>,
state: State<Handle<Node>>,
},
}
impl AddStateCommand {
pub fn new(node_handle: Handle<Node>, layer_index: usize, state: State) -> Self {
pub fn new(node_handle: Handle<Node>, layer_index: usize, state: State<Handle<Node>>) -> Self {
Self::NonExecuted {
node_handle,
layer_index,
@@ -163,7 +167,7 @@ impl AddStateCommand {
fn fetch_machine<'a>(
context: &'a mut GameSceneContext,
node_handle: Handle<Node>,
) -> &'a mut Machine {
) -> &'a mut Machine<Handle<Node>> {
context.scene.graph[node_handle]
.query_component_mut::<AnimationBlendingStateMachine>()
.unwrap()
@@ -279,24 +283,28 @@ pub enum AddPoseNodeCommand {
NonExecuted {
node_handle: Handle<Node>,
layer_index: usize,
node: PoseNode,
node: PoseNode<Handle<Node>>,
},
Executed {
node_handle: Handle<Node>,
layer_index: usize,
handle: Handle<PoseNode>,
prev_root_node: Handle<PoseNode>,
handle: Handle<PoseNode<Handle<Node>>>,
prev_root_node: Handle<PoseNode<Handle<Node>>>,
},
Reverted {
node_handle: Handle<Node>,
layer_index: usize,
ticket: Ticket<PoseNode>,
node: PoseNode,
ticket: Ticket<PoseNode<Handle<Node>>>,
node: PoseNode<Handle<Node>>,
},
}
impl AddPoseNodeCommand {
pub fn new(node_handle: Handle<Node>, layer_index: usize, node: PoseNode) -> Self {
pub fn new(
node_handle: Handle<Node>,
layer_index: usize,
node: PoseNode<Handle<Node>>,
) -> Self {
Self::NonExecuted {
node_handle,
layer_index,
@@ -464,8 +472,8 @@ macro_rules! define_move_command {
};
}
define_move_command!(MoveStateNodeCommand, State, states_mut);
define_move_command!(MovePoseNodeCommand, PoseNode, nodes_mut);
define_move_command!(MoveStateNodeCommand, State<Handle<Node>>, states_mut);
define_move_command!(MovePoseNodeCommand, PoseNode<Handle<Node>>, nodes_mut);
macro_rules! define_free_command {
($name:ident, $ent_type:ty, $container:ident) => {
@@ -578,9 +586,13 @@ macro_rules! define_free_command {
};
}
define_free_command!(DeleteStateCommand, State, states_mut);
define_free_command!(DeletePoseNodeCommand, PoseNode, nodes_mut);
define_free_command!(DeleteTransitionCommand, Transition, transitions_mut);
define_free_command!(DeleteStateCommand, State<Handle<Node>>, states_mut);
define_free_command!(DeletePoseNodeCommand, PoseNode<Handle<Node>>, nodes_mut);
define_free_command!(
DeleteTransitionCommand,
Transition<Handle<Node>>,
transitions_mut
);
#[macro_export]
macro_rules! define_push_element_to_collection_command {
@@ -700,7 +712,7 @@ macro_rules! define_set_collection_element_command {
pub struct SetMachineEntryStateCommand {
pub node_handle: Handle<Node>,
pub layer: usize,
pub entry: Handle<State>,
pub entry: Handle<State<Handle<Node>>>,
}
impl SetMachineEntryStateCommand {
@@ -765,7 +777,7 @@ macro_rules! define_absm_swap_command {
};
}
define_absm_swap_command!(SetStateRootPoseCommand<Handle<State>, Handle<PoseNode>>[layer_index: usize](self, context) {
define_absm_swap_command!(SetStateRootPoseCommand<Handle<State<Handle<Node>>>, Handle<PoseNode<Handle<Node>>>>[layer_index: usize](self, context) {
let machine = fetch_machine(context, self.node_handle);
&mut machine.layers_mut()[self.layer_index].states_mut()[self.handle].root
});
@@ -804,7 +816,7 @@ impl GameSceneCommandTrait for SetLayerNameCommand {
#[derive(Debug)]
pub struct AddLayerCommand {
pub absm_node_handle: Handle<Node>,
pub layer: Option<MachineLayer>,
pub layer: Option<MachineLayer<Handle<Node>>>,
}
impl GameSceneCommandTrait for AddLayerCommand {
@@ -825,7 +837,7 @@ impl GameSceneCommandTrait for AddLayerCommand {
pub struct RemoveLayerCommand {
pub absm_node_handle: Handle<Node>,
pub layer_index: usize,
pub layer: Option<MachineLayer>,
pub layer: Option<MachineLayer<Handle<Node>>>,
}
impl RemoveLayerCommand {
@@ -858,7 +870,7 @@ impl GameSceneCommandTrait for RemoveLayerCommand {
pub struct SetLayerMaskCommand {
pub absm_node_handle: Handle<Node>,
pub layer_index: usize,
pub mask: LayerMask,
pub mask: LayerMask<Handle<Node>>,
}
impl SetLayerMaskCommand {
+1 -1
View File
@@ -15,7 +15,7 @@ define_universal_commands! {
GameSceneCommandTrait,
GameSceneCommand,
GameSceneContext,
Handle<PoseNode>,
Handle<PoseNode<Handle<Node>>>,
ctx,
handle,
self,
+1 -1
View File
@@ -15,7 +15,7 @@ define_universal_commands!(
GameSceneCommandTrait,
GameSceneCommand,
GameSceneContext,
Handle<State>,
Handle<State<Handle<Node>>>,
ctx,
handle,
self,
+1 -1
View File
@@ -15,7 +15,7 @@ define_universal_commands!(
GameSceneCommandTrait,
GameSceneCommand,
GameSceneContext,
Handle<Transition>,
Handle<Transition<Handle<Node>>>,
ctx,
handle,
self,
+5 -5
View File
@@ -59,7 +59,7 @@ const NORMAL_ROOT_COLOR: Color = Color::opaque(40, 80, 0);
const SELECTED_ROOT_COLOR: Color = Color::opaque(60, 100, 0);
struct PreviewModeData {
machine: Machine,
machine: Machine<Handle<Node>>,
nodes: Vec<(Handle<Node>, Node)>,
}
@@ -175,7 +175,7 @@ impl AbsmEditor {
fn enter_preview_mode(
&mut self,
machine: Machine,
machine: Machine<Handle<Node>>,
animation_targets: FxHashSet<Handle<Node>>,
scene: &Scene,
ui: &UserInterface,
@@ -525,7 +525,7 @@ impl AbsmEditor {
AbsmNodeMessage::Enter => {
if let Some(node) = ui
.node(message.destination())
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
{
if let Some(layer_index) = selection.layer {
self.state_viewer.set_state(
@@ -541,7 +541,7 @@ impl AbsmEditor {
AbsmNodeMessage::Edit => {
if let Some(node) = ui
.node(message.destination())
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
{
if let Some(layer_index) = selection.layer {
let model_ref = &absm_node.machine().layers()[layer_index].nodes()
@@ -556,7 +556,7 @@ impl AbsmEditor {
AbsmNodeMessage::AddInput => {
if let Some(node) = ui
.node(message.destination())
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
{
if let Some(layer_index) = selection.layer {
let model_ref = &absm_node.machine().layers()[layer_index].nodes()
+3 -3
View File
@@ -6,9 +6,9 @@ use fyrox::{
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum SelectedEntity {
Transition(Handle<Transition>),
State(Handle<State>),
PoseNode(Handle<PoseNode>),
Transition(Handle<Transition<Handle<Node>>>),
State(Handle<State<Handle<Node>>>),
PoseNode(Handle<PoseNode<Handle<Node>>>),
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
+4 -3
View File
@@ -1,4 +1,5 @@
use fyrox::core::uuid_provider;
use fyrox::scene::node::Node;
use fyrox::{
animation::machine::PoseNode,
core::{algebra::Vector2, color::Color, pool::Handle},
@@ -44,7 +45,7 @@ pub enum SocketDirection {
pub struct Socket {
widget: Widget,
click_position: Option<Vector2<f32>>,
pub parent_node: Handle<PoseNode>,
pub parent_node: Handle<PoseNode<Handle<Node>>>,
pub direction: SocketDirection,
#[allow(dead_code)] // TODO
editor: Handle<UiNode>,
@@ -124,7 +125,7 @@ impl Control for Socket {
pub struct SocketBuilder {
widget_builder: WidgetBuilder,
parent_node: Handle<PoseNode>,
parent_node: Handle<PoseNode<Handle<Node>>>,
direction: SocketDirection,
editor: Handle<UiNode>,
index: usize,
@@ -143,7 +144,7 @@ impl SocketBuilder {
}
}
pub fn with_parent_node(mut self, parent_node: Handle<PoseNode>) -> Self {
pub fn with_parent_node(mut self, parent_node: Handle<PoseNode<Handle<Node>>>) -> Self {
self.parent_node = parent_node;
self
}
+2 -2
View File
@@ -277,7 +277,7 @@ impl NodeContextMenu {
layer: layer_index,
entry: ui
.node(self.placement_target)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.unwrap()
.model_handle,
});
@@ -296,7 +296,7 @@ impl NodeContextMenu {
.children()
.iter()
.cloned()
.filter(|c| ui.node(*c).has_component::<AbsmNode<State>>())
.filter(|c| ui.node(*c).has_component::<AbsmNode<State<Handle<Node>>>>())
.collect::<Vec<_>>();
ui.send_message(AbsmCanvasMessage::commit_transition_to_all_nodes(
self.canvas,
+22 -15
View File
@@ -41,9 +41,12 @@ pub struct StateGraphViewer {
last_selection: AbsmSelection,
}
fn fetch_state_node_model_handle(handle: Handle<UiNode>, ui: &UserInterface) -> Handle<State> {
fn fetch_state_node_model_handle(
handle: Handle<UiNode>,
ui: &UserInterface,
) -> Handle<State<Handle<Node>>> {
ui.node(handle)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.unwrap()
.model_handle
}
@@ -93,7 +96,11 @@ impl StateGraphViewer {
}
}
pub fn activate_transition(&self, ui: &UserInterface, transition: Handle<Transition>) {
pub fn activate_transition(
&self,
ui: &UserInterface,
transition: Handle<Transition<Handle<Node>>>,
) {
if let Some(view_handle) = ui.node(self.canvas).children().iter().cloned().find(|c| {
ui.node(*c)
.query_component::<TransitionView>()
@@ -108,7 +115,7 @@ impl StateGraphViewer {
}
}
pub fn activate_state(&self, ui: &UserInterface, state: Handle<State>) {
pub fn activate_state(&self, ui: &UserInterface, state: Handle<State<Handle<Node>>>) {
for (state_view_handle, state_view_ref) in ui
.node(self.canvas)
.children()
@@ -116,7 +123,7 @@ impl StateGraphViewer {
.cloned()
.filter_map(|c| {
ui.node(c)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.map(|state_view_ref| (c, state_view_ref))
})
{
@@ -184,8 +191,8 @@ impl StateGraphViewer {
.filter_map(|n| {
let node_ref = ui.node(*n);
if let Some(state_node) =
node_ref.query_component::<AbsmNode<State>>()
if let Some(state_node) = node_ref
.query_component::<AbsmNode<State<Handle<Node>>>>()
{
Some(SelectedEntity::State(state_node.model_handle))
} else {
@@ -263,7 +270,7 @@ impl StateGraphViewer {
pub fn sync_to_model(
&mut self,
machine_layer: &MachineLayer,
machine_layer: &MachineLayer<Handle<Node>>,
ui: &mut UserInterface,
editor_selection: &Selection,
) {
@@ -287,7 +294,7 @@ impl StateGraphViewer {
.children()
.iter()
.cloned()
.filter(|c| ui.node(*c).has_component::<AbsmNode<State>>())
.filter(|c| ui.node(*c).has_component::<AbsmNode<State<Handle<Node>>>>())
.collect::<Vec<_>>();
transitions = canvas
@@ -307,7 +314,7 @@ impl StateGraphViewer {
for (state_handle, state) in machine_layer.states().pair_iter() {
if states.iter().all(|state_view| {
ui.node(*state_view)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.unwrap()
.model_handle
!= state_handle
@@ -351,7 +358,7 @@ impl StateGraphViewer {
(
state_view,
ui.node(state_view)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.unwrap()
.model_handle,
)
@@ -381,7 +388,7 @@ impl StateGraphViewer {
for state in states.iter() {
let state_node = ui
.node(*state)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.unwrap();
let state_model_handle = state_node.model_handle;
let state_model_ref = &machine_layer.states()[state_node.model_handle];
@@ -451,7 +458,7 @@ impl StateGraphViewer {
!= transition_handle
}) {
fn find_state_view(
state_handle: Handle<State>,
state_handle: Handle<State<Handle<Node>>>,
states: &[Handle<UiNode>],
ui: &UserInterface,
) -> Handle<UiNode> {
@@ -459,7 +466,7 @@ impl StateGraphViewer {
.iter()
.find(|s| {
ui.node(**s)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.unwrap()
.model_handle
== state_handle
@@ -547,7 +554,7 @@ impl StateGraphViewer {
}),
SelectedEntity::State(state) => states.iter().cloned().find(|s| {
ui.node(*s)
.query_component::<AbsmNode<State>>()
.query_component::<AbsmNode<State<Handle<Node>>>>()
.unwrap()
.model_handle
== *state
+5 -5
View File
@@ -99,7 +99,7 @@ impl CanvasContextMenu {
&mut self,
sender: &MessageSender,
message: &UiMessage,
current_state: Handle<State>,
current_state: Handle<State<Handle<Node>>>,
ui: &mut UserInterface,
absm_node_handle: Handle<Node>,
layer_index: usize,
@@ -221,7 +221,7 @@ impl NodeContextMenu {
pub fn handle_ui_message(
&mut self,
message: &UiMessage,
machine_layer: &MachineLayer,
machine_layer: &MachineLayer<Handle<Node>>,
sender: &MessageSender,
ui: &UserInterface,
editor_selection: &Selection,
@@ -256,7 +256,7 @@ impl NodeContextMenu {
} else if message.destination() == self.set_as_root {
let root = ui
.node(self.placement_target)
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
.unwrap()
.model_handle;
@@ -307,7 +307,7 @@ impl ConnectionContextMenu {
message: &UiMessage,
ui: &mut UserInterface,
sender: &MessageSender,
machine_layer: &MachineLayer,
machine_layer: &MachineLayer<Handle<Node>>,
absm_node_handle: Handle<Node>,
layer_index: usize,
) {
@@ -320,7 +320,7 @@ impl ConnectionContextMenu {
let dest_node_ref = ui
.node(connection_ref.dest_node)
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
.unwrap();
let index = dest_node_ref
+29 -18
View File
@@ -49,7 +49,7 @@ mod context;
pub struct StateViewer {
pub window: Handle<UiNode>,
canvas: Handle<UiNode>,
state: Handle<State>,
state: Handle<State<Handle<Node>>>,
canvas_context_menu: CanvasContextMenu,
node_context_menu: NodeContextMenu,
connection_context_menu: ConnectionContextMenu,
@@ -60,7 +60,7 @@ fn create_socket(
direction: SocketDirection,
index: usize,
show_index: bool,
parent_node: Handle<PoseNode>,
parent_node: Handle<PoseNode<Handle<Node>>>,
ui: &mut UserInterface,
) -> Handle<UiNode> {
SocketBuilder::new(WidgetBuilder::new().with_margin(Thickness::uniform(2.0)))
@@ -74,7 +74,7 @@ fn create_socket(
fn create_sockets(
count: usize,
direction: SocketDirection,
parent_node: Handle<PoseNode>,
parent_node: Handle<PoseNode<Handle<Node>>>,
ui: &mut UserInterface,
) -> Vec<Handle<UiNode>> {
(0..count)
@@ -82,9 +82,12 @@ fn create_sockets(
.collect::<Vec<_>>()
}
fn fetch_pose_node_model_handle(handle: Handle<UiNode>, ui: &UserInterface) -> Handle<PoseNode> {
fn fetch_pose_node_model_handle(
handle: Handle<UiNode>,
ui: &UserInterface,
) -> Handle<PoseNode<Handle<Node>>> {
ui.node(handle)
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
.unwrap()
.model_handle
}
@@ -92,7 +95,7 @@ fn fetch_pose_node_model_handle(handle: Handle<UiNode>, ui: &UserInterface) -> H
fn fetch_socket_pose_node_model_handle(
handle: Handle<UiNode>,
ui: &UserInterface,
) -> Handle<PoseNode> {
) -> Handle<PoseNode<Handle<Node>>> {
ui.node(handle)
.query_component::<Socket>()
.unwrap()
@@ -102,7 +105,7 @@ fn fetch_socket_pose_node_model_handle(
fn make_play_animation_name(
graph: &Graph,
absm_node: &AnimationBlendingStateMachine,
animation: Handle<Animation>,
animation: Handle<Animation<Handle<Node>>>,
) -> String {
if let Some(animation) = graph
.try_get_of_type::<AnimationPlayer>(absm_node.animation_player())
@@ -115,7 +118,7 @@ fn make_play_animation_name(
}
fn make_pose_node_name(
model_ref: &PoseNode,
model_ref: &PoseNode<Handle<Node>>,
graph: &Graph,
absm_node: &AnimationBlendingStateMachine,
) -> String {
@@ -177,7 +180,7 @@ impl StateViewer {
pub fn set_state(
&mut self,
state: Handle<State>,
state: Handle<State<Handle<Node>>>,
absm_node: &AnimationBlendingStateMachine,
layer_index: usize,
ui: &UserInterface,
@@ -280,7 +283,7 @@ impl StateViewer {
.filter_map(|n| {
let node_ref = ui.node(*n);
node_ref.query_component::<AbsmNode<PoseNode>>().map(
node_ref.query_component::<AbsmNode<PoseNode<Handle<Node>>>>().map(
|state_node| {
SelectedEntity::PoseNode(
state_node.model_handle,
@@ -380,7 +383,7 @@ impl StateViewer {
pub fn sync_to_model(
&mut self,
ui: &mut UserInterface,
machine_layer: &MachineLayer,
machine_layer: &MachineLayer<Handle<Node>>,
editor_selection: &Selection,
absm_node: &AnimationBlendingStateMachine,
graph: &Graph,
@@ -401,7 +404,9 @@ impl StateViewer {
.iter()
.cloned()
.filter(|h| {
if let Some(pose_node) = ui.node(*h).query_component::<AbsmNode<PoseNode>>()
if let Some(pose_node) = ui
.node(*h)
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
{
if machine_layer
.nodes()
@@ -444,7 +449,7 @@ impl StateViewer {
for &pose_definition in models.iter() {
if views.iter().all(|v| {
ui.node(*v)
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
.unwrap()
.model_handle
!= pose_definition
@@ -527,7 +532,7 @@ impl StateViewer {
for &view in views.clone().iter() {
let view_ref = ui
.node(view)
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
.unwrap();
if machine_layer
@@ -553,7 +558,7 @@ impl StateViewer {
for &view in &views {
let view_ref = ui
.node(view)
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
.unwrap();
let model_handle = view_ref.model_handle;
let model_ref = &machine_layer.nodes()[model_handle];
@@ -640,7 +645,10 @@ impl StateViewer {
for model in models.iter().cloned() {
let dest_ref = views
.iter()
.filter_map(|v| ui.node(*v).query_component::<AbsmNode<PoseNode>>())
.filter_map(|v| {
ui.node(*v)
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
})
.find(|v| v.model_handle == model)
.unwrap();
let dest_handle = dest_ref.handle();
@@ -654,7 +662,10 @@ impl StateViewer {
if machine_layer.nodes().is_valid_handle(child) {
let source = views
.iter()
.filter_map(|v| ui.node(*v).query_component::<AbsmNode<PoseNode>>())
.filter_map(|v| {
ui.node(*v)
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
})
.find(|v| v.model_handle == child)
.unwrap();
@@ -695,7 +706,7 @@ impl StateViewer {
}
SelectedEntity::PoseNode(pose_node) => views.iter().cloned().find(|s| {
ui.node(*s)
.query_component::<AbsmNode<PoseNode>>()
.query_component::<AbsmNode<PoseNode<Handle<Node>>>>()
.unwrap()
.model_handle
== *pose_node
+3 -2
View File
@@ -1,4 +1,3 @@
use crate::message::MessageSender;
use crate::{
absm::{
command::{AddLayerCommand, RemoveLayerCommand, SetLayerMaskCommand, SetLayerNameCommand},
@@ -7,6 +6,7 @@ use crate::{
},
gui::make_dropdown_list_option,
load_image,
message::MessageSender,
scene::{
commands::{ChangeSelectionCommand, CommandGroup, GameSceneCommand},
selector::{HierarchyNode, NodeSelectorMessage, NodeSelectorWindowBuilder},
@@ -287,7 +287,8 @@ impl Toolbar {
.mask()
.inner()
.iter()
.map(|h| ErasedHandle::from(*h))
.cloned()
.map(ErasedHandle::from)
.collect::<Vec<_>>();
ui.send_message(NodeSelectorMessage::selection(
+7 -2
View File
@@ -7,6 +7,7 @@ use crate::{
};
use fyrox::animation::machine::Transition;
use fyrox::core::uuid_provider;
use fyrox::scene::node::Node;
use fyrox::{
core::{algebra::Vector2, color::Color, color::Hsv, math::Rect, pool::Handle},
core::{reflect::prelude::*, visitor::prelude::*},
@@ -46,7 +47,7 @@ impl TransitionMessage {
pub struct TransitionView {
widget: Widget,
pub segment: Segment,
pub model_handle: Handle<Transition>,
pub model_handle: Handle<Transition<Handle<Node>>>,
selectable: Selectable,
activity_factor: f32,
}
@@ -182,7 +183,11 @@ impl TransitionBuilder {
self
}
pub fn build(self, model_handle: Handle<Transition>, ctx: &mut BuildContext) -> Handle<UiNode> {
pub fn build(
self,
model_handle: Handle<Transition<Handle<Node>>>,
ctx: &mut BuildContext,
) -> Handle<UiNode> {
let transition = TransitionView {
widget: self
.widget_builder
+37 -29
View File
@@ -34,12 +34,16 @@ fn fetch_animation_player<'a>(
#[derive(Debug)]
pub struct AddTrackCommand {
animation_player: Handle<Node>,
animation: Handle<Animation>,
track: Option<Track>,
animation: Handle<Animation<Handle<Node>>>,
track: Option<Track<Handle<Node>>>,
}
impl AddTrackCommand {
pub fn new(animation_player: Handle<Node>, animation: Handle<Animation>, track: Track) -> Self {
pub fn new(
animation_player: Handle<Node>,
animation: Handle<Animation<Handle<Node>>>,
track: Track<Handle<Node>>,
) -> Self {
Self {
animation_player,
animation,
@@ -68,13 +72,17 @@ impl GameSceneCommandTrait for AddTrackCommand {
#[derive(Debug)]
pub struct RemoveTrackCommand {
animation_player: Handle<Node>,
animation: Handle<Animation>,
animation: Handle<Animation<Handle<Node>>>,
index: usize,
track: Option<Track>,
track: Option<Track<Handle<Node>>>,
}
impl RemoveTrackCommand {
pub fn new(animation_player: Handle<Node>, animation: Handle<Animation>, index: usize) -> Self {
pub fn new(
animation_player: Handle<Node>,
animation: Handle<Animation<Handle<Node>>>,
index: usize,
) -> Self {
Self {
animation_player,
animation,
@@ -105,7 +113,7 @@ impl GameSceneCommandTrait for RemoveTrackCommand {
#[derive(Debug)]
pub struct ReplaceTrackCurveCommand {
pub animation_player: Handle<Node>,
pub animation: Handle<Animation>,
pub animation: Handle<Animation<Handle<Node>>>,
pub curve: Curve,
}
@@ -146,23 +154,23 @@ pub enum AddAnimationCommand {
Unknown,
NonExecuted {
animation_player: Handle<Node>,
animation: Animation,
animation: Animation<Handle<Node>>,
},
Executed {
animation_player: Handle<Node>,
animation: Handle<Animation>,
animation: Handle<Animation<Handle<Node>>>,
selection: Selection,
},
Reverted {
animation_player: Handle<Node>,
animation: Animation,
ticket: Ticket<Animation>,
animation: Animation<Handle<Node>>,
ticket: Ticket<Animation<Handle<Node>>>,
selection: Selection,
},
}
impl AddAnimationCommand {
pub fn new(animation_player: Handle<Node>, animation: Animation) -> Self {
pub fn new(animation_player: Handle<Node>, animation: Animation<Handle<Node>>) -> Self {
Self::NonExecuted {
animation_player,
animation,
@@ -265,21 +273,21 @@ pub enum RemoveAnimationCommand {
Unknown,
NonExecuted {
animation_player: Handle<Node>,
animation: Handle<Animation>,
animation: Handle<Animation<Handle<Node>>>,
},
Executed {
animation_player: Handle<Node>,
animation: Animation,
ticket: Ticket<Animation>,
animation: Animation<Handle<Node>>,
ticket: Ticket<Animation<Handle<Node>>>,
},
Reverted {
animation_player: Handle<Node>,
animation: Handle<Animation>,
animation: Handle<Animation<Handle<Node>>>,
},
}
impl RemoveAnimationCommand {
pub fn new(animation_player: Handle<Node>, animation: Handle<Animation>) -> Self {
pub fn new(animation_player: Handle<Node>, animation: Handle<Animation<Handle<Node>>>) -> Self {
Self::NonExecuted {
animation_player,
animation,
@@ -353,8 +361,8 @@ impl GameSceneCommandTrait for RemoveAnimationCommand {
#[derive(Debug)]
pub struct ReplaceAnimationCommand {
pub animation_player: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation: Animation,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub animation: Animation<Handle<Node>>,
}
impl ReplaceAnimationCommand {
@@ -388,7 +396,7 @@ macro_rules! define_animation_swap_command {
#[derive(Debug)]
pub struct $name {
pub node_handle: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub value: $value_type,
}
@@ -416,9 +424,9 @@ macro_rules! define_animation_swap_command {
fn fetch_animation<'a>(
animation_player: Handle<Node>,
animation: Handle<Animation>,
animation: Handle<Animation<Handle<Node>>>,
ctx: &'a mut GameSceneContext,
) -> &'a mut Animation {
) -> &'a mut Animation<Handle<Node>> {
fetch_animation_player(animation_player, ctx)
.animations_mut()
.index_mut(animation)
@@ -459,7 +467,7 @@ define_animation_swap_command!(SetAnimationEnabledCommand<bool>(self, context) {
self.value = old;
});
define_animation_swap_command!(SetAnimationRootMotionSettingsCommand<Option<RootMotionSettings>>(self, context) {
define_animation_swap_command!(SetAnimationRootMotionSettingsCommand<Option<RootMotionSettings<Handle<Node>>>>(self, context) {
let animation = fetch_animation(self.node_handle, self.animation_handle, context);
let old = animation.root_motion_settings_ref().cloned();
animation.set_root_motion_settings(self.value.clone());
@@ -469,7 +477,7 @@ define_animation_swap_command!(SetAnimationRootMotionSettingsCommand<Option<Root
#[derive(Debug)]
pub struct AddAnimationSignal {
pub animation_player_handle: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub signal: Option<AnimationSignal>,
}
@@ -492,7 +500,7 @@ impl GameSceneCommandTrait for AddAnimationSignal {
#[derive(Debug)]
pub struct MoveAnimationSignal {
pub animation_player_handle: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub signal: Uuid,
pub time: f32,
}
@@ -528,7 +536,7 @@ impl GameSceneCommandTrait for MoveAnimationSignal {
#[derive(Debug)]
pub struct RemoveAnimationSignal {
pub animation_player_handle: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub signal_index: usize,
pub signal: Option<AnimationSignal>,
}
@@ -554,7 +562,7 @@ impl GameSceneCommandTrait for RemoveAnimationSignal {
#[derive(Debug)]
pub struct SetTrackEnabledCommand {
pub animation_player_handle: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub track: Uuid,
pub enabled: bool,
}
@@ -590,7 +598,7 @@ impl GameSceneCommandTrait for SetTrackEnabledCommand {
#[derive(Debug)]
pub struct SetTrackTargetCommand {
pub animation_player_handle: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub track: Uuid,
pub target: Handle<Node>,
}
@@ -626,7 +634,7 @@ impl GameSceneCommandTrait for SetTrackTargetCommand {
#[derive(Debug)]
pub struct SetTrackBindingCommand {
pub animation_player_handle: Handle<Node>,
pub animation_handle: Handle<Animation>,
pub animation_handle: Handle<Animation<Handle<Node>>>,
pub track: Uuid,
pub binding: ValueBinding,
}
+1 -1
View File
@@ -27,5 +27,5 @@ define_universal_commands!(
.unwrap()
},
node_handle: Handle<Node>,
animation_handle: Handle<Animation>
animation_handle: Handle<Animation<Handle<Node>>>
);
+1 -1
View File
@@ -14,7 +14,7 @@ pub enum SelectedEntity {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AnimationSelection {
pub animation_player: Handle<Node>,
pub animation: Handle<Animation>,
pub animation: Handle<Animation<Handle<Node>>>,
pub entities: Vec<SelectedEntity>,
}
+8 -4
View File
@@ -194,7 +194,7 @@ impl RootMotionDropdownArea {
game_scene: &GameScene,
selection: &AnimationSelection,
) {
let send_command = |settings: Option<RootMotionSettings>| {
let send_command = |settings: Option<RootMotionSettings<Handle<Node>>>| {
sender.do_scene_command(SetAnimationRootMotionSettingsCommand {
node_handle: selection.animation_player,
animation_handle: selection.animation,
@@ -285,7 +285,11 @@ impl RootMotionDropdownArea {
node_handle: selection.animation_player,
animation_handle: selection.animation,
value: Some(RootMotionSettings {
node: node_selection.first().cloned().unwrap_or_default().into(),
node: node_selection
.first()
.cloned()
.map(Handle::from)
.unwrap_or_default(),
..*settings
}),
});
@@ -365,7 +369,7 @@ pub enum ToolbarAction {
None,
EnterPreviewMode,
LeavePreviewMode,
SelectAnimation(Handle<Animation>),
SelectAnimation(Handle<Animation<Handle<Node>>>),
PlayPause,
Stop,
}
@@ -863,7 +867,7 @@ impl Toolbar {
.items()[*index];
let animation = ui
.node(item)
.user_data_cloned::<Handle<Animation>>()
.user_data_cloned::<Handle<Animation<Handle<Node>>>>()
.unwrap();
sender.do_scene_command(ChangeSelectionCommand::new(
Selection::Animation(AnimationSelection {
+6 -5
View File
@@ -584,7 +584,7 @@ pub struct TrackList {
context_menu: TrackContextMenu,
property_binding_mode: PropertyBindingMode,
scroll_viewer: Handle<UiNode>,
selected_animation: Handle<Animation>,
selected_animation: Handle<Animation<Handle<Node>>>,
}
#[derive(Clone)]
@@ -735,7 +735,7 @@ impl TrackList {
game_scene: &GameScene,
sender: &MessageSender,
animation_player: Handle<Node>,
animation: Handle<Animation>,
animation: Handle<Animation<Handle<Node>>>,
ui: &mut UserInterface,
scene: &Scene,
) {
@@ -1266,8 +1266,8 @@ impl TrackList {
pub fn sync_to_model(
&mut self,
animation: &Animation,
animation_handle: Handle<Animation>,
animation: &Animation<Handle<Node>>,
animation_handle: Handle<Animation<Handle<Node>>>,
graph: &Graph,
editor_selection: &Selection,
ui: &mut UserInterface,
@@ -1348,7 +1348,8 @@ impl TrackList {
.map(|v| ui.node(*v))
.all(|v| v.query_component::<TrackView>().unwrap().id != model_track.id())
{
let parent_group = match self.group_views.entry(model_track.target()) {
let parent_group = match self.group_views.entry(model_track.target())
{
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let ctx = &mut ui.build_ctx();
+10 -5
View File
@@ -1,6 +1,7 @@
//! Animation selector for `Handle<Animation>` fields.
use crate::{gui::make_dropdown_list_option_universal, inspector::EditorEnvironment, Message};
use fyrox::scene::node::Node;
use fyrox::{
animation::{machine::Machine, Animation, AnimationContainer},
core::pool::Handle,
@@ -25,14 +26,16 @@ pub struct AnimationPropertyEditorDefinition;
impl PropertyEditorDefinition for AnimationPropertyEditorDefinition {
fn value_type_id(&self) -> TypeId {
TypeId::of::<Handle<Animation>>()
TypeId::of::<Handle<Animation<Handle<Node>>>>()
}
fn create_instance(
&self,
ctx: PropertyEditorBuildContext,
) -> Result<PropertyEditorInstance, InspectorError> {
let value = ctx.property_info.cast_value::<Handle<Animation>>()?;
let value = ctx
.property_info
.cast_value::<Handle<Animation<Handle<Node>>>>()?;
if let Some(environment) = EditorEnvironment::try_get_from(&ctx.environment) {
Ok(PropertyEditorInstance::Simple {
editor: DropdownListBuilder::new(WidgetBuilder::new())
@@ -68,7 +71,9 @@ impl PropertyEditorDefinition for AnimationPropertyEditorDefinition {
&self,
ctx: PropertyEditorMessageContext,
) -> Result<Option<UiMessage>, InspectorError> {
let value = ctx.property_info.cast_value::<Handle<Animation>>()?;
let value = ctx
.property_info
.cast_value::<Handle<Animation<Handle<Node>>>>()?;
if let Some(environment) = EditorEnvironment::try_get_from(&ctx.environment) {
if let Some(index) = environment
.available_animations
@@ -113,7 +118,7 @@ pub struct AnimationContainerPropertyEditorDefinition;
impl PropertyEditorDefinition for AnimationContainerPropertyEditorDefinition {
fn value_type_id(&self) -> TypeId {
TypeId::of::<AnimationContainer>()
TypeId::of::<AnimationContainer<Handle<Node>>>()
}
fn create_instance(
@@ -151,7 +156,7 @@ pub struct MachinePropertyEditorDefinition;
impl PropertyEditorDefinition for MachinePropertyEditorDefinition {
fn value_type_id(&self) -> TypeId {
TypeId::of::<Machine>()
TypeId::of::<Machine<Handle<Node>>>()
}
fn create_instance(
+56 -24
View File
@@ -365,39 +365,71 @@ pub fn make_property_editors_container(sender: MessageSender) -> PropertyEditorD
container.insert(InheritablePropertyEditorDefinition::<SurfaceSharedData>::new());
container.insert(InheritablePropertyEditorDefinition::<Status>::new());
container.insert(InspectablePropertyEditorDefinition::<BasePoseNode>::new());
container.insert(InspectablePropertyEditorDefinition::<IndexedBlendInput>::new());
container.insert(VecCollectionPropertyEditorDefinition::<IndexedBlendInput>::new());
container.insert(InspectablePropertyEditorDefinition::<BlendSpacePoint>::new());
container.insert(VecCollectionPropertyEditorDefinition::<BlendSpacePoint>::new());
container.insert(InspectablePropertyEditorDefinition::<BlendPose>::new());
container.insert(VecCollectionPropertyEditorDefinition::<BlendPose>::new());
container.insert(InspectablePropertyEditorDefinition::<
BasePoseNode<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
IndexedBlendInput<Handle<Node>>,
>::new());
container.insert(VecCollectionPropertyEditorDefinition::<
IndexedBlendInput<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
BlendSpacePoint<Handle<Node>>,
>::new());
container.insert(VecCollectionPropertyEditorDefinition::<
BlendSpacePoint<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<BlendPose<Handle<Node>>>::new());
container.insert(VecCollectionPropertyEditorDefinition::<
BlendPose<Handle<Node>>,
>::new());
container.insert(EnumPropertyEditorDefinition::<PoseWeight>::new());
container.insert(EnumPropertyEditorDefinition::<StateAction>::new());
container.insert(InspectablePropertyEditorDefinition::<StateActionWrapper>::new());
container.insert(VecCollectionPropertyEditorDefinition::<StateActionWrapper>::new());
container.insert(InspectablePropertyEditorDefinition::<BlendAnimationsByIndex>::new());
container.insert(InspectablePropertyEditorDefinition::<BlendAnimations>::new());
container.insert(InspectablePropertyEditorDefinition::<BlendSpace>::new());
container.insert(InspectablePropertyEditorDefinition::<PlayAnimation>::new());
container.insert(EnumPropertyEditorDefinition::<StateAction<Handle<Node>>>::new());
container.insert(InspectablePropertyEditorDefinition::<
StateActionWrapper<Handle<Node>>,
>::new());
container.insert(VecCollectionPropertyEditorDefinition::<
StateActionWrapper<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
BlendAnimationsByIndex<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
BlendAnimations<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
BlendSpace<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
PlayAnimation<Handle<Node>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<Handle<PoseNode>>::new());
container.insert(InspectablePropertyEditorDefinition::<Handle<State>>::new());
container.insert(InspectablePropertyEditorDefinition::<
Handle<PoseNode<Handle<Node>>>,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
Handle<State<Handle<Node>>>,
>::new());
container.insert(VecCollectionPropertyEditorDefinition::<Handle<Animation>>::new());
container.insert(VecCollectionPropertyEditorDefinition::<
Handle<Animation<Handle<Node>>>,
>::new());
container.insert(AnimationPropertyEditorDefinition);
container.insert(AnimationContainerPropertyEditorDefinition);
container.insert(InheritablePropertyEditorDefinition::<AnimationContainer>::new());
container.insert(InheritablePropertyEditorDefinition::<
AnimationContainer<Handle<Node>>,
>::new());
container.insert(MachinePropertyEditorDefinition);
container.insert(InheritablePropertyEditorDefinition::<Machine>::new());
container.insert(InheritablePropertyEditorDefinition::<Machine<Handle<Node>>>::new());
container.insert(EnumPropertyEditorDefinition::<LogicNode>::new());
container.insert(InspectablePropertyEditorDefinition::<AndNode>::new());
container.insert(InspectablePropertyEditorDefinition::<OrNode>::new());
container.insert(InspectablePropertyEditorDefinition::<XorNode>::new());
container.insert(InspectablePropertyEditorDefinition::<NotNode>::new());
container.insert(EnumPropertyEditorDefinition::<LogicNode<Handle<Node>>>::new());
container.insert(InspectablePropertyEditorDefinition::<AndNode<Handle<Node>>>::new());
container.insert(InspectablePropertyEditorDefinition::<OrNode<Handle<Node>>>::new());
container.insert(InspectablePropertyEditorDefinition::<XorNode<Handle<Node>>>::new());
container.insert(InspectablePropertyEditorDefinition::<NotNode<Handle<Node>>>::new());
container.insert(InspectablePropertyEditorDefinition::<ParticleSystemRng>::new());
container.insert(EnumPropertyEditorDefinition::<PolygonFillMode>::new());
+2 -1
View File
@@ -8,6 +8,7 @@ use crate::{
utils::window_content,
Brush, Engine, Message, Mode, WidgetMessage, WrapMode, MSG_SYNC_FLAG,
};
use fyrox::scene::node::Node;
use fyrox::{
animation::Animation,
asset::manager::ResourceManager,
@@ -42,7 +43,7 @@ pub mod handlers;
#[derive(Clone)]
pub struct AnimationDefinition {
name: String,
handle: Handle<Animation>,
handle: Handle<Animation<Handle<Node>>>,
}
pub struct EditorEnvironment {
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "fyrox-animation"
version = "0.1.0"
edition = "2021"
[dependencies]
fyrox-core = { version = "0.26.0", path = "../fyrox-core" }
fyrox-resource = { version = "0.10.0", path = "../fyrox-resource" }
strum = "0.25.0"
strum_macros = "0.25.0"
fxhash = "0.2.1"
spade = "2.1.0"
@@ -2,7 +2,6 @@
//! of parametric curves. See [`TrackDataContainer`] docs for more info.
use crate::{
animation::value::TrackValue,
core::{
algebra::{Vector2, Vector3, Vector4},
curve::Curve,
@@ -10,6 +9,7 @@ use crate::{
reflect::prelude::*,
visitor::prelude::*,
},
value::TrackValue,
};
/// The kind of track output value, the animation system works only with numeric properties and the number
@@ -1,10 +1,9 @@
//! Animation allows you to change properties of scene nodes at runtime using a set of key frames.
//! Animation allows you to change properties of arbitrary objects at runtime using a set of key frames.
//! See [`Animation`] docs for more info.
#![warn(missing_docs)]
use crate::{
animation::track::Track,
core::{
algebra::{UnitQuaternion, Vector3},
math::wrapf,
@@ -13,22 +12,23 @@ use crate::{
uuid::Uuid,
visitor::{Visit, VisitResult, Visitor},
},
scene::{
graph::{Graph, NodePool},
node::Node,
},
utils::{self, NameProvider},
track::Track,
};
use fyrox_core::uuid_provider;
use fyrox_core::{NameProvider, TypeUuidProvider};
use std::hash::Hash;
use std::{
collections::VecDeque,
fmt::Debug,
ops::{Index, IndexMut, Range},
};
use crate::animation::value::{TrackValue, ValueBinding};
pub use fyrox_core as core;
use fyrox_core::pool::ErasedHandle;
use fyrox_core::uuid::uuid;
pub use pose::{AnimationPose, NodePose};
pub use signal::{AnimationEvent, AnimationSignal};
use value::{TrackValue, ValueBinding};
pub mod container;
pub mod machine;
@@ -40,8 +40,8 @@ pub mod value;
/// # Overview
///
/// Animation allows you to change properties of scene nodes at runtime using a set of key frames. Animation
/// consists of multiple tracks, where each track is bound to a property of a scene node. A track can animate
/// Animation allows you to change properties of arbitrary entities at runtime using a set of key frames. Animation
/// consists of multiple tracks, where each track is bound to a property of an entity. A track can animate
/// any numeric properties, starting from numbers (including `bool`) end ending by 2/3/4 dimensional vectors.
/// Each component (number, x/y/z/w vector components) is stored in a _parametric curve_ (see
/// [`crate::core::curve::Curve`] docs for more info). Every parametric curve contains zero or more _key frames_.
@@ -110,33 +110,24 @@ pub mod value;
///
/// # Examples
///
/// Usually, animations are created from the editor or some external tool and then imported in the engine. Before trying the example
/// below, please read the docs for [`crate::scene::animation::AnimationPlayer`] node, it is much more convenient way of animating
/// other nodes. The node can be created from the editor and you don't even need to write any code.
///
/// Use the following example code as a guide **only** if you need to create procedural animations:
/// Usually, animations are created from the editor or some external tool and then imported in the engine.
/// However, sometimes there's a need for procedural animations. Use the following example code as
/// a guide **only** if you need to create procedural animations:
///
/// ```rust
/// use fyrox::{
/// animation::{
/// container::{TrackDataContainer, TrackValueKind},
/// track::Track,
/// value::ValueBinding,
/// Animation,
/// },
/// use fyrox_animation::{
/// container::{TrackDataContainer, TrackValueKind},
/// track::Track,
/// value::ValueBinding,
/// Animation,
/// core::{
/// curve::{Curve, CurveKey, CurveKeyKind},
/// pool::Handle,
/// },
/// scene::{
/// node::Node,
/// base::BaseBuilder,
/// graph::Graph,
/// pivot::PivotBuilder
/// }
/// };
/// use fyrox_core::pool::ErasedHandle;
///
/// fn create_animation(node: Handle<Node>) -> Animation {
/// fn create_animation(target: ErasedHandle) -> Animation<ErasedHandle> {
/// let mut frames_container = TrackDataContainer::new(TrackValueKind::Vector3);
///
/// // We'll animate only X coordinate (at index 0).
@@ -148,7 +139,7 @@ pub mod value;
///
/// // Create a track that will animated the node using the curve above.
/// let mut track = Track::new(frames_container, ValueBinding::Position);
/// track.set_target(node);
/// track.set_target(target);
///
/// // Finally create an animation and set its time slice and turn it on.
/// let mut animation = Animation::default();
@@ -159,17 +150,12 @@ pub mod value;
/// animation
/// }
///
/// // Create a graph with a node.
/// let mut graph = Graph::new();
/// let some_node = PivotBuilder::new(BaseBuilder::new()).build(&mut graph);
///
/// // Create the animation.
/// let mut animation = create_animation(some_node);
/// let mut animation = create_animation(Default::default());
///
/// // Emulate some ticks (like it was updated from the main loop of your game).
/// for _ in 0..10 {
/// animation.tick(1.0 / 60.0);
/// animation.pose().apply(&mut graph);
/// }
/// ```
///
@@ -177,10 +163,10 @@ pub mod value;
/// is only for the sake of completeness of the example. In the real games you need to add the animation to an animation
/// player scene node and it will do the job for you.
#[derive(Debug, Reflect, Visit, PartialEq)]
pub struct Animation {
pub struct Animation<T: EntityId> {
#[visit(optional)]
name: String,
tracks: Vec<Track>,
tracks: Vec<Track<T>>,
time_position: f32,
#[visit(optional)]
time_slice: Range<f32>,
@@ -190,7 +176,7 @@ pub struct Animation {
signals: Vec<AnimationSignal>,
#[visit(optional)]
root_motion_settings: Option<RootMotionSettings>,
root_motion_settings: Option<RootMotionSettings<T>>,
#[reflect(hidden)]
#[visit(skip)]
@@ -199,23 +185,36 @@ pub struct Animation {
// Non-serialized
#[reflect(hidden)]
#[visit(skip)]
pose: AnimationPose,
pose: AnimationPose<T>,
// Non-serialized
#[reflect(hidden)]
#[visit(skip)]
events: VecDeque<AnimationEvent>,
}
uuid_provider!(Animation = "aade8e9d-e2cf-401d-a4d1-59c6943645f3");
impl<T: EntityId> TypeUuidProvider for Animation<T> {
fn type_uuid() -> Uuid {
uuid!("aade8e9d-e2cf-401d-a4d1-59c6943645f3")
}
}
/// Identifier of an entity, that can be animated.
pub trait EntityId:
Default + Copy + Reflect + Visit + PartialEq + Eq + Hash + Debug + Ord + PartialEq + 'static
{
}
impl<T: 'static> EntityId for Handle<T> {}
impl EntityId for ErasedHandle {}
/// Root motion settings. It allows you to set a node (root) from which the motion will be taken
/// as well as filter out some unnecessary parts of the motion (i.e. do not extract motion on
/// Y axis).
#[derive(Default, Debug, Clone, PartialEq, Reflect, Visit)]
pub struct RootMotionSettings {
pub struct RootMotionSettings<T: EntityId> {
/// A handle to a node which movement will be extracted and put in root motion field of an animation
/// to which these settings were set to.
pub node: Handle<Node>,
pub node: T,
/// Keeps X part of the translational part of the motion.
pub ignore_x_movement: bool,
/// Keeps Y part of the translational part of the motion.
@@ -248,13 +247,13 @@ impl RootMotion {
}
}
impl NameProvider for Animation {
impl<T: EntityId> NameProvider for Animation<T> {
fn name(&self) -> &str {
&self.name
}
}
impl Clone for Animation {
impl<T: EntityId> Clone for Animation<T> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
@@ -273,7 +272,7 @@ impl Clone for Animation {
}
}
impl Animation {
impl<T: EntityId> Animation<T> {
/// Sets a new name for the animation. The name then could be used to find the animation in a container.
pub fn set_name<S: AsRef<str>>(&mut self, name: S) {
self.name = name.as_ref().to_owned();
@@ -286,22 +285,22 @@ impl Animation {
/// Adds new track to the animation. Animation can have unlimited number of tracks, each track is responsible
/// for animation of a single scene node.
pub fn add_track(&mut self, track: Track) {
pub fn add_track(&mut self, track: Track<T>) {
self.tracks.push(track);
}
/// Removes a track at given index.
pub fn remove_track(&mut self, index: usize) -> Track {
pub fn remove_track(&mut self, index: usize) -> Track<T> {
self.tracks.remove(index)
}
/// Inserts a track at given index.
pub fn insert_track(&mut self, index: usize, track: Track) {
pub fn insert_track(&mut self, index: usize, track: Track<T>) {
self.tracks.insert(index, track)
}
/// Removes last track from the list of tracks of the animation.
pub fn pop_track(&mut self) -> Option<Track> {
pub fn pop_track(&mut self) -> Option<Track<T>> {
self.tracks.pop()
}
@@ -319,7 +318,7 @@ impl Animation {
}
/// Returns a reference to tracks container.
pub fn tracks(&self) -> &[Track] {
pub fn tracks(&self) -> &[Track<T>] {
&self.tracks
}
@@ -401,7 +400,7 @@ impl Animation {
}
fn update_root_motion(&mut self, prev_time_position: f32) {
fn fetch_position_at_time(tracks: &[Track], time: f32) -> Vector3<f32> {
fn fetch_position_at_time<T: EntityId>(tracks: &[Track<T>], time: f32) -> Vector3<f32> {
tracks
.iter()
.find(|track| track.binding() == &ValueBinding::Position)
@@ -416,7 +415,10 @@ impl Animation {
.unwrap_or_default()
}
fn fetch_rotation_at_time(tracks: &[Track], time: f32) -> UnitQuaternion<f32> {
fn fetch_rotation_at_time<T: EntityId>(
tracks: &[Track<T>],
time: f32,
) -> UnitQuaternion<f32> {
tracks
.iter()
.find(|track| track.binding() == &ValueBinding::Rotation)
@@ -550,17 +552,17 @@ impl Animation {
}
/// Sets new root motion settings.
pub fn set_root_motion_settings(&mut self, settings: Option<RootMotionSettings>) {
pub fn set_root_motion_settings(&mut self, settings: Option<RootMotionSettings<T>>) {
self.root_motion_settings = settings;
}
/// Returns a reference to the root motion settings (if any).
pub fn root_motion_settings_ref(&self) -> Option<&RootMotionSettings> {
pub fn root_motion_settings_ref(&self) -> Option<&RootMotionSettings<T>> {
self.root_motion_settings.as_ref()
}
/// Returns a reference to the root motion settings (if any).
pub fn root_motion_settings_mut(&mut self) -> Option<&mut RootMotionSettings> {
pub fn root_motion_settings_mut(&mut self) -> Option<&mut RootMotionSettings<T>> {
self.root_motion_settings.as_mut()
}
@@ -639,7 +641,7 @@ impl Animation {
}
/// Returns a mutable reference to the track container.
pub fn tracks_mut(&mut self) -> &mut [Track] {
pub fn tracks_mut(&mut self) -> &mut [Track<T>] {
&mut self.tracks
}
@@ -678,44 +680,13 @@ impl Animation {
/// to remove undesired animation tracks.
pub fn retain_tracks<F>(&mut self, filter: F)
where
F: FnMut(&Track) -> bool,
F: FnMut(&Track<T>) -> bool,
{
self.tracks.retain(filter)
}
/// Enables or disables animation tracks for nodes in hierarchy starting from given root. Could be useful to enable
/// or disable animation for skeleton parts, i.e. you don't want legs to be animated and you know that legs starts
/// from torso bone, then you could do this.
///
/// ```
/// use fyrox::scene::node::Node;
/// use fyrox::animation::Animation;
/// use fyrox::core::pool::Handle;
/// use fyrox::scene::graph::Graph;
///
/// fn disable_legs(torso_bone: Handle<Node>, aim_animation: &mut Animation, graph: &Graph) {
/// aim_animation.set_tracks_enabled_from(torso_bone, false, graph)
/// }
/// ```
///
/// After this legs won't be animated and animation could be blended together with run animation so it will produce
/// new animation - run and aim.
pub fn set_tracks_enabled_from(&mut self, handle: Handle<Node>, enabled: bool, graph: &Graph) {
let mut stack = vec![handle];
while let Some(node) = stack.pop() {
for track in self.tracks.iter_mut() {
if track.target() == node {
track.set_enabled(enabled);
}
}
for child in graph[node].children() {
stack.push(*child);
}
}
}
/// Tries to find all tracks that refer to a given node and enables or disables them.
pub fn set_node_track_enabled(&mut self, handle: Handle<Node>, enabled: bool) {
pub fn set_node_track_enabled(&mut self, handle: T, enabled: bool) {
for track in self.tracks.iter_mut() {
if track.target() == handle {
track.set_enabled(enabled);
@@ -724,14 +695,14 @@ impl Animation {
}
/// Returns an iterator that yields a number of references to tracks that refer to a given node.
pub fn tracks_of(&self, handle: Handle<Node>) -> impl Iterator<Item = &Track> {
pub fn tracks_of(&self, handle: T) -> impl Iterator<Item = &Track<T>> {
self.tracks
.iter()
.filter(move |track| track.target() == handle)
}
/// Returns an iterator that yields a number of references to tracks that refer to a given node.
pub fn tracks_of_mut(&mut self, handle: Handle<Node>) -> impl Iterator<Item = &mut Track> {
pub fn tracks_of_mut(&mut self, handle: T) -> impl Iterator<Item = &mut Track<T>> {
self.tracks
.iter_mut()
.filter(move |track| track.target() == handle)
@@ -743,7 +714,7 @@ impl Animation {
&self,
name: S,
) -> Option<(usize, &AnimationSignal)> {
utils::find_by_name_ref(self.signals.iter().enumerate(), name)
core::find_by_name_ref(self.signals.iter().enumerate(), name)
}
/// Tries to find a signal by its name. Returns index of the signal and its reference.
@@ -752,7 +723,7 @@ impl Animation {
&mut self,
name: S,
) -> Option<(usize, &mut AnimationSignal)> {
utils::find_by_name_mut(self.signals.iter_mut().enumerate(), name)
core::find_by_name_mut(self.signals.iter_mut().enumerate(), name)
}
/// Returns `true` if there's a signal with given name and id.
@@ -779,12 +750,12 @@ impl Animation {
}
/// Returns current pose of the animation (a final result that can be applied to a scene graph).
pub fn pose(&self) -> &AnimationPose {
pub fn pose(&self) -> &AnimationPose<T> {
&self.pose
}
}
impl Default for Animation {
impl<T: EntityId> Default for Animation<T> {
fn default() -> Self {
Self {
name: Default::default(),
@@ -806,17 +777,17 @@ impl Default for Animation {
/// A container for animations. It is a tiny wrapper around [`Pool`], you should never create the container yourself,
/// it is managed by the engine.
#[derive(Debug, Clone, Reflect, PartialEq)]
pub struct AnimationContainer {
pool: Pool<Animation>,
pub struct AnimationContainer<T: EntityId> {
pool: Pool<Animation<T>>,
}
impl Default for AnimationContainer {
impl<T: EntityId> Default for AnimationContainer<T> {
fn default() -> Self {
Self::new()
}
}
impl AnimationContainer {
impl<T: EntityId> AnimationContainer<T> {
/// Creates an empty animation container.
pub fn new() -> Self {
Self { pool: Pool::new() }
@@ -830,57 +801,62 @@ impl AnimationContainer {
/// Returns an iterator yielding a references to animations in the container.
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &Animation> {
pub fn iter(&self) -> impl Iterator<Item = &Animation<T>> {
self.pool.iter()
}
/// Returns an iterator yielding a pair (handle, reference) to animations in the container.
#[inline]
pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Animation>, &Animation)> {
pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Animation<T>>, &Animation<T>)> {
self.pool.pair_iter()
}
/// Returns an iterator yielding a pair (handle, reference) to animations in the container.
#[inline]
pub fn pair_iter_mut(&mut self) -> impl Iterator<Item = (Handle<Animation>, &mut Animation)> {
pub fn pair_iter_mut(
&mut self,
) -> impl Iterator<Item = (Handle<Animation<T>>, &mut Animation<T>)> {
self.pool.pair_iter_mut()
}
/// Returns an iterator yielding a references to animations in the container.
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Animation> {
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Animation<T>> {
self.pool.iter_mut()
}
/// Adds a new animation to the container and returns its handle.
#[inline]
pub fn add(&mut self, animation: Animation) -> Handle<Animation> {
pub fn add(&mut self, animation: Animation<T>) -> Handle<Animation<T>> {
self.pool.spawn(animation)
}
/// Tries to remove an animation from the container by its handle.
#[inline]
pub fn remove(&mut self, handle: Handle<Animation>) -> Option<Animation> {
pub fn remove(&mut self, handle: Handle<Animation<T>>) -> Option<Animation<T>> {
self.pool.try_free(handle)
}
/// Extracts animation from container and reserves its handle. It is used to temporarily take
/// ownership over animation, and then put animation back using given ticket.
pub fn take_reserve(&mut self, handle: Handle<Animation>) -> (Ticket<Animation>, Animation) {
pub fn take_reserve(
&mut self,
handle: Handle<Animation<T>>,
) -> (Ticket<Animation<T>>, Animation<T>) {
self.pool.take_reserve(handle)
}
/// Puts animation back by given ticket.
pub fn put_back(
&mut self,
ticket: Ticket<Animation>,
animation: Animation,
) -> Handle<Animation> {
ticket: Ticket<Animation<T>>,
animation: Animation<T>,
) -> Handle<Animation<T>> {
self.pool.put_back(ticket, animation)
}
/// Makes animation handle vacant again.
pub fn forget_ticket(&mut self, ticket: Ticket<Animation>) {
pub fn forget_ticket(&mut self, ticket: Ticket<Animation<T>>) {
self.pool.forget_ticket(ticket)
}
@@ -892,25 +868,25 @@ impl AnimationContainer {
/// Tries to borrow a reference to an animation in the container. Panics if the handle is invalid.
#[inline]
pub fn get(&self, handle: Handle<Animation>) -> &Animation {
pub fn get(&self, handle: Handle<Animation<T>>) -> &Animation<T> {
self.pool.borrow(handle)
}
/// Tries to borrow a mutable reference to an animation in the container. Panics if the handle is invalid.
#[inline]
pub fn get_mut(&mut self, handle: Handle<Animation>) -> &mut Animation {
pub fn get_mut(&mut self, handle: Handle<Animation<T>>) -> &mut Animation<T> {
self.pool.borrow_mut(handle)
}
/// Tries to borrow a reference to an animation in the container.
#[inline]
pub fn try_get(&self, handle: Handle<Animation>) -> Option<&Animation> {
pub fn try_get(&self, handle: Handle<Animation<T>>) -> Option<&Animation<T>> {
self.pool.try_borrow(handle)
}
/// Tries to borrow a mutable reference to an animation in the container.
#[inline]
pub fn try_get_mut(&mut self, handle: Handle<Animation>) -> Option<&mut Animation> {
pub fn try_get_mut(&mut self, handle: Handle<Animation<T>>) -> Option<&mut Animation<T>> {
self.pool.try_borrow_mut(handle)
}
@@ -919,8 +895,8 @@ impl AnimationContainer {
pub fn find_by_name_ref<S: AsRef<str>>(
&self,
name: S,
) -> Option<(Handle<Animation>, &Animation)> {
utils::find_by_name_ref(self.pool.pair_iter(), name)
) -> Option<(Handle<Animation<T>>, &Animation<T>)> {
core::find_by_name_ref(self.pool.pair_iter(), name)
}
/// Tries to find an animation by its name in the container.
@@ -928,8 +904,8 @@ impl AnimationContainer {
pub fn find_by_name_mut<S: AsRef<str>>(
&mut self,
name: S,
) -> Option<(Handle<Animation>, &mut Animation)> {
utils::find_by_name_mut(self.pool.pair_iter_mut(), name)
) -> Option<(Handle<Animation<T>>, &mut Animation<T>)> {
core::find_by_name_mut(self.pool.pair_iter_mut(), name)
}
/// Removes every animation from the container that does not satisfy a particular condition represented by the given
@@ -937,22 +913,11 @@ impl AnimationContainer {
#[inline]
pub fn retain<P>(&mut self, pred: P)
where
P: FnMut(&Animation) -> bool,
P: FnMut(&Animation<T>) -> bool,
{
self.pool.retain(pred)
}
/// Updates all animations in the container and applies their poses to respective nodes. This method is intended to
/// be used only by the internals of the engine!
pub fn update_animations(&mut self, nodes: &mut NodePool, apply: bool, dt: f32) {
for animation in self.pool.iter_mut().filter(|anim| anim.enabled) {
animation.tick(dt);
if apply {
animation.pose.apply_internal(nodes);
}
}
}
/// Removes queued animation events from every animation in the container.
///
/// # Potential use cases
@@ -968,7 +933,7 @@ impl AnimationContainer {
}
}
impl Visit for AnimationContainer {
impl<T: EntityId> Visit for AnimationContainer<T> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
if visitor.is_reading() && self.pool.get_capacity() != 0 {
panic!("Animation pool must be empty on load!");
@@ -982,16 +947,16 @@ impl Visit for AnimationContainer {
}
}
impl Index<Handle<Animation>> for AnimationContainer {
type Output = Animation;
impl<T: EntityId> Index<Handle<Animation<T>>> for AnimationContainer<T> {
type Output = Animation<T>;
fn index(&self, index: Handle<Animation>) -> &Self::Output {
fn index(&self, index: Handle<Animation<T>>) -> &Self::Output {
&self.pool[index]
}
}
impl IndexMut<Handle<Animation>> for AnimationContainer {
fn index_mut(&mut self, index: Handle<Animation>) -> &mut Self::Output {
impl<T: EntityId> IndexMut<Handle<Animation<T>>> for AnimationContainer<T> {
fn index_mut(&mut self, index: Handle<Animation<T>>) -> &mut Self::Output {
&mut self.pool[index]
}
}
@@ -2,42 +2,43 @@
//! needed to works with such events.
use crate::{
animation::machine::{State, Transition},
core::pool::Handle,
machine::{State, Transition},
EntityId,
};
use std::collections::VecDeque;
/// Specific state machine event.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
pub enum Event<T: EntityId> {
/// Occurs when enter some state. See module docs for example.
StateEnter(Handle<State>),
StateEnter(Handle<State<T>>),
/// Occurs when leaving some state. See module docs for example.
StateLeave(Handle<State>),
StateLeave(Handle<State<T>>),
/// Occurs when a transition is done and a new active state was set.
ActiveStateChanged {
/// Previously active state.
prev: Handle<State>,
prev: Handle<State<T>>,
/// New active state.
new: Handle<State>,
new: Handle<State<T>>,
},
/// Occurs when active transition was changed.
ActiveTransitionChanged(Handle<Transition>),
ActiveTransitionChanged(Handle<Transition<T>>),
}
/// A simple event queue with fixed capacity. It is used to store a fixed amount of events and discard any
/// events when the queue is full.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FixedEventQueue {
queue: VecDeque<Event>,
pub struct FixedEventQueue<T: EntityId> {
queue: VecDeque<Event<T>>,
limit: u32,
}
impl Default for FixedEventQueue {
impl<T: EntityId> Default for FixedEventQueue<T> {
fn default() -> Self {
Self {
queue: Default::default(),
@@ -46,7 +47,7 @@ impl Default for FixedEventQueue {
}
}
impl FixedEventQueue {
impl<T: EntityId> FixedEventQueue<T> {
/// Creates a new queue with given limit.
pub fn new(limit: u32) -> Self {
Self {
@@ -56,14 +57,14 @@ impl FixedEventQueue {
}
/// Pushes an event to the queue.
pub fn push(&mut self, event: Event) {
pub fn push(&mut self, event: Event<T>) {
if self.queue.len() < (self.limit as usize) {
self.queue.push_back(event);
}
}
/// Pops an event from the queue.
pub fn pop(&mut self) -> Option<Event> {
pub fn pop(&mut self) -> Option<Event<T>> {
self.queue.pop_front()
}
}
@@ -1,25 +1,20 @@
//! Layer is a separate state graph that usually animates only a part of nodes from animations. See docs of [`MachineLayer`]
//! for more info.
use crate::animation::machine::node::AnimationEventCollectionStrategy;
use crate::animation::machine::AnimationPoseSource;
use crate::animation::AnimationEvent;
use crate::{
animation::{
machine::{
event::FixedEventQueue, Event, LayerMask, ParameterContainer, PoseNode, State,
Transition,
},
Animation, AnimationContainer, AnimationPose,
},
core::{
log::{Log, MessageKind},
pool::{Handle, Pool},
reflect::prelude::*,
visitor::prelude::*,
},
utils::{self, NameProvider},
machine::{
event::FixedEventQueue, node::AnimationEventCollectionStrategy, AnimationPoseSource, Event,
LayerMask, ParameterContainer, PoseNode, State, Transition,
},
Animation, AnimationContainer, AnimationEvent, AnimationPose, EntityId,
};
use fyrox_core::{find_by_name_mut, find_by_name_ref, NameProvider};
/// Layer is a separate state graph. Layers mainly used to animate different parts of humanoid (but not only) characters. For
/// example there could a layer for upper body and a layer for lower body. Upper body layer could contain animations for aiming,
@@ -30,20 +25,21 @@ use crate::{
/// # Example
///
/// ```rust
/// use fyrox::{
/// animation::machine::{
/// use fyrox_animation::{
/// machine::{
/// State, Transition, PoseNode, MachineLayer,
/// Parameter, PlayAnimation, PoseWeight, BlendAnimations, BlendPose
/// },
/// core::pool::Handle
/// };
/// use fyrox_core::pool::ErasedHandle;
///
/// // Assume that these are correct handles.
/// let idle_animation = Handle::default();
/// let walk_animation = Handle::default();
/// let aim_animation = Handle::default();
///
/// let mut root_layer = MachineLayer::new();
/// let mut root_layer = MachineLayer::<ErasedHandle>::new();
///
/// let aim = root_layer.add_node(PoseNode::PlayAnimation(PlayAnimation::new(aim_animation)));
/// let walk = root_layer.add_node(PoseNode::PlayAnimation(PlayAnimation::new(walk_animation)));
@@ -66,45 +62,45 @@ use crate::{
///
/// ```
#[derive(Default, Debug, Visit, Reflect, Clone, PartialEq)]
pub struct MachineLayer {
pub struct MachineLayer<T: EntityId> {
name: String,
weight: f32,
mask: LayerMask,
mask: LayerMask<T>,
#[reflect(hidden)]
nodes: Pool<PoseNode>,
nodes: Pool<PoseNode<T>>,
#[reflect(hidden)]
transitions: Pool<Transition>,
transitions: Pool<Transition<T>>,
#[reflect(hidden)]
states: Pool<State>,
states: Pool<State<T>>,
#[reflect(hidden)]
active_state: Handle<State>,
active_state: Handle<State<T>>,
#[reflect(hidden)]
entry_state: Handle<State>,
entry_state: Handle<State<T>>,
#[reflect(hidden)]
active_transition: Handle<Transition>,
active_transition: Handle<Transition<T>>,
#[visit(skip)]
#[reflect(hidden)]
final_pose: AnimationPose,
final_pose: AnimationPose<T>,
#[visit(skip)]
#[reflect(hidden)]
events: FixedEventQueue,
events: FixedEventQueue<T>,
#[visit(skip)]
#[reflect(hidden)]
debug: bool,
}
impl NameProvider for MachineLayer {
impl<T: EntityId> NameProvider for MachineLayer<T> {
fn name(&self) -> &str {
&self.name
}
@@ -112,25 +108,25 @@ impl NameProvider for MachineLayer {
/// A source of animation events coming from a layer.
#[derive(Default)]
pub enum AnimationEventsSource {
pub enum AnimationEventsSource<T: EntityId> {
/// Layer is malformed and no events were gathered.
#[default]
Unknown,
/// Animation events were gathered from a state.
State {
/// A handle of a state, from which the events were collected.
handle: Handle<State>,
handle: Handle<State<T>>,
/// A name of a state, from which the events were collected.
name: String,
},
/// Animation events were gathered from both states of a transition.
Transition {
/// A handle of an active transition.
handle: Handle<Transition>,
handle: Handle<Transition<T>>,
/// A handle of a source state of an active transition.
source_state_handle: Handle<State>,
source_state_handle: Handle<State<T>>,
/// A handle of a destination state of an active transition.
dest_state_handle: Handle<State>,
dest_state_handle: Handle<State<T>>,
/// A name of a source state of an active transition.
source_state_name: String,
/// A name of a destination state of an active transition.
@@ -141,14 +137,14 @@ pub enum AnimationEventsSource {
/// A collection of events gathered from an active state (or a transition between states). See docs of [`MachineLayer::collect_active_animations_events`]
/// for more info and usage examples.
#[derive(Default)]
pub struct LayerAnimationEventsCollection {
pub struct LayerAnimationEventsCollection<T: EntityId> {
/// A source of events.
pub source: AnimationEventsSource,
pub source: AnimationEventsSource<T>,
/// Actual animation events, defined as a tuple `(animation handle, event)`.
pub events: Vec<(Handle<Animation>, AnimationEvent)>,
pub events: Vec<(Handle<Animation<T>>, AnimationEvent)>,
}
impl MachineLayer {
impl<T: EntityId> MachineLayer<T> {
/// Creates a new machine layer. See examples in [`MachineLayer`] docs.
#[inline]
pub fn new() -> Self {
@@ -182,21 +178,21 @@ impl MachineLayer {
/// Adds a new node to the layer and returns its handle.
#[inline]
pub fn add_node(&mut self, node: PoseNode) -> Handle<PoseNode> {
pub fn add_node(&mut self, node: PoseNode<T>) -> Handle<PoseNode<T>> {
self.nodes.spawn(node)
}
/// Sets new entry state of the layer. Entry state will always be active on the first frame and will remain active
/// until some transition won't change it.
#[inline]
pub fn set_entry_state(&mut self, entry_state: Handle<State>) {
pub fn set_entry_state(&mut self, entry_state: Handle<State<T>>) {
self.active_state = entry_state;
self.entry_state = entry_state;
}
/// Returns a handle of current entry state.
#[inline]
pub fn entry_state(&self) -> Handle<State> {
pub fn entry_state(&self) -> Handle<State<T>> {
self.entry_state
}
@@ -209,7 +205,7 @@ impl MachineLayer {
/// Adds a new state to the layer and returns its handle.
#[inline]
pub fn add_state(&mut self, state: State) -> Handle<State> {
pub fn add_state(&mut self, state: State<T>) -> Handle<State<T>> {
let state = self.states.spawn(state);
if self.active_state.is_none() {
self.active_state = state;
@@ -219,19 +215,19 @@ impl MachineLayer {
/// Adds a new transition to the layer and returns its handle.
#[inline]
pub fn add_transition(&mut self, transition: Transition) -> Handle<Transition> {
pub fn add_transition(&mut self, transition: Transition<T>) -> Handle<Transition<T>> {
self.transitions.spawn(transition)
}
/// Borrows a state using its handle, panics if the handle is invalid.
#[inline]
pub fn get_state(&self, state: Handle<State>) -> &State {
pub fn get_state(&self, state: Handle<State<T>>) -> &State<T> {
&self.states[state]
}
/// Borrows a transition using its handle, panics if the handle is invalid.
#[inline]
pub fn get_transition(&self, transition: Handle<Transition>) -> &Transition {
pub fn get_transition(&self, transition: Handle<Transition<T>>) -> &Transition<T> {
&self.transitions[transition]
}
@@ -241,9 +237,10 @@ impl MachineLayer {
/// # Example
///
/// ```rust
/// use fyrox::animation::machine::{Event, MachineLayer};
/// use fyrox_animation::machine::{Event, MachineLayer};
/// use fyrox_core::pool::ErasedHandle;
///
/// let mut layer = MachineLayer::new();
/// let mut layer = MachineLayer::<ErasedHandle>::new();
///
/// while let Some(event) = layer.pop_event() {
/// match event {
@@ -263,7 +260,7 @@ impl MachineLayer {
/// }
/// ```
#[inline]
pub fn pop_event(&mut self) -> Option<Event> {
pub fn pop_event(&mut self) -> Option<Event<T>> {
self.events.pop()
}
@@ -290,9 +287,9 @@ impl MachineLayer {
pub fn collect_active_animations_events(
&self,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
strategy: AnimationEventCollectionStrategy,
) -> LayerAnimationEventsCollection {
) -> LayerAnimationEventsCollection<T> {
if let Some(state) = self.states.try_borrow(self.active_state) {
return LayerAnimationEventsCollection {
source: AnimationEventsSource::State {
@@ -384,25 +381,25 @@ impl MachineLayer {
/// Tries to borrow a node by its handle, panics if the handle is invalid.
#[inline]
pub fn node(&self, handle: Handle<PoseNode>) -> &PoseNode {
pub fn node(&self, handle: Handle<PoseNode<T>>) -> &PoseNode<T> {
&self.nodes[handle]
}
/// Tries to borrow a node by its handle, panics if the handle is invalid.
#[inline]
pub fn node_mut(&mut self, handle: Handle<PoseNode>) -> &mut PoseNode {
pub fn node_mut(&mut self, handle: Handle<PoseNode<T>>) -> &mut PoseNode<T> {
&mut self.nodes[handle]
}
/// Returns a reference to inner node container.
#[inline]
pub fn nodes(&self) -> &Pool<PoseNode> {
pub fn nodes(&self) -> &Pool<PoseNode<T>> {
&self.nodes
}
/// Returns a reference to inner node container.
#[inline]
pub fn nodes_mut(&mut self) -> &mut Pool<PoseNode> {
pub fn nodes_mut(&mut self) -> &mut Pool<PoseNode<T>> {
&mut self.nodes
}
@@ -410,38 +407,38 @@ impl MachineLayer {
/// state is active. For example jumping could be done only from `idle` and `run` state, and not from
/// `crouch` and other states.
#[inline]
pub fn active_state(&self) -> Handle<State> {
pub fn active_state(&self) -> Handle<State<T>> {
self.active_state
}
/// Returns a handle of active transition. It is not empty only while a transition is active (doing blending
/// between states).
#[inline]
pub fn active_transition(&self) -> Handle<Transition> {
pub fn active_transition(&self) -> Handle<Transition<T>> {
self.active_transition
}
/// Tries to borrow a transition using its handle, panics if the handle is invalid.
#[inline]
pub fn transition(&self, handle: Handle<Transition>) -> &Transition {
pub fn transition(&self, handle: Handle<Transition<T>>) -> &Transition<T> {
&self.transitions[handle]
}
/// Tries to borrow a transition using its handle, panics if the handle is invalid.
#[inline]
pub fn transition_mut(&mut self, handle: Handle<Transition>) -> &mut Transition {
pub fn transition_mut(&mut self, handle: Handle<Transition<T>>) -> &mut Transition<T> {
&mut self.transitions[handle]
}
/// Returns a reference to inner transitions container.
#[inline]
pub fn transitions(&self) -> &Pool<Transition> {
pub fn transitions(&self) -> &Pool<Transition<T>> {
&self.transitions
}
/// Returns a reference to inner transitions container.
#[inline]
pub fn transitions_mut(&mut self) -> &mut Pool<Transition> {
pub fn transitions_mut(&mut self) -> &mut Pool<Transition<T>> {
&mut self.transitions
}
@@ -450,8 +447,8 @@ impl MachineLayer {
pub fn find_transition_by_name_ref<S: AsRef<str>>(
&self,
name: S,
) -> Option<(Handle<Transition>, &Transition)> {
utils::find_by_name_ref(self.transitions.pair_iter(), name)
) -> Option<(Handle<Transition<T>>, &Transition<T>)> {
find_by_name_ref(self.transitions.pair_iter(), name)
}
/// Tries to find a transition by its name.
@@ -459,19 +456,19 @@ impl MachineLayer {
pub fn find_transition_by_name_mut<S: AsRef<str>>(
&mut self,
name: S,
) -> Option<(Handle<Transition>, &mut Transition)> {
utils::find_by_name_mut(self.transitions.pair_iter_mut(), name)
) -> Option<(Handle<Transition<T>>, &mut Transition<T>)> {
find_by_name_mut(self.transitions.pair_iter_mut(), name)
}
/// Tries to borrow a state using its handle, panics if the handle is invalid.
#[inline]
pub fn state(&self, handle: Handle<State>) -> &State {
pub fn state(&self, handle: Handle<State<T>>) -> &State<T> {
&self.states[handle]
}
/// Tries to borrow a state using its handle, panics if the handle is invalid.
#[inline]
pub fn state_mut(&mut self, handle: Handle<State>) -> &mut State {
pub fn state_mut(&mut self, handle: Handle<State<T>>) -> &mut State<T> {
&mut self.states[handle]
}
@@ -480,8 +477,8 @@ impl MachineLayer {
pub fn find_state_by_name_ref<S: AsRef<str>>(
&self,
name: S,
) -> Option<(Handle<State>, &State)> {
utils::find_by_name_ref(self.states.pair_iter(), name)
) -> Option<(Handle<State<T>>, &State<T>)> {
find_by_name_ref(self.states.pair_iter(), name)
}
/// Tries to find a state by its name.
@@ -489,19 +486,19 @@ impl MachineLayer {
pub fn find_state_by_name_mut<S: AsRef<str>>(
&mut self,
name: S,
) -> Option<(Handle<State>, &mut State)> {
utils::find_by_name_mut(self.states.pair_iter_mut(), name)
) -> Option<(Handle<State<T>>, &mut State<T>)> {
find_by_name_mut(self.states.pair_iter_mut(), name)
}
/// Returns a reference to inner states container.
#[inline]
pub fn states(&self) -> &Pool<State> {
pub fn states(&self) -> &Pool<State<T>> {
&self.states
}
/// Returns a reference to inner states container.
#[inline]
pub fn states_mut(&mut self) -> &mut Pool<State> {
pub fn states_mut(&mut self) -> &mut Pool<State<T>> {
&mut self.states
}
@@ -520,19 +517,19 @@ impl MachineLayer {
/// Sets new layer mask. See docs of [`LayerMask`] for more info about layer masks.
#[inline]
pub fn set_mask(&mut self, mask: LayerMask) -> LayerMask {
pub fn set_mask(&mut self, mask: LayerMask<T>) -> LayerMask<T> {
std::mem::replace(&mut self.mask, mask)
}
/// Returns a reference to current layer mask.
#[inline]
pub fn mask(&self) -> &LayerMask {
pub fn mask(&self) -> &LayerMask<T> {
&self.mask
}
/// Returns final pose of the layer.
#[inline]
pub fn pose(&self) -> &AnimationPose {
pub fn pose(&self) -> &AnimationPose<T> {
&self.final_pose
}
@@ -541,8 +538,8 @@ impl MachineLayer {
/// to listen for animation events and react to them.
pub fn animations_of_state(
&self,
state: Handle<State>,
) -> impl Iterator<Item = Handle<Animation>> + '_ {
state: Handle<State<T>>,
) -> impl Iterator<Item = Handle<Animation<T>>> + '_ {
self.nodes.iter().filter_map(move |node| {
if node.parent_state == state {
if let PoseNode::PlayAnimation(play_animation) = node {
@@ -559,8 +556,8 @@ impl MachineLayer {
/// Returns `true` if all animations of the given state has ended, `false` - otherwise.
pub fn is_all_animations_of_state_ended(
&self,
state: Handle<State>,
animations: &AnimationContainer,
state: Handle<State<T>>,
animations: &AnimationContainer<T>,
) -> bool {
self.animations_of_state(state)
.filter_map(|a| animations.try_get(a))
@@ -570,10 +567,10 @@ impl MachineLayer {
#[inline]
pub(super) fn evaluate_pose(
&mut self,
animations: &mut AnimationContainer,
animations: &mut AnimationContainer<T>,
parameters: &ParameterContainer,
dt: f32,
) -> &AnimationPose {
) -> &AnimationPose<T> {
self.final_pose.reset();
if self.active_state.is_some() || self.active_transition.is_some() {
@@ -1,36 +1,27 @@
//! Layer mask is a sort of blacklist that prevents layer from animating certain nodes. See [`LayerMask`] docs
//! for more info.
use crate::{
core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},
scene::{graph::Graph, node::Node},
};
use crate::core::{reflect::prelude::*, visitor::prelude::*};
use crate::EntityId;
/// Layer mask is a sort of blacklist that prevents layer from animating certain nodes. Its main use case is to
/// disable animation on animation layers for specific body parts of humanoid (but not only) characters. The
/// mask holds handles of nodes that **will not** be animated.
#[derive(Default, Debug, Visit, Reflect, Clone, PartialEq, Eq)]
pub struct LayerMask {
excluded_bones: Vec<Handle<Node>>,
pub struct LayerMask<T: EntityId> {
excluded_bones: Vec<T>,
}
impl From<Vec<Handle<Node>>> for LayerMask {
fn from(mut excluded_bones: Vec<Handle<Node>>) -> Self {
excluded_bones.sort_by_key(|h| h.index());
impl<T: EntityId> From<Vec<T>> for LayerMask<T> {
fn from(mut excluded_bones: Vec<T>) -> Self {
excluded_bones.sort();
Self { excluded_bones }
}
}
impl LayerMask {
/// 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.
pub fn from_hierarchy(graph: &Graph, root: Handle<Node>) -> Self {
Self::from(graph.traverse_handle_iter(root).collect::<Vec<_>>())
}
impl<T: EntityId> LayerMask<T> {
/// Merges a given layer mask in the current mask, handles will be automatically de-duplicated.
pub fn merge(&mut self, other: LayerMask) {
pub fn merge(&mut self, other: LayerMask<T>) {
for handle in other.into_inner() {
if !self.contains(handle) {
self.add(handle);
@@ -46,10 +37,8 @@ impl LayerMask {
///
/// The method has O(log(n)) complexity, which means it is very fast for most use cases.
#[inline]
pub fn add(&mut self, node: Handle<Node>) {
let index = self
.excluded_bones
.partition_point(|h| h.index() < node.index());
pub fn add(&mut self, node: T) {
let index = self.excluded_bones.partition_point(|h| h < &node);
self.excluded_bones.insert(index, node);
}
@@ -59,37 +48,14 @@ impl LayerMask {
/// # Performance
///
/// The method has O(log(n)) complexity, which means it is very fast for most use cases.
pub fn remove(&mut self, node: Handle<Node>) {
pub fn remove(&mut self, node: T) {
if let Some(index) = self.index_of(node) {
self.excluded_bones.remove(index);
}
}
fn index_of(&self, node: Handle<Node>) -> Option<usize> {
if let Ok(mut index) = self
.excluded_bones
.binary_search_by(|h| h.index().cmp(&node.index()))
{
// We could have multiple handles with the same index, but different generation.
// In this case we check every handle.
loop {
if let Some(current) = self.excluded_bones.get(index) {
if current.index() == node.index() {
if current.generation() == node.generation() {
return Some(index);
}
} else {
return None;
}
} else {
return None;
}
index += 1;
}
}
None
fn index_of(&self, id: T) -> Option<usize> {
self.excluded_bones.binary_search(&id).ok()
}
/// Checks if the mask contains a given node handle or not.
@@ -98,7 +64,7 @@ impl LayerMask {
///
/// The method has O(log(n)) complexity, which means it is very fast for most use cases.
#[inline]
pub fn contains(&self, node: Handle<Node>) -> bool {
pub fn contains(&self, node: T) -> bool {
self.index_of(node).is_some()
}
@@ -108,20 +74,20 @@ impl LayerMask {
///
/// The method has O(log(n)) complexity, which means it is very fast for most use cases.
#[inline]
pub fn should_animate(&self, node: Handle<Node>) -> bool {
pub fn should_animate(&self, node: T) -> bool {
!self.contains(node)
}
/// Return a reference to inner container. There's only non-mutable version because inner container must always
/// be sorted.
#[inline]
pub fn inner(&self) -> &Vec<Handle<Node>> {
pub fn inner(&self) -> &Vec<T> {
&self.excluded_bones
}
/// Converts the mask into inner container.
#[inline]
pub fn into_inner(self) -> Vec<Handle<Node>> {
pub fn into_inner(self) -> Vec<T> {
self.excluded_bones
}
}
@@ -6,15 +6,15 @@
#![warn(missing_docs)]
use crate::{
animation::{AnimationContainer, AnimationPose},
core::{
reflect::prelude::*,
visitor::{Visit, VisitResult, Visitor},
},
utils,
AnimationContainer, AnimationPose, EntityId,
};
pub use event::Event;
use fyrox_core::{find_by_name_mut, find_by_name_ref};
pub use layer::MachineLayer;
pub use mask::LayerMask;
pub use node::{
@@ -122,20 +122,21 @@ pub mod transition;
/// using code like so:
///
/// ```no_run
/// use fyrox::{
/// animation::machine::{
/// use fyrox_animation::{
/// machine::{
/// Machine, State, Transition, PoseNode,
/// Parameter, PlayAnimation, PoseWeight, BlendAnimations, BlendPose
/// },
/// core::pool::Handle
/// };
/// use fyrox_core::pool::ErasedHandle;
///
/// // Assume that these are correct handles.
/// let idle_animation = Handle::default();
/// let walk_animation = Handle::default();
/// let aim_animation = Handle::default();
///
/// let mut machine = Machine::new();
/// let mut machine = Machine::<ErasedHandle>::new();
///
/// let root_layer = &mut machine.layers_mut()[0];
///
@@ -167,18 +168,18 @@ pub mod transition;
///
/// Complex state machines quite hard to create from code, you should use ABSM editor instead whenever possible.
#[derive(Default, Debug, Visit, Reflect, Clone, PartialEq)]
pub struct Machine {
pub struct Machine<T: EntityId> {
parameters: ParameterContainer,
#[visit(optional)]
layers: Vec<MachineLayer>,
layers: Vec<MachineLayer<T>>,
#[visit(skip)]
#[reflect(hidden)]
final_pose: AnimationPose,
final_pose: AnimationPose<T>,
}
impl Machine {
impl<T: EntityId> Machine<T> {
/// Creates a new animation blending state machine with a single animation layer.
#[inline]
pub fn new() -> Self {
@@ -193,9 +194,10 @@ impl Machine {
/// The method returns a reference to the machine, so the calls could be chained:
///
/// ```rust
/// use fyrox::animation::machine::{Machine, Parameter};
/// use fyrox_animation::machine::{Machine, Parameter};
/// use fyrox_core::pool::ErasedHandle;
///
/// let mut machine = Machine::new();
/// let mut machine = Machine::<ErasedHandle>::new();
///
/// machine
/// .set_parameter("Run", Parameter::Rule(true))
@@ -229,44 +231,47 @@ impl Machine {
/// Adds a new layer to the animation blending state machine.
#[inline]
pub fn add_layer(&mut self, layer: MachineLayer) {
pub fn add_layer(&mut self, layer: MachineLayer<T>) {
self.layers.push(layer)
}
/// Removes a layer at given index. Panics if index is out-of-bounds.
#[inline]
pub fn remove_layer(&mut self, index: usize) -> MachineLayer {
pub fn remove_layer(&mut self, index: usize) -> MachineLayer<T> {
self.layers.remove(index)
}
/// Inserts a layer at given position, panics in index is out-of-bounds.
#[inline]
pub fn insert_layer(&mut self, index: usize, layer: MachineLayer) {
pub fn insert_layer(&mut self, index: usize, layer: MachineLayer<T>) {
self.layers.insert(index, layer)
}
/// Removes last layer from the list.
#[inline]
pub fn pop_layer(&mut self) -> Option<MachineLayer> {
pub fn pop_layer(&mut self) -> Option<MachineLayer<T>> {
self.layers.pop()
}
/// Returns a shared reference to the list of layers.
#[inline]
pub fn layers(&self) -> &[MachineLayer] {
pub fn layers(&self) -> &[MachineLayer<T>] {
&self.layers
}
/// Returns a mutable reference to the list of layers.
#[inline]
pub fn layers_mut(&mut self) -> &mut [MachineLayer] {
pub fn layers_mut(&mut self) -> &mut [MachineLayer<T>] {
&mut self.layers
}
/// Tries to find a layer by its name. Returns index of the layer and its reference.
#[inline]
pub fn find_layer_by_name_ref<S: AsRef<str>>(&self, name: S) -> Option<(usize, &MachineLayer)> {
utils::find_by_name_ref(self.layers.iter().enumerate(), name)
pub fn find_layer_by_name_ref<S: AsRef<str>>(
&self,
name: S,
) -> Option<(usize, &MachineLayer<T>)> {
find_by_name_ref(self.layers.iter().enumerate(), name)
}
/// Tries to find a layer by its name. Returns index of the layer and its reference.
@@ -274,23 +279,23 @@ impl Machine {
pub fn find_by_name_mut<S: AsRef<str>>(
&mut self,
name: S,
) -> Option<(usize, &mut MachineLayer)> {
utils::find_by_name_mut(self.layers.iter_mut().enumerate(), name)
) -> Option<(usize, &mut MachineLayer<T>)> {
find_by_name_mut(self.layers.iter_mut().enumerate(), name)
}
/// Returns final pose of the machine.
#[inline]
pub fn pose(&self) -> &AnimationPose {
pub fn pose(&self) -> &AnimationPose<T> {
&self.final_pose
}
/// Computes final animation pose that could be then applied to a scene graph.
/// Computes final animation pose that could be then applied to a set of entities graph.
#[inline]
pub fn evaluate_pose(
&mut self,
animations: &mut AnimationContainer,
animations: &mut AnimationContainer<T>,
dt: f32,
) -> &AnimationPose {
) -> &AnimationPose<T> {
self.final_pose.reset();
for layer in self.layers.iter_mut() {
@@ -1,22 +1,19 @@
//! Various animation blending nodes.
use crate::animation::machine::node::AnimationEventCollectionStrategy;
use crate::animation::{Animation, AnimationEvent};
use crate::{
animation::{
machine::{
node::BasePoseNode, AnimationPoseSource, Parameter, ParameterContainer, PoseNode,
PoseWeight,
},
AnimationContainer, AnimationPose,
},
core::{
pool::{Handle, Pool},
reflect::prelude::*,
visitor::{Visit, VisitResult, Visitor},
},
machine::{
node::AnimationEventCollectionStrategy, node::BasePoseNode, AnimationPoseSource, Parameter,
ParameterContainer, PoseNode, PoseWeight,
},
Animation, AnimationContainer, AnimationEvent, AnimationPose, EntityId,
};
use fyrox_core::uuid_provider;
use fyrox_core::uuid::{uuid, Uuid};
use fyrox_core::TypeUuidProvider;
use std::cmp::Ordering;
use std::{
cell::{Cell, Ref, RefCell},
@@ -26,20 +23,24 @@ use std::{
/// Weighted proxy for animation pose. It has an input pose source and a weight, that tells in which proportion
/// the pose should be blended into final pose.
#[derive(Default, Debug, Visit, Clone, Reflect, PartialEq)]
pub struct BlendPose {
pub struct BlendPose<T: EntityId> {
/// Weight of the pose.
pub weight: PoseWeight,
/// A source of animation pose.
#[reflect(hidden)]
pub pose_source: Handle<PoseNode>,
pub pose_source: Handle<PoseNode<T>>,
}
uuid_provider!(BlendPose = "b01d7639-7b39-4eaf-87e6-29fd5221951b");
impl<T: EntityId> TypeUuidProvider for BlendPose<T> {
fn type_uuid() -> Uuid {
uuid!("b01d7639-7b39-4eaf-87e6-29fd5221951b")
}
}
impl BlendPose {
impl<T: EntityId> BlendPose<T> {
/// Creates new instance of blend pose with given weight and animation pose.
pub fn new(weight: PoseWeight, pose_source: Handle<PoseNode>) -> Self {
pub fn new(weight: PoseWeight, pose_source: Handle<PoseNode<T>>) -> Self {
Self {
weight,
pose_source,
@@ -48,7 +49,7 @@ impl BlendPose {
/// Specialized constructor that creates blend pose with constant weight.
/// `weight` should be positive.
pub fn with_constant_weight(weight: f32, pose_source: Handle<PoseNode>) -> Self {
pub fn with_constant_weight(weight: f32, pose_source: Handle<PoseNode<T>>) -> Self {
Self {
weight: PoseWeight::Constant(weight),
pose_source,
@@ -57,7 +58,7 @@ impl BlendPose {
/// Specialized constructor that creates blend pose with parametrized weight.
/// `param_id` must be name of Weight parameter in machine.
pub fn with_param_weight(param_id: &str, pose_source: Handle<PoseNode>) -> Self {
pub fn with_param_weight(param_id: &str, pose_source: Handle<PoseNode<T>>) -> Self {
Self {
weight: PoseWeight::Parameter(param_id.to_owned()),
pose_source,
@@ -73,36 +74,36 @@ impl BlendPose {
/// can decrease weight of hit animation over time and increase weight of run animation, so character will recover
/// from his wounds.
#[derive(Default, Debug, Visit, Clone, Reflect, PartialEq)]
pub struct BlendAnimations {
pub struct BlendAnimations<T: EntityId> {
/// Base node.
pub base: BasePoseNode,
pub base: BasePoseNode<T>,
/// A list of pose sources. See [`BlendPose`] docs for more info.
pub pose_sources: Vec<BlendPose>,
pub pose_sources: Vec<BlendPose<T>>,
/// Output pose of the node, contains final result of blending all input poses.
#[visit(skip)]
#[reflect(hidden)]
pub output_pose: RefCell<AnimationPose>,
pub output_pose: RefCell<AnimationPose<T>>,
}
impl Deref for BlendAnimations {
type Target = BasePoseNode;
impl<T: EntityId> Deref for BlendAnimations<T> {
type Target = BasePoseNode<T>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for BlendAnimations {
impl<T: EntityId> DerefMut for BlendAnimations<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl BlendAnimations {
impl<T: EntityId> BlendAnimations<T> {
/// Creates new animation blend node with given poses.
pub fn new(poses: Vec<BlendPose>) -> Self {
pub fn new(poses: Vec<BlendPose<T>>) -> Self {
Self {
base: Default::default(),
pose_sources: poses,
@@ -111,19 +112,19 @@ impl BlendAnimations {
}
/// Returns a set of handles to children pose nodes.
pub fn children(&self) -> Vec<Handle<PoseNode>> {
pub fn children(&self) -> Vec<Handle<PoseNode<T>>> {
self.pose_sources.iter().map(|s| s.pose_source).collect()
}
}
impl AnimationPoseSource for BlendAnimations {
impl<T: EntityId> AnimationPoseSource<T> for BlendAnimations<T> {
fn eval_pose(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
dt: f32,
) -> Ref<AnimationPose> {
) -> Ref<AnimationPose<T>> {
self.output_pose.borrow_mut().reset();
for blend_pose in self.pose_sources.iter() {
let weight = match blend_pose.weight {
@@ -149,17 +150,17 @@ impl AnimationPoseSource for BlendAnimations {
self.output_pose.borrow()
}
fn pose(&self) -> Ref<AnimationPose> {
fn pose(&self) -> Ref<AnimationPose<T>> {
self.output_pose.borrow()
}
fn collect_animation_events(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
strategy: AnimationEventCollectionStrategy,
) -> Vec<(Handle<Animation>, AnimationEvent)> {
) -> Vec<(Handle<Animation<T>>, AnimationEvent)> {
match strategy {
AnimationEventCollectionStrategy::All => {
let mut events = Vec::new();
@@ -209,16 +210,20 @@ impl AnimationPoseSource for BlendAnimations {
/// An animation pose with specific blend time. Blend time tells the engine how many time it should use to perform
/// blending to this pose.
#[derive(Default, Debug, Visit, Clone, Reflect, PartialEq)]
pub struct IndexedBlendInput {
pub struct IndexedBlendInput<T: EntityId> {
/// Blend time tells the engine how many time it should use to perform blending to this pose.
pub blend_time: f32,
/// A handle to pose node source.
#[reflect(hidden)]
pub pose_source: Handle<PoseNode>,
pub pose_source: Handle<PoseNode<T>>,
}
uuid_provider!(IndexedBlendInput = "92fcc992-9a68-4152-8449-657546faa286");
impl<T: EntityId> TypeUuidProvider for IndexedBlendInput<T> {
fn type_uuid() -> Uuid {
uuid!("92fcc992-9a68-4152-8449-657546faa286")
}
}
/// A node that switches between given animations using index and smoothly blends from one animation to another
/// while switching. It is very useful for situations when you need to switch between different animations. For
@@ -229,15 +234,15 @@ uuid_provider!(IndexedBlendInput = "92fcc992-9a68-4152-8449-657546faa286");
/// parameter in the machine parameters. The node will automatically perform smooth transition between different
/// animations.
#[derive(Default, Debug, Visit, Clone, Reflect, PartialEq)]
pub struct BlendAnimationsByIndex {
pub struct BlendAnimationsByIndex<T: EntityId> {
/// Base node.
pub base: BasePoseNode,
pub base: BasePoseNode<T>,
/// A name of index parameter that will be used to switch between input poses.
pub index_parameter: String,
/// A set of input poses.
pub inputs: Vec<IndexedBlendInput>,
pub inputs: Vec<IndexedBlendInput<T>>,
/// Index of a previously active input pose.
#[reflect(hidden)]
@@ -250,26 +255,26 @@ pub struct BlendAnimationsByIndex {
/// Output pose of the node.
#[visit(skip)]
#[reflect(hidden)]
pub output_pose: RefCell<AnimationPose>,
pub output_pose: RefCell<AnimationPose<T>>,
}
impl Deref for BlendAnimationsByIndex {
type Target = BasePoseNode;
impl<T: EntityId> Deref for BlendAnimationsByIndex<T> {
type Target = BasePoseNode<T>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for BlendAnimationsByIndex {
impl<T: EntityId> DerefMut for BlendAnimationsByIndex<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl BlendAnimationsByIndex {
impl<T: EntityId> BlendAnimationsByIndex<T> {
/// Creates new [`BlendAnimationsByIndex`] node using given index parameter name and a set of inputs.
pub fn new(index_parameter: String, inputs: Vec<IndexedBlendInput>) -> Self {
pub fn new(index_parameter: String, inputs: Vec<IndexedBlendInput<T>>) -> Self {
Self {
base: Default::default(),
index_parameter,
@@ -281,19 +286,19 @@ impl BlendAnimationsByIndex {
}
/// Return a set of handle of children nodes.
pub fn children(&self) -> Vec<Handle<PoseNode>> {
pub fn children(&self) -> Vec<Handle<PoseNode<T>>> {
self.inputs.iter().map(|s| s.pose_source).collect()
}
}
impl AnimationPoseSource for BlendAnimationsByIndex {
impl<T: EntityId> AnimationPoseSource<T> for BlendAnimationsByIndex<T> {
fn eval_pose(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
dt: f32,
) -> Ref<AnimationPose> {
) -> Ref<AnimationPose<T>> {
self.output_pose.borrow_mut().reset();
if let Some(&Parameter::Index(current_index)) = params.get(&self.index_parameter) {
@@ -347,17 +352,17 @@ impl AnimationPoseSource for BlendAnimationsByIndex {
self.output_pose.borrow()
}
fn pose(&self) -> Ref<AnimationPose> {
fn pose(&self) -> Ref<AnimationPose<T>> {
self.output_pose.borrow()
}
fn collect_animation_events(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
strategy: AnimationEventCollectionStrategy,
) -> Vec<(Handle<Animation>, AnimationEvent)> {
) -> Vec<(Handle<Animation<T>>, AnimationEvent)> {
if let Some(&Parameter::Index(current_index)) = params.get(&self.index_parameter) {
if let Some(prev_index) = self.prev_index.get() {
if prev_index != current_index {
@@ -1,14 +1,6 @@
#![allow(missing_docs)] // TODO
use crate::animation::machine::node::AnimationEventCollectionStrategy;
use crate::animation::{Animation, AnimationEvent};
use crate::{
animation::{
machine::{
node::BasePoseNode, AnimationPoseSource, Parameter, ParameterContainer, PoseNode,
},
AnimationContainer, AnimationPose,
},
core::{
algebra::Vector2,
math::{self, TriangleDefinition},
@@ -16,8 +8,14 @@ use crate::{
reflect::prelude::*,
visitor::prelude::*,
},
machine::{
node::AnimationEventCollectionStrategy, node::BasePoseNode, AnimationPoseSource, Parameter,
ParameterContainer, PoseNode,
},
Animation, AnimationContainer, AnimationEvent, AnimationPose, EntityId,
};
use fyrox_core::uuid_provider;
use fyrox_core::uuid::{uuid, Uuid};
use fyrox_core::TypeUuidProvider;
use spade::{DelaunayTriangulation, Point2, Triangulation};
use std::cmp::Ordering;
use std::{
@@ -26,18 +24,23 @@ use std::{
};
#[derive(Debug, Visit, Clone, Reflect, PartialEq, Default)]
pub struct BlendSpacePoint {
pub struct BlendSpacePoint<T: EntityId> {
pub position: Vector2<f32>,
pub pose_source: Handle<PoseNode>,
pub pose_source: Handle<PoseNode<T>>,
}
impl<T: EntityId> TypeUuidProvider for BlendSpacePoint<T> {
fn type_uuid() -> Uuid {
uuid!("d163b4b9-aed6-447f-bb93-b7e539099417")
}
}
uuid_provider!(BlendSpacePoint = "d163b4b9-aed6-447f-bb93-b7e539099417");
#[derive(Debug, Visit, Clone, Reflect, PartialEq)]
pub struct BlendSpace {
base: BasePoseNode,
pub struct BlendSpace<T: EntityId> {
base: BasePoseNode<T>,
#[reflect(hidden)]
points: Vec<BlendSpacePoint>,
points: Vec<BlendSpacePoint<T>>,
#[reflect(hidden)]
triangles: Vec<TriangleDefinition>,
@@ -62,10 +65,10 @@ pub struct BlendSpace {
#[reflect(hidden)]
#[visit(skip)]
pose: RefCell<AnimationPose>,
pose: RefCell<AnimationPose<T>>,
}
impl Default for BlendSpace {
impl<T: EntityId> Default for BlendSpace<T> {
fn default() -> Self {
Self {
base: Default::default(),
@@ -82,28 +85,28 @@ impl Default for BlendSpace {
}
}
impl Deref for BlendSpace {
type Target = BasePoseNode;
impl<T: EntityId> Deref for BlendSpace<T> {
type Target = BasePoseNode<T>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for BlendSpace {
impl<T: EntityId> DerefMut for BlendSpace<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl AnimationPoseSource for BlendSpace {
impl<T: EntityId> AnimationPoseSource<T> for BlendSpace<T> {
fn eval_pose(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
dt: f32,
) -> Ref<AnimationPose> {
) -> Ref<AnimationPose<T>> {
let mut pose = self.pose.borrow_mut();
pose.reset();
@@ -132,17 +135,17 @@ impl AnimationPoseSource for BlendSpace {
self.pose.borrow()
}
fn pose(&self) -> Ref<AnimationPose> {
fn pose(&self) -> Ref<AnimationPose<T>> {
self.pose.borrow()
}
fn collect_animation_events(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
strategy: AnimationEventCollectionStrategy,
) -> Vec<(Handle<Animation>, AnimationEvent)> {
) -> Vec<(Handle<Animation<T>>, AnimationEvent)> {
if let Some(Parameter::SamplingPoint(sampling_point)) = params.get(&self.sampling_parameter)
{
if let Some(weights) = self.fetch_weights(*sampling_point) {
@@ -200,38 +203,38 @@ impl AnimationPoseSource for BlendSpace {
}
}
pub struct PointsMut<'a> {
blend_space: &'a mut BlendSpace,
pub struct PointsMut<'a, T: EntityId> {
blend_space: &'a mut BlendSpace<T>,
}
impl<'a> Deref for PointsMut<'a> {
type Target = Vec<BlendSpacePoint>;
impl<'a, T: EntityId> Deref for PointsMut<'a, T> {
type Target = Vec<BlendSpacePoint<T>>;
fn deref(&self) -> &Self::Target {
&self.blend_space.points
}
}
impl<'a> DerefMut for PointsMut<'a> {
impl<'a, T: EntityId> DerefMut for PointsMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.blend_space.points
}
}
impl<'a> Drop for PointsMut<'a> {
impl<'a, T: EntityId> Drop for PointsMut<'a, T> {
fn drop(&mut self) {
self.blend_space.triangulate();
}
}
impl BlendSpace {
pub fn add_point(&mut self, point: BlendSpacePoint) -> bool {
impl<T: EntityId> BlendSpace<T> {
pub fn add_point(&mut self, point: BlendSpacePoint<T>) -> bool {
self.points.push(point);
self.triangulate()
}
/// Sets new points to the blend space.
pub fn set_points(&mut self, points: Vec<BlendSpacePoint>) -> bool {
pub fn set_points(&mut self, points: Vec<BlendSpacePoint<T>>) -> bool {
self.points = points;
self.triangulate()
}
@@ -241,11 +244,11 @@ impl BlendSpace {
self.triangles.clear();
}
pub fn points(&self) -> &[BlendSpacePoint] {
pub fn points(&self) -> &[BlendSpacePoint<T>] {
&self.points
}
pub fn points_mut(&mut self) -> PointsMut {
pub fn points_mut(&mut self) -> PointsMut<T> {
PointsMut { blend_space: self }
}
@@ -253,7 +256,7 @@ impl BlendSpace {
&self.triangles
}
pub fn children(&self) -> Vec<Handle<PoseNode>> {
pub fn children(&self) -> Vec<Handle<PoseNode<T>>> {
self.points.iter().map(|p| p.pose_source).collect()
}
@@ -429,13 +432,14 @@ impl BlendSpace {
#[cfg(test)]
mod test {
use crate::{
animation::machine::node::blendspace::{BlendSpace, BlendSpacePoint},
core::{algebra::Vector2, math::TriangleDefinition},
machine::node::blendspace::{BlendSpace, BlendSpacePoint},
};
use fyrox_core::pool::ErasedHandle;
#[test]
fn test_blend_space_triangulation() {
let mut blend_space = BlendSpace::default();
let mut blend_space = BlendSpace::<ErasedHandle>::default();
blend_space.set_points(vec![
BlendSpacePoint {
@@ -466,14 +470,14 @@ mod test {
#[test]
fn test_empty_blend_space_sampling() {
assert!(BlendSpace::default()
assert!(BlendSpace::<ErasedHandle>::default()
.fetch_weights(Default::default())
.is_none())
}
#[test]
fn test_single_point_blend_space_sampling() {
let mut blend_space = BlendSpace::default();
let mut blend_space = BlendSpace::<ErasedHandle>::default();
blend_space.set_points(vec![BlendSpacePoint {
position: Vector2::new(0.0, 0.0),
@@ -488,7 +492,7 @@ mod test {
#[test]
fn test_two_points_blend_space_sampling() {
let mut blend_space = BlendSpace::default();
let mut blend_space = BlendSpace::<ErasedHandle>::default();
blend_space.set_points(vec![
BlendSpacePoint {
@@ -1,21 +1,18 @@
//! Node is a part of animation blending tree, that backs a state with animation data. See [`PoseNode`] docs for
//! more info.
use crate::animation::AnimationEvent;
use crate::{
animation::{
machine::{
node::{blend::BlendAnimations, blendspace::BlendSpace, play::PlayAnimation},
BlendAnimationsByIndex, BlendPose, IndexedBlendInput, ParameterContainer, State,
},
Animation, AnimationContainer, AnimationPose,
},
core::{
algebra::Vector2,
pool::{Handle, Pool},
reflect::prelude::*,
visitor::prelude::*,
},
machine::{
node::{blend::BlendAnimations, blendspace::BlendSpace, play::PlayAnimation},
BlendAnimationsByIndex, BlendPose, IndexedBlendInput, ParameterContainer, State,
},
Animation, AnimationContainer, AnimationEvent, AnimationPose, EntityId,
};
use std::{
cell::Ref,
@@ -28,45 +25,45 @@ pub mod play;
/// A set of common data fields that is used in every node.
#[derive(Debug, Visit, Clone, Default, Reflect, PartialEq)]
pub struct BasePoseNode {
pub struct BasePoseNode<T: EntityId> {
/// Position on the canvas, it is editor-specific data.
pub position: Vector2<f32>,
/// A handle of parent state that "owns" the node.
#[reflect(hidden)]
pub parent_state: Handle<State>,
pub parent_state: Handle<State<T>>,
}
/// Specialized node that provides animation pose. See documentation for each variant.
#[derive(Debug, Visit, Clone, Reflect, PartialEq)]
pub enum PoseNode {
pub enum PoseNode<T: EntityId> {
/// See docs for [`PlayAnimation`].
PlayAnimation(PlayAnimation),
PlayAnimation(PlayAnimation<T>),
/// See docs for [`BlendAnimations`].
BlendAnimations(BlendAnimations),
BlendAnimations(BlendAnimations<T>),
/// See docs for [`BlendAnimationsByIndex`].
BlendAnimationsByIndex(BlendAnimationsByIndex),
BlendAnimationsByIndex(BlendAnimationsByIndex<T>),
/// See doc for [`BlendSpace`]
BlendSpace(BlendSpace),
BlendSpace(BlendSpace<T>),
}
impl Default for PoseNode {
impl<T: EntityId> Default for PoseNode<T> {
fn default() -> Self {
Self::PlayAnimation(Default::default())
}
}
impl PoseNode {
impl<T: EntityId> PoseNode<T> {
/// Creates new node that plays an animation.
pub fn make_play_animation(animation: Handle<Animation>) -> Self {
pub fn make_play_animation(animation: Handle<Animation<T>>) -> Self {
Self::PlayAnimation(PlayAnimation::new(animation))
}
/// Creates new node that blends multiple poses into one.
pub fn make_blend_animations(poses: Vec<BlendPose>) -> Self {
pub fn make_blend_animations(poses: Vec<BlendPose<T>>) -> Self {
Self::BlendAnimations(BlendAnimations::new(poses))
}
@@ -74,13 +71,13 @@ impl PoseNode {
/// one animation to another while switching.
pub fn make_blend_animations_by_index(
index_parameter: String,
inputs: Vec<IndexedBlendInput>,
inputs: Vec<IndexedBlendInput<T>>,
) -> Self {
Self::BlendAnimationsByIndex(BlendAnimationsByIndex::new(index_parameter, inputs))
}
/// Returns a set of handles to children pose nodes.
pub fn children(&self) -> Vec<Handle<PoseNode>> {
pub fn children(&self) -> Vec<Handle<PoseNode<T>>> {
match self {
Self::PlayAnimation(_) => {
// No children nodes.
@@ -104,15 +101,15 @@ macro_rules! static_dispatch {
};
}
impl Deref for PoseNode {
type Target = BasePoseNode;
impl<T: EntityId> Deref for PoseNode<T> {
type Target = BasePoseNode<T>;
fn deref(&self) -> &Self::Target {
static_dispatch!(self, deref,)
}
}
impl DerefMut for PoseNode {
impl<T: EntityId> DerefMut for PoseNode<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
static_dispatch!(self, deref_mut,)
}
@@ -130,51 +127,51 @@ pub enum AnimationEventCollectionStrategy {
}
/// A trait that responsible for animation pose evaluation.
pub trait AnimationPoseSource {
pub trait AnimationPoseSource<T: EntityId> {
/// Evaluates animation pose and returns a reference to it.
fn eval_pose(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
dt: f32,
) -> Ref<AnimationPose>;
) -> Ref<AnimationPose<T>>;
/// Returns a reference to inner pose of a node.
fn pose(&self) -> Ref<AnimationPose>;
fn pose(&self) -> Ref<AnimationPose<T>>;
/// Collects animation events from internals.
fn collect_animation_events(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
strategy: AnimationEventCollectionStrategy,
) -> Vec<(Handle<Animation>, AnimationEvent)>;
) -> Vec<(Handle<Animation<T>>, AnimationEvent)>;
}
impl AnimationPoseSource for PoseNode {
impl<T: EntityId> AnimationPoseSource<T> for PoseNode<T> {
fn eval_pose(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
dt: f32,
) -> Ref<AnimationPose> {
) -> Ref<AnimationPose<T>> {
static_dispatch!(self, eval_pose, nodes, params, animations, dt)
}
fn pose(&self) -> Ref<AnimationPose> {
fn pose(&self) -> Ref<AnimationPose<T>> {
static_dispatch!(self, pose,)
}
fn collect_animation_events(
&self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
strategy: AnimationEventCollectionStrategy,
) -> Vec<(Handle<Animation>, AnimationEvent)> {
) -> Vec<(Handle<Animation<T>>, AnimationEvent)> {
static_dispatch!(
self,
collect_animation_events,
@@ -1,20 +1,17 @@
//! A simplest pose node that extracts pose from a specific animation and prepares it for further use.
use crate::animation::machine::node::AnimationEventCollectionStrategy;
use crate::animation::AnimationEvent;
use crate::{
animation::{
machine::{
node::{AnimationPoseSource, BasePoseNode},
ParameterContainer, PoseNode,
},
Animation, AnimationContainer, AnimationPose,
},
core::{
pool::{Handle, Pool},
reflect::prelude::*,
visitor::prelude::*,
},
machine::{
node::AnimationEventCollectionStrategy,
node::{AnimationPoseSource, BasePoseNode},
ParameterContainer, PoseNode,
},
Animation, AnimationContainer, AnimationEvent, AnimationPose, EntityId,
};
use std::{
cell::{Ref, RefCell},
@@ -25,37 +22,37 @@ use std::{
/// Animation handle should point to an animation in some animation container see [`AnimationContainer`] docs
/// for more info.
#[derive(Default, Debug, Visit, Clone, Reflect, PartialEq)]
pub struct PlayAnimation {
pub struct PlayAnimation<T: EntityId> {
/// Base node.
pub base: BasePoseNode,
pub base: BasePoseNode<T>,
/// A handle to animation.
pub animation: Handle<Animation>,
pub animation: Handle<Animation<T>>,
/// Output pose, it contains a filtered (see [`crate::animation::machine::LayerMask`] for more info) pose from
/// Output pose, it contains a filtered (see [`crate::machine::LayerMask`] for more info) pose from
/// the animation specified by the `animation` field.
#[visit(skip)]
#[reflect(hidden)]
pub output_pose: RefCell<AnimationPose>,
pub output_pose: RefCell<AnimationPose<T>>,
}
impl Deref for PlayAnimation {
type Target = BasePoseNode;
impl<T: EntityId> Deref for PlayAnimation<T> {
type Target = BasePoseNode<T>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for PlayAnimation {
impl<T: EntityId> DerefMut for PlayAnimation<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl PlayAnimation {
impl<T: EntityId> PlayAnimation<T> {
/// Creates new PlayAnimation node with given animation handle.
pub fn new(animation: Handle<Animation>) -> Self {
pub fn new(animation: Handle<Animation<T>>) -> Self {
Self {
base: Default::default(),
animation,
@@ -64,14 +61,14 @@ impl PlayAnimation {
}
}
impl AnimationPoseSource for PlayAnimation {
impl<T: EntityId> AnimationPoseSource<T> for PlayAnimation<T> {
fn eval_pose(
&self,
_nodes: &Pool<PoseNode>,
_nodes: &Pool<PoseNode<T>>,
_params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
_dt: f32,
) -> Ref<AnimationPose> {
) -> Ref<AnimationPose<T>> {
if let Some(animation) = animations.try_get(self.animation) {
let mut output_pose = self.output_pose.borrow_mut();
animation.pose().clone_into(&mut output_pose);
@@ -81,17 +78,17 @@ impl AnimationPoseSource for PlayAnimation {
self.output_pose.borrow()
}
fn pose(&self) -> Ref<AnimationPose> {
fn pose(&self) -> Ref<AnimationPose<T>> {
self.output_pose.borrow()
}
fn collect_animation_events(
&self,
_nodes: &Pool<PoseNode>,
_nodes: &Pool<PoseNode<T>>,
_params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
_strategy: AnimationEventCollectionStrategy,
) -> Vec<(Handle<Animation>, AnimationEvent)> {
) -> Vec<(Handle<Animation<T>>, AnimationEvent)> {
animations
.try_get(self.animation)
.map(|a| {
@@ -1,20 +1,18 @@
//! State is a final "container" for animation pose. See [`State`] docs for more info.
use crate::{
animation::{
machine::{AnimationPoseSource, ParameterContainer, PoseNode},
Animation, AnimationContainer, AnimationPose,
},
core::{
algebra::Vector2,
pool::{Handle, Pool},
rand::{self, seq::IteratorRandom},
reflect::prelude::*,
visitor::prelude::*,
},
rand::{self, seq::IteratorRandom},
utils::NameProvider,
machine::{AnimationPoseSource, ParameterContainer, PoseNode},
Animation, AnimationContainer, AnimationPose, EntityId,
};
use fyrox_core::uuid_provider;
use fyrox_core::uuid::{uuid, Uuid};
use fyrox_core::{NameProvider, TypeUuidProvider};
use std::{
cell::Ref,
ops::{Deref, DerefMut},
@@ -23,19 +21,23 @@ use strum_macros::{AsRefStr, EnumString, EnumVariantNames};
#[doc(hidden)]
#[derive(Default, Debug, Visit, Reflect, Clone, PartialEq)]
pub struct StateActionWrapper(pub StateAction);
pub struct StateActionWrapper<T: EntityId>(pub StateAction<T>);
uuid_provider!(StateActionWrapper = "d686fac8-5cc1-46b1-82a4-7f4438cc078d");
impl<T: EntityId> TypeUuidProvider for StateActionWrapper<T> {
fn type_uuid() -> Uuid {
uuid!("d686fac8-5cc1-46b1-82a4-7f4438cc078d")
}
}
impl Deref for StateActionWrapper {
type Target = StateAction;
impl<T: EntityId> Deref for StateActionWrapper<T> {
type Target = StateAction<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for StateActionWrapper {
impl<T: EntityId> DerefMut for StateActionWrapper<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
@@ -50,28 +52,32 @@ impl DerefMut for StateActionWrapper {
#[derive(
Default, Debug, Visit, Reflect, Clone, PartialEq, EnumVariantNames, EnumString, AsRefStr,
)]
pub enum StateAction {
pub enum StateAction<T: EntityId> {
/// No action.
#[default]
None,
/// Rewinds the animation.
RewindAnimation(Handle<Animation>),
RewindAnimation(Handle<Animation<T>>),
/// Enables the animation.
EnableAnimation(Handle<Animation>),
EnableAnimation(Handle<Animation<T>>),
/// Disables the animation.
DisableAnimation(Handle<Animation>),
DisableAnimation(Handle<Animation<T>>),
/// Enables random animation from the list. It could be useful if you want to add randomization
/// to your state machine. For example, you may have few melee attack animations and all of them
/// are suitable for every situation, in this case you can add randomization to make attacks less
/// predictable.
EnableRandomAnimation(Vec<Handle<Animation>>),
EnableRandomAnimation(Vec<Handle<Animation<T>>>),
}
uuid_provider!(StateAction = "c50a15cc-0f63-4409-bbe0-74b9d3e94755");
impl<T: EntityId> TypeUuidProvider for StateAction<T> {
fn type_uuid() -> Uuid {
uuid!("c50a15cc-0f63-4409-bbe0-74b9d3e94755")
}
}
impl StateAction {
impl<T: EntityId> StateAction<T> {
/// Applies the action to the given animation container.
pub fn apply(&self, animations: &mut AnimationContainer) {
pub fn apply(&self, animations: &mut AnimationContainer<T>) {
match self {
StateAction::None => {}
StateAction::RewindAnimation(animation) => {
@@ -103,7 +109,7 @@ impl StateAction {
/// State is a final "container" for animation pose. It has backing pose node which provides a set of values.
/// States can be connected with each other using _transitions_, states with transitions form a state graph.
#[derive(Default, Debug, Visit, Clone, Reflect, PartialEq)]
pub struct State {
pub struct State<T: EntityId> {
/// Position of state on the canvas. It is editor-specific data.
pub position: Vector2<f32>,
@@ -112,26 +118,26 @@ pub struct State {
/// A set of actions that will be executed when entering the state.
#[visit(optional)]
pub on_enter_actions: Vec<StateActionWrapper>,
pub on_enter_actions: Vec<StateActionWrapper<T>>,
/// A set of actions that will be executed when leaving the state.
#[visit(optional)]
pub on_leave_actions: Vec<StateActionWrapper>,
pub on_leave_actions: Vec<StateActionWrapper<T>>,
/// Root node of the state that provides the state with animation data.
#[reflect(read_only)]
pub root: Handle<PoseNode>,
pub root: Handle<PoseNode<T>>,
}
impl NameProvider for State {
impl<T: EntityId> NameProvider for State<T> {
fn name(&self) -> &str {
&self.name
}
}
impl State {
impl<T: EntityId> State<T> {
/// Creates new instance of state with a given pose.
pub fn new(name: &str, root: Handle<PoseNode>) -> Self {
pub fn new(name: &str, root: Handle<PoseNode<T>>) -> Self {
Self {
position: Default::default(),
name: name.to_owned(),
@@ -142,15 +148,15 @@ impl State {
}
/// Returns a final pose of the state.
pub fn pose<'a>(&self, nodes: &'a Pool<PoseNode>) -> Option<Ref<'a, AnimationPose>> {
pub fn pose<'a>(&self, nodes: &'a Pool<PoseNode<T>>) -> Option<Ref<'a, AnimationPose<T>>> {
nodes.try_borrow(self.root).map(|root| root.pose())
}
pub(super) fn update(
&mut self,
nodes: &Pool<PoseNode>,
nodes: &Pool<PoseNode<T>>,
params: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
dt: f32,
) {
if let Some(root) = nodes.try_borrow(self.root) {
@@ -1,14 +1,12 @@
//! Transition is a connection between two states with a rule that defines possibility of actual transition with blending.
use crate::{
animation::{
machine::{Parameter, ParameterContainer, State},
Animation, AnimationContainer,
},
core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},
utils::NameProvider,
machine::{Parameter, ParameterContainer, State},
Animation, AnimationContainer, EntityId,
};
use fyrox_core::uuid_provider;
use fyrox_core::uuid::{uuid, Uuid};
use fyrox_core::{NameProvider, TypeUuidProvider};
use std::any::{type_name, Any, TypeId};
use strum_macros::{AsRefStr, EnumString, EnumVariantNames};
@@ -16,14 +14,14 @@ macro_rules! define_two_args_node {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Debug, Clone, PartialEq)]
pub struct $name {
pub struct $name <T:EntityId> {
/// Left argument.
pub lhs: Box<LogicNode>,
pub lhs: Box<LogicNode<T>>,
/// Right argument.
pub rhs: Box<LogicNode>,
pub rhs: Box<LogicNode<T>>,
}
impl Default for $name {
impl<T:EntityId> Default for $name<T> {
fn default() -> Self {
Self {
lhs: Box::new(LogicNode::Parameter(Default::default())),
@@ -32,7 +30,7 @@ macro_rules! define_two_args_node {
}
}
impl Visit for $name {
impl<T:EntityId> Visit for $name<T> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut guard = visitor.enter_region(name)?;
@@ -43,7 +41,7 @@ macro_rules! define_two_args_node {
}
}
impl Reflect for $name {
impl<T:EntityId> Reflect for $name<T> {
fn type_name(&self) -> &'static str {
type_name::<Self>()
}
@@ -162,12 +160,12 @@ define_two_args_node!(
/// Calculates logical NOT of an argument. Output value will be `true` if the value of the argument is `false`.
#[derive(Debug, Clone, PartialEq)]
pub struct NotNode {
pub struct NotNode<T: EntityId> {
/// Argument to be negated.
pub lhs: Box<LogicNode>,
pub lhs: Box<LogicNode<T>>,
}
impl Default for NotNode {
impl<T: EntityId> Default for NotNode<T> {
fn default() -> Self {
Self {
lhs: Box::new(LogicNode::Parameter(Default::default())),
@@ -175,7 +173,7 @@ impl Default for NotNode {
}
}
impl Visit for NotNode {
impl<T: EntityId> Visit for NotNode<T> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut guard = visitor.enter_region(name)?;
@@ -185,7 +183,7 @@ impl Visit for NotNode {
}
}
impl Reflect for NotNode {
impl<T: EntityId> Reflect for NotNode<T> {
fn type_name(&self) -> &'static str {
type_name::<Self>()
}
@@ -266,18 +264,19 @@ impl Reflect for NotNode {
/// # Examples
///
/// ```rust
/// use fyrox::animation::AnimationContainer;
/// use fyrox::animation::machine::{
/// use fyrox_animation::AnimationContainer;
/// use fyrox_animation::machine::{
/// transition::{AndNode, LogicNode, NotNode},
/// Parameter, ParameterContainer,
/// };
/// use fyrox_core::pool::ErasedHandle;
///
/// let mut parameters = ParameterContainer::default();
/// parameters.add("Run", Parameter::Rule(false));
/// parameters.add("Jump", Parameter::Rule(true));
///
/// // !Run && Jump
/// let transition_logic = LogicNode::And(AndNode {
/// let transition_logic = LogicNode::<ErasedHandle>::And(AndNode {
/// lhs: Box::new(LogicNode::Not(NotNode {
/// lhs: Box::new(LogicNode::Parameter("Run".to_string())),
/// })),
@@ -287,35 +286,39 @@ impl Reflect for NotNode {
/// assert_eq!(transition_logic.calculate_value(&parameters, &AnimationContainer::default()), true);
/// ```
#[derive(Debug, Visit, Clone, Reflect, PartialEq, AsRefStr, EnumString, EnumVariantNames)]
pub enum LogicNode {
pub enum LogicNode<T: EntityId> {
/// Fetches a value of `Rule` parameter and returns its value. `false` if the parameter is not found.
Parameter(String),
/// Calculates logical AND between two arguments. Output value will be `true` iff both of the arguments is `true`.
And(AndNode),
And(AndNode<T>),
/// Calculates logical OR between two arguments. Output value will be `true` iff any of the arguments is `true`.
Or(OrNode),
Or(OrNode<T>),
/// Calculates logical XOR (excluding OR) between two arguments. Output value will be `true` iff the arguments differ.
Xor(XorNode),
Xor(XorNode<T>),
/// Calculates logical NOT of an argument. Output value will be `true` if the value of the argument is `false`.
Not(NotNode),
Not(NotNode<T>),
/// Returns `true` if the animation has ended, `false` - otherwise.
IsAnimationEnded(Handle<Animation>),
IsAnimationEnded(Handle<Animation<T>>),
}
uuid_provider!(LogicNode = "98a5b767-5560-4ed7-ad40-1625a8868e39");
impl<T: EntityId> TypeUuidProvider for LogicNode<T> {
fn type_uuid() -> Uuid {
uuid!("98a5b767-5560-4ed7-ad40-1625a8868e39")
}
}
impl Default for LogicNode {
impl<T: EntityId> Default for LogicNode<T> {
fn default() -> Self {
Self::Parameter(Default::default())
}
}
impl LogicNode {
impl<T: EntityId> LogicNode<T> {
/// Calculates final value of the logic node.
pub fn calculate_value(
&self,
parameters: &ParameterContainer,
animations: &AnimationContainer,
animations: &AnimationContainer<T>,
) -> bool {
match self {
LogicNode::Parameter(rule_name) => parameters.get(rule_name).map_or(false, |p| {
@@ -350,7 +353,7 @@ impl LogicNode {
/// Transition is a connection between two states with a rule that defines possibility of actual transition with blending.
#[derive(Default, Debug, Clone, Reflect, PartialEq)]
pub struct Transition {
pub struct Transition<T: EntityId> {
/// The name of the transition, it is used for debug output.
#[reflect(description = "The name of the transition, it is used for debug output.")]
pub(crate) name: String,
@@ -363,21 +366,21 @@ pub struct Transition {
pub(crate) elapsed_time: f32,
#[reflect(read_only)]
pub(crate) source: Handle<State>,
pub(crate) source: Handle<State<T>>,
#[reflect(read_only)]
pub(crate) dest: Handle<State>,
pub(crate) dest: Handle<State<T>>,
#[reflect(
description = "Computational graph that can use any amount of Rule parameters to calculate transition value."
)]
pub(crate) condition: LogicNode,
pub(crate) condition: LogicNode<T>,
/// 0 - evaluates `src` pose, 1 - `dest`, 0..1 - blends `src` and `dest`
pub(crate) blend_factor: f32,
}
impl Visit for Transition {
impl<T: EntityId> Visit for Transition<T> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut guard = visitor.enter_region(name)?;
@@ -412,22 +415,22 @@ impl Visit for Transition {
}
}
impl NameProvider for Transition {
impl<T: EntityId> NameProvider for Transition<T> {
fn name(&self) -> &str {
&self.name
}
}
impl Transition {
impl<T: EntityId> Transition<T> {
/// Creates a new named transition between two states with a given time and a name of a parameter that
/// will be used to check if it is possible to activate the transition.
pub fn new(
name: &str,
src: Handle<State>,
dest: Handle<State>,
src: Handle<State<T>>,
dest: Handle<State<T>>,
time: f32,
rule: &str,
) -> Transition {
) -> Transition<T> {
Self {
name: name.to_owned(),
transition_time: time,
@@ -453,23 +456,23 @@ impl Transition {
/// Returns a handle to source state.
#[inline]
pub fn source(&self) -> Handle<State> {
pub fn source(&self) -> Handle<State<T>> {
self.source
}
/// Returns a handle to destination state.
#[inline]
pub fn dest(&self) -> Handle<State> {
pub fn dest(&self) -> Handle<State<T>> {
self.dest
}
/// Sets new condition for the transition.
pub fn set_condition(&mut self, condition: LogicNode) {
pub fn set_condition(&mut self, condition: LogicNode<T>) {
self.condition = condition;
}
/// Returns a reference to the current condition of the transition.
pub fn condition(&self) -> &LogicNode {
pub fn condition(&self) -> &LogicNode<T> {
&self.condition
}
@@ -1,54 +1,38 @@
//! Pose is a set of property values of a node ([`NodePose`]) or a set of nodes ([`AnimationPose`]).
use crate::{
animation::{value::BoundValue, value::BoundValueCollection, RootMotion},
core::{
log::{Log, MessageKind},
pool::Handle,
},
scene::{graph::Graph, graph::NodePool, node::Node},
};
use crate::{value::BoundValue, value::BoundValueCollection, EntityId, RootMotion};
use fxhash::FxHashMap;
use std::collections::hash_map::Entry;
/// A "captured" state of properties of some animated scene node. The pose can be considered as container of values of some
/// properties.
#[derive(Clone, Debug, PartialEq)]
pub struct NodePose {
#[derive(Clone, Debug, PartialEq, Default)]
pub struct NodePose<T: EntityId> {
/// A handle of an animated node.
pub node: Handle<Node>,
pub node: T,
/// A set of property values.
pub values: BoundValueCollection,
}
impl Default for NodePose {
fn default() -> Self {
Self {
node: Handle::NONE,
values: Default::default(),
}
}
}
impl NodePose {
impl<T: EntityId> NodePose<T> {
/// Performs a blending of the current with some other pose. See [`super::value::TrackValue::blend_with`] docs for more
/// info.
pub fn blend_with(&mut self, other: &NodePose, weight: f32) {
pub fn blend_with(&mut self, other: &NodePose<T>, weight: f32) {
self.values.blend_with(&other.values, weight)
}
}
/// Animations pose is a set of node poses. See [`NodePose`] docs for more info.
#[derive(Default, Debug, Clone, PartialEq)]
pub struct AnimationPose {
poses: FxHashMap<Handle<Node>, NodePose>,
pub struct AnimationPose<T: EntityId> {
poses: FxHashMap<T, NodePose<T>>,
root_motion: Option<RootMotion>,
}
impl AnimationPose {
impl<T: EntityId> AnimationPose<T> {
/// Clears the set of node poses in the given animation pose and clones poses from the current animation pose to the given.
pub fn clone_into(&self, dest: &mut AnimationPose) {
pub fn clone_into(&self, dest: &mut AnimationPose<T>) {
dest.reset();
for (handle, local_pose) in self.poses.iter() {
dest.poses.insert(*handle, local_pose.clone());
@@ -69,7 +53,7 @@ impl AnimationPose {
/// Blends current animation pose with another using a weight coefficient. Missing node poses (from either animation poses)
/// will become a simple copies of a respective node pose.
pub fn blend_with(&mut self, other: &AnimationPose, weight: f32) {
pub fn blend_with(&mut self, other: &AnimationPose<T>, weight: f32) {
for (handle, other_pose) in other.poses.iter() {
if let Some(current_pose) = self.poses.get_mut(handle) {
current_pose.blend_with(other_pose, weight);
@@ -83,11 +67,11 @@ impl AnimationPose {
.blend_with(&other.root_motion.clone().unwrap_or_default(), weight);
}
fn add_node_pose(&mut self, local_pose: NodePose) {
fn add_node_pose(&mut self, local_pose: NodePose<T>) {
self.poses.insert(local_pose.node, local_pose);
}
pub(super) fn add_to_node_pose(&mut self, node: Handle<Node>, bound_value: BoundValue) {
pub(super) fn add_to_node_pose(&mut self, node: T, bound_value: BoundValue) {
match self.poses.entry(node) {
Entry::Occupied(entry) => {
entry.into_mut().values.values.push(bound_value);
@@ -109,48 +93,12 @@ impl AnimationPose {
}
/// Returns a reference to inner node pose map.
pub fn poses(&self) -> &FxHashMap<Handle<Node>, NodePose> {
pub fn poses(&self) -> &FxHashMap<T, NodePose<T>> {
&self.poses
}
/// Returns a reference to inner node pose map.
pub fn poses_mut(&mut self) -> &mut FxHashMap<Handle<Node>, NodePose> {
pub fn poses_mut(&mut self) -> &mut FxHashMap<T, NodePose<T>> {
&mut self.poses
}
pub(crate) fn apply_internal(&self, nodes: &mut NodePool) {
for (node, local_pose) in self.poses.iter() {
if node.is_none() {
Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
} else if let Some(node) = nodes.try_borrow_mut(*node) {
local_pose.values.apply(node);
}
}
}
/// Tries to set each value to the each property from the animation pose to respective scene nodes.
pub fn apply(&self, graph: &mut Graph) {
for (node, local_pose) in self.poses.iter() {
if node.is_none() {
Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
} else if let Some(node) = graph.try_get_mut(*node) {
local_pose.values.apply(node);
}
}
}
/// Calls given callback function for each node and allows you to apply pose with your own
/// rules. This could be useful if you need to ignore transform some part of pose for a node.
pub fn apply_with<C>(&self, graph: &mut Graph, mut callback: C)
where
C: FnMut(&mut Node, Handle<Node>, &NodePose),
{
for (node, local_pose) in self.poses.iter() {
if node.is_none() {
Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
} else if let Some(node_ref) = graph.try_get_mut(*node) {
callback(node_ref, *node, local_pose);
}
}
}
}
@@ -1,9 +1,7 @@
//! Signal is a named marker on specific time position on the animation timeline. See [`AnimationSignal`] docs for more info.
use crate::{
core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*},
utils::NameProvider,
};
use crate::core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*};
use fyrox_core::NameProvider;
/// An event happened in an animation.
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -4,11 +4,11 @@
#![warn(missing_docs)]
use crate::{
animation::spritesheet::signal::Signal,
core::{algebra::Vector2, math::Rect, reflect::prelude::*, visitor::prelude::*},
resource::texture::TextureResource,
spritesheet::signal::Signal,
};
use fyrox_core::uuid_provider;
use fyrox_resource::untyped::UntypedResource;
use std::collections::vec_deque::VecDeque;
use strum_macros::{AsRefStr, EnumString, EnumVariantNames};
@@ -58,7 +58,7 @@ pub struct SpriteSheetFramesContainer {
size: Vector2<u32>,
frames: Vec<Vector2<u32>>,
#[visit(optional)]
texture: Option<TextureResource>,
texture: Option<UntypedResource>,
}
impl SpriteSheetFramesContainer {
@@ -115,7 +115,7 @@ impl SpriteSheetFramesContainer {
}
/// Returns current texture of the container. To set a texture use sprite sheet animation methods.
pub fn texture(&self) -> Option<TextureResource> {
pub fn texture(&self) -> Option<UntypedResource> {
self.texture.clone()
}
}
@@ -143,7 +143,7 @@ pub struct SpriteSheetAnimation {
signals: Vec<Signal>,
#[visit(optional)]
#[reflect(setter = "set_texture")]
texture: Option<TextureResource>,
texture: Option<UntypedResource>,
#[reflect(hidden)]
#[visit(skip)]
events: VecDeque<Event>,
@@ -221,8 +221,8 @@ impl SpriteSheetAnimation {
/// like this:
///
/// ```rust
/// # use fyrox::{
/// # animation::spritesheet::{ImageParameters, SpriteSheetAnimation},
/// # use fyrox_animation::{
/// # spritesheet::{ImageParameters, SpriteSheetAnimation},
/// # core::math::Rect,
/// # };
/// fn extract_animations() {
@@ -310,13 +310,13 @@ impl SpriteSheetAnimation {
}
/// Sets new texture for the animation.
pub fn set_texture(&mut self, texture: Option<TextureResource>) -> Option<TextureResource> {
pub fn set_texture(&mut self, texture: Option<UntypedResource>) -> Option<UntypedResource> {
self.frames_container.texture = texture.clone();
std::mem::replace(&mut self.texture, texture)
}
/// Returns current texture of the animation.
pub fn texture(&self) -> Option<TextureResource> {
pub fn texture(&self) -> Option<UntypedResource> {
self.texture.clone()
}
@@ -510,7 +510,7 @@ impl SpriteSheetAnimation {
#[cfg(test)]
mod test {
use crate::animation::spritesheet::{
use crate::spritesheet::{
signal::Signal, Event, ImageParameters, SpriteSheetAnimation, Status,
};
use fyrox_core::algebra::Vector2;
@@ -1,12 +1,10 @@
//! Track is responsible in animating a property of a single scene node. See [`Track`] docs for more info.
use crate::{
animation::{
container::{TrackDataContainer, TrackValueKind},
value::{BoundValue, ValueBinding},
},
core::{pool::Handle, reflect::prelude::*, uuid::Uuid, visitor::prelude::*},
scene::node::Node,
container::{TrackDataContainer, TrackValueKind},
core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*},
value::{BoundValue, ValueBinding},
EntityId,
};
use std::fmt::Debug;
@@ -14,15 +12,15 @@ use std::fmt::Debug;
/// that contains the actual property data. Parametric curves allows the engine to perform various interpolations between
/// key values.
#[derive(Debug, Reflect, Clone, PartialEq)]
pub struct Track {
pub struct Track<T: EntityId> {
binding: ValueBinding,
frames: TrackDataContainer,
enabled: bool,
target: Handle<Node>,
target: T,
id: Uuid,
}
impl Visit for Track {
impl<T: EntityId> Visit for Track<T> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut region = visitor.enter_region(name)?;
@@ -37,7 +35,7 @@ impl Visit for Track {
}
}
impl Default for Track {
impl<T: EntityId> Default for Track<T> {
fn default() -> Self {
Self {
binding: ValueBinding::Position,
@@ -49,7 +47,7 @@ impl Default for Track {
}
}
impl Track {
impl<T: EntityId> Track<T> {
/// Creates a new track that will animate a property in the given binding. The `container` must have enough parametric
/// curves to be able to produces property values.
pub fn new(container: TrackDataContainer, binding: ValueBinding) -> Self {
@@ -88,7 +86,7 @@ impl Track {
}
/// Sets target of the track.
pub fn with_target(mut self, target: Handle<Node>) -> Self {
pub fn with_target(mut self, target: T) -> Self {
self.target = target;
self
}
@@ -104,12 +102,12 @@ impl Track {
}
/// Sets a handle of a node that will be animated.
pub fn set_target(&mut self, target: Handle<Node>) {
pub fn set_target(&mut self, target: T) {
self.target = target;
}
/// Returns a handle of a node that will be animated.
pub fn target(&self) -> Handle<Node> {
pub fn target(&self) -> T {
self.target
}
@@ -1,16 +1,12 @@
//! A module that contains everything related to numeric values of animation tracks. See [`TrackValue`] docs
//! for more info.
use crate::{
core::{
algebra::{UnitQuaternion, Vector2, Vector3, Vector4},
log::Log,
math::lerpf,
num_traits::AsPrimitive,
reflect::{prelude::*, SetFieldByPathError},
visitor::prelude::*,
},
scene::node::Node,
use crate::core::{
algebra::{UnitQuaternion, Vector2, Vector3, Vector4},
math::lerpf,
num_traits::AsPrimitive,
reflect::prelude::*,
visitor::prelude::*,
};
use std::fmt::{Debug, Display, Formatter};
@@ -327,67 +323,4 @@ impl BoundValueCollection {
}
}
}
/// Tries to set each value from the collection to the respective property (by binding) of the given scene node.
pub fn apply(&self, node_ref: &mut Node) {
for bound_value in self.values.iter() {
match bound_value.binding {
ValueBinding::Position => {
if let TrackValue::Vector3(v) = bound_value.value {
node_ref.local_transform_mut().set_position(v);
} else {
Log::err(
"Unable to apply position, because underlying type is not Vector3!",
)
}
}
ValueBinding::Scale => {
if let TrackValue::Vector3(v) = bound_value.value {
node_ref.local_transform_mut().set_scale(v);
} else {
Log::err("Unable to apply scaling, because underlying type is not Vector3!")
}
}
ValueBinding::Rotation => {
if let TrackValue::UnitQuaternion(v) = bound_value.value {
node_ref.local_transform_mut().set_rotation(v);
} else {
Log::err("Unable to apply rotation, because underlying type is not UnitQuaternion!")
}
}
ValueBinding::Property {
name: ref property_name,
value_type,
} => {
if let Some(casted) = bound_value.value.numeric_type_cast(value_type) {
let mut casted = Some(casted);
node_ref.as_reflect_mut(&mut |node_ref| {
node_ref.set_field_by_path(
property_name,
casted.take().unwrap(),
&mut |result| {
if let Err(err) = result {
match err {
SetFieldByPathError::InvalidPath { reason, .. } => {
Log::err(format!(
"Failed to set property {}! Invalid path: {}",
property_name, reason
));
}
SetFieldByPathError::InvalidValue(_) => {
Log::err(format!(
"Failed to set property {}! Types mismatch!",
property_name
));
}
}
}
},
)
})
}
}
}
}
}
}
+28 -2
View File
@@ -306,8 +306,8 @@ pub trait TypeUuidProvider: Sized {
#[macro_export]
macro_rules! uuid_provider {
($type:ty = $uuid:expr) => {
impl $crate::TypeUuidProvider for $type {
($type:ident $(<$($generics:tt),*>)? = $uuid:expr) => {
impl$(<$($generics),*>)? $crate::TypeUuidProvider for $type $(<$($generics),*>)? {
fn type_uuid() -> $crate::uuid::Uuid {
$crate::uuid::uuid!($uuid)
}
@@ -455,6 +455,32 @@ impl<T: ?Sized> Default for PhantomDataSendSync<T> {
}
}
/// A trait for entities that have name.
pub trait NameProvider {
/// Returns a reference to the name of the entity.
fn name(&self) -> &str;
}
/// Tries to find an entity by its name in a series of entities produced by an iterator.
pub fn find_by_name_ref<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a T)>
where
T: NameProvider,
I: Iterator<Item = (K, &'a T)>,
S: AsRef<str>,
{
iter.find(|(_, value)| value.name() == name.as_ref())
}
/// Tries to find an entity by its name in a series of entities produced by an iterator.
pub fn find_by_name_mut<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a mut T)>
where
T: NameProvider,
I: Iterator<Item = (K, &'a mut T)>,
S: AsRef<str>,
{
iter.find(|(_, value)| value.name() == name.as_ref())
}
#[cfg(test)]
mod test {
use std::path::Path;
+13
View File
@@ -33,6 +33,7 @@ use crate::{
use arrayvec::ArrayVec;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::cmp::Ordering;
use std::{
fmt::{Debug, Display, Formatter},
future::Future,
@@ -215,6 +216,18 @@ impl<T: TypeUuidProvider> TypeUuidProvider for Handle<T> {
}
}
impl<T> PartialOrd for Handle<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for Handle<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.index.cmp(&other.index)
}
}
unsafe impl<T> Send for Handle<T> {}
unsafe impl<T> Sync for Handle<T> {}
+1 -1
View File
@@ -9,7 +9,7 @@
#![allow(clippy::from_over_into)]
#![allow(clippy::approx_constant)]
pub mod animation;
pub use fyrox_animation as animation;
pub mod engine;
pub mod material;
pub mod plugin;
+3 -3
View File
@@ -638,7 +638,7 @@ async fn convert_model(
model: &FbxModel,
resource_manager: ResourceManager,
graph: &mut Graph,
animation: &mut Animation,
animation: &mut Animation<Handle<Node>>,
model_path: &Path,
model_import_options: &ModelImportOptions,
) -> Result<Handle<Node>, FbxError> {
@@ -682,7 +682,7 @@ async fn convert_model(
}
fn fill_track<F: Fn(f32) -> f32>(
track: &mut Track,
track: &mut Track<Handle<Node>>,
fbx_scene: &FbxScene,
fbx_track: &FbxAnimationCurveNode,
default: Vector3<f32>,
@@ -730,7 +730,7 @@ async fn convert_model(
}
}
fn add_vec3_key(track: &mut Track, value: Vector3<f32>) {
fn add_vec3_key(track: &mut Track<Handle<Node>>, value: Vector3<f32>) {
let curves = track.data_container_mut().curves_mut();
curves[0].add_key(CurveKey::new(0.0, value.x, CurveKeyKind::Constant));
curves[1].add_key(CurveKey::new(0.0, value.y, CurveKeyKind::Constant));
+23 -7
View File
@@ -123,7 +123,11 @@ pub trait ModelResourceExtension: Sized {
///
/// Most of the 3d model formats can contain only one animation, so in most cases
/// this function will return vector with only one animation.
fn retarget_animations_directly(&self, root: Handle<Node>, graph: &Graph) -> Vec<Animation>;
fn retarget_animations_directly(
&self,
root: Handle<Node>,
graph: &Graph,
) -> Vec<Animation<Handle<Node>>>;
/// Tries to retarget animations from given model resource to a node hierarchy starting
/// from `root` on a given scene. Unlike [`Self::retarget_animations_directly`], it automatically
@@ -138,7 +142,7 @@ pub trait ModelResourceExtension: Sized {
root: Handle<Node>,
dest_animation_player: Handle<Node>,
graph: &mut Graph,
) -> Vec<Handle<Animation>>;
) -> Vec<Handle<Animation<Handle<Node>>>>;
/// Tries to retarget animations from given model resource to a node hierarchy starting
/// from `root` on a given scene. Unlike [`Self::retarget_animations_directly`], it automatically
@@ -147,7 +151,11 @@ pub trait ModelResourceExtension: Sized {
/// # Panic
///
/// Panics if there's no animation player in the given hierarchy (descendant nodes of `root`).
fn retarget_animations(&self, root: Handle<Node>, graph: &mut Graph) -> Vec<Handle<Animation>>;
fn retarget_animations(
&self,
root: Handle<Node>,
graph: &mut Graph,
) -> Vec<Handle<Animation<Handle<Node>>>>;
}
impl ModelResourceExtension for ModelResource {
@@ -208,7 +216,11 @@ impl ModelResourceExtension for ModelResource {
root
}
fn retarget_animations_directly(&self, root: Handle<Node>, graph: &Graph) -> Vec<Animation> {
fn retarget_animations_directly(
&self,
root: Handle<Node>,
graph: &Graph,
) -> Vec<Animation<Handle<Node>>> {
let mut retargetted_animations = Vec::new();
let mut header = self.state();
@@ -232,7 +244,7 @@ impl ModelResourceExtension for ModelResource {
track.set_target(instance_node);
}
None => {
track.set_target(Handle::NONE);
track.set_target(Default::default());
Log::writeln(
MessageKind::Error,
format!(
@@ -259,7 +271,7 @@ impl ModelResourceExtension for ModelResource {
root: Handle<Node>,
dest_animation_player: Handle<Node>,
graph: &mut Graph,
) -> Vec<Handle<Animation>> {
) -> Vec<Handle<Animation<Handle<Node>>>> {
let mut animation_handles = Vec::new();
let animations = self.retarget_animations_directly(root, graph);
@@ -275,7 +287,11 @@ impl ModelResourceExtension for ModelResource {
animation_handles
}
fn retarget_animations(&self, root: Handle<Node>, graph: &mut Graph) -> Vec<Handle<Animation>> {
fn retarget_animations(
&self,
root: Handle<Node>,
graph: &mut Graph,
) -> Vec<Handle<Animation<Handle<Node>>>> {
if let Some((animation_player, _)) = graph.find(root, &mut |n| {
n.query_component_ref::<AnimationPlayer>().is_some()
}) {
+7 -7
View File
@@ -13,7 +13,7 @@ use crate::{
TypeUuidProvider,
},
scene::{
animation::AnimationPlayer,
animation::{AnimationPlayer, AnimationPoseExt},
base::{Base, BaseBuilder},
graph::Graph,
node::{Node, NodeTrait, UpdateContext},
@@ -106,23 +106,23 @@ use std::ops::{Deref, DerefMut};
#[derive(Visit, Reflect, Clone, Debug, Default)]
pub struct AnimationBlendingStateMachine {
base: Base,
machine: InheritableVariable<Machine>,
machine: InheritableVariable<Machine<Handle<Node>>>,
animation_player: InheritableVariable<Handle<Node>>,
}
impl AnimationBlendingStateMachine {
/// Sets new state machine to the node.
pub fn set_machine(&mut self, machine: Machine) {
pub fn set_machine(&mut self, machine: Machine<Handle<Node>>) {
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> {
pub fn machine(&self) -> &InheritableVariable<Machine<Handle<Node>>> {
&self.machine
}
/// Returns a mutable reference to the state machine used by the node.
pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine<Handle<Node>>> {
&mut self.machine
}
@@ -214,7 +214,7 @@ impl NodeTrait for AnimationBlendingStateMachine {
/// Animation blending state machine builder allows you to create state machines in declarative manner.
pub struct AnimationBlendingStateMachineBuilder {
base_builder: BaseBuilder,
machine: Machine,
machine: Machine<Handle<Node>>,
animation_player: Handle<Node>,
}
@@ -229,7 +229,7 @@ impl AnimationBlendingStateMachineBuilder {
}
/// Sets the desired state machine.
pub fn with_machine(mut self, machine: Machine) -> Self {
pub fn with_machine(mut self, machine: Machine<Handle<Node>>) -> Self {
self.machine = machine;
self
}
+167 -9
View File
@@ -2,8 +2,12 @@
//! See [`AnimationPlayer`] docs for more info.
use crate::{
animation::AnimationContainer,
animation::{
value::{BoundValueCollection, TrackValue, ValueBinding},
AnimationContainer, AnimationPose, NodePose,
},
core::{
log::{Log, MessageKind},
math::aabb::AxisAlignedBoundingBox,
pool::Handle,
reflect::prelude::*,
@@ -14,14 +18,168 @@ use crate::{
},
scene::{
base::{Base, BaseBuilder},
graph::Graph,
graph::{Graph, NodePool},
node::{Node, NodeTrait, UpdateContext},
},
};
use fyrox_animation::machine::LayerMask;
use fyrox_core::pool::ErasedHandle;
use std::ops::{Deref, DerefMut};
pub mod absm;
/// Extension trait for [`AnimationContainer`].
pub trait AnimationContainerExt {
/// Updates all animations in the container and applies their poses to respective nodes. This method is intended to
/// be used only by the internals of the engine!
fn update_animations(&mut self, nodes: &mut NodePool, apply: bool, dt: f32);
}
impl AnimationContainerExt for AnimationContainer<Handle<Node>> {
fn update_animations(&mut self, nodes: &mut NodePool, apply: bool, dt: f32) {
for animation in self.iter_mut().filter(|anim| anim.is_enabled()) {
animation.tick(dt);
if apply {
animation.pose().apply_internal(nodes);
}
}
}
}
/// Extension trait for [`AnimationPose`].
pub trait AnimationPoseExt {
/// Tries to set each value to the each property from the animation pose to respective scene nodes.
fn apply_internal(&self, nodes: &mut NodePool);
/// Tries to set each value to the each property from the animation pose to respective scene nodes.
fn apply(&self, graph: &mut Graph);
/// Calls given callback function for each node and allows you to apply pose with your own
/// rules. This could be useful if you need to ignore transform some part of pose for a node.
fn apply_with<C>(&self, graph: &mut Graph, callback: C)
where
C: FnMut(&mut Node, Handle<Node>, &NodePose<Handle<Node>>);
}
impl AnimationPoseExt for AnimationPose<Handle<Node>> {
fn apply_internal(&self, nodes: &mut NodePool) {
for (node, local_pose) in self.poses() {
if node.is_none() {
Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
} else if let Some(node) = nodes.try_borrow_mut(*node) {
local_pose.values.apply(node);
}
}
}
fn apply(&self, graph: &mut Graph) {
for (node, local_pose) in self.poses() {
if node.is_none() {
Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
} else if let Some(node) = graph.try_get_mut(*node) {
local_pose.values.apply(node);
}
}
}
fn apply_with<C>(&self, graph: &mut Graph, mut callback: C)
where
C: FnMut(&mut Node, Handle<Node>, &NodePose<Handle<Node>>),
{
for (node, local_pose) in self.poses() {
if node.is_none() {
Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
} else if let Some(node_ref) = graph.try_get_mut(*node) {
callback(node_ref, *node, local_pose);
}
}
}
}
/// Extension trait for [`BoundValueCollection`].
pub trait BoundValueCollectionExt {
/// Tries to set each value from the collection to the respective property (by binding) of the given scene node.
fn apply(&self, node_ref: &mut Node);
}
impl BoundValueCollectionExt for BoundValueCollection {
fn apply(&self, node_ref: &mut Node) {
for bound_value in self.values.iter() {
match bound_value.binding {
ValueBinding::Position => {
if let TrackValue::Vector3(v) = bound_value.value {
node_ref.local_transform_mut().set_position(v);
} else {
Log::err(
"Unable to apply position, because underlying type is not Vector3!",
)
}
}
ValueBinding::Scale => {
if let TrackValue::Vector3(v) = bound_value.value {
node_ref.local_transform_mut().set_scale(v);
} else {
Log::err("Unable to apply scaling, because underlying type is not Vector3!")
}
}
ValueBinding::Rotation => {
if let TrackValue::UnitQuaternion(v) = bound_value.value {
node_ref.local_transform_mut().set_rotation(v);
} else {
Log::err("Unable to apply rotation, because underlying type is not UnitQuaternion!")
}
}
ValueBinding::Property {
name: ref property_name,
value_type,
} => {
if let Some(casted) = bound_value.value.numeric_type_cast(value_type) {
let mut casted = Some(casted);
node_ref.as_reflect_mut(&mut |node_ref| {
node_ref.set_field_by_path(
property_name,
casted.take().unwrap(),
&mut |result| {
if let Err(err) = result {
match err {
SetFieldByPathError::InvalidPath { reason, .. } => {
Log::err(format!(
"Failed to set property {}! Invalid path: {}",
property_name, reason
));
}
SetFieldByPathError::InvalidValue(_) => {
Log::err(format!(
"Failed to set property {}! Types mismatch!",
property_name
));
}
}
}
},
)
})
}
}
}
}
}
}
/// 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: ErasedHandle) -> Self;
}
impl LayerMaskExt for LayerMask<Handle<Node>> {
fn from_hierarchy(graph: &Graph, root: ErasedHandle) -> Self {
Self::from(graph.traverse_handle_iter(root.into()).collect::<Vec<_>>())
}
}
/// Animation player is a node that contains multiple animations. It updates and plays all the animations.
/// The node could be a source of animations for animation blending state machines. To learn more about
/// animations, see [`crate::animation::Animation`] docs.
@@ -47,7 +205,7 @@ pub mod absm;
/// scene::{animation::AnimationPlayerBuilder, base::BaseBuilder, graph::Graph, node::Node},
/// };
///
/// fn create_bounce_animation(animated_node: Handle<Node>) -> Animation {
/// fn create_bounce_animation(animated_node: Handle<Node>) -> Animation<Handle<Node>> {
/// let mut frames_container = TrackDataContainer::new(TrackValueKind::Vector3);
///
/// // We'll animate only Y coordinate (at index 1).
@@ -94,7 +252,7 @@ pub mod absm;
#[derive(Visit, Reflect, Clone, Debug)]
pub struct AnimationPlayer {
base: Base,
animations: InheritableVariable<AnimationContainer>,
animations: InheritableVariable<AnimationContainer<Handle<Node>>>,
auto_apply: bool,
}
@@ -124,18 +282,18 @@ impl AnimationPlayer {
}
/// Returns a reference to internal animations container.
pub fn animations(&self) -> &InheritableVariable<AnimationContainer> {
pub fn animations(&self) -> &InheritableVariable<AnimationContainer<Handle<Node>>> {
&self.animations
}
/// Returns a reference to internal animations container. Keep in mind that mutable access to [`InheritableVariable`]
/// may have side effects if used inappropriately. Checks docs for [`InheritableVariable`] for more info.
pub fn animations_mut(&mut self) -> &mut InheritableVariable<AnimationContainer> {
pub fn animations_mut(&mut self) -> &mut InheritableVariable<AnimationContainer<Handle<Node>>> {
&mut self.animations
}
/// Sets new animations container of the animation player.
pub fn set_animations(&mut self, animations: AnimationContainer) {
pub fn set_animations(&mut self, animations: AnimationContainer<Handle<Node>>) {
self.animations.set_value_and_mark_modified(animations);
}
}
@@ -187,7 +345,7 @@ impl NodeTrait for AnimationPlayer {
/// A builder for [`AnimationPlayer`] node.
pub struct AnimationPlayerBuilder {
base_builder: BaseBuilder,
animations: AnimationContainer,
animations: AnimationContainer<Handle<Node>>,
auto_apply: bool,
}
@@ -202,7 +360,7 @@ impl AnimationPlayerBuilder {
}
/// Sets a container with desired animations.
pub fn with_animations(mut self, animations: AnimationContainer) -> Self {
pub fn with_animations(mut self, animations: AnimationContainer<Handle<Node>>) -> Self {
self.animations = animations;
self
}
-26
View File
@@ -782,32 +782,6 @@ pub fn into_any_arc<T: Any + Send + Sync>(
}
}
/// A trait for entities that have name.
pub trait NameProvider {
/// Returns a reference to the name of the entity.
fn name(&self) -> &str;
}
/// Tries to find an entity by its name in a series of entities produced by an iterator.
pub fn find_by_name_ref<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a T)>
where
T: NameProvider,
I: Iterator<Item = (K, &'a T)>,
S: AsRef<str>,
{
iter.find(|(_, value)| value.name() == name.as_ref())
}
/// Tries to find an entity by its name in a series of entities produced by an iterator.
pub fn find_by_name_mut<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a mut T)>
where
T: NameProvider,
I: Iterator<Item = (K, &'a mut T)>,
S: AsRef<str>,
{
iter.find(|(_, value)| value.name() == name.as_ref())
}
/// Converts `Vector3<f32>` -> `Vector3<f16>`.
pub fn vec3_f16_from_f32(v: Vector3<f32>) -> Vector3<f16> {
v.map(f16::from_f32)
+2 -2
View File
@@ -965,7 +965,7 @@ mod test {
#[test]
fn test_navmesh() {
let mut navmesh = Navmesh::new(
let navmesh = Navmesh::new(
vec![
TriangleDefinition([0, 1, 3]),
TriangleDefinition([1, 2, 3]),
@@ -989,7 +989,7 @@ mod test {
let mut agent = NavmeshAgent::new();
agent.set_target(Vector3::new(3.0, 0.0, 1.0));
agent.update(1.0 / 60.0, &mut navmesh).unwrap();
agent.update(1.0 / 60.0, &navmesh).unwrap();
let graph = &navmesh.graph;