Skip to content

Commit

Permalink
fix: looping
Browse files Browse the repository at this point in the history
  • Loading branch information
simbleau committed Feb 10, 2024
1 parent 2aa1779 commit 5cc0f20
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 51 deletions.
192 changes: 157 additions & 35 deletions demo/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::time::Duration;

use bevy::{asset::AssetMetaCheck, prelude::*};
use bevy_egui::{
egui::{self},
Expand Down Expand Up @@ -56,16 +58,19 @@ fn setup_vector_graphics(mut commands: Commands, asset_server: ResMut<AssetServe
.with_transition(PlayerTransition::OnMouseLeave { state: "rev" })
.with_playback_settings(PlaybackSettings {
looping: PlaybackLoopBehavior::DoNotLoop,
speed: 0.25,
..default()
}),
)
.with_state(
PlayerState::new("rev")
.with_playback_settings(PlaybackSettings {
looping: PlaybackLoopBehavior::DoNotLoop,
direction: PlaybackDirection::Reverse,
looping: PlaybackLoopBehavior::Amount(1),
speed: 0.25,
..default()
})
.with_transition(PlayerTransition::OnMouseEnter { state: "play" })
.with_transition(PlayerTransition::OnComplete { state: "stopped" }),
),
);
Expand Down Expand Up @@ -173,19 +178,6 @@ fn ui(
player.stop();
}
});
ui.horizontal(|ui| {
ui.label("Set Speed");
let mut speed = playback_settings.speed;
if ui.add(egui::Slider::new(&mut speed, 0.05..=2.0)).changed() {
for state in player.states_mut() {
let playback_settings = state
.playback_settings
.get_or_insert(PlaybackSettings::default());
playback_settings.speed = speed;
}
playback_settings.speed = speed;
};
});

ui.separator();

Expand All @@ -205,28 +197,149 @@ fn ui(

ui.heading("Current State");
ui.label(format!("Id: {}", player.state().id));
ui.label(format!("Autoplay: {}", playback_settings.autoplay));
ui.label(format!("Direction: {:?}", playback_settings.direction));
ui.label(format!(
"Intermission: {:?}",
playback_settings.intermission
));
ui.label(format!("Loop Behavior: {:?}", playback_settings.looping));
ui.label(format!(
"Segments: {:?}",
playback_settings
.segments
.start
.max(composition.frames.start)
..playback_settings.segments.end.min(composition.frames.end)
));
ui.label(format!("Speed: {}", playback_settings.speed));
ui.heading(format!("Transitions: {}", player.state().transitions.len()));
for transition in player.state().transitions.iter() {
ui.label(format!("{transition:?}"));
}
ui.horizontal(|ui| {
ui.label("Autoplay");
let autoplaying = playback_settings.autoplay.to_string();
if ui
.checkbox(&mut playback_settings.autoplay, autoplaying.to_string())
.changed()
{
player.state_mut().playback_settings.autoplay = playback_settings.autoplay;
};
});
ui.vertical(|ui| {
ui.label("Direction");
ui.horizontal(|ui| {
ui.separator();
if ui
.radio_value(
&mut playback_settings.direction,
PlaybackDirection::Normal,
"Normal",
)
.changed()
{
player.state_mut().playback_settings.direction = playback_settings.direction;
}
});
ui.horizontal(|ui| {
ui.separator();
if ui
.radio_value(
&mut playback_settings.direction,
PlaybackDirection::Reverse,
"Reverse",
)
.changed()
{
player.state_mut().playback_settings.direction = playback_settings.direction;
}
});
});

ui.separator();
ui.horizontal(|ui| {
ui.label("Intermission");
let mut intermission = playback_settings.intermission.as_secs_f32();
if ui
.add(egui::Slider::new(&mut intermission, 0.0..=5.0))
.changed()
{
player.state_mut().playback_settings.intermission =
Duration::from_secs_f32(intermission);
playback_settings.intermission = Duration::from_secs_f32(intermission);
};
});
ui.vertical(|ui| {
ui.label("Looping");
ui.horizontal(|ui| {
ui.separator();
let selected = matches!(playback_settings.looping, PlaybackLoopBehavior::DoNotLoop);
if ui.radio(selected, "Do not loop").clicked() {
player.state_mut().playback_settings.looping = PlaybackLoopBehavior::DoNotLoop;
playback_settings.looping = PlaybackLoopBehavior::DoNotLoop;
}
});
ui.horizontal(|ui| {
ui.separator();
let selected =
matches!(playback_settings.looping, PlaybackLoopBehavior::Amount(..));
let mut amt = match playback_settings.looping {
PlaybackLoopBehavior::Amount(amt) => amt,
_ => 0,
};
let clicked = ui.radio(selected, "Amount").clicked();
if ui
.add_enabled(selected, egui::Slider::new(&mut amt, 0..=5))
.changed()
|| clicked
{
player.state_mut().playback_settings.looping =
PlaybackLoopBehavior::Amount(amt);
playback_settings.looping = PlaybackLoopBehavior::Amount(amt);
};
});
ui.horizontal(|ui| {
ui.separator();
let selected = matches!(playback_settings.looping, PlaybackLoopBehavior::Loop);
if ui.radio(selected, "Loop").clicked() {
player.state_mut().playback_settings.looping = PlaybackLoopBehavior::Loop;
playback_settings.looping = PlaybackLoopBehavior::Loop;
}
});
});

ui.vertical(|ui| {
ui.label("Segments");
ui.horizontal(|ui| {
ui.separator();
ui.label("Start");
let mut start = playback_settings.segments.start;
if ui
.add(
egui::Slider::new(
&mut start,
composition.frames.start
..=playback_settings.segments.end.min(composition.frames.end),
)
.integer(),
)
.changed()
{
player.state_mut().playback_settings.segments.start = start;
playback_settings.segments.start = start;
};
});
ui.horizontal(|ui| {
ui.separator();
ui.label("End");
let mut end = playback_settings.segments.end;
if ui
.add(
egui::Slider::new(
&mut end,
playback_settings
.segments
.start
.max(composition.frames.start)
..=composition.frames.end,
)
.integer(),
)
.changed()
{
player.state_mut().playback_settings.segments.end = end;
playback_settings.segments.end = end;
};
});
});
ui.horizontal(|ui| {
ui.label("Speed");
let mut speed = playback_settings.speed;
if ui.add(egui::Slider::new(&mut speed, 0.05..=2.0)).changed() {
player.state_mut().playback_settings.speed = speed;
playback_settings.speed = speed;
};
});

ui.heading("Theme");
for layer in metadata.get_layers() {
Expand All @@ -238,11 +351,20 @@ fn ui(
.changed()
{
let [r, g, b, a] = color_edit;
player
.state_mut()
.theme
.edit(layer, Color::rgba(r, g, b, a));
theme.edit(layer, Color::rgba(r, g, b, a));
};
ui.label(layer);
});
}

