fixed animation import in animation editor + added AnimationSource trait

This commit is contained in:
Dmitry Stepanov
2024-02-12 12:05:44 +03:00
parent b212453b80
commit cb51d66083
6 changed files with 230 additions and 80 deletions
+7 -4
View File
@@ -14,6 +14,8 @@ use crate::{
scene::{commands::ChangeSelectionCommand, Selection},
send_sync_message, Message,
};
use fyrox::graph::PrefabData;
use fyrox::resource::model::AnimationSource;
use fyrox::{
asset::manager::ResourceManager,
core::{
@@ -237,7 +239,7 @@ impl AnimationEditor {
));
}
pub fn handle_ui_message<G, N>(
pub fn handle_ui_message<P, G, N>(
&mut self,
message: &UiMessage,
editor_selection: &Selection,
@@ -248,8 +250,9 @@ impl AnimationEditor {
sender: &MessageSender,
node_overrides: &mut FxHashSet<Handle<N>>,
) where
G: SceneGraph<Node = N>,
N: SceneGraphNode<SceneGraph = G>,
P: PrefabData<Graph = G> + AnimationSource<Node = N, SceneGraph = G, Prefab = P>,
G: SceneGraph<Node = N, Prefab = P>,
N: SceneGraphNode<SceneGraph = G, ResourceData = P>,
{
let selection = fetch_selection(editor_selection);
@@ -514,7 +517,7 @@ impl AnimationEditor {
// Revert state of nodes.
for (handle, node) in preview_data.downcast::<PreviewModeData<N>>().unwrap().nodes {
assert!(node_overrides.remove(&handle));
node_overrides.remove(&handle);
*graph.node_mut(handle) = node;
}
}
+15 -11
View File
@@ -26,7 +26,7 @@ use fyrox::{
pool::Handle,
},
generic_animation::{Animation, AnimationContainer, RootMotionSettings},
graph::{BaseSceneGraph, SceneGraph, SceneGraphNode},
graph::{BaseSceneGraph, PrefabData, SceneGraph, SceneGraphNode},
gui::{
border::BorderBuilder,
button::{Button, ButtonBuilder, ButtonMessage},
@@ -48,7 +48,7 @@ use fyrox::{
BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface,
VerticalAlignment, BRUSH_BRIGHT, BRUSH_LIGHT,
},
resource::model::{Model, ModelResourceExtension},
resource::model::AnimationSource,
};
use std::path::Path;
@@ -991,7 +991,7 @@ impl Toolbar {
ToolbarAction::None
}
pub fn post_handle_ui_message<G, N>(
pub fn post_handle_ui_message<P, G, N>(
&mut self,
message: &UiMessage,
sender: &MessageSender,
@@ -1002,8 +1002,9 @@ impl Toolbar {
editor_selection: &Selection,
resource_manager: &ResourceManager,
) where
G: SceneGraph<Node = N>,
N: SceneGraphNode<SceneGraph = G>,
P: PrefabData<Graph = G> + AnimationSource<Node = N, SceneGraph = G, Prefab = P>,
G: SceneGraph<Node = N, Prefab = P>,
N: SceneGraphNode<SceneGraph = G, ResourceData = P>,
{
if let Some(ButtonMessage::Click) = message.data() {
if message.destination() == self.import || message.destination() == self.reimport {
@@ -1046,12 +1047,15 @@ impl Toolbar {
}
} else if let Some(FileSelectorMessage::Commit(path)) = message.data() {
if message.destination() == self.import_file_selector {
match block_on(resource_manager.request::<Model>(path)) {
match block_on(resource_manager.request::<P>(path)) {
Ok(model) => {
// TODO
/*
let mut animations = model
.retarget_animations_directly(self.selected_import_root.into(), graph);
let model_kind = model.kind();
let data = model.data_ref();
let mut animations = data.retarget_animations_directly(
self.selected_import_root.into(),
graph,
model_kind,
);
let file_stem = path
.file_stem()
@@ -1097,7 +1101,7 @@ impl Toolbar {
}
}
}
}*/
}
}
Err(err) => Log::err(format!(
"Failed to load {} animation file! Reason: {:?}",
+19
View File
@@ -12,6 +12,7 @@ use fyrox_core::{
ComponentProvider, NameProvider,
};
use fyrox_resource::{untyped::UntypedResource, Resource, TypedResourceData};
use std::any::Any;
use std::cmp::Ordering;
use std::fmt::Debug;
use std::{
@@ -537,6 +538,20 @@ pub trait SceneGraphNode: AbstractSceneNode + Clone + 'static {
previous_value
}
/// Tries to borrow a component of given type.
#[inline]
fn component_ref<T: Any>(&self) -> Option<&T> {
ComponentProvider::query_component_ref(self, TypeId::of::<T>())
.and_then(|c| c.downcast_ref())
}
/// Tries to borrow a component of given type.
#[inline]
fn component_mut<T: Any>(&mut self) -> Option<&mut T> {
ComponentProvider::query_component_mut(self, TypeId::of::<T>())
.and_then(|c| c.downcast_mut())
}
}
pub trait PrefabData: TypedResourceData + 'static {
@@ -697,6 +712,10 @@ pub trait SceneGraph: BaseSceneGraph {
/// Creates new iterator that iterates over internal collection giving (handle; node) pairs.
fn pair_iter(&self) -> impl Iterator<Item = (Handle<Self::Node>, &Self::Node)>;
/// Creates an iterator that has linear iteration order over internal collection
/// of nodes. It does *not* perform any tree traversal!
fn linear_iter(&self) -> impl Iterator<Item = &Self::Node>;
/// Creates an iterator that has linear iteration order over internal collection
/// of nodes. It does *not* perform any tree traversal!
fn linear_iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Node>;
+5
View File
@@ -2937,6 +2937,11 @@ impl SceneGraph for UserInterface {
self.nodes.pair_iter()
}
#[inline]
fn linear_iter(&self) -> impl Iterator<Item = &Self::Node> {
self.nodes.iter()
}
#[inline]
fn linear_iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Node> {
self.nodes.iter_mut()
+179 -65
View File
@@ -20,8 +20,8 @@
use crate::{
asset::{
io::ResourceIo, manager::ResourceManager, options::ImportOptions, Resource, ResourceData,
MODEL_RESOURCE_UUID,
io::ResourceIo, manager::ResourceManager, options::ImportOptions, untyped::ResourceKind,
Resource, ResourceData, MODEL_RESOURCE_UUID,
},
core::{
algebra::{UnitQuaternion, Vector3},
@@ -30,23 +30,21 @@ use crate::{
reflect::prelude::*,
uuid::Uuid,
uuid_provider,
variable::InheritableVariable,
visitor::{Visit, VisitError, VisitResult, Visitor},
TypeUuidProvider,
NameProvider, TypeUuidProvider,
},
engine::SerializationContext,
graph::{NodeHandleMap, PrefabData, SceneGraph},
generic_animation::AnimationContainer,
graph::{BaseSceneGraph, NodeHandleMap, NodeMapping, PrefabData, SceneGraph, SceneGraphNode},
resource::fbx::{self, error::FbxError},
scene::{
animation::{Animation, AnimationPlayer},
base::SceneNodeId,
graph::Graph,
node::Node,
transform::Transform,
animation::Animation, base::SceneNodeId, graph::Graph, node::Node, transform::Transform,
Scene, SceneLoader,
},
};
use fxhash::FxHashMap;
use fyrox_graph::NodeMapping;
use fyrox_ui::{UiNode, UserInterface};
use serde::{Deserialize, Serialize};
use std::{
any::Any,
@@ -178,6 +176,144 @@ impl<'a, 'b, 'c> InstantiationContext<'a, 'b, 'c> {
}
}
/// Common trait that has animation retargetting methods.
pub trait AnimationSource {
/// Prefab type.
type Prefab: PrefabData<Graph = Self::SceneGraph>;
/// Scene graph type.
type SceneGraph: SceneGraph<Node = Self::Node, Prefab = Self::Prefab>;
/// Scene node type.
type Node: SceneGraphNode<SceneGraph = Self::SceneGraph, ResourceData = Self::Prefab>;
/// Returns a reference to an inner graph.
fn inner_graph(&self) -> &Self::SceneGraph;
/// Tries to retarget animations from given model resource to a node hierarchy starting
/// from `root` on a given scene.
///
/// Animation retargeting allows you to "transfer" animation from a model to a model
/// instance on a scene. Imagine you have a character that should have multiple animations
/// like idle, run, shoot, walk, etc. and you want to store each animation in a separate
/// file. Then when you creating a character on a level you want to have all possible
/// animations assigned to a character, this is where this function comes into play:
/// you just load a model of your character with skeleton, but without any animations,
/// then you load several "models" which have only skeleton with some animation (such
/// "models" can be considered as "animation" resources). After this you need to
/// instantiate model on your level and retarget all animations you need to that instance
/// from other "models". All you have after this is a handle to a model and bunch of
/// handles to specific animations. After this animations can be blended in any combinations
/// you need to. For example idle animation can be blended with walk animation when your
/// character starts walking.
///
/// # Notes
///
/// 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<Self::Node>,
graph: &Self::SceneGraph,
self_kind: ResourceKind,
) -> Vec<fyrox_animation::Animation<Handle<Self::Node>>> {
let mut retargetted_animations = Vec::new();
let model_graph = self.inner_graph();
for src_node_ref in model_graph.linear_iter() {
if let Some(src_animations) = src_node_ref
.component_ref::<InheritableVariable<AnimationContainer<Handle<Self::Node>>>>()
{
for src_anim in src_animations.iter() {
let mut anim_copy = src_anim.clone();
// Remap animation track nodes from resource to instance. This is required
// 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_graph.node(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()) {
Some((instance_node, _)) => {
// One-to-one track mapping so there is [i] indexing.
track.set_target(instance_node);
}
None => {
track.set_target(Default::default());
Log::writeln(
MessageKind::Error,
format!(
"Failed to retarget animation {:?} for node {}",
self_kind,
ref_node.name()
),
);
}
}
}
retargetted_animations.push(anim_copy);
}
}
}
retargetted_animations
}
/// 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
/// adds retargetted animations to the specified animation player in the hierarchy of given `root`.
///
/// # Panic
///
/// Panics if `dest_animation_player` is invalid handle, or the node does not have [`AnimationPlayer`]
/// component.
fn retarget_animations_to_player(
&self,
root: Handle<Self::Node>,
dest_animation_player: Handle<Self::Node>,
graph: &mut Self::SceneGraph,
self_kind: ResourceKind,
) -> Vec<Handle<fyrox_animation::Animation<Handle<Self::Node>>>> {
let mut animation_handles = Vec::new();
let animations = self.retarget_animations_directly(root, graph, self_kind);
let dest_animations = graph
.node_mut(dest_animation_player)
.component_mut::<InheritableVariable<AnimationContainer<Handle<Self::Node>>>>()
.unwrap();
for animation in animations {
animation_handles.push(dest_animations.add(animation));
}
animation_handles
}
/// 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
/// adds retargetted animations to a first animation player in the hierarchy of given `root`.
///
/// # Panic
///
/// Panics if there's no animation player in the given hierarchy (descendant nodes of `root`).
fn retarget_animations(
&self,
root: Handle<Self::Node>,
graph: &mut Self::SceneGraph,
self_kind: ResourceKind,
) -> Vec<Handle<fyrox_animation::Animation<Handle<Self::Node>>>> {
if let Some((animation_player, _)) = graph.find(root, &mut |n| {
n.component_ref::<InheritableVariable<AnimationContainer<Handle<Self::Node>>>>()
.is_some()
}) {
self.retarget_animations_to_player(root, animation_player, graph, self_kind)
} else {
Default::default()
}
}
}
/// Type alias for model resources.
pub type ModelResource = Resource<Model>;
@@ -263,6 +399,26 @@ pub trait ModelResourceExtension: Sized {
fn generate_ids(&self) -> FxHashMap<Handle<Node>, SceneNodeId>;
}
impl AnimationSource for Model {
type Prefab = Model;
type SceneGraph = Graph;
type Node = Node;
fn inner_graph(&self) -> &Self::SceneGraph {
&self.scene.graph
}
}
impl AnimationSource for UserInterface {
type Prefab = UserInterface;
type SceneGraph = UserInterface;
type Node = UiNode;
fn inner_graph(&self) -> &Self::SceneGraph {
self
}
}
impl ModelResourceExtension for ModelResource {
fn instantiate_from<Pre>(
model: ModelResource,
@@ -318,49 +474,13 @@ impl ModelResourceExtension for ModelResource {
}
fn retarget_animations_directly(&self, root: Handle<Node>, graph: &Graph) -> Vec<Animation> {
let mut retargetted_animations = Vec::new();
let mut header = self.state();
let self_kind = header.kind().clone();
if let Some(model) = header.data() {
for src_node_ref in model.scene.graph.linear_iter() {
if let Some(src_player) = src_node_ref.query_component_ref::<AnimationPlayer>() {
for src_anim in src_player.animations().iter() {
let mut anim_copy = src_anim.clone();
// Remap animation track nodes from resource to instance. This is required
// 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 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()) {
Some((instance_node, _)) => {
// One-to-one track mapping so there is [i] indexing.
track.set_target(instance_node);
}
None => {
track.set_target(Default::default());
Log::writeln(
MessageKind::Error,
format!(
"Failed to retarget animation {:?} for node {}",
self_kind,
ref_node.name()
),
);
}
}
}
retargetted_animations.push(anim_copy);
}
}
}
model.retarget_animations_directly(root, graph, self_kind)
} else {
Default::default()
}
retargetted_animations
}
fn retarget_animations_to_player(
@@ -369,26 +489,20 @@ impl ModelResourceExtension for ModelResource {
dest_animation_player: Handle<Node>,
graph: &mut Graph,
) -> Vec<Handle<Animation>> {
let mut animation_handles = Vec::new();
let animations = self.retarget_animations_directly(root, graph);
let dest_animation_player = graph[dest_animation_player]
.query_component_mut::<AnimationPlayer>()
.unwrap();
for animation in animations {
animation_handles.push(dest_animation_player.animations_mut().add(animation));
let mut header = self.state();
let self_kind = header.kind().clone();
if let Some(model) = header.data() {
model.retarget_animations_to_player(root, dest_animation_player, graph, self_kind)
} else {
Default::default()
}
animation_handles
}
fn retarget_animations(&self, root: Handle<Node>, graph: &mut Graph) -> Vec<Handle<Animation>> {
if let Some((animation_player, _)) = graph.find(root, &mut |n| {
n.query_component_ref::<AnimationPlayer>().is_some()
}) {
self.retarget_animations_to_player(root, animation_player, graph)
let mut header = self.state();
let self_kind = header.kind().clone();
if let Some(model) = header.data() {
model.retarget_animations(root, graph, self_kind)
} else {
Default::default()
}
+5
View File
@@ -1635,6 +1635,11 @@ impl SceneGraph for Graph {
self.pool.pair_iter()
}
#[inline]
fn linear_iter(&self) -> impl Iterator<Item = &Self::Node> {
self.pool.iter()
}
#[inline]
fn linear_iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Node> {
self.pool.iter_mut()