diff --git a/demo/src/main.rs b/demo/src/main.rs index 6320ab2..d53c560 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use bevy::{asset::AssetMetaCheck, prelude::*}; use bevy_egui::{ egui::{self}, @@ -56,16 +58,19 @@ fn setup_vector_graphics(mut commands: Commands, asset_server: ResMut 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() { @@ -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:?}")); + } }); } diff --git a/src/playback/playhead.rs b/src/playback/playhead.rs index a0d9f95..b5466c7 100644 --- a/src/playback/playhead.rs +++ b/src/playback/playhead.rs @@ -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, diff --git a/src/player/player_state.rs b/src/player/player_state.rs index 375d4e9..75d4a79 100644 --- a/src/player/player_state.rs +++ b/src/player/player_state.rs @@ -6,8 +6,8 @@ use bevy::prelude::*; pub struct PlayerState { pub id: &'static str, pub asset: Option>, - pub theme: Option, - pub playback_settings: Option, + pub theme: Theme, + pub playback_settings: PlaybackSettings, pub transitions: Vec, /// Whether to reset the playhead when you transition away from this state pub reset_playhead_on_transition: bool, @@ -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, @@ -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 } diff --git a/src/player/plugin.rs b/src/player/plugin.rs index 99387eb..982781c 100644 --- a/src/player/plugin.rs +++ b/src/player/plugin.rs @@ -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. @@ -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 @@ -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 @@ -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 @@ -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) @@ -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();