-
Notifications
You must be signed in to change notification settings - Fork 7
SMPTE Timecodes & Timelines, Sequences and Keyframes. #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Alright, i think this is ready for review! :D In here i'll show how the Timelines & Sequences API works: TimelinesA timeline is like a director: It takes care of storing the sequences and it contains a "Sequence Player" that allows you to play all of the animations that are stored in a specified framerate. let mut timeline_one = Timeline::new(Framerate::Fixed(24.0)); // Fixed Timeline
let mut timeline_two = Timeline::new(Framerate::Interpolated(24.0)); // Interpolated Timeline You can play forwards, and backwards based on what you want, and you can use Timestamps, Timecodes and Durations. let mut timeline = Timeline::new(Framerate::Interpolated(24.0));
// If you're in a game/real-time context, you can use
// your engine's delta time to play the timeline.
timeline.add_by_duration(time.delta());
// And also play it backwards.
timeline.sub_by_duration(time.delta()); SequencesSequences store all of the keyframes of the animation of a specific variable. This is an example for creating sequences in a timeline: let mut t = Timeline::new(Framerate::Interpolated(12.0));
// Let's create a Sequence called "count", that'll be an 'f64'.
let s: &mut Sequence<f64> = t.new_sequence("count").unwrap();
// "count" will be an animation that goes from 0 to 1 in one second.
s.add_keyframe_at_timestamp(Keyframe { value: 0.0 }, tcode_hms!(00:00:00));
s.add_keyframe_at_timestamp(Keyframe { value: 1.0 }, tcode_hms!(00:00:01)); As long as the type contains interpoli's Here's an example with kurbo's let s: &mut Sequence<Vec2> = t.new_sequence("position").unwrap();
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(200.0, 200.0),
},
&tcode_hms!(00:00:00),
);
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(300.0, 200.0),
},
&tcode_hms!(00:00:01),
);
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(800.0, 400.0),
},
&tcode_hms!(00:00:02),
);
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(700.0, 500.0),
},
&tcode_hms!(00:00:03),
); To finally use the animation's result, by taking the Vec2 example, you can get the value via let player_position: Vec2 = t.tween_by_name::<Vec2>("position"); If you're aiming for performance, you can get the sequence's pointer and store it as a reference. let pointer = t.get_sequence_pointer("position").unwrap();
// More code here...
let player_position: Vec2 = t.tween_by_pointer::<Vec2>(pointer); Here's an example of the Timelines that you can see and run for yourself in here. Static TimelinesThese work like Timelines, but the size of the Sequences are always known at compile-time, so they can only have one type (or you can use an enum if you want to store different types). let mut timeline: StaticTimeline<Vec2> = StaticTimeline::new(Framerate::Fixed(24.0));
let sequence = timeline.new_sequence("sequence").unwrap();
sequence.add_keyframes_at_timestamp(vec![
(
Keyframe {
value: Vec2::new(0.0, 1.0),
},
&tcode_hmsf!(00:00:01:00),
),
(
Keyframe {
value: Vec2::new(1.0, 1.0),
},
&tcode_hmsf!(00:00:02:00),
),
(
Keyframe {
value: Vec2::new(1.0, 2.0),
},
&tcode_hmsf!(00:00:03:00),
),
]); This gives even more performance at the cost of flexibility, so if you're in a situation where you need to squeeze as much performance as you want, you can use these. Stuff that i didn't add yet (Otherwise this PR was going to be really big to review).
|
I'm gonna need a ton of help and wisdom from everyone, because there's probably a lot of stuff that i'm doing wrong or can be handled much better. So any kind of input is appreciated c: |
I'm making this a draft for now, this PR is to document the progress i've been making so far with this experiment.
This experiment can also fail. If you see any kind of flaw, please feel free to point it out.
The final goals of this (if succeeds) are:
Progress.
isize
instead off64
).as_string()
prettier (print out '00:00:00:00' instead of '0:0:0:0').Add OpenTimelineIO Compatibility. (https://github.com/AcademySoftwareFoundation/OpenTimelineIO)(Definetly in a later PR, because this current one is getting really big).Add States. (Easing, Hold, etc.)(idk what to do here, ask Bruce or Aaron)Add functions to make API easier.(States need to be implemented for this)Sync based on denominators (Get help from @xorgy for this).(Probably in a later PR)TO-DO In Review
SMPTE Timecodes (Update 2).
SMPTE Timecodes are time units that are set like a clock: HH:MM:SS:FF (Hours, Minutes, Seconds, Frames).
For convenience, there's a macro that helps visualize this concept better in code:
(Sadly, Rust's macros don't allow me to use ":" so i have to use ";" instead, if there's a workaround for this, please let me know)EDIT: I fixed it!
Timecodes can be used as a Timestamp, or as a value for a Timeline, by setting the Framerate:
There's
Framerate::Fixed(n)
andFramerate::Interpolated(n)
. Once Timelines become a thing, fixed framerates will round up to the nearest frame when the tween is calculated, which will be useful for frame-by-frame animations. Interpolated frames on the other hand, will interpolate regardless of the framerate you're running (unless you explicitly set "hold" frames).Timecodes with a framerate can advance and reverse by frames, seconds, minutes, hours and use
Duration
(Instant
coming soon).Example: Play one second frame-by-frame.
Example: Add by Duration.
Example: Sub by Duration.
The draft repository contains more examples in form of tests inside of
lib.rs
.This is all of the progress for now, for any questions, please feel free to ask :)