Skip to content

Commit

Permalink
refactor: strip back features
Browse files Browse the repository at this point in the history
  • Loading branch information
simbleau committed Feb 5, 2024
1 parent 2ffe4b3 commit 1a5b946
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 99 deletions.
18 changes: 1 addition & 17 deletions demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ fn ui(
ui.separator();

ui.heading("Player Operations");
ui.label("Note: Player operations apply to ALL states!");
ui.label("Note: Player operations only affect current playback!");
ui.horizontal(|ui| {
ui.label("Set Speed");
let mut speed = playback_settings.speed;
Expand All @@ -207,22 +207,6 @@ fn ui(
player.set_intermission(intermission);
};
});
ui.horizontal(|ui| {
ui.label("Set Loop Behavior");
let looping = playback_settings.looping;
if ui
.radio(looping == AnimationLoopBehavior::None, "None")
.clicked()
{
player.set_loop_behavior(AnimationLoopBehavior::None);
}
if ui
.radio(looping == AnimationLoopBehavior::Loop, "Loop")
.clicked()
{
player.set_loop_behavior(AnimationLoopBehavior::Loop);
}
});

ui.separator();

Expand Down
4 changes: 1 addition & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ pub use assets::{
};
pub use font::VelloFontLoader;
pub use lottie_player::{AnimationState, AnimationTransition, LottiePlayer};
pub use playback_settings::{
AnimationDirection, AnimationLoopBehavior, AnimationPlayMode, PlaybackSettings,
};
pub use playback_settings::{AnimationDirection, AnimationLoopBehavior, PlaybackSettings};
pub use plugin::VelloPlugin;
pub use rendertarget::VelloCanvasMaterial;
pub use theme::Theme;
Expand Down
121 changes: 50 additions & 71 deletions src/lottie_player.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{playback_settings::AnimationLoopBehavior, PlaybackSettings, Theme, VelloAsset};
use crate::{PlaybackSettings, Theme, VelloAsset};
use bevy::{prelude::*, utils::hashbrown::HashMap};

/// A lottie player that closely mirrors the behavior and functionality for dotLottie Interactivity.
Expand All @@ -18,8 +18,6 @@ pub struct LottiePlayer {
pending_seek_frame: Option<f32>,
/// A pending intermission to change to.
pending_intermission: Option<f32>,
/// A pending loop behavior to change to.
pending_loop_behavior: Option<AnimationLoopBehavior>,
/// A pending speed to change to.
pending_speed: Option<f32>,
/// Whether the player has started.
Expand All @@ -31,19 +29,26 @@ pub struct LottiePlayer {
}

impl LottiePlayer {
/// The current state.
/// Retrieve an immutable reference to the current state.
pub fn state(&self) -> &AnimationState {
self.states
.get(self.current_state)
.unwrap_or_else(|| panic!("state not found: '{}'", self.current_state))
}

/// The states in the player
/// Retrieve a mutable reference to the current state.
pub fn state_mut(&mut self) -> &mut AnimationState {
self.states
.get_mut(self.current_state)
.unwrap_or_else(|| panic!("state not found: '{}'", self.current_state))
}

/// Returns an immutable iterator of the states for this player.
pub fn states(&self) -> impl Iterator<Item = &AnimationState> {
self.states.values()
}

/// The states in the player
/// Returns a mutable iterator of the states for this player.
pub fn states_mut(&mut self) -> impl Iterator<Item = &mut AnimationState> {
self.states.values_mut()
}
Expand All @@ -64,17 +69,11 @@ impl LottiePlayer {
self.pending_seek_frame = Some(frame);
}

/// Sets the pause between loops. Applies to all animations.
/// Sets the pause between loops. Applies only to the current playback, not any underlying states.
pub fn set_intermission(&mut self, intermission: f32) {
self.pending_intermission = Some(intermission);
}

/// Sets the loop behavior. Applies to all animations.
pub fn set_loop_behavior(&mut self, loop_behavior: AnimationLoopBehavior) {
self.pending_loop_behavior = Some(loop_behavior);
}

/// Sets the animation speed. Applies to all animations.
/// Sets the animation speed. Applies only to the current playback, not any underlying states.
pub fn set_speed(&mut self, speed: f32) {
self.pending_speed = Some(speed);
}
Expand Down Expand Up @@ -121,7 +120,6 @@ impl LottiePlayer {
next_state: Some(initial_state),
pending_seek_frame: None,
pending_intermission: None,
pending_loop_behavior: None,
pending_speed: None,
states: HashMap::new(),
started: false,
Expand Down Expand Up @@ -266,7 +264,6 @@ pub mod systems {
};

if let Some(intermission) = player.pending_intermission.take() {
debug!("changed intermission: {intermission}");
// This math is particularly hairy. Several things are going on:
// 1) Preserve the loops completed thus far
// 2) Do not jump frames
Expand All @@ -285,90 +282,49 @@ pub mod systems {
&& *rendered_frames >= loops_completed * length
&& *rendered_frames < loops_completed * length + playback_settings.intermission;
if in_intermission {
debug!("in intermission, resetting to {intermission}");
*rendered_frames = (loops_completed * (length + intermission)).prev();
} else {
debug!("not in intermission, applying delta to {intermission}");
let dt_intermission = intermission - playback_settings.intermission;
let dt_frames = dt_intermission * loops_completed;
debug!("loops: {loops_completed}, dt_intermission: {dt_intermission}, dt_frames: {dt_frames}");
*rendered_frames = (*rendered_frames + dt_frames).max(0.0);
}
// Apply
for playback_settings in player
.states
.values_mut()
.flat_map(|s| s.playback_settings.as_mut())
.chain([playback_settings.as_mut()])
{
playback_settings.intermission = intermission;
}
}
if let Some(loop_behavior) = player.pending_loop_behavior.take() {
// Apply
for playback_settings in player
.states
.values_mut()
.flat_map(|s| s.playback_settings.as_mut())
.chain([playback_settings.as_mut()])
{
playback_settings.looping = loop_behavior;
}
playback_settings.intermission = intermission;
}
if let Some(seek_frame) = player.pending_seek_frame.take() {
let start_frame = playback_settings
.segments
.start
.max(composition.frames.start);
let end_frame = playback_settings.segments.end.min(composition.frames.end);
// Bound the seek frame
let bounded_frame = seek_frame.clamp(start_frame, end_frame.prev());
let seek_frame = match playback_settings.direction {
AnimationDirection::Normal => seek_frame.clamp(start_frame, end_frame),
AnimationDirection::Reverse => {
end_frame - seek_frame.clamp(start_frame, end_frame)
}
AnimationDirection::Normal => bounded_frame,
AnimationDirection::Reverse => end_frame - bounded_frame,
};
// Preserve the current number of loops when seeking.
let length = end_frame - start_frame + playback_settings.intermission;
let loops_completed = (*rendered_frames / length).trunc();
*rendered_frames = loops_completed * length + seek_frame;
}
if let Some(speed) = player.pending_speed.take() {
// Apply
for playback_settings in player
.states
.values_mut()
.flat_map(|s| s.playback_settings.as_mut())
.chain([playback_settings.as_mut()])
{
playback_settings.speed = speed;
error!("{}", playback_settings.speed);
}
playback_settings.speed = speed;
}
}
}

/// Advance all the playheads in the scene
pub fn advance_playheads(
mut query: Query<(&mut LottiePlayer, &PlaybackSettings, &Handle<VelloAsset>)>,
mut query: Query<(
&Handle<VelloAsset>,
Option<&mut LottiePlayer>,
Option<&PlaybackSettings>,
)>,
mut assets: ResMut<Assets<VelloAsset>>,
time: Res<Time>,
) {
let dt = time.delta_seconds();
for (mut player, playback_settings, asset_handle) in query.iter_mut() {
if player.stopped {
continue;
}
// Auto play
if playback_settings.autoplay && !player.started {
player.playing = true;
}
// Return if paused
if !player.playing {
continue;
}

// Continue, assuming we are currently playing.
for (asset_handle, player, playback_settings) in query.iter_mut() {
// Get asset
let Some(VelloAsset {
data:
VelloAssetData::Lottie {
Expand All @@ -382,6 +338,25 @@ pub mod systems {
continue;
};

let playback_settings = playback_settings.cloned().unwrap_or_default();
let Some(mut player) = player else {
*rendered_frames += dt * playback_settings.speed * composition.frame_rate;
return;
};

if player.stopped {
continue;
}
// Auto play
if playback_settings.autoplay && !player.started {
player.playing = true;
}
// Return if paused
if !player.playing {
continue;
}

// At this point, we are playing
if first_frame.is_none() {
first_frame.replace(Instant::now());
player.started = true;
Expand Down Expand Up @@ -505,8 +480,12 @@ pub mod systems {
buttons: Res<Input<MouseButton>>,
mut hovered: Local<bool>,
) {
let window = windows.single();
let (camera, view) = query_view.single();
let Ok(window) = windows.get_single() else {
return;
};
let Ok((camera, view)) = query_view.get_single() else {
return;
};

let pointer_pos = window
.cursor_position()
Expand Down
8 changes: 0 additions & 8 deletions src/playback_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,3 @@ pub enum AnimationLoopBehavior {
#[default]
Loop,
}

/// Play mode. When set to bounce, every other loop switches animation direction.
#[derive(PartialEq, Component, Default, Clone, Copy, Debug, Reflect)]
pub enum AnimationPlayMode {
#[default]
Normal,
Bounce,
}

0 comments on commit 1a5b946

Please sign in to comment.