mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
initial attempt to decouple animation tracks data into a shared resource
- significantly reduces memory constumption when cloning animations, since it does not need to clone the tracks anymore. - animation resource can be shared across multiple animations using the same tracks - significantly speeds up instantiation of animation player scene node - the editor is still broken and requires proper fixes - no backward compatibility yet
This commit is contained in:
@@ -16,6 +16,7 @@ rust-version = "1.72"
|
||||
|
||||
[dependencies]
|
||||
fyrox-core = { version = "0.28.1", path = "../fyrox-core" }
|
||||
fyrox-resource = { version = "0.12.0", path = "../fyrox-resource" }
|
||||
strum = "0.26.1"
|
||||
strum_macros = "0.26.1"
|
||||
fxhash = "0.2.1"
|
||||
|
||||
+152
-76
@@ -28,29 +28,33 @@ use crate::{
|
||||
core::{
|
||||
algebra::{UnitQuaternion, Vector3},
|
||||
math::wrapf,
|
||||
pool::{Handle, Pool, Ticket},
|
||||
pool::{ErasedHandle, Handle, Pool, Ticket},
|
||||
reflect::prelude::*,
|
||||
uuid::Uuid,
|
||||
type_traits::prelude::*,
|
||||
uuid::{uuid, Uuid},
|
||||
visitor::{Visit, VisitResult, Visitor},
|
||||
ImmutableString, NameProvider,
|
||||
},
|
||||
track::Track,
|
||||
};
|
||||
use core::ImmutableString;
|
||||
use fyrox_core::{NameProvider, TypeUuidProvider};
|
||||
use std::hash::Hash;
|
||||
use fxhash::FxHashMap;
|
||||
use fyrox_resource::{Resource, ResourceData};
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::VecDeque,
|
||||
error::Error,
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
ops::{Index, IndexMut, Range},
|
||||
path::Path,
|
||||
};
|
||||
use value::{nlerp, TrackValue, ValueBinding};
|
||||
|
||||
use crate::track::TrackBinding;
|
||||
pub use fyrox_core as core;
|
||||
use fyrox_core::pool::ErasedHandle;
|
||||
use fyrox_core::uuid::uuid;
|
||||
|
||||
use fyrox_resource::untyped::ResourceKind;
|
||||
pub use pose::{AnimationPose, NodePose};
|
||||
pub use signal::{AnimationEvent, AnimationSignal};
|
||||
use value::{nlerp, TrackValue, ValueBinding};
|
||||
|
||||
pub mod container;
|
||||
pub mod machine;
|
||||
@@ -60,6 +64,85 @@ pub mod spritesheet;
|
||||
pub mod track;
|
||||
pub mod value;
|
||||
|
||||
#[derive(Default, Debug, Reflect, Clone, PartialEq, TypeUuidProvider)]
|
||||
#[type_uuid(id = "044d9f7c-5c6c-4b29-8de9-d0d975a48256")]
|
||||
pub struct AnimationData {
|
||||
pub tracks: Vec<Track>,
|
||||
}
|
||||
|
||||
impl AnimationData {
|
||||
/// 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) {
|
||||
self.tracks.push(track);
|
||||
}
|
||||
|
||||
/// Removes a track at given index.
|
||||
pub fn remove_track(&mut self, index: usize) -> Track {
|
||||
self.tracks.remove(index)
|
||||
}
|
||||
|
||||
/// Inserts a track at given index.
|
||||
pub fn insert_track(&mut self, index: usize, track: Track) {
|
||||
self.tracks.insert(index, track)
|
||||
}
|
||||
|
||||
/// Removes last track from the list of tracks of the animation.
|
||||
pub fn pop_track(&mut self) -> Option<Track> {
|
||||
self.tracks.pop()
|
||||
}
|
||||
|
||||
/// Returns a reference to tracks container.
|
||||
pub fn tracks(&self) -> &[Track] {
|
||||
&self.tracks
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the track container.
|
||||
pub fn tracks_mut(&mut self) -> &mut [Track] {
|
||||
&mut self.tracks
|
||||
}
|
||||
|
||||
/// Removes all tracks from the animation for which the given `filter` closure returns `false`. Could be useful
|
||||
/// to remove undesired animation tracks.
|
||||
pub fn retain_tracks<F>(&mut self, filter: F)
|
||||
where
|
||||
F: FnMut(&Track) -> bool,
|
||||
{
|
||||
self.tracks.retain(filter)
|
||||
}
|
||||
}
|
||||
|
||||
impl Visit for AnimationData {
|
||||
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
|
||||
self.tracks.visit(name, visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceData for AnimationData {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn type_uuid(&self) -> Uuid {
|
||||
<AnimationData as TypeUuidProvider>::type_uuid()
|
||||
}
|
||||
|
||||
fn save(&mut self, _path: &Path) -> Result<(), Box<dyn Error>> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn can_be_saved(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub type AnimationDataResource = Resource<AnimationData>;
|
||||
|
||||
/// # Overview
|
||||
///
|
||||
/// Animation allows you to change properties of arbitrary entities at runtime using a set of key frames. Animation
|
||||
@@ -188,7 +271,8 @@ pub mod value;
|
||||
pub struct Animation<T: EntityId> {
|
||||
#[visit(optional)]
|
||||
name: ImmutableString,
|
||||
tracks: Vec<Track<T>>,
|
||||
tracks_data: AnimationDataResource,
|
||||
track_bindings: FxHashMap<Uuid, TrackBinding<T>>,
|
||||
time_position: f32,
|
||||
#[visit(optional)]
|
||||
time_slice: Range<f32>,
|
||||
@@ -224,7 +308,7 @@ impl<T: EntityId> TypeUuidProvider for Animation<T> {
|
||||
|
||||
/// Identifier of an entity, that can be animated.
|
||||
pub trait EntityId:
|
||||
Default + Copy + Reflect + Visit + PartialEq + Eq + Hash + Debug + Ord + PartialEq + 'static
|
||||
Default + Send + Copy + Reflect + Visit + PartialEq + Eq + Hash + Debug + Ord + PartialEq + 'static
|
||||
{
|
||||
}
|
||||
|
||||
@@ -284,7 +368,7 @@ impl<T: EntityId> Clone for Animation<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
name: self.name.clone(),
|
||||
tracks: self.tracks.clone(),
|
||||
tracks_data: self.tracks_data.clone(),
|
||||
speed: self.speed,
|
||||
time_position: self.time_position,
|
||||
looped: self.looped,
|
||||
@@ -296,6 +380,7 @@ impl<T: EntityId> Clone for Animation<T> {
|
||||
time_slice: self.time_slice.clone(),
|
||||
root_motion: self.root_motion.clone(),
|
||||
max_event_capacity: 32,
|
||||
track_bindings: self.track_bindings.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,25 +406,8 @@ impl<T: EntityId> Animation<T> {
|
||||
self.name.as_ref()
|
||||
}
|
||||
|
||||
/// 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<T>) {
|
||||
self.tracks.push(track);
|
||||
}
|
||||
|
||||
/// Removes a track at given index.
|
||||
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<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<T>> {
|
||||
self.tracks.pop()
|
||||
pub fn tracks_data(&self) -> &AnimationDataResource {
|
||||
&self.tracks_data
|
||||
}
|
||||
|
||||
/// Calculates new length of the animation based on the content of its tracks. It looks for the most "right"
|
||||
@@ -347,19 +415,18 @@ impl<T: EntityId> Animation<T> {
|
||||
/// in case if you formed animation from code using just curves and don't know the actual length of the
|
||||
/// animation.
|
||||
pub fn fit_length_to_content(&mut self) {
|
||||
let state = self.tracks_data.state();
|
||||
let Some(tracks_data) = state.data_ref() else {
|
||||
return;
|
||||
};
|
||||
self.time_slice.start = 0.0;
|
||||
for track in self.tracks.iter_mut() {
|
||||
for track in tracks_data.tracks.iter() {
|
||||
if track.time_length() > self.time_slice.end {
|
||||
self.time_slice.end = track.time_length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to tracks container.
|
||||
pub fn tracks(&self) -> &[Track<T>] {
|
||||
&self.tracks
|
||||
}
|
||||
|
||||
/// Sets new time position of the animation. The actual time position the animation will have after the call,
|
||||
/// can be different in two reasons:
|
||||
///
|
||||
@@ -436,7 +503,13 @@ impl<T: EntityId> Animation<T> {
|
||||
}
|
||||
|
||||
fn update_root_motion(&mut self, prev_time_position: f32) {
|
||||
fn fetch_position_at_time<T: EntityId>(tracks: &[Track<T>], time: f32) -> Vector3<f32> {
|
||||
let state = self.tracks_data.state();
|
||||
let Some(tracks_data) = state.data_ref() else {
|
||||
return;
|
||||
};
|
||||
let tracks = &tracks_data.tracks;
|
||||
|
||||
fn fetch_position_at_time(tracks: &[Track], time: f32) -> Vector3<f32> {
|
||||
tracks
|
||||
.iter()
|
||||
.find(|track| track.binding() == &ValueBinding::Position)
|
||||
@@ -451,10 +524,7 @@ impl<T: EntityId> Animation<T> {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn fetch_rotation_at_time<T: EntityId>(
|
||||
tracks: &[Track<T>],
|
||||
time: f32,
|
||||
) -> UnitQuaternion<f32> {
|
||||
fn fetch_rotation_at_time(tracks: &[Track], time: f32) -> UnitQuaternion<f32> {
|
||||
tracks
|
||||
.iter()
|
||||
.find(|track| track.binding() == &ValueBinding::Rotation)
|
||||
@@ -499,9 +569,9 @@ impl<T: EntityId> Animation<T> {
|
||||
if let TrackValue::Vector3(pose_position) = bound_value.value {
|
||||
if new_loop_cycle_started {
|
||||
root_motion.prev_position =
|
||||
fetch_position_at_time(&self.tracks, cycle_start_time);
|
||||
fetch_position_at_time(tracks, cycle_start_time);
|
||||
root_motion.position_offset_remainder = Some(
|
||||
fetch_position_at_time(&self.tracks, cycle_end_time)
|
||||
fetch_position_at_time(tracks, cycle_end_time)
|
||||
- pose_position,
|
||||
);
|
||||
} else {
|
||||
@@ -536,7 +606,7 @@ impl<T: EntityId> Animation<T> {
|
||||
|
||||
// Reset position so the root won't move.
|
||||
let start_position =
|
||||
fetch_position_at_time(&self.tracks, self.time_slice.start);
|
||||
fetch_position_at_time(tracks, self.time_slice.start);
|
||||
|
||||
bound_value.value = TrackValue::Vector3(Vector3::new(
|
||||
if root_motion_settings.ignore_x_movement {
|
||||
@@ -562,9 +632,9 @@ impl<T: EntityId> Animation<T> {
|
||||
if !root_motion_settings.ignore_rotations {
|
||||
if new_loop_cycle_started {
|
||||
root_motion.prev_rotation =
|
||||
fetch_rotation_at_time(&self.tracks, cycle_start_time);
|
||||
fetch_rotation_at_time(tracks, cycle_start_time);
|
||||
root_motion.rotation_remainder = Some(
|
||||
fetch_rotation_at_time(&self.tracks, cycle_end_time)
|
||||
fetch_rotation_at_time(tracks, cycle_end_time)
|
||||
.inverse()
|
||||
* pose_rotation,
|
||||
);
|
||||
@@ -584,7 +654,7 @@ impl<T: EntityId> Animation<T> {
|
||||
|
||||
// Reset rotation so the root won't rotate.
|
||||
bound_value.value = TrackValue::UnitQuaternion(
|
||||
fetch_rotation_at_time(&self.tracks, self.time_slice.start),
|
||||
fetch_rotation_at_time(tracks, self.time_slice.start),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -686,9 +756,20 @@ impl<T: EntityId> Animation<T> {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the track container.
|
||||
pub fn tracks_mut(&mut self) -> &mut [Track<T>] {
|
||||
&mut self.tracks
|
||||
pub fn add_track(&mut self, target: T, track: Track) {
|
||||
let mut state = self.tracks_data.state();
|
||||
let Some(tracks_data) = state.data() else {
|
||||
return;
|
||||
};
|
||||
let id = track.id();
|
||||
tracks_data.tracks.push(track);
|
||||
self.track_bindings.insert(
|
||||
id,
|
||||
TrackBinding {
|
||||
enabled: true,
|
||||
target,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a new animation signal to the animation. See [`AnimationSignal`] docs for more info and examples.
|
||||
@@ -722,36 +803,21 @@ impl<T: EntityId> Animation<T> {
|
||||
&mut self.signals
|
||||
}
|
||||
|
||||
/// Removes all tracks from the animation for which the given `filter` closure returns `false`. Could be useful
|
||||
/// to remove undesired animation tracks.
|
||||
pub fn retain_tracks<F>(&mut self, filter: F)
|
||||
where
|
||||
F: FnMut(&Track<T>) -> bool,
|
||||
{
|
||||
self.tracks.retain(filter)
|
||||
}
|
||||
|
||||
/// 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: T, enabled: bool) {
|
||||
for track in self.tracks.iter_mut() {
|
||||
for track in self.track_bindings.values_mut() {
|
||||
if track.target() == handle {
|
||||
track.set_enabled(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator that yields a number of references to tracks that refer to a given node.
|
||||
pub fn tracks_of(&self, handle: T) -> impl Iterator<Item = &Track<T>> {
|
||||
self.tracks
|
||||
.iter()
|
||||
.filter(move |track| track.target() == handle)
|
||||
pub fn track_bindings(&self) -> &FxHashMap<Uuid, TrackBinding<T>> {
|
||||
&self.track_bindings
|
||||
}
|
||||
|
||||
/// 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: T) -> impl Iterator<Item = &mut Track<T>> {
|
||||
self.tracks
|
||||
.iter_mut()
|
||||
.filter(move |track| track.target() == handle)
|
||||
pub fn track_bindings_mut(&mut self) -> &mut FxHashMap<Uuid, TrackBinding<T>> {
|
||||
&mut self.track_bindings
|
||||
}
|
||||
|
||||
/// Tries to find a layer by its name. Returns index of the signal and its reference.
|
||||
@@ -781,15 +847,24 @@ impl<T: EntityId> Animation<T> {
|
||||
|
||||
/// Removes all tracks from the animation.
|
||||
pub fn remove_tracks(&mut self) {
|
||||
self.tracks.clear();
|
||||
self.track_bindings.clear();
|
||||
}
|
||||
|
||||
fn update_pose(&mut self) {
|
||||
let state = self.tracks_data.state();
|
||||
let Some(tracks_data) = state.data_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.pose.reset();
|
||||
for track in self.tracks.iter() {
|
||||
if track.is_enabled() {
|
||||
for track in tracks_data.tracks.iter() {
|
||||
let Some(binding) = self.track_bindings.get(&track.id()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if binding.is_enabled() {
|
||||
if let Some(bound_value) = track.fetch(self.time_position) {
|
||||
self.pose.add_to_node_pose(track.target(), bound_value);
|
||||
self.pose.add_to_node_pose(binding.target(), bound_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -805,7 +880,7 @@ impl<T: EntityId> Default for Animation<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: Default::default(),
|
||||
tracks: Vec::new(),
|
||||
tracks_data: Resource::new_ok(ResourceKind::Embedded, AnimationData::default()),
|
||||
speed: 1.0,
|
||||
time_position: 0.0,
|
||||
enabled: true,
|
||||
@@ -817,6 +892,7 @@ impl<T: EntityId> Default for Animation<T> {
|
||||
time_slice: Default::default(),
|
||||
root_motion: None,
|
||||
max_event_capacity: 32,
|
||||
track_bindings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,25 +28,63 @@ use crate::{
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Visit, Reflect, Clone, PartialEq)]
|
||||
pub struct TrackBinding<T: EntityId> {
|
||||
pub enabled: bool,
|
||||
pub target: T,
|
||||
}
|
||||
|
||||
impl<T: EntityId> Default for TrackBinding<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
target: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EntityId> TrackBinding<T> {
|
||||
/// Sets a handle of a node that will be animated.
|
||||
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) -> T {
|
||||
self.target
|
||||
}
|
||||
|
||||
/// Enables or disables the track. Disabled tracks won't animate their nodes/properties.
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
self.enabled = enabled;
|
||||
}
|
||||
|
||||
/// Returns `true` if the track is enabled, `false` - otherwise.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
/// Sets target of the track.
|
||||
pub fn with_target(mut self, target: T) -> Self {
|
||||
self.target = target;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Track is responsible in animating a property of a single scene node. The track consists up to 4 parametric curves
|
||||
/// 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<T: EntityId> {
|
||||
pub struct Track {
|
||||
binding: ValueBinding,
|
||||
frames: TrackDataContainer,
|
||||
enabled: bool,
|
||||
target: T,
|
||||
id: Uuid,
|
||||
}
|
||||
|
||||
impl<T: EntityId> Visit for Track<T> {
|
||||
impl Visit for Track {
|
||||
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
|
||||
let mut region = visitor.enter_region(name)?;
|
||||
|
||||
self.target.visit("Node", &mut region)?;
|
||||
self.enabled.visit("Enabled", &mut region)?;
|
||||
|
||||
let _ = self.binding.visit("Binding", &mut region); // Backward compatibility
|
||||
let _ = self.id.visit("Id", &mut region); // Backward compatibility
|
||||
let _ = self.frames.visit("Frames", &mut region); // Backward compatibility
|
||||
@@ -55,19 +93,17 @@ impl<T: EntityId> Visit for Track<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EntityId> Default for Track<T> {
|
||||
impl Default for Track {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
binding: ValueBinding::Position,
|
||||
frames: TrackDataContainer::default(),
|
||||
enabled: true,
|
||||
target: Default::default(),
|
||||
id: Uuid::new_v4(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EntityId> Track<T> {
|
||||
impl Track {
|
||||
/// 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 {
|
||||
@@ -105,12 +141,6 @@ impl<T: EntityId> Track<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets target of the track.
|
||||
pub fn with_target(mut self, target: T) -> Self {
|
||||
self.target = target;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets new track binding. See [`ValueBinding`] docs for more info.
|
||||
pub fn set_binding(&mut self, binding: ValueBinding) {
|
||||
self.binding = binding;
|
||||
@@ -121,16 +151,6 @@ impl<T: EntityId> Track<T> {
|
||||
&self.binding
|
||||
}
|
||||
|
||||
/// Sets a handle of a node that will be animated.
|
||||
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) -> T {
|
||||
self.target
|
||||
}
|
||||
|
||||
/// Returns a reference to the data container.
|
||||
pub fn data_container(&self) -> &TrackDataContainer {
|
||||
&self.frames
|
||||
@@ -154,16 +174,6 @@ impl<T: EntityId> Track<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Enables or disables the track. Disabled tracks won't animate their nodes/properties.
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
self.enabled = enabled;
|
||||
}
|
||||
|
||||
/// Returns `true` if the track is enabled, `false` - otherwise.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
/// Returns length of the track in seconds.
|
||||
pub fn time_length(&self) -> f32 {
|
||||
self.frames.time_length()
|
||||
|
||||
@@ -750,7 +750,6 @@ async fn convert_model(
|
||||
|
||||
// Convert to engine format
|
||||
let mut translation_track = Track::new_position();
|
||||
translation_track.set_target(node_handle);
|
||||
if let Some(lcl_translation) = lcl_translation {
|
||||
fill_track(
|
||||
&mut translation_track,
|
||||
@@ -764,7 +763,6 @@ async fn convert_model(
|
||||
}
|
||||
|
||||
let mut rotation_track = Track::new_rotation();
|
||||
rotation_track.set_target(node_handle);
|
||||
if let Some(lcl_rotation) = lcl_rotation {
|
||||
fill_track(
|
||||
&mut rotation_track,
|
||||
@@ -778,16 +776,15 @@ async fn convert_model(
|
||||
}
|
||||
|
||||
let mut scale_track = Track::new_scale();
|
||||
scale_track.set_target(node_handle);
|
||||
if let Some(lcl_scale) = lcl_scale {
|
||||
fill_track(&mut scale_track, fbx_scene, lcl_scale, model.scale, |v| v);
|
||||
} else {
|
||||
add_vec3_key(&mut scale_track, model.scale);
|
||||
}
|
||||
|
||||
animation.add_track(translation_track);
|
||||
animation.add_track(rotation_track);
|
||||
animation.add_track(scale_track);
|
||||
animation.add_track(node_handle, translation_track);
|
||||
animation.add_track(node_handle, rotation_track);
|
||||
animation.add_track(node_handle, scale_track);
|
||||
}
|
||||
|
||||
animation.fit_length_to_content();
|
||||
@@ -829,7 +826,7 @@ async fn convert(
|
||||
}
|
||||
|
||||
// Do not create animation player if there's no animation content.
|
||||
if !animation.tracks().is_empty() {
|
||||
if !animation.tracks_data().data_ref().tracks().is_empty() {
|
||||
let mut animations_container = AnimationContainer::new();
|
||||
animations_container.add(animation);
|
||||
AnimationPlayerBuilder::new(BaseBuilder::new().with_name("AnimationPlayer"))
|
||||
|
||||
@@ -185,15 +185,15 @@ impl ImportedTrack {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn into_track(self) -> Track<Handle<Node>> {
|
||||
fn into_track(self) -> (Handle<Node>, Track) {
|
||||
let mut data = TrackDataContainer::new(self.target.kind());
|
||||
for (i, curve) in self.curves.into_vec().into_iter().enumerate() {
|
||||
data.curves_mut()[i] = Curve::from(curve);
|
||||
}
|
||||
let mut track = Track::new(data, self.target.value_binding());
|
||||
track.set_target(self.target.handle);
|
||||
track.set_enabled(true);
|
||||
track
|
||||
(
|
||||
self.target.handle,
|
||||
Track::new(data, self.target.value_binding()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,8 @@ impl ImportedAnimation {
|
||||
result.set_name(self.name);
|
||||
result.set_time_slice(self.start..self.end);
|
||||
for t in self.tracks {
|
||||
result.add_track(t.into_track());
|
||||
let (node, track) = t.into_track();
|
||||
result.add_track(node, track);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -249,17 +249,30 @@ pub trait AnimationSource {
|
||||
// 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];
|
||||
let src_anim_track_data = src_anim.tracks_data().state();
|
||||
let Some(src_anim_track_data) = src_anim_track_data.data_ref() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for ref_track in src_anim_track_data.tracks.iter() {
|
||||
let Some(ref_track_binding) =
|
||||
src_anim.track_bindings().get(&ref_track.id())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let ref_node = &model_graph.node(ref_track_binding.target);
|
||||
let track_binding = anim_copy
|
||||
.track_bindings_mut()
|
||||
.get_mut(&ref_track.id())
|
||||
.unwrap();
|
||||
// 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);
|
||||
track_binding.set_target(instance_node);
|
||||
}
|
||||
None => {
|
||||
track.set_target(Default::default());
|
||||
track_binding.set_target(Default::default());
|
||||
Log::writeln(
|
||||
MessageKind::Error,
|
||||
format!(
|
||||
|
||||
@@ -48,7 +48,7 @@ pub mod spritesheet;
|
||||
/// Scene specific animation.
|
||||
pub type Animation = crate::generic_animation::Animation<Handle<Node>>;
|
||||
/// Scene specific animation track.
|
||||
pub type Track = crate::generic_animation::track::Track<Handle<Node>>;
|
||||
pub type Track = crate::generic_animation::track::Track;
|
||||
/// Scene specific animation container.
|
||||
pub type AnimationContainer = crate::generic_animation::AnimationContainer<Handle<Node>>;
|
||||
/// Scene specific animation pose.
|
||||
|
||||
@@ -139,6 +139,14 @@ where
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data_ref(&self) -> Option<&T> {
|
||||
if let ResourceState::Ok(ref data) = self.guard.state {
|
||||
ResourceData::as_any(&**data).downcast_ref::<T>()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A resource of particular data type. It is a typed wrapper around [`UntypedResource`] which
|
||||
|
||||
@@ -65,7 +65,7 @@ impl AnimationPlayerMessage {
|
||||
/// UI-specific animation.
|
||||
pub type Animation = crate::generic_animation::Animation<Handle<UiNode>>;
|
||||
/// UI-specific animation track.
|
||||
pub type Track = crate::generic_animation::track::Track<Handle<UiNode>>;
|
||||
pub type Track = crate::generic_animation::track::Track;
|
||||
/// UI-specific animation container.
|
||||
pub type AnimationContainer = crate::generic_animation::AnimationContainer<Handle<UiNode>>;
|
||||
/// UI-specific animation pose.
|
||||
|
||||
Reference in New Issue
Block a user