moved low level animation modules into fyrox-animation crate

This commit is contained in:
Dmitry Stepanov
2023-12-26 13:11:10 +03:00
parent df812064ff
commit 186c74960e
35 changed files with 360 additions and 403 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"
+1 -1
View File
@@ -484,7 +484,7 @@ impl AbsmEditor {
let mut animation_targets = FxHashSet::default();
for animation in animation_player.animations_mut().iter_mut() {
for track in animation.tracks() {
animation_targets.insert(track.target());
animation_targets.insert(Handle::<Node>::from(track.target()));
}
}
+7 -18
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},
@@ -14,8 +14,6 @@ use crate::{
},
send_sync_message,
};
use fyrox::core::pool::ErasedHandle;
use fyrox::scene::node::Node;
use fyrox::{
animation::machine::{LayerMask, MachineLayer},
core::pool::Handle,
@@ -252,14 +250,14 @@ impl Toolbar {
.cloned()
.filter(|n| {
graph
.try_get(*n)
.map_or(false, |n| !unique_nodes.contains(&n.parent()))
.try_get((*n).into())
.map_or(false, |n| !unique_nodes.contains(&(n.parent().into())))
})
.collect::<Vec<_>>();
for local_root in local_roots {
root.children.push(HierarchyNode::from_scene_node(
local_root,
local_root.into(),
game_scene.editor_objects_root,
graph,
));
@@ -283,12 +281,8 @@ impl Toolbar {
if let Some(layer_index) = selection.layer {
if let Some(layer) = absm_node.machine().layers().get(layer_index) {
let selection = layer
.mask()
.inner()
.iter()
.map(|h| ErasedHandle::from(*h))
.collect::<Vec<_>>();
let selection =
layer.mask().inner().iter().cloned().collect::<Vec<_>>();
ui.send_message(NodeSelectorMessage::selection(
self.node_selector,
@@ -332,12 +326,7 @@ impl Toolbar {
&& message.direction() == MessageDirection::FromWidget
{
if let Some(layer_index) = selection.layer {
let new_mask = LayerMask::from(
mask_selection
.iter()
.map(|h| Handle::<Node>::from(*h))
.collect::<Vec<_>>(),
);
let new_mask = LayerMask::from(mask_selection.clone());
sender.do_scene_command(SetLayerMaskCommand {
absm_node_handle: selection.absm_node_handle,
layer_index,
+1 -1
View File
@@ -603,7 +603,7 @@ impl SetTrackTargetCommand {
.find(|t| t.id() == self.track)
.unwrap();
let old = track.target();
let old = track.target().into();
track.set_target(self.target);
self.target = old;
}
+1 -1
View File
@@ -353,7 +353,7 @@ impl AnimationEditor {
let animation_targets = animation
.tracks()
.iter()
.map(|t| t.target())
.map(|t| Handle::<Node>::from(t.target()))
.collect::<FxHashSet<_>>();
self.enter_preview_mode(
+3 -3
View File
@@ -263,7 +263,7 @@ impl RootMotionDropdownArea {
self.node_selector,
MessageDirection::ToWidget,
if settings.node.is_some() {
vec![settings.node.into()]
vec![settings.node]
} else {
vec![]
},
@@ -285,7 +285,7 @@ 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().unwrap_or_default(),
..*settings
}),
});
@@ -346,7 +346,7 @@ impl RootMotionDropdownArea {
MessageDirection::ToWidget,
scene
.graph
.try_get(settings.node)
.try_get(settings.node.into())
.map(|n| n.name().to_owned())
.unwrap_or_else(|| String::from("<Unassigned>")),
),
+11 -10
View File
@@ -852,21 +852,21 @@ impl TrackList {
sender.do_scene_command(AddTrackCommand::new(
animation_player,
animation,
Track::new_position().with_target(self.selected_node),
Track::new_position().with_target(self.selected_node.into()),
));
}
PropertyBindingMode::Rotation => {
sender.do_scene_command(AddTrackCommand::new(
animation_player,
animation,
Track::new_rotation().with_target(self.selected_node),
Track::new_rotation().with_target(self.selected_node.into()),
));
}
PropertyBindingMode::Scale => {
sender.do_scene_command(AddTrackCommand::new(
animation_player,
animation,
Track::new_scale().with_target(self.selected_node),
Track::new_scale().with_target(self.selected_node.into()),
));
}
}
@@ -1185,7 +1185,7 @@ impl TrackList {
.find(|t| t.id() == first_selected_track)
{
self.context_menu.property_rebinding_selector =
Self::open_property_selector(graph, track.target(), ui);
Self::open_property_selector(graph, track.target().into(), ui);
}
}
@@ -1223,7 +1223,7 @@ impl TrackList {
return;
};
let Some(node) = graph.try_get(track.target()) else {
let Some(node) = graph.try_get(track.target().into()) else {
Log::err("Invalid node handle!");
return;
};
@@ -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().into())
{
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let ctx = &mut ui.build_ctx();
@@ -1361,7 +1362,7 @@ impl TrackList {
.with_text(format!(
"{} ({}:{})",
graph
.try_get(model_track.target())
.try_get(model_track.target().into())
.map(|n| n.name())
.unwrap_or_default(),
model_track.target().index(),
@@ -1420,7 +1421,7 @@ impl TrackList {
)
.with_track_enabled(model_track.is_enabled())
.with_id(model_track.id())
.with_target(model_track.target())
.with_target(model_track.target().into())
.with_name(format!("{}", model_track.binding()))
.build(ctx);
@@ -1495,8 +1496,8 @@ impl TrackList {
}
let mut validation_result = Ok(());
if let Some(target) = graph.try_get(track_model.target()) {
if let Some(parent_group) = self.group_views.get(&track_model.target()) {
if let Some(target) = graph.try_get(track_model.target().into()) {
if let Some(parent_group) = self.group_views.get(&track_model.target().into()) {
send_sync_message(
ui,
TextMessage::text(
+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
@@ -4,7 +4,6 @@
#![warn(missing_docs)]
use crate::{
animation::track::Track,
core::{
algebra::{UnitQuaternion, Vector3},
math::wrapf,
@@ -13,22 +12,21 @@ 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::{uuid_provider, NameProvider};
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;
pub use pose::{AnimationPose, NodePose};
pub use signal::{AnimationEvent, AnimationSignal};
use value::{TrackValue, ValueBinding};
pub mod container;
pub mod machine;
@@ -117,26 +115,19 @@ pub mod value;
/// 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 {
/// 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);
/// }
/// ```
///
@@ -215,7 +201,7 @@ uuid_provider!(Animation = "aade8e9d-e2cf-401d-a4d1-59c6943645f3");
pub struct RootMotionSettings {
/// 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: ErasedHandle,
/// 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.
@@ -683,39 +669,8 @@ impl Animation {
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: ErasedHandle, enabled: bool) {
for track in self.tracks.iter_mut() {
if track.target() == handle {
track.set_enabled(enabled);
@@ -724,14 +679,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: ErasedHandle) -> impl Iterator<Item = &Track> {
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: ErasedHandle) -> impl Iterator<Item = &mut Track> {
self.tracks
.iter_mut()
.filter(move |track| track.target() == handle)
@@ -743,7 +698,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 +707,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.
@@ -920,7 +875,7 @@ impl AnimationContainer {
&self,
name: S,
) -> Option<(Handle<Animation>, &Animation)> {
utils::find_by_name_ref(self.pool.pair_iter(), name)
core::find_by_name_ref(self.pool.pair_iter(), name)
}
/// Tries to find an animation by its name in the container.
@@ -929,7 +884,7 @@ impl AnimationContainer {
&mut self,
name: S,
) -> Option<(Handle<Animation>, &mut Animation)> {
utils::find_by_name_mut(self.pool.pair_iter_mut(), name)
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
@@ -942,17 +897,6 @@ impl AnimationContainer {
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
@@ -2,8 +2,8 @@
//! needed to works with such events.
use crate::{
animation::machine::{State, Transition},
core::pool::Handle,
machine::{State, Transition},
};
use std::collections::VecDeque;
@@ -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,
};
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,8 +25,8 @@ use crate::{
/// # Example
///
/// ```rust
/// use fyrox::{
/// animation::machine::{
/// use fyrox_animation::{
/// machine::{
/// State, Transition, PoseNode, MachineLayer,
/// Parameter, PlayAnimation, PoseWeight, BlendAnimations, BlendPose
/// },
@@ -241,7 +236,7 @@ impl MachineLayer {
/// # Example
///
/// ```rust
/// use fyrox::animation::machine::{Event, MachineLayer};
/// use fyrox_animation::machine::{Event, MachineLayer};
///
/// let mut layer = MachineLayer::new();
///
@@ -451,7 +446,7 @@ impl MachineLayer {
&self,
name: S,
) -> Option<(Handle<Transition>, &Transition)> {
utils::find_by_name_ref(self.transitions.pair_iter(), name)
find_by_name_ref(self.transitions.pair_iter(), name)
}
/// Tries to find a transition by its name.
@@ -460,7 +455,7 @@ impl MachineLayer {
&mut self,
name: S,
) -> Option<(Handle<Transition>, &mut Transition)> {
utils::find_by_name_mut(self.transitions.pair_iter_mut(), name)
find_by_name_mut(self.transitions.pair_iter_mut(), name)
}
/// Tries to borrow a state using its handle, panics if the handle is invalid.
@@ -481,7 +476,7 @@ impl MachineLayer {
&self,
name: S,
) -> Option<(Handle<State>, &State)> {
utils::find_by_name_ref(self.states.pair_iter(), name)
find_by_name_ref(self.states.pair_iter(), name)
}
/// Tries to find a state by its name.
@@ -490,7 +485,7 @@ impl MachineLayer {
&mut self,
name: S,
) -> Option<(Handle<State>, &mut State)> {
utils::find_by_name_mut(self.states.pair_iter_mut(), name)
find_by_name_mut(self.states.pair_iter_mut(), name)
}
/// Returns a reference to inner states container.
@@ -1,34 +1,24 @@
//! 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::{pool::ErasedHandle, reflect::prelude::*, visitor::prelude::*};
/// 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>>,
excluded_bones: Vec<ErasedHandle>,
}
impl From<Vec<Handle<Node>>> for LayerMask {
fn from(mut excluded_bones: Vec<Handle<Node>>) -> Self {
impl From<Vec<ErasedHandle>> for LayerMask {
fn from(mut excluded_bones: Vec<ErasedHandle>) -> Self {
excluded_bones.sort_by_key(|h| h.index());
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<_>>())
}
/// Merges a given layer mask in the current mask, handles will be automatically de-duplicated.
pub fn merge(&mut self, other: LayerMask) {
for handle in other.into_inner() {
@@ -46,7 +36,7 @@ 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>) {
pub fn add(&mut self, node: ErasedHandle) {
let index = self
.excluded_bones
.partition_point(|h| h.index() < node.index());
@@ -59,13 +49,13 @@ 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: ErasedHandle) {
if let Some(index) = self.index_of(node) {
self.excluded_bones.remove(index);
}
}
fn index_of(&self, node: Handle<Node>) -> Option<usize> {
fn index_of(&self, node: ErasedHandle) -> Option<usize> {
if let Ok(mut index) = self
.excluded_bones
.binary_search_by(|h| h.index().cmp(&node.index()))
@@ -98,7 +88,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: ErasedHandle) -> bool {
self.index_of(node).is_some()
}
@@ -108,20 +98,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: ErasedHandle) -> 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<ErasedHandle> {
&self.excluded_bones
}
/// Converts the mask into inner container.
#[inline]
pub fn into_inner(self) -> Vec<Handle<Node>> {
pub fn into_inner(self) -> Vec<ErasedHandle> {
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,
};
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,8 +122,8 @@ 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
/// },
@@ -193,7 +193,7 @@ 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};
///
/// let mut machine = Machine::new();
///
@@ -266,7 +266,7 @@ impl Machine {
/// 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)
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.
@@ -275,7 +275,7 @@ impl Machine {
&mut self,
name: S,
) -> Option<(usize, &mut MachineLayer)> {
utils::find_by_name_mut(self.layers.iter_mut().enumerate(), name)
find_by_name_mut(self.layers.iter_mut().enumerate(), name)
}
/// Returns final pose of the machine.
@@ -1,20 +1,16 @@
//! 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,
};
use fyrox_core::uuid_provider;
use std::cmp::Ordering;
@@ -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,6 +8,11 @@ use crate::{
reflect::prelude::*,
visitor::prelude::*,
},
machine::{
node::AnimationEventCollectionStrategy, node::BasePoseNode, AnimationPoseSource, Parameter,
ParameterContainer, PoseNode,
},
Animation, AnimationContainer, AnimationEvent, AnimationPose,
};
use fyrox_core::uuid_provider;
use spade::{DelaunayTriangulation, Point2, Triangulation};
@@ -429,8 +426,8 @@ impl BlendSpace {
#[cfg(test)]
mod test {
use crate::{
animation::machine::node::blendspace::{BlendSpace, BlendSpacePoint},
core::{algebra::Vector2, math::TriangleDefinition},
machine::node::blendspace::{BlendSpace, BlendSpacePoint},
};
#[test]
@@ -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,
};
use std::{
cell::Ref,
@@ -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,
};
use std::{
cell::{Ref, RefCell},
@@ -1,20 +1,17 @@
//! 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,
};
use fyrox_core::uuid_provider;
use fyrox_core::{uuid_provider, NameProvider};
use std::{
cell::Ref,
ops::{Deref, DerefMut},
@@ -1,14 +1,11 @@
//! 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,
};
use fyrox_core::uuid_provider;
use fyrox_core::{uuid_provider, NameProvider};
use std::any::{type_name, Any, TypeId};
use strum_macros::{AsRefStr, EnumString, EnumVariantNames};
@@ -266,8 +263,8 @@ 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,
/// };
@@ -1,36 +1,21 @@
//! 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, RootMotion};
use fxhash::FxHashMap;
use fyrox_core::pool::ErasedHandle;
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)]
#[derive(Clone, Debug, PartialEq, Default)]
pub struct NodePose {
/// A handle of an animated node.
pub node: Handle<Node>,
pub node: ErasedHandle,
/// A set of property values.
pub values: BoundValueCollection,
}
impl Default for NodePose {
fn default() -> Self {
Self {
node: Handle::NONE,
values: Default::default(),
}
}
}
impl NodePose {
/// Performs a blending of the current with some other pose. See [`super::value::TrackValue::blend_with`] docs for more
/// info.
@@ -42,7 +27,7 @@ impl NodePose {
/// 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>,
poses: FxHashMap<ErasedHandle, NodePose>,
root_motion: Option<RootMotion>,
}
@@ -87,7 +72,7 @@ impl AnimationPose {
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: ErasedHandle, bound_value: BoundValue) {
match self.poses.entry(node) {
Entry::Occupied(entry) => {
entry.into_mut().values.values.push(bound_value);
@@ -109,48 +94,12 @@ impl AnimationPose {
}
/// Returns a reference to inner node pose map.
pub fn poses(&self) -> &FxHashMap<Handle<Node>, NodePose> {
pub fn poses(&self) -> &FxHashMap<ErasedHandle, NodePose> {
&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<ErasedHandle, NodePose> {
&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,13 +1,11 @@
//! 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},
};
use fyrox_core::pool::ErasedHandle;
use std::fmt::Debug;
/// Track is responsible in animating a property of a single scene node. The track consists up to 4 parametric curves
@@ -18,7 +16,7 @@ pub struct Track {
binding: ValueBinding,
frames: TrackDataContainer,
enabled: bool,
target: Handle<Node>,
target: ErasedHandle,
id: Uuid,
}
@@ -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: ErasedHandle) -> 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>) {
self.target = target;
pub fn set_target<H: Into<ErasedHandle>>(&mut self, target: H) {
self.target = target.into();
}
/// Returns a handle of a node that will be animated.
pub fn target(&self) -> Handle<Node> {
pub fn target(&self) -> ErasedHandle {
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
));
}
}
}
},
)
})
}
}
}
}
}
}
+26
View File
@@ -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;
+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;
+4 -2
View File
@@ -42,6 +42,7 @@ use crate::{
Scene, SceneLoader,
},
};
use fyrox_core::pool::ErasedHandle;
use fyrox_core::uuid_provider;
use fyrox_resource::io::ResourceIo;
use serde::{Deserialize, Serialize};
@@ -223,7 +224,8 @@ impl ModelResourceExtension for ModelResource {
// because we've made a plain copy and it has tracks with node handles mapped
// to nodes of internal scene.
for (i, ref_track) in src_anim.tracks().iter().enumerate() {
let ref_node = &model.scene.graph[ref_track.target()];
let ref_node =
&model.scene.graph[Handle::<Node>::from(ref_track.target())];
let track = &mut anim_copy.tracks_mut()[i];
// Find instantiated node that corresponds to node in resource
match graph.find_by_name(root, ref_node.name()) {
@@ -232,7 +234,7 @@ impl ModelResourceExtension for ModelResource {
track.set_target(instance_node);
}
None => {
track.set_target(Handle::NONE);
track.set_target(ErasedHandle::default());
Log::writeln(
MessageKind::Error,
format!(
+1 -1
View File
@@ -13,7 +13,7 @@ use crate::{
TypeUuidProvider,
},
scene::{
animation::AnimationPlayer,
animation::{AnimationPlayer, AnimationPoseExt},
base::{Base, BaseBuilder},
graph::Graph,
node::{Node, NodeTrait, UpdateContext},
+165 -2
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,173 @@ 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 {
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);
}
impl AnimationPoseExt for AnimationPose {
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(Handle::from(*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(Handle::from(*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),
{
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(Handle::from(*node)) {
callback(node_ref, Handle::from(*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 {
fn from_hierarchy(graph: &Graph, root: ErasedHandle) -> Self {
Self::from(
graph
.traverse_handle_iter(root.into())
.map(ErasedHandle::from)
.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.
-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;