Browse Source

Animation: properly handle Player with empty duration.

Turns out with current design we can allow fun things even for empty
durations. I like this.
pull/191/head
Vladimír Vondruš 8 years ago
parent
commit
e323ea7f26
  1. 23
      src/Magnum/Animation/Player.h
  2. 31
      src/Magnum/Animation/Player.hpp
  3. 53
      src/Magnum/Animation/Test/PlayerTest.cpp

23
src/Magnum/Animation/Player.h

@ -112,13 +112,19 @@ interpolated value changes, which is useful for triggering other events. See
@ref addRawCallback() that allows for greater control and further performance
optimizations. See its documentation for a usage example code snippet.
By default, the @ref duration() of an animation is calculated implicitly from
all added tracks. You can use @ref setDuration() to specify a custom duration
--- if it extends beyond the keyframe values, values of begin/end keyframes
will be extrapolated according to @ref Extrapolation specified for every track;
if it will be shorter, only a slice of the animation will be played. The
animation is implicitly played only once, use @ref setPlayCount() to set a
number of repeats or make it repeat indefinitely.
The animation is implicitly played only once, use @ref setPlayCount() to set a
number of repeats or make it repeat indefinitely. By default, the
@ref duration() of an animation is calculated implicitly from all added tracks.
You can use @ref setDuration() to specify a custom duration:
- If it extends beyond the keyframe values, values of begin/end keyframes
will be extrapolated according to @ref Extrapolation specified for every
track.
- If it will be shorter, only a slice of the animation will be played.
- If duration size is empty (min and and max se to the same value) and
@ref setPlayCount() is set to inifite, then the animator will indefinitely
give out value from a key that's at the start of the duration. If play
count is finite, the animation will get stopped right away.
@section Animation-Player-playback Animation playback
@ -239,7 +245,8 @@ template<class T, class K
*
* The function gets time from when the animation started and combined
* duration of all tracks; returns play iteration index and key value
* inside given iteration.
* inside given iteration. The combined duration is guaranteed to be
* always non-zero, zero durations are handled by the player itself.
*/
typedef std::pair<UnsignedInt, K>(*Scaler)(T, K);

31
src/Magnum/Animation/Player.hpp

@ -164,16 +164,29 @@ template<class T, class K> Player<T, K>& Player<T, K>::advance(T time) {
the future, do nothing */
} else if(_state != State::Playing || time < _startTime) return *this;
/* Calculate current play iteration and key value in that iteration. If we
exceeded play count, stop the animation and give out value at duration
end. */
UnsignedInt playCount;
/* If the player duration is empty, we can't call the scaler. If play count
is infinite, infinitely advance to a key at duration start. If not, stop
the animation. */
K key;
std::tie(playCount, key) = _scaler(timeToUse - _startTime, _duration.size()[0]);
if(_playCount && playCount >= _playCount) {
_state = State::Stopped;
_startTime = {};
key = _duration.size()[0];
const K duration = _duration.size()[0];
if(duration == K{}) {
key = K{};
if(_playCount != 0) {
_state = State::Stopped;
_startTime = {};
}
/* Otherwise calculate current play iteration and key value in that
iteration. If we exceeded play count, stop the animation and give out
value at duration end. */
} else {
UnsignedInt playCount;
std::tie(playCount, key) = _scaler(timeToUse - _startTime, duration);
if(_playCount && playCount >= _playCount) {
_state = State::Stopped;
_startTime = {};
key = duration;
}
}
/* Advance all tracks. Properly handle durations that don't start at 0. */

53
src/Magnum/Animation/Test/PlayerTest.cpp

@ -54,6 +54,8 @@ struct PlayerTest: TestSuite::Tester {
void advancePlayCount();
void advancePlayCountInfinite();
void advanceChrono();
void advanceZeroDuration();
void advanceZeroDurationChrono();
void setState();
@ -108,6 +110,8 @@ PlayerTest::PlayerTest() {
&PlayerTest::advancePlayCount,
&PlayerTest::advancePlayCountInfinite,
&PlayerTest::advanceChrono,
&PlayerTest::advanceZeroDuration,
&PlayerTest::advanceZeroDurationChrono,
&PlayerTest::setState,
@ -560,6 +564,55 @@ void PlayerTest::advanceChrono() {
CORRADE_COMPARE(value, 4.0f);
}
void PlayerTest::advanceZeroDuration() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value)
/* 1.75 secs since the start of the original duration */
.setDuration(Range1D::fromSize(1.0f + 1.75f, 0.0f))
.setPlayCount(0)
.play(2.0f);
CORRADE_COMPARE(player.duration().size(), 0.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(1.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(value, -1.0f);
/* After that, the value at 1.75 secs is returned independent of time */
player.advance(100.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(value, 4.0f);
}
void PlayerTest::advanceZeroDurationChrono() {
Float value = -1.0f;
Player<std::chrono::nanoseconds, Float> player;
player.add(Track, value)
/* 1.75 secs since the start of the original duration */
.setDuration(Range1D::fromSize(1.0f + 1.75f, 0.0f))
.setPlayCount(0)
.play(std::chrono::seconds{2});
CORRADE_COMPARE(player.duration().size(), 0.0f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(value, -1.0f);
/* Still before starting time, nothing is done */
player.advance(std::chrono::milliseconds{1750});
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(value, -1.0f);
/* After that, the value at 1.75 seconds is returned independent of the
time */
player.advance(std::chrono::seconds{100});
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(value, 4.0f);
}
void PlayerTest::setState() {
Player<Float> player;
CORRADE_COMPARE(player.state(), State::Stopped);

Loading…
Cancel
Save