use super::animation::{
ANIMATION_STATE_SETUP, MixFrom, MixInterpolation, RotateTimelineState, StateTimelineApply,
apply_state_timeline,
};
use crate::model::TimelineKind;
use crate::runtime::{MixBlend, MixDirection};
use crate::{Animation, Event, Skeleton, SkeletonData};
use std::collections::{HashMap, VecDeque};
use std::sync::{
Arc,
atomic::{AtomicU64, Ordering},
};
const EMPTY_ANIMATION_ID: u64 = u64::MAX;
const EMPTY_ANIMATION_NAME: &str = "<empty>";
static NEXT_STATE_ID: AtomicU64 = AtomicU64::new(1);
fn empty_animation() -> Animation {
Animation {
name: EMPTY_ANIMATION_NAME.to_string(),
duration: 0.0,
color: crate::Animation::DEFAULT_COLOR,
event_timeline: None,
bone_timelines: Vec::new(),
deform_timelines: Vec::new(),
sequence_timelines: Vec::new(),
slot_attachment_timelines: Vec::new(),
slot_color_timelines: Vec::new(),
slot_rgb_timelines: Vec::new(),
slot_alpha_timelines: Vec::new(),
slot_rgba2_timelines: Vec::new(),
slot_rgb2_timelines: Vec::new(),
ik_constraint_timelines: Vec::new(),
transform_constraint_timelines: Vec::new(),
path_constraint_timelines: Vec::new(),
physics_constraint_timelines: Vec::new(),
physics_reset_timelines: Vec::new(),
slider_time_timelines: Vec::new(),
slider_mix_timelines: Vec::new(),
draw_order_timeline: None,
draw_order_folder_timelines: Vec::new(),
timeline_order: Vec::new(),
}
}
fn animation_identity(animation: &Animation) -> u64 {
animation as *const Animation as usize as u64
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct TimelineApplyMode {
from: MixFrom,
hold: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct EntryId {
index: usize,
generation: u32,
}
#[derive(Debug)]
struct EntrySlot {
generation: u32,
entry: Option<TrackEntry>,
}
#[derive(Clone, Debug)]
pub struct AnimationStateData {
skeleton_data: Arc<SkeletonData>,
default_mix: f32,
mixes: HashMap<(String, String), f32>,
}
impl AnimationStateData {
pub fn new(skeleton_data: Arc<SkeletonData>) -> Self {
Self {
skeleton_data,
default_mix: 0.0,
mixes: HashMap::new(),
}
}
pub fn get_skeleton_data(&mut self) -> &mut SkeletonData {
Arc::make_mut(&mut self.skeleton_data)
}
pub fn get_default_mix(&self) -> f32 {
self.default_mix
}
pub fn set_default_mix(&mut self, duration: f32) {
self.default_mix = duration;
}
pub fn set_mix(&mut self, from: impl MixKeyInput, to: impl MixKeyInput, duration: f32) {
let from = from.into_mix_key(self.skeleton_data.as_ref());
let to = to.into_mix_key(self.skeleton_data.as_ref());
self.mixes.insert((from, to), duration);
}
pub fn get_mix(&self, from: &Animation, to: &Animation) -> f32 {
self.mix_duration(&from.name, &to.name)
}
pub fn clear(&mut self) {
self.default_mix = 0.0;
self.mixes.clear();
}
fn mix_duration(&self, from: &str, to: &str) -> f32 {
self.mixes
.get(&(from.to_string(), to.to_string()))
.copied()
.unwrap_or(self.default_mix)
}
}
impl AnimationState {
fn mix_duration_from_last(&self, last: Option<EntryId>, animation_name: &str) -> f32 {
last.and_then(|last| {
self.entry(last).map(|last_ref| {
self.data
.mix_duration(last_ref.animation.name.as_str(), animation_name)
})
})
.unwrap_or(0.0)
}
}
mod sealed {
pub trait Sealed {}
}
impl sealed::Sealed for &str {}
impl sealed::Sealed for &String {}
impl sealed::Sealed for String {}
impl sealed::Sealed for &Animation {}
impl sealed::Sealed for Animation {}
#[doc(hidden)]
pub trait MixKeyInput: sealed::Sealed {
fn into_mix_key(self, skeleton_data: &SkeletonData) -> String;
}
impl MixKeyInput for &str {
fn into_mix_key(self, skeleton_data: &SkeletonData) -> String {
assert!(
skeleton_data.find_animation(self).is_some(),
"unknown animation: {self}"
);
self.to_string()
}
}
impl MixKeyInput for &String {
fn into_mix_key(self, skeleton_data: &SkeletonData) -> String {
self.as_str().into_mix_key(skeleton_data)
}
}
impl MixKeyInput for &Animation {
fn into_mix_key(self, _skeleton_data: &SkeletonData) -> String {
self.name.clone()
}
}
#[doc(hidden)]
pub trait AnimationInput: sealed::Sealed {
fn into_animation(self, state: &AnimationState) -> (u64, Animation);
}
impl AnimationInput for &str {
fn into_animation(self, state: &AnimationState) -> (u64, Animation) {
state.resolve_animation_name(self)
}
}
impl AnimationInput for &String {
fn into_animation(self, state: &AnimationState) -> (u64, Animation) {
self.as_str().into_animation(state)
}
}
impl AnimationInput for String {
fn into_animation(self, state: &AnimationState) -> (u64, Animation) {
self.as_str().into_animation(state)
}
}
impl AnimationInput for &Animation {
fn into_animation(self, _state: &AnimationState) -> (u64, Animation) {
(animation_identity(self), self.clone())
}
}
impl AnimationInput for Animation {
fn into_animation(self, _state: &AnimationState) -> (u64, Animation) {
(animation_identity(&self), self)
}
}
pub struct TrackEntry {
track_index: usize,
animation_identity: u64,
animation: Animation,
looped: bool,
additive: bool,
reverse: bool,
shortest_rotation: bool,
animation_start: f32,
animation_end: f32,
mix_duration: f32,
mix_time: f32,
mix_interpolation: MixInterpolation,
keep_hold: bool,
previous: Option<EntryId>,
next: Option<EntryId>,
mixing_from: Option<EntryId>,
delay: f32,
track_time: f32,
track_end: f32,
time_scale: f32,
animation_last_time: f32,
track_last_time: f32,
next_animation_last_time: f32,
next_track_last_time: f32,
alpha: f32,
total_alpha: f32,
mixing_to: Option<EntryId>,
alpha_attachment_threshold: f32,
mix_attachment_threshold: f32,
mix_draw_order_threshold: f32,
event_threshold: f32,
listener: Option<Box<dyn TrackEntryListener>>,
timeline_mode: Vec<TimelineApplyMode>,
timeline_hold_mix: Vec<Option<EntryId>>,
rotation_state: Vec<f32>,
}
impl std::fmt::Debug for TrackEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TrackEntry")
.field("track_index", &self.track_index)
.field("animation_identity", &self.animation_identity)
.field("animation", &self.animation)
.field("looped", &self.looped)
.field("additive", &self.additive)
.field("reverse", &self.reverse)
.field("shortest_rotation", &self.shortest_rotation)
.field("animation_start", &self.animation_start)
.field("animation_end", &self.animation_end)
.field("mix_duration", &self.mix_duration)
.field("mix_time", &self.mix_time)
.field("previous", &self.previous)
.field("next", &self.next)
.field("mixing_from", &self.mixing_from)
.field("delay", &self.delay)
.field("track_time", &self.track_time)
.field("track_end", &self.track_end)
.field("time_scale", &self.time_scale)
.field("animation_last_time", &self.animation_last_time)
.field("track_last_time", &self.track_last_time)
.field("event_threshold", &self.event_threshold)
.finish()
}
}
impl TrackEntry {
fn new(
track_index: usize,
animation_identity: u64,
animation: &Animation,
looped: bool,
) -> Self {
let track_end = f32::MAX;
Self {
track_index,
animation_identity,
animation: animation.clone(),
looped,
additive: false,
reverse: false,
shortest_rotation: false,
animation_start: 0.0,
animation_end: animation.duration,
mix_duration: 0.0,
mix_time: 0.0,
mix_interpolation: MixInterpolation::default(),
keep_hold: false,
previous: None,
next: None,
mixing_from: None,
delay: 0.0,
track_time: 0.0,
track_end,
time_scale: 1.0,
animation_last_time: -1.0,
track_last_time: -1.0,
next_animation_last_time: -1.0,
next_track_last_time: -1.0,
alpha: 1.0,
total_alpha: 0.0,
mixing_to: None,
alpha_attachment_threshold: 0.0,
mix_attachment_threshold: 0.0,
mix_draw_order_threshold: 0.0,
event_threshold: 0.0,
listener: None,
timeline_mode: Vec::new(),
timeline_hold_mix: Vec::new(),
rotation_state: Vec::new(),
}
}
pub fn get_track_index(&self) -> usize {
self.track_index
}
pub fn get_animation(&self) -> &Animation {
&self.animation
}
pub fn get_loop(&self) -> bool {
self.looped
}
pub fn get_additive(&self) -> bool {
self.additive
}
pub fn is_complete(&self) -> bool {
self.track_time >= self.animation_end - self.animation_start
}
pub fn was_applied(&self) -> bool {
self.next_track_last_time != -1.0
}
pub fn is_empty_animation(&self) -> bool {
self.animation_identity == EMPTY_ANIMATION_ID
}
pub fn get_reverse(&self) -> bool {
self.reverse
}
pub fn get_shortest_rotation(&self) -> bool {
self.shortest_rotation
}
pub fn get_animation_start(&self) -> f32 {
self.animation_start
}
pub fn get_animation_end(&self) -> f32 {
self.animation_end
}
pub fn get_animation_last(&self) -> f32 {
self.animation_last_time
}
pub fn get_delay(&self) -> f32 {
self.delay
}
pub fn get_track_time(&self) -> f32 {
self.track_time
}
pub fn get_track_end(&self) -> f32 {
self.track_end
}
pub fn get_time_scale(&self) -> f32 {
self.time_scale
}
pub fn get_mix_duration(&self) -> f32 {
self.mix_duration
}
pub fn get_mix_time(&self) -> f32 {
self.mix_time
}
pub fn get_mix_interpolation(&self) -> MixInterpolation {
self.mix_interpolation
}
pub fn get_alpha(&self) -> f32 {
self.alpha
}
pub fn get_alpha_attachment_threshold(&self) -> f32 {
self.alpha_attachment_threshold
}
pub fn get_mix_attachment_threshold(&self) -> f32 {
self.mix_attachment_threshold
}
pub fn get_mix_draw_order_threshold(&self) -> f32 {
self.mix_draw_order_threshold
}
pub fn get_event_threshold(&self) -> f32 {
self.event_threshold
}
pub fn get_animation_time(&self) -> f32 {
if self.looped {
let duration = self.animation_end - self.animation_start;
if duration == 0.0 {
return self.animation_start;
}
self.track_time % duration + self.animation_start
} else {
(self.track_time + self.animation_start).min(self.animation_end)
}
}
pub fn get_track_complete(&self) -> f32 {
let duration = self.animation_end - self.animation_start;
if duration != 0.0 {
if self.looped {
return duration * (1.0 + (self.track_time / duration).trunc());
}
if self.track_time < duration {
return duration;
}
}
self.track_time
}
pub(crate) fn mix_percent(&self) -> f32 {
if self.mix_duration == 0.0 {
return 1.0;
}
let mix = self.mix_time / self.mix_duration;
if mix >= 1.0 {
return 1.0;
}
if self.mix_interpolation == MixInterpolation::Linear {
return mix;
}
self.mix_interpolation.apply(mix).clamp(0.0, 1.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TrackEntryHandle {
state_id: u64,
id: EntryId,
}
impl TrackEntryHandle {
pub fn entry<'a>(&self, state: &'a AnimationState) -> Option<&'a TrackEntry> {
state.entry_for_handle(*self)
}
pub fn get_animation_state<'a>(&self, state: &'a AnimationState) -> Option<&'a AnimationState> {
if self.state_id == state.state_id {
Some(state)
} else {
None
}
}
pub fn get_mixing_from(&self, state: &AnimationState) -> Option<TrackEntryHandle> {
state
.entry_for_handle(*self)
.and_then(|entry| entry.mixing_from)
.filter(|id| state.entry(*id).is_some())
.map(|id| state.handle(id))
}
pub fn get_mixing_to(&self, state: &AnimationState) -> Option<TrackEntryHandle> {
state
.entry_for_handle(*self)
.and_then(|entry| entry.mixing_to)
.filter(|id| state.entry(*id).is_some())
.map(|id| state.handle(id))
}
pub fn get_previous(&self, state: &AnimationState) -> Option<TrackEntryHandle> {
state
.entry_for_handle(*self)
.and_then(|entry| entry.previous)
.filter(|id| state.entry(*id).is_some())
.map(|id| state.handle(id))
}
pub fn get_next(&self, state: &AnimationState) -> Option<TrackEntryHandle> {
state
.entry_for_handle(*self)
.and_then(|entry| entry.next)
.filter(|id| state.entry(*id).is_some())
.map(|id| state.handle(id))
}
pub fn is_next_ready(&self, state: &AnimationState) -> bool {
let Some(entry) = state.entry_for_handle(*self) else {
return false;
};
let Some(next) = self
.get_next(state)
.and_then(|handle| state.entry_for_handle(handle))
else {
return false;
};
entry.next_track_last_time - next.delay >= 0.0
}
fn with_entry_mut(&self, state: &mut AnimationState, f: impl FnOnce(&mut TrackEntry)) {
if self.state_id != state.state_id {
return;
}
let Some(entry) = state.entry_mut(self.id) else {
return;
};
f(entry);
}
pub fn set_listener<L: TrackEntryListener + 'static>(
&self,
state: &mut AnimationState,
listener: L,
) {
self.with_entry_mut(state, |entry| {
entry.listener = Some(Box::new(listener));
});
}
pub fn set_track_end(&self, state: &mut AnimationState, track_end: f32) {
self.with_entry_mut(state, |entry| {
entry.track_end = track_end;
});
}
pub fn set_animation(&self, state: &mut AnimationState, animation: &Animation) {
self.with_entry_mut(state, |entry| {
entry.animation_identity = animation_identity(animation);
entry.animation = animation.clone();
});
}
pub fn set_delay(&self, state: &mut AnimationState, delay: f32) {
if delay < 0.0 {
return;
}
self.with_entry_mut(state, |entry| {
entry.delay = delay;
});
}
pub fn set_time_scale(&self, state: &mut AnimationState, time_scale: f32) {
self.with_entry_mut(state, |entry| {
entry.time_scale = time_scale;
});
}
pub fn set_track_time(&self, state: &mut AnimationState, track_time: f32) {
self.with_entry_mut(state, |entry| {
entry.track_time = track_time;
});
}
pub fn set_loop(&self, state: &mut AnimationState, looped: bool) {
self.with_entry_mut(state, |entry| {
entry.looped = looped;
});
}
pub fn set_additive(&self, state: &mut AnimationState, additive: bool) {
self.with_entry_mut(state, |entry| {
entry.additive = additive;
});
}
pub fn set_mix_duration(&self, state: &mut AnimationState, mix_duration: f32) {
self.with_entry_mut(state, |entry| {
entry.mix_duration = mix_duration;
});
}
pub fn set_mix_duration_with_delay(
&self,
state: &mut AnimationState,
mix_duration: f32,
delay: f32,
) {
let previous_track_complete = state
.entry_for_handle(*self)
.and_then(|entry| entry.previous)
.and_then(|previous| state.entry(previous))
.map(|entry| entry.get_track_complete());
let resolved_delay = if delay <= 0.0 {
previous_track_complete
.map(|track_complete| (delay + track_complete - mix_duration).max(0.0))
.unwrap_or(0.0)
} else {
delay
};
self.with_entry_mut(state, |entry| {
entry.mix_duration = mix_duration;
entry.delay = resolved_delay;
});
}
pub fn set_mix_time(&self, state: &mut AnimationState, mix_time: f32) {
self.with_entry_mut(state, |entry| {
entry.mix_time = mix_time;
});
}
pub fn set_mix_interpolation(
&self,
state: &mut AnimationState,
mix_interpolation: MixInterpolation,
) {
self.with_entry_mut(state, |entry| {
entry.mix_interpolation = mix_interpolation;
});
}
pub fn set_alpha(&self, state: &mut AnimationState, alpha: f32) {
self.with_entry_mut(state, |entry| {
entry.alpha = alpha;
});
}
pub fn set_reverse(&self, state: &mut AnimationState, reverse: bool) {
self.with_entry_mut(state, |entry| {
entry.reverse = reverse;
});
}
pub fn set_shortest_rotation(&self, state: &mut AnimationState, shortest_rotation: bool) {
self.with_entry_mut(state, |entry| {
entry.shortest_rotation = shortest_rotation;
});
}
pub fn reset_rotation_directions(&self, state: &mut AnimationState) {
self.with_entry_mut(state, |entry| {
entry.rotation_state.clear();
});
}
pub fn set_alpha_attachment_threshold(&self, state: &mut AnimationState, threshold: f32) {
self.with_entry_mut(state, |entry| {
entry.alpha_attachment_threshold = threshold;
});
}
pub fn set_mix_attachment_threshold(&self, state: &mut AnimationState, threshold: f32) {
self.with_entry_mut(state, |entry| {
entry.mix_attachment_threshold = threshold;
});
}
pub fn set_mix_draw_order_threshold(&self, state: &mut AnimationState, threshold: f32) {
self.with_entry_mut(state, |entry| {
entry.mix_draw_order_threshold = threshold;
});
}
pub fn set_event_threshold(&self, state: &mut AnimationState, threshold: f32) {
self.with_entry_mut(state, |entry| {
entry.event_threshold = threshold;
});
}
pub fn set_animation_start(&self, state: &mut AnimationState, animation_start: f32) {
self.with_entry_mut(state, |entry| {
entry.animation_start = animation_start;
});
}
pub fn set_animation_end(&self, state: &mut AnimationState, animation_end: f32) {
self.with_entry_mut(state, |entry| {
entry.animation_end = animation_end;
});
}
pub fn set_animation_last(&self, state: &mut AnimationState, animation_last: f32) {
self.with_entry_mut(state, |entry| {
entry.animation_last_time = animation_last;
entry.next_animation_last_time = animation_last;
});
}
}
#[derive(Clone, Debug)]
pub enum AnimationStateEvent {
Start,
Interrupt,
End,
Dispose,
Complete,
Event(Event),
}
pub trait TrackEntryListener {
fn on_event(
&mut self,
state: &mut AnimationState,
entry: TrackEntryHandle,
event: &AnimationStateEvent,
);
}
pub trait AnimationStateListener {
fn on_event(
&mut self,
state: &mut AnimationState,
entry: TrackEntryHandle,
event: &AnimationStateEvent,
);
}
#[derive(Clone, Debug)]
struct QueuedEvent {
entry: EntryId,
event: AnimationStateEvent,
}
#[derive(Default)]
struct Track {
current: Option<EntryId>,
queue: VecDeque<EntryId>,
}
pub struct AnimationState {
state_id: u64,
data: AnimationStateData,
tracks: Vec<Track>,
entries: Vec<EntrySlot>,
free_list: Vec<usize>,
event_queue: VecDeque<QueuedEvent>,
time_scale: f32,
listener: Option<Box<dyn AnimationStateListener>>,
pub(crate) drain_disabled: bool,
manual_track_entry_disposal: bool,
animations_changed: bool,
property_ids: HashMap<u64, EntryId>,
unkeyed_state: i32,
}
impl AnimationState {
pub fn new(data: AnimationStateData) -> Self {
Self {
state_id: NEXT_STATE_ID.fetch_add(1, Ordering::Relaxed),
data,
tracks: Vec::new(),
entries: Vec::new(),
free_list: Vec::new(),
event_queue: VecDeque::new(),
time_scale: 1.0,
listener: None,
drain_disabled: false,
manual_track_entry_disposal: false,
animations_changed: false,
property_ids: HashMap::new(),
unkeyed_state: 0,
}
}
pub fn set_listener<L: AnimationStateListener + 'static>(&mut self, listener: L) {
self.listener = Some(Box::new(listener));
}
pub fn disable_queue(&mut self) {
self.drain_disabled = true;
}
pub fn enable_queue(&mut self) {
self.drain_disabled = false;
}
pub fn set_manual_track_entry_disposal(&mut self, manual: bool) {
self.manual_track_entry_disposal = manual;
}
pub fn get_manual_track_entry_disposal(&self) -> bool {
self.manual_track_entry_disposal
}
pub fn dispose_track_entry(&mut self, handle: TrackEntryHandle) {
if handle.state_id == self.state_id {
self.free_entry(handle.id);
}
}
pub fn get_time_scale(&self) -> f32 {
self.time_scale
}
pub fn set_time_scale(&mut self, time_scale: f32) {
self.time_scale = time_scale;
}
pub fn get_data(&mut self) -> &mut AnimationStateData {
&mut self.data
}
pub fn get_tracks(&self) -> Vec<Option<TrackEntryHandle>> {
self.tracks
.iter()
.map(|track| track.current.map(|id| self.handle(id)))
.collect()
}
fn handle(&self, id: EntryId) -> TrackEntryHandle {
TrackEntryHandle {
state_id: self.state_id,
id,
}
}
fn entry_for_handle(&self, handle: TrackEntryHandle) -> Option<&TrackEntry> {
if handle.state_id != self.state_id {
return None;
}
self.entry(handle.id)
}
fn compute_mix_from(&mut self, track_id: EntryId, kind: TimelineKind, ids: &[u64]) -> MixFrom {
let mut mix_from = MixFrom::Setup;
for (i, id) in ids.iter().copied().enumerate() {
match self.property_ids.get(&id).copied() {
None => {
self.property_ids.insert(id, track_id);
}
Some(owner) if owner == track_id => mix_from = MixFrom::First,
Some(_) => {
for id in ids.iter().skip(i + 1).copied() {
self.property_ids.entry(id).or_insert(track_id);
}
return MixFrom::Current;
}
}
}
if matches!(kind, TimelineKind::DrawOrderFolder(_)) {
let draw_order_id = crate::model::draw_order_property_id();
if let Some(owner) = self.property_ids.get(&draw_order_id).copied() {
return if owner == track_id {
MixFrom::First
} else {
MixFrom::Current
};
}
}
mix_from
}
fn animations_changed(&mut self) {
self.animations_changed = false;
let current_ids = self
.tracks
.iter()
.filter_map(|t| t.current)
.collect::<Vec<_>>();
for track_id in current_ids {
let mut entry_id = track_id;
while let Some(from) = self.entry(entry_id).and_then(|e| e.mixing_from) {
entry_id = from;
}
let mut chain = Vec::new();
let mut cur = Some(entry_id);
while let Some(id) = cur {
chain.push(id);
cur = self.entry(id).and_then(|e| e.mixing_to);
}
for id in chain {
self.compute_hold(id, track_id);
}
}
self.property_ids.clear();
}
fn compute_hold(&mut self, entry_id: EntryId, track_id: EntryId) {
let (animation, to_id, entry_additive, keep_hold, previous_timeline_mode) =
match self.entry(entry_id) {
Some(entry) => (
entry.animation.clone(),
entry.mixing_to,
entry.additive,
entry.keep_hold,
entry.timeline_mode.clone(),
),
None => return,
};
let kinds = animation.timeline_order().to_vec();
let mut timeline_mode = vec![
TimelineApplyMode {
from: MixFrom::Setup,
hold: false,
};
kinds.len()
];
let mut timeline_hold_mix = vec![None; kinds.len()];
for (i, kind) in kinds.iter().copied().enumerate() {
let ids = animation.timeline_property_ids(kind);
let mix_from = self.compute_mix_from(track_id, kind, &ids);
timeline_mode[i].from = mix_from;
let timeline_additive = animation.timeline_kind_additive(kind);
if entry_additive && timeline_additive {
continue;
}
if let Some(to_id) = to_id
&& !animation.timeline_kind_instant(kind)
{
let to_holds_property = match self.entry(to_id) {
Some(to) => {
!(to.additive && timeline_additive) && to.animation.has_timeline(&ids)
}
None => false,
};
if to_holds_property {
let mut next = self.entry(to_id).and_then(|e| e.mixing_to);
let mut hold_mix = None;
while let Some(next_id) = next {
let Some(next_entry) = self.entry(next_id) else {
break;
};
if next_entry.additive && timeline_additive
|| !next_entry.animation.has_timeline(&ids)
{
if next_entry.mix_duration > 0.0 {
hold_mix = Some(next_id);
}
break;
}
next = next_entry.mixing_to;
}
timeline_mode[i].hold = true;
timeline_hold_mix[i] = hold_mix;
}
}
if keep_hold {
timeline_mode[i].hold = previous_timeline_mode
.get(i)
.map(|mode| mode.hold)
.unwrap_or(false);
}
}
if let Some(entry) = self.entry_mut(entry_id) {
entry.timeline_mode = timeline_mode;
entry.timeline_hold_mix = timeline_hold_mix;
}
}
pub fn get_track(&self, track_index: usize) -> Option<TrackEntryHandle> {
let id = self.tracks.get(track_index)?.current?;
self.entry(id)?;
Some(self.handle(id))
}
fn resolve_animation_name(&self, animation_name: impl AsRef<str>) -> (u64, Animation) {
let animation_name = animation_name.as_ref();
let animation = self
.data
.skeleton_data
.as_ref()
.find_animation(animation_name)
.unwrap_or_else(|| panic!("unknown animation: {}", animation_name));
(animation_identity(animation), animation.clone())
}
pub fn set_animation(
&mut self,
track_index: usize,
animation: impl AnimationInput,
looped: bool,
) -> TrackEntryHandle {
let (animation_id, animation) = animation.into_animation(self);
self.set_animation_internal(track_index, animation_id, animation, looped)
}
pub fn set_empty_animation(
&mut self,
track_index: usize,
mix_duration: f32,
) -> TrackEntryHandle {
let entry =
self.set_animation_internal(track_index, EMPTY_ANIMATION_ID, empty_animation(), false);
entry.set_mix_duration(self, mix_duration);
entry.set_track_end(self, mix_duration);
entry
}
pub fn set_empty_animations(&mut self, mix_duration: f32) {
let current_track_indices = self
.tracks
.iter()
.enumerate()
.filter_map(|(track_index, track)| track.current.map(|_| track_index))
.collect::<Vec<_>>();
if current_track_indices.is_empty() {
return;
}
let old_drain_disabled = self.drain_disabled;
self.drain_disabled = true;
for track_index in current_track_indices {
self.set_empty_animation(track_index, mix_duration);
}
self.drain_disabled = old_drain_disabled;
self.drain_event_queue();
}
fn set_animation_internal(
&mut self,
track_index: usize,
entry_animation_id: u64,
animation: Animation,
looped: bool,
) -> TrackEntryHandle {
self.ensure_track(track_index);
let (old_current, queued_entries) = {
let track = &mut self.tracks[track_index];
let old_current = track.current.take();
let queued_entries = track.queue.drain(..).collect::<Vec<_>>();
(old_current, queued_entries)
};
if let Some(old_current) = old_current
&& let Some(entry) = self.entry_mut(old_current)
{
entry.next = None;
}
let entry_id = self.alloc_entry(TrackEntry::new(
track_index,
entry_animation_id,
&animation,
looped,
));
let mut previous_for_mix = old_current;
let mut interrupt_previous = true;
let mut dispose_old_immediately = false;
if let Some(old) = old_current {
let old_is_unapplied = self
.entry(old)
.is_some_and(|entry| entry.next_track_last_time == -1.0);
let old_is_same_animation = self
.entry(old)
.is_some_and(|entry| entry.animation_identity == entry_animation_id);
if old_is_unapplied && old_is_same_animation {
dispose_old_immediately = true;
previous_for_mix = self.entry(old).and_then(|entry| entry.mixing_from);
interrupt_previous = false;
}
}
if let Some(previous) = previous_for_mix {
let previous_name = self
.entry(previous)
.map(|entry| entry.animation.name.as_str())
.unwrap_or(EMPTY_ANIMATION_NAME);
let mix_duration = self
.data
.mix_duration(previous_name, animation.name.as_str());
if let Some(entry_ref) = self.entry_mut(entry_id) {
entry_ref.mix_duration = mix_duration;
entry_ref.mixing_from = Some(previous);
entry_ref.mix_time = 0.0;
}
if let Some(prev) = self.entry_mut(previous) {
prev.mixing_to = Some(entry_id);
prev.rotation_state.clear();
}
}
self.tracks[track_index].current = Some(entry_id);
if dispose_old_immediately {
if let Some(old) = old_current {
push_event(&mut self.event_queue, old, AnimationStateEvent::Interrupt);
push_event(&mut self.event_queue, old, AnimationStateEvent::End);
self.animations_changed = true;
}
for queued in queued_entries {
push_event(&mut self.event_queue, queued, AnimationStateEvent::Dispose);
}
} else {
for queued in queued_entries {
push_event(&mut self.event_queue, queued, AnimationStateEvent::Dispose);
}
if let Some(old) = old_current
&& interrupt_previous
{
push_event(&mut self.event_queue, old, AnimationStateEvent::Interrupt);
}
}
push_event(&mut self.event_queue, entry_id, AnimationStateEvent::Start);
self.animations_changed = true;
self.drain_event_queue();
self.handle(entry_id)
}
pub fn add_animation(
&mut self,
track_index: usize,
animation: impl AnimationInput,
looped: bool,
delay: f32,
) -> TrackEntryHandle {
let (animation_id, animation) = animation.into_animation(self);
self.add_animation_internal(track_index, animation_id, animation, looped, delay)
}
fn add_animation_internal(
&mut self,
track_index: usize,
animation_id: u64,
animation: Animation,
looped: bool,
delay: f32,
) -> TrackEntryHandle {
self.ensure_track(track_index);
let last = {
let track = &self.tracks[track_index];
track.queue.back().copied().or(track.current)
};
let entry_id = self.alloc_entry(TrackEntry::new(
track_index,
animation_id,
&animation,
looped,
));
if let Some(entry_ref) = self.entry_mut(entry_id) {
entry_ref.previous = last;
}
if let Some(last) = last
&& let Some(last_entry) = self.entry_mut(last)
{
last_entry.next = Some(entry_id);
}
let mix_duration = self.mix_duration_from_last(last, animation.name.as_str());
let entry = self.handle(entry_id);
let track_empty = self.tracks[track_index].current.is_none();
if track_empty {
self.tracks[track_index].current = Some(entry_id);
push_event(&mut self.event_queue, entry_id, AnimationStateEvent::Start);
self.animations_changed = true;
self.drain_event_queue();
entry.set_mix_duration_with_delay(self, mix_duration, delay);
} else {
entry.set_mix_duration_with_delay(self, mix_duration, delay);
self.tracks[track_index].queue.push_back(entry_id);
}
self.handle(entry_id)
}
pub fn add_empty_animation(
&mut self,
track_index: usize,
mix_duration: f32,
delay: f32,
) -> TrackEntryHandle {
self.ensure_track(track_index);
let last = {
let track = &self.tracks[track_index];
track.queue.back().copied().or(track.current)
};
let animation = empty_animation();
let entry_id = self.alloc_entry(TrackEntry::new(
track_index,
EMPTY_ANIMATION_ID,
&animation,
false,
));
if let Some(entry_ref) = self.entry_mut(entry_id) {
entry_ref.previous = last;
}
if let Some(last) = last
&& let Some(last_entry) = self.entry_mut(last)
{
last_entry.next = Some(entry_id);
}
let mix_duration_to_empty = self.mix_duration_from_last(last, EMPTY_ANIMATION_NAME);
let entry = self.handle(entry_id);
let track_empty = self.tracks[track_index].current.is_none();
if track_empty {
self.tracks[track_index].current = Some(entry_id);
push_event(&mut self.event_queue, entry_id, AnimationStateEvent::Start);
self.animations_changed = true;
self.drain_event_queue();
entry.set_mix_duration_with_delay(self, mix_duration_to_empty, delay);
} else {
entry.set_mix_duration_with_delay(self, mix_duration_to_empty, delay);
self.tracks[track_index].queue.push_back(entry_id);
}
if delay <= 0.0
&& let Some(entry_ref) = self.entry_mut(entry_id)
{
entry_ref.delay = (entry_ref.delay + entry_ref.mix_duration - mix_duration).max(0.0);
}
if let Some(entry_ref) = self.entry_mut(entry_id) {
entry_ref.mix_duration = mix_duration;
entry_ref.track_end = mix_duration;
}
self.handle(entry_id)
}
pub fn update(&mut self, delta: f32) {
let delta = delta * self.time_scale;
let mut pending = VecDeque::new();
let tracks_len = self.tracks.len();
for track_index in 0..tracks_len {
let Some(current_id) = self.tracks[track_index].current else {
continue;
};
let (current_delta, track_last, mixing_from, track_end) = {
let Some(current) = self.entry_mut(current_id) else {
self.tracks[track_index].current = None;
continue;
};
current.animation_last_time = current.next_animation_last_time;
current.track_last_time = current.next_track_last_time;
let mut current_delta = delta * current.time_scale;
if current.delay > 0.0 {
current.delay -= current_delta;
if current.delay > 0.0 {
continue;
}
current_delta = -current.delay;
current.delay = 0.0;
}
(
current_delta,
current.track_last_time,
current.mixing_from,
current.track_end,
)
};
if let Some(next_id) = self.tracks[track_index].queue.front().copied() {
let next_delay = self.entry(next_id).map(|next| next.delay).unwrap_or(0.0);
let next_time = track_last - next_delay;
if next_time >= 0.0 {
let old_time_scale =
self.entry(current_id).map(|e| e.time_scale).unwrap_or(0.0);
if let Some(current) = self.entry_mut(current_id) {
current.track_time += current_delta;
}
let next_id = self.tracks[track_index]
.queue
.pop_front()
.expect("queue front existed");
if let Some(next) = self.entry_mut(next_id) {
next.delay = 0.0;
next.previous = None;
if old_time_scale != 0.0 {
next.track_time +=
(next_time / old_time_scale + delta) * next.time_scale;
}
next.mixing_from = Some(current_id);
next.mix_time = 0.0;
}
if let Some(current) = self.entry_mut(current_id) {
current.next = None;
current.mixing_to = Some(next_id);
current.rotation_state.clear();
}
let mut mix_id = Some(next_id);
while let Some(id) = mix_id {
let Some(from_id) = self.entry(id).and_then(|e| e.mixing_from) else {
break;
};
if let Some(entry) = self.entry_mut(id) {
entry.mix_time += delta;
}
mix_id = Some(from_id);
}
push_event(&mut pending, current_id, AnimationStateEvent::Interrupt);
push_event(&mut pending, next_id, AnimationStateEvent::Start);
self.animations_changed = true;
self.tracks[track_index].current = Some(next_id);
continue;
}
} else if mixing_from.is_none() && track_last >= track_end {
push_event(&mut pending, current_id, AnimationStateEvent::End);
self.animations_changed = true;
self.tracks[track_index].current = None;
continue;
}
if mixing_from.is_some() && self.update_mixing_from(current_id, delta, &mut pending) {
let mut from = self.entry_mut(current_id).and_then(|entry| {
let from = entry.mixing_from;
entry.mixing_from = None;
from
});
if let Some(from_id) = from
&& let Some(entry) = self.entry_mut(from_id)
{
entry.mixing_to = None;
}
while let Some(from_id) = from {
from = self.entry(from_id).and_then(|entry| entry.mixing_from);
push_event(&mut pending, from_id, AnimationStateEvent::End);
}
}
if let Some(current) = self.entry_mut(current_id) {
current.track_time += current_delta;
}
}
self.event_queue.append(&mut pending);
self.drain_event_queue();
}
pub fn apply(&mut self, skeleton: &mut Skeleton) -> bool {
if self.animations_changed {
self.animations_changed();
}
let mut applied = false;
let mut pending = VecDeque::new();
let current_ids = self
.tracks
.iter()
.filter_map(|track| track.current)
.collect::<Vec<_>>();
for current_id in current_ids {
let (track_index, delay) = match self.entry(current_id) {
Some(entry) => (entry.track_index, entry.delay),
None => continue,
};
if delay > 0.0 {
continue;
}
applied = true;
let blend = if track_index == 0 {
MixBlend::First
} else {
MixBlend::Replace
};
let mut alpha = self.entry(current_id).map(|e| e.alpha).unwrap_or(1.0);
if self.entry(current_id).and_then(|e| e.mixing_from).is_some() {
alpha *= self.apply_mixing_from_pose(current_id, skeleton, blend, &mut pending);
} else {
let track_end_reached = {
let track = &self.tracks[track_index];
let queued_empty = track.queue.is_empty();
let reached = self
.entry(current_id)
.is_some_and(|e| e.track_time >= e.track_end);
queued_empty && reached
};
if track_end_reached {
alpha = 0.0;
}
}
let (animation, time, alpha_attachment_threshold, reverse) =
match self.entry(current_id) {
Some(e) => (
e.animation.clone(),
e.get_animation_time(),
e.alpha_attachment_threshold,
e.reverse,
),
None => continue,
};
let apply_time = if reverse {
animation.duration - time
} else {
time
};
let mut attachments = alpha >= alpha_attachment_threshold;
if track_index == 0 && alpha == 1.0 {
attachments = true;
}
self.apply_entry_pose(
current_id,
&animation,
skeleton,
apply_time,
alpha,
blend,
attachments,
);
self.apply_entry_events_and_complete(current_id, None, true, &mut pending);
}
let setup_state = self.unkeyed_state + ANIMATION_STATE_SETUP;
for (i, slot) in skeleton.slots.iter_mut().enumerate() {
if slot.attachment_state == setup_state {
slot.attachment = skeleton
.data
.slots
.get(i)
.and_then(|s| s.attachment.clone());
}
}
self.unkeyed_state = self.unkeyed_state.wrapping_add(2);
self.event_queue.append(&mut pending);
self.drain_event_queue();
applied
}
#[allow(clippy::too_many_arguments)]
fn apply_entry_pose(
&mut self,
entry_id: EntryId,
animation: &Animation,
skeleton: &mut Skeleton,
time: f32,
alpha: f32,
blend: MixBlend,
attachments: bool,
) {
let (entry_additive, shortest_rotation) = self
.entry(entry_id)
.map(|e| (e.additive, e.additive || e.shortest_rotation))
.unwrap_or((false, false));
let track_index = self.entry(entry_id).map(|e| e.track_index).unwrap_or(0);
let special_case = track_index == 0 && alpha == 1.0;
let kinds = animation.timeline_order().to_vec();
let mut timeline_mode = self
.entry(entry_id)
.map(|e| e.timeline_mode.clone())
.unwrap_or_default();
if timeline_mode.len() != kinds.len() {
self.animations_changed = true;
self.animations_changed();
timeline_mode = self
.entry(entry_id)
.map(|e| e.timeline_mode.clone())
.unwrap_or_default();
}
let first_frame = self
.entry_mut(entry_id)
.map(|entry| {
let expected_len = kinds.len() * 2;
let first = entry.rotation_state.len() != expected_len;
if first {
entry.rotation_state.resize(expected_len, 0.0);
}
first
})
.unwrap_or(false);
let unkeyed_state = self.unkeyed_state;
let physics_last_time = self
.entry(entry_id)
.map(|e| e.animation_last_time)
.unwrap_or(-1.0);
for (i, kind) in kinds.into_iter().enumerate() {
let mode = timeline_mode.get(i).copied().unwrap_or(TimelineApplyMode {
from: MixFrom::First,
hold: false,
});
let timeline_blend = if special_case {
blend
} else {
match mode.from {
MixFrom::Current => blend,
MixFrom::Setup => MixBlend::Setup,
MixFrom::First => MixBlend::First,
}
};
let effective_from = if special_case {
MixFrom::Setup
} else {
mode.from
};
let effective_add = entry_additive && !special_case;
let additive_blend = if effective_add && timeline_blend != MixBlend::Setup {
MixBlend::Add
} else {
timeline_blend
};
let from = effective_from;
let uses_mixed_rotation = !shortest_rotation
&& !special_case
&& alpha < 1.0
&& additive_blend != MixBlend::Add
&& matches!(
kind,
TimelineKind::Bone(ti)
if matches!(&animation.bone_timelines[ti], crate::BoneTimeline::Rotate(_))
);
let rotate = if uses_mixed_rotation {
let Some(entry) = self.entry_mut(entry_id) else {
continue;
};
Some(RotateTimelineState {
state: entry.rotation_state.as_mut_slice(),
index: i,
first_frame,
})
} else {
None
};
apply_state_timeline(
animation,
kind,
skeleton,
StateTimelineApply {
time,
alpha,
timeline_blend,
additive_blend,
from,
additive: effective_add,
transform_additive: entry_additive,
direction: MixDirection::In,
attachments,
unkeyed_state,
draw_order_out: false,
physics_last_time,
physics_time: time,
rotate,
},
);
}
}
fn apply_mixing_from_pose(
&mut self,
to: EntryId,
skeleton: &mut Skeleton,
blend: MixBlend,
out: &mut VecDeque<QueuedEvent>,
) -> f32 {
let Some(from) = self.entry(to).and_then(|entry| entry.mixing_from) else {
return 1.0;
};
let from_mix = if self
.entry(from)
.and_then(|entry| entry.mixing_from)
.is_some()
{
self.apply_mixing_from_pose(from, skeleton, blend, out)
} else {
1.0
};
let (mix_duration, mix, to_alpha) = self
.entry(to)
.map(|to_ref| (to_ref.mix_duration, to_ref.mix_percent(), to_ref.alpha))
.unwrap_or((0.0, 1.0, 1.0));
let (
from_animation,
from_time,
from_reverse,
from_additive,
from_shortest_rotation,
from_alpha,
from_thresholds,
) = match self.entry(from) {
Some(from_ref) => (
from_ref.animation.clone(),
from_ref.get_animation_time(),
from_ref.reverse,
from_ref.additive,
from_ref.additive || from_ref.shortest_rotation,
from_ref.alpha,
(
from_ref.alpha_attachment_threshold,
from_ref.mix_attachment_threshold,
from_ref.mix_draw_order_threshold,
),
),
None => return 1.0,
};
let from_blend = if mix_duration == 0.0 && blend == MixBlend::First {
MixBlend::Setup
} else {
blend
};
let a = from_alpha * from_mix;
let keep = 1.0 - mix * to_alpha;
let alpha_mix = a * (1.0 - mix);
let alpha_hold = if keep > 0.0 { alpha_mix / keep } else { a };
if let Some(from_entry) = self.entry_mut(from) {
from_entry.total_alpha = 0.0;
}
let attachments = mix < from_thresholds.1;
let draw_order = mix < from_thresholds.2;
let from_apply_time = if from_reverse {
from_animation.duration - from_time
} else {
from_time
};
{
let kinds = from_animation.timeline_order().to_vec();
let (timeline_mode, timeline_hold_mix) = match self.entry(from) {
Some(e) => (e.timeline_mode.clone(), e.timeline_hold_mix.clone()),
None => (Vec::new(), Vec::new()),
};
let alpha_attachment_threshold = from_thresholds.0;
let first_frame = self
.entry_mut(from)
.map(|entry| {
let expected_len = kinds.len() * 2;
let first = entry.rotation_state.len() != expected_len;
if first {
entry.rotation_state.resize(expected_len, 0.0);
}
first
})
.unwrap_or(false);
let unkeyed_state = self.unkeyed_state;
let mut total_alpha = 0.0f32;
for (i, kind) in kinds.into_iter().enumerate() {
let mode = timeline_mode.get(i).copied().unwrap_or(TimelineApplyMode {
from: MixFrom::First,
hold: false,
});
let timeline_blend = match mode.from {
MixFrom::Current => from_blend,
MixFrom::Setup => MixBlend::Setup,
MixFrom::First => MixBlend::First,
};
let alpha = if mode.hold {
let hold_mix = timeline_hold_mix.get(i).copied().flatten();
if let Some(hold_mix) = hold_mix {
let factor = self
.entry(hold_mix)
.map(|e| 1.0 - e.mix_percent())
.unwrap_or(0.0);
alpha_hold * factor
} else {
alpha_hold
}
} else {
alpha_mix
};
if !mode.hold
&& !draw_order
&& kind == TimelineKind::DrawOrder
&& mode.from == MixFrom::Current
{
continue;
}
total_alpha += alpha;
let additive_blend = if from_additive && timeline_blend != MixBlend::Setup {
MixBlend::Add
} else {
timeline_blend
};
let from_mode = mode.from;
let physics_last_time = self
.entry(from)
.map(|e| e.animation_last_time)
.unwrap_or(-1.0);
let physics_time = from_apply_time;
let uses_mixed_rotation = !from_shortest_rotation
&& alpha < 1.0
&& additive_blend != MixBlend::Add
&& matches!(
kind,
TimelineKind::Bone(ti)
if matches!(&from_animation.bone_timelines[ti], crate::BoneTimeline::Rotate(_))
);
let rotate = if uses_mixed_rotation {
let Some(entry) = self.entry_mut(from) else {
continue;
};
Some(RotateTimelineState {
state: entry.rotation_state.as_mut_slice(),
index: i,
first_frame,
})
} else {
None
};
apply_state_timeline(
&from_animation,
kind,
skeleton,
StateTimelineApply {
time: from_apply_time,
alpha,
timeline_blend,
additive_blend,
from: from_mode,
additive: from_additive,
transform_additive: from_additive,
direction: MixDirection::Out,
attachments: attachments && alpha >= alpha_attachment_threshold,
unkeyed_state,
draw_order_out: !draw_order || mode.from == MixFrom::Current,
physics_last_time,
physics_time,
rotate,
},
);
}
if let Some(from_entry) = self.entry_mut(from) {
from_entry.total_alpha = total_alpha;
}
}
if mix_duration > 0.0 {
self.apply_entry_events_and_complete(from, Some(mix), true, out);
} else if let Some(from_ref) = self.entry_mut(from) {
let animation_time = from_ref.get_animation_time();
from_ref.next_animation_last_time = animation_time;
from_ref.next_track_last_time = from_ref.track_time;
}
mix
}
pub fn clear_track(&mut self, track_index: usize) {
self.clear_track_internal(track_index);
self.drain_event_queue();
}
pub fn clear_tracks(&mut self) {
let tracks_len = self.tracks.len();
for i in 0..tracks_len {
self.clear_track_internal(i);
}
self.tracks.clear();
self.drain_event_queue();
}
fn ensure_track(&mut self, track_index: usize) {
if track_index >= self.tracks.len() {
self.tracks.resize_with(track_index + 1, Track::default);
}
}
fn alloc_entry(&mut self, entry: TrackEntry) -> EntryId {
if let Some(index) = self.free_list.pop() {
let slot = &mut self.entries[index];
slot.entry = Some(entry);
EntryId {
index,
generation: slot.generation,
}
} else {
let index = self.entries.len();
self.entries.push(EntrySlot {
generation: 0,
entry: Some(entry),
});
EntryId {
index,
generation: 0,
}
}
}
fn entry(&self, id: EntryId) -> Option<&TrackEntry> {
let slot = self.entries.get(id.index)?;
if slot.generation != id.generation {
return None;
}
slot.entry.as_ref()
}
fn entry_mut(&mut self, id: EntryId) -> Option<&mut TrackEntry> {
let slot = self.entries.get_mut(id.index)?;
if slot.generation != id.generation {
return None;
}
slot.entry.as_mut()
}
fn free_entry(&mut self, id: EntryId) {
let Some(slot) = self.entries.get_mut(id.index) else {
return;
};
if slot.generation != id.generation {
return;
}
slot.entry = None;
slot.generation = slot.generation.wrapping_add(1);
self.free_list.push(id.index);
}
fn take_entry_listener(&mut self, id: EntryId) -> Option<Box<dyn TrackEntryListener>> {
self.entry_mut(id).and_then(|entry| entry.listener.take())
}
fn restore_entry_listener(&mut self, id: EntryId, listener: Box<dyn TrackEntryListener>) {
if let Some(entry) = self.entry_mut(id)
&& entry.listener.is_none()
{
entry.listener = Some(listener);
}
}
fn update_mixing_from(
&mut self,
to: EntryId,
delta: f32,
out: &mut VecDeque<QueuedEvent>,
) -> bool {
let Some(from) = self.entry(to).and_then(|entry| entry.mixing_from) else {
return true;
};
let finished = self.update_mixing_from(from, delta, out);
if let Some(from_entry) = self.entry_mut(from) {
from_entry.animation_last_time = from_entry.next_animation_last_time;
from_entry.track_last_time = from_entry.next_track_last_time;
}
let (to_next_track_last, to_mix_time, to_mix_duration) = self
.entry(to)
.map(|to_ref| {
(
to_ref.next_track_last_time,
to_ref.mix_time,
to_ref.mix_duration,
)
})
.unwrap_or((-1.0, 0.0, 0.0));
if to_next_track_last != -1.0 && to_mix_time >= to_mix_duration {
let from_total_alpha = self.entry(from).map(|e| e.total_alpha).unwrap_or(0.0);
if to_mix_duration == 0.0 || from_total_alpha == 0.0 {
let next_from = self.entry(from).and_then(|from_ref| from_ref.mixing_from);
if let Some(to_entry) = self.entry_mut(to) {
to_entry.mixing_from = next_from;
}
if let Some(next_from) = next_from
&& let Some(entry) = self.entry_mut(next_from)
{
entry.mixing_to = Some(to);
}
if from_total_alpha == 0.0 {
let mut keep_id = Some(to);
while let Some(entry_id) = keep_id {
let Some(next_id) = self.entry(entry_id).and_then(|entry| entry.mixing_to)
else {
break;
};
if let Some(entry) = self.entry_mut(entry_id) {
entry.keep_hold = true;
}
keep_id = Some(next_id);
}
}
if let Some(from_entry) = self.entry_mut(from) {
from_entry.mixing_to = None;
from_entry.mixing_from = None;
}
push_event(out, from, AnimationStateEvent::End);
self.animations_changed = true;
}
return finished;
}
if let Some(from_entry) = self.entry_mut(from) {
from_entry.track_time += delta * from_entry.time_scale;
}
if let Some(to_entry) = self.entry_mut(to) {
to_entry.mix_time += delta;
}
false
}
fn apply_entry_events_and_complete(
&mut self,
entry_id: EntryId,
mix: Option<f32>,
events_enabled: bool,
out: &mut VecDeque<QueuedEvent>,
) {
let Some(entry) = self.entry(entry_id) else {
return;
};
let animation_start = entry.animation_start;
let animation_end = entry.animation_end;
let duration = animation_end - animation_start;
let animation_time = entry.get_animation_time();
let animation_last = entry.animation_last_time;
let track_last = entry.track_last_time;
let track_time = entry.track_time;
let reverse = entry.reverse;
let can_issue_events = match mix {
None => true,
Some(mix) => mix < entry.event_threshold,
};
let mut events = Vec::new();
if events_enabled
&& can_issue_events
&& let Some(timeline) = &entry.animation.event_timeline
{
if reverse {
collect_events_reverse(
timeline,
entry.animation.duration,
animation_last,
animation_time,
&mut events,
);
} else {
collect_events(timeline, animation_last, animation_time, &mut events);
}
}
let complete = if entry.looped {
if duration == 0.0 {
true
} else {
let cycles = (track_time / duration) as i32;
cycles > 0 && cycles > (track_last / duration) as i32
}
} else {
animation_time >= animation_end && animation_last < animation_end
};
let track_last_wrapped = if duration != 0.0 {
track_last % duration
} else {
0.0
};
let mut split = track_last_wrapped;
if reverse {
split = duration - split;
}
let mut split_index = events.len();
for (i, ev) in events.iter().enumerate() {
let event_time = ev.get_time();
if (event_time < split) != reverse {
split_index = i;
break;
}
if event_time >= animation_start && event_time <= animation_end {
push_event(out, entry_id, AnimationStateEvent::Event(ev.clone()));
}
}
if complete {
push_event(out, entry_id, AnimationStateEvent::Complete);
}
for ev in &events[split_index..] {
let event_time = ev.get_time();
if event_time >= animation_start && event_time <= animation_end {
push_event(out, entry_id, AnimationStateEvent::Event(ev.clone()));
}
}
if let Some(entry) = self.entry_mut(entry_id) {
entry.next_animation_last_time = animation_time;
entry.next_track_last_time = track_time;
}
}
fn clear_track_internal(&mut self, track_index: usize) {
if track_index >= self.tracks.len() {
return;
}
let (current, queued) = {
let track = &mut self.tracks[track_index];
let current = track.current.take();
let queued = if current.is_some() {
track.queue.drain(..).collect::<Vec<_>>()
} else {
Vec::new()
};
(current, queued)
};
if let Some(entry_id) = current {
let mut from = self.entry_mut(entry_id).and_then(|entry| {
let from = entry.mixing_from;
entry.mixing_from = None;
entry.mixing_to = None;
entry.next = None;
from
});
push_event(&mut self.event_queue, entry_id, AnimationStateEvent::End);
self.animations_changed = true;
for entry in queued {
push_event(&mut self.event_queue, entry, AnimationStateEvent::Dispose);
}
while let Some(mixing_from) = from {
from = self.entry_mut(mixing_from).and_then(|entry| {
let from = entry.mixing_from;
entry.mixing_from = None;
entry.mixing_to = None;
entry.next = None;
from
});
push_event(&mut self.event_queue, mixing_from, AnimationStateEvent::End);
}
}
}
fn drain_event_queue(&mut self) {
if self.drain_disabled {
return;
}
self.drain_disabled = true;
while let Some(queued) = self.event_queue.pop_front() {
let entry_id = queued.entry;
let event = queued.event;
let handle = self.handle(entry_id);
match event {
AnimationStateEvent::End => {
let entry_listener =
self.notify_event_listeners(entry_id, handle, &AnimationStateEvent::End);
self.restore_entry_listener_after_event(entry_id, entry_listener);
let handle = self.handle(entry_id);
let entry_listener = self.notify_event_listeners(
entry_id,
handle,
&AnimationStateEvent::Dispose,
);
self.finish_dispose_event(entry_id, entry_listener);
}
AnimationStateEvent::Dispose => {
let entry_listener = self.notify_event_listeners(
entry_id,
handle,
&AnimationStateEvent::Dispose,
);
self.finish_dispose_event(entry_id, entry_listener);
}
event => {
let entry_listener = self.notify_event_listeners(entry_id, handle, &event);
self.restore_entry_listener_after_event(entry_id, entry_listener);
}
}
}
self.drain_disabled = false;
}
fn notify_event_listeners(
&mut self,
entry_id: EntryId,
entry: TrackEntryHandle,
event: &AnimationStateEvent,
) -> Option<Box<dyn TrackEntryListener>> {
let mut entry_listener = self.take_entry_listener(entry_id);
if let Some(listener) = entry_listener.as_mut() {
listener.on_event(self, entry, event);
}
let mut state_listener = self.listener.take();
if let Some(listener) = state_listener.as_mut() {
listener.on_event(self, entry, event);
}
if self.listener.is_none() {
self.listener = state_listener;
}
entry_listener
}
fn restore_entry_listener_after_event(
&mut self,
entry_id: EntryId,
entry_listener: Option<Box<dyn TrackEntryListener>>,
) {
if let Some(listener) = entry_listener {
self.restore_entry_listener(entry_id, listener);
}
}
fn finish_dispose_event(
&mut self,
entry_id: EntryId,
entry_listener: Option<Box<dyn TrackEntryListener>>,
) {
if self.manual_track_entry_disposal {
self.restore_entry_listener_after_event(entry_id, entry_listener);
} else {
self.free_entry(entry_id);
}
}
}
fn push_event(out: &mut VecDeque<QueuedEvent>, entry: EntryId, event: AnimationStateEvent) {
out.push_back(QueuedEvent { entry, event });
}
pub(super) fn collect_events(
timeline: &crate::EventTimeline,
last_time: f32,
time: f32,
out: &mut Vec<Event>,
) {
if timeline.get_events().is_empty() {
return;
}
let mut emit_range = |from: f32, to: f32| {
for ev in timeline.get_events() {
if ev.get_time() > from && ev.get_time() <= to {
out.push(ev.clone());
}
}
};
if last_time > time {
emit_range(last_time, f32::MAX);
emit_range(-1.0, time);
} else {
emit_range(last_time, time);
}
}
pub(super) fn collect_events_reverse(
timeline: &crate::EventTimeline,
duration: f32,
animation_last: f32,
animation_time: f32,
out: &mut Vec<Event>,
) {
if timeline.get_events().is_empty() {
return;
}
let from = duration - animation_last;
let to = duration - animation_time;
if from >= to {
for ev in timeline.get_events() {
if ev.get_time() < to {
continue;
}
if ev.get_time() >= from {
break;
}
out.push(ev.clone());
}
} else {
for ev in timeline.get_events() {
if ev.get_time() >= from {
break;
}
out.push(ev.clone());
}
let mut index = 0usize;
let events = timeline.get_events();
while index < events.len() && events[index].get_time() < to {
index += 1;
}
for ev in &events[index..] {
out.push(ev.clone());
}
}
}