ui.heading(format!("Transitions: {}", player.state().transitions.len()));
for transition in player.state().transitions.iter() {
ui.label(format!("{transition:?}"));
}
});
}

Expand Down
5 changes: 5 additions & 0 deletions src/playback/playhead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ impl Playhead {
self.frame = frame;
}

/// Reset the amount of loops completed
pub fn reset_loops(&mut self) {
self.loops_completed = 0;
}

pub(crate) fn new(frame: f32) -> Self {
Self {
frame,
Expand Down
12 changes: 6 additions & 6 deletions src/player/player_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use bevy::prelude::*;
pub struct PlayerState {
pub id: &'static str,
pub asset: Option<Handle<VelloAsset>>,
pub theme: Option<Theme>,
pub playback_settings: Option<PlaybackSettings>,
pub theme: Theme,
pub playback_settings: PlaybackSettings,
pub transitions: Vec<PlayerTransition>,
/// Whether to reset the playhead when you transition away from this state
pub reset_playhead_on_transition: bool,
Expand All @@ -20,8 +20,8 @@ impl PlayerState {
Self {
id,
asset: Default::default(),
playback_settings: None,
theme: None,
playback_settings: Default::default(),
theme: Default::default(),
transitions: vec![],
reset_playhead_on_transition: false,
reset_playhead_on_start: false,
Expand All @@ -34,12 +34,12 @@ impl PlayerState {
}

pub fn with_theme(mut self, theme: Theme) -> Self {
self.theme.replace(theme);
self.theme = theme;
self
}

pub fn with_playback_settings(mut self, playback_settings: PlaybackSettings) -> Self {
self.playback_settings.replace(playback_settings);
self.playback_settings = playback_settings;
self
}

Expand Down
23 changes: 13 additions & 10 deletions src/player/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub mod systems {
PlayerTransition, Playhead, VectorFile, VelloAsset,
};
use bevy::prelude::*;
use std::time::Duration;
use std::time::{Duration, Instant};
use vello_svg::usvg::strict_num::Ulps;

/// Spawn playheads for Lotties. Every Lottie gets exactly 1 playhead.
Expand Down Expand Up @@ -121,6 +121,9 @@ pub mod systems {
return;
}

// Set first render
playhead.first_render.get_or_insert(Instant::now());

// Advance playhead
playhead.frame += time.delta_seconds()
* playback_settings.speed
Expand All @@ -134,8 +137,8 @@ pub mod systems {
PlaybackLoopBehavior::DoNotLoop => false,
};
if playhead.frame > end_frame {
playhead.loops_completed += 1;
if looping {
playhead.loops_completed += 1;
// Trigger intermission, if applicable
if playback_settings.intermission > Duration::ZERO {
playhead
Expand All @@ -150,8 +153,8 @@ pub mod systems {
playhead.frame = end_frame;
}
} else if playhead.frame < start_frame {
playhead.loops_completed += 1;
if looping {
playhead.loops_completed += 1;
// Trigger intermission, if applicable
if playback_settings.intermission > Duration::ZERO {
playhead
Expand Down Expand Up @@ -347,14 +350,14 @@ pub mod systems {
|| target_state.reset_playhead_on_start
|| cur_handle.id() != target_handle.id()
{
let playback_settings =
target_state.playback_settings.clone().unwrap_or_default();
let frame = match playback_settings.direction {
PlaybackDirection::Normal => playback_settings
let frame = match target_state.playback_settings.direction {
PlaybackDirection::Normal => target_state
.playback_settings
.segments
.start
.max(composition.frames.start),
PlaybackDirection::Reverse => playback_settings
PlaybackDirection::Reverse => target_state
.playback_settings
.segments
.end
.min(composition.frames.end)
Expand All @@ -370,8 +373,8 @@ pub mod systems {
*cur_handle = target_handle.clone();
commands
.entity(entity)
.insert(target_state.playback_settings.clone().unwrap_or_default())
.insert(target_state.theme.clone().unwrap_or_default());
.insert(target_state.playback_settings.clone())
.insert(target_state.theme.clone());

// Reset playhead state
playhead.intermission.take();
Expand Down

0 comments on commit 5cc0f20

Please sign in to comment.