Skip to content

Commit

Permalink
fix: bug fixes with looping
Browse files Browse the repository at this point in the history
  • Loading branch information
Spencer C. Imbleau committed Feb 10, 2024
1 parent 28c4359 commit 1491920
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 75 deletions.
35 changes: 16 additions & 19 deletions demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ fn setup_vector_graphics(mut commands: Commands, asset_server: ResMut<AssetServe
PlayerState::new("play")
.with_transition(PlayerTransition::OnMouseLeave { state: "rev" })
.with_playback_settings(PlaybackSettings {
looping: PlaybackLoopBehavior::Once,
looping: PlaybackLoopBehavior::Amount(3),
..default()
}),
)
.with_state(
PlayerState::new("rev")
.with_playback_settings(PlaybackSettings {
direction: PlaybackDirection::Reverse,
looping: PlaybackLoopBehavior::Once,
looping: PlaybackLoopBehavior::Amount(1),
..default()
})
.with_transition(PlayerTransition::OnComplete { state: "stopped" }),
Expand Down Expand Up @@ -117,7 +117,7 @@ fn ui(
)>,
assets: Res<Assets<VelloAsset>>,
) {
let Ok((mut player, mut playhead, mut playback_settings, mut color_swaps, handle)) =
let Ok((mut player, mut playhead, mut playback_settings, mut theme, handle)) =
player.get_single_mut()
else {
return;
Expand All @@ -140,21 +140,18 @@ fn ui(
let mut frame = playhead.frame();
ui.label("Playhead");
if ui
.add(
egui::Slider::new(
&mut frame,
playback_settings
.add(egui::Slider::new(
&mut frame,
playback_settings
.segments
.start
.max(composition.frames.start)
..=playback_settings
.segments
.start
.max(composition.frames.start)
..=playback_settings
.segments
.end
.min(composition.frames.end)
.prev(),
)
.integer(),
)
.end
.min(composition.frames.end)
.prev(),
))
.changed()
{
player.pause();
Expand Down Expand Up @@ -230,15 +227,15 @@ fn ui(

ui.heading("Theme");
for layer in metadata.get_layers() {
let color = color_swaps.get_mut(layer).cloned().unwrap_or_default();
let color = theme.get_mut(layer).cloned().unwrap_or_default();
let mut color_edit = [color.r(), color.g(), color.b(), color.a()];
ui.horizontal(|ui| {
if ui
.color_edit_button_rgba_unmultiplied(&mut color_edit)
.changed()
{
let [r, g, b, a] = color_edit;
color_swaps.edit(layer, Color::rgba(r, g, b, a));
theme.edit(layer, Color::rgba(r, g, b, a));
};
ui.label(layer);
});
Expand Down
11 changes: 11 additions & 0 deletions src/playback/playback_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ use std::ops::Range;
///
/// You can add this component directly to a `VelloAssetBundle` entity to adjust playback settings.
pub struct PlaybackSettings {
/// Whether to automatically start the animation.
pub autoplay: bool,
/// The direction of the animation.
pub direction: PlaybackDirection,
/// The speed of the animation as a multiplier. 1.0 is normal speed. Anything less than 1 is slower, and anything greater than 1 is faster.
pub speed: f32,
/// An amount of frames spent idle between loops.
pub intermission: f32,
/// Whether to loop, and how many.
pub looping: PlaybackLoopBehavior,
/// The segments (frames) of the animation to play. Values out of range will be ignored.
pub segments: Range<f32>,
}

Expand All @@ -31,16 +37,21 @@ impl Default for PlaybackSettings {
/// The direction to play the segments of a lottie animation.
#[derive(PartialEq, Component, Default, Clone, Copy, Debug, Reflect)]
pub enum PlaybackDirection {
/// Play in the default direction, first frame to last frame.
#[default]
Normal = 1,
/// Play in the reverse direction, last frame to first frame.
Reverse = -1,
}

/// How often to loop.
#[derive(PartialEq, Component, Default, Clone, Copy, Debug, Reflect)]
pub enum PlaybackLoopBehavior {
/// Do not loop. This is equivalent to `PlaybackLoopBehavior::Amount(0)`.
Once,
/// Complete a specified number of loops.
Amount(usize),
/// Loop continuously.
#[default]
Loop,
}
7 changes: 0 additions & 7 deletions src/playback/playhead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,4 @@ impl Playhead {
loops_completed: 0,
}
}

pub(crate) fn reset_to(&mut self, frame: f32) {
self.frame = frame;
self.intermission_frame = 0.0;
self.loops_completed = 0;
self.first_render.take();
}
}
98 changes: 54 additions & 44 deletions src/player/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,6 @@ pub mod systems {
};

let playback_settings = playback_settings.cloned().unwrap_or_default();
let start_frame = playback_settings
.segments
.start
.max(composition.frames.start);
let end_frame = playback_settings
.segments
.end
.min(composition.frames.end)
.prev();

if let Some(mut player) = player {
if player.stopped {
continue;
Expand All @@ -103,6 +93,16 @@ pub mod systems {
}
}

let start_frame = playback_settings
.segments
.start
.max(composition.frames.start);
let end_frame = playback_settings
.segments
.end
.min(composition.frames.end)
.prev();

// Advance playhead
playhead.frame += time.delta_seconds()
* playback_settings.speed
Expand All @@ -112,22 +112,22 @@ pub mod systems {
// Keep the playhead bounded between segments
let looping = match playback_settings.looping {
PlaybackLoopBehavior::Loop => true,
PlaybackLoopBehavior::Amount(amt) => amt < playhead.loops_completed,
PlaybackLoopBehavior::Amount(amt) => playhead.loops_completed < amt,
PlaybackLoopBehavior::Once => false,
};
if playhead.frame > end_frame {
playhead.loops_completed += 1;
if looping {
// Wrap around to the beginning of the segment
playhead.frame = start_frame + (playhead.frame - end_frame);
playhead.loops_completed += 1;
} else {
playhead.frame = end_frame;
}
} else if playhead.frame < start_frame {
playhead.loops_completed += 1;
if looping {
// Wrap around to the beginning of the segment
playhead.frame = end_frame - (start_frame - playhead.frame);
playhead.loops_completed += 1;
} else {
playhead.frame = start_frame;
}
Expand Down Expand Up @@ -204,14 +204,23 @@ pub mod systems {
}
PlayerTransition::OnComplete { state } => {
if let VectorFile::Lottie { composition } = &current_asset.data {
let loops_needed = match playback_settings.looping {
PlaybackLoopBehavior::Once => Some(0),
PlaybackLoopBehavior::Amount(amt) => Some(amt),
PlaybackLoopBehavior::Loop => Some(0),
};
match playback_settings.direction {
PlaybackDirection::Normal => {
let end_frame = playback_settings
.segments
.end
.min(composition.frames.end)
.prev();
if playhead.frame == end_frame || playhead.loops_completed > 0 {
if playhead.frame == end_frame
&& loops_needed.is_some_and(|needed| {
playhead.loops_completed >= needed
})
{
player.next_state = Some(state);
break;
}
Expand All @@ -221,7 +230,10 @@ pub mod systems {
.segments
.start
.max(composition.frames.start);
if playhead.frame == start_frame || playhead.loops_completed > 0
if playhead.frame == start_frame
&& loops_needed.is_some_and(|needed| {
playhead.loops_completed >= needed
})
{
player.next_state = Some(state);
break;
Expand Down Expand Up @@ -279,9 +291,6 @@ pub mod systems {
};
info!("animation controller transitioning to={next_state}");

player.started = false;
player.playing = false;

let target_state = player
.states
.get(&next_state)
Expand All @@ -294,10 +303,6 @@ pub mod systems {
return;
};

// Switch to asset
let changed_assets = cur_handle.id() != target_handle.id();
*cur_handle = target_handle.clone();

// Reset playhead state
match &mut asset.data {
VectorFile::Svg { original: _ } => {
Expand All @@ -306,37 +311,42 @@ pub mod systems {
VectorFile::Lottie { composition } => {
if player.state().reset_playhead_on_transition
|| target_state.reset_playhead_on_start
|| changed_assets
|| cur_handle.id() != target_handle.id()
{
let playback_settings =
target_state.playback_settings.clone().unwrap_or_default();
match playback_settings.direction {
PlaybackDirection::Normal => {
let start_frame = playback_settings
.segments
.start
.max(composition.frames.start);
playhead.reset_to(start_frame);
}
PlaybackDirection::Reverse => {
let end_frame = playback_settings
.segments
.end
.min(composition.frames.end)
.prev();
playhead.reset_to(end_frame);
}
}
let frame = match playback_settings.direction {
PlaybackDirection::Normal => playback_settings
.segments
.start
.max(composition.frames.start),
PlaybackDirection::Reverse => playback_settings
.segments
.end
.min(composition.frames.end)
.prev(),
};
// Reset playhead
playhead.frame = frame;
}
}
}

if let Some(theme) = target_state.theme.clone() {
commands.entity(entity).insert(theme);
}
// Swap asset, theme, playback settings
*cur_handle = target_handle.clone();
commands
.entity(entity)
.insert(target_state.playback_settings.clone().unwrap_or_default());
.insert(target_state.playback_settings.clone().unwrap_or_default())
.insert(target_state.theme.clone().unwrap_or_default());

// Reset playhead state
playhead.intermission_frame = 0.0;
playhead.loops_completed = 0;
playhead.first_render.take();

// Reset player state
player.started = false;
player.playing = false;
player.current_state = next_state;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use bevy::{
pub struct ExtractedRenderVector {
pub asset: VelloAsset,
pub transform: GlobalTransform,
pub color_swaps: Option<Theme>,
pub theme: Option<Theme>,
pub render_mode: CoordinateSpace,
pub playhead: f32,
pub alpha: f32,
Expand Down Expand Up @@ -44,7 +44,7 @@ pub fn vector_instances(
transform,
playhead,
playback_settings,
color_swaps,
theme,
alpha,
ui_node,
view_visibility,
Expand Down Expand Up @@ -83,7 +83,7 @@ pub fn vector_instances(
commands.spawn(ExtractedRenderVector {
asset: asset.to_owned(),
transform: *transform,
color_swaps: color_swaps.cloned(),
theme: theme.cloned(),
render_mode: *render_mode,
playhead,
alpha: alpha.map(|a| a.0).unwrap_or(1.0),
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub fn render_scene(
match render_item {
RenderItem::Vector(ExtractedRenderVector {
asset,
color_swaps,
theme,
alpha,
playhead,
..
Expand All @@ -94,7 +94,7 @@ pub fn render_scene(
debug!("playhead: {playhead}, t: {t}");
velottie_renderer.0.render(
{
color_swaps
theme
.as_ref()
.map(|cs| cs.recolor(composition))
.as_ref()
Expand Down

0 comments on commit 1491920

Please sign in to comment.