summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2020-03-27 11:47:20 +0100
committerPaul Lemire <paul.lemire@kdab.com>2020-03-30 07:27:33 +0100
commit08281b3661547688b19cdaa52d5cf7bbdc171f56 (patch)
tree7dc52b973bad09f909912baf939765bf0b24ecc4
parent62d11afb41e0c3b8a21b4a307cebdfc406ecdf98 (diff)
Properly stop running animations when using a negative playrate
When reaching a normalized time of 0 they would otherwise continue to run Change-Id: Idaea755d3a12f9c9da9c25732c2221e9b3f9f4c7 Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r--src/animation/backend/animationutils.cpp8
-rw-r--r--src/animation/backend/animationutils_p.h12
-rw-r--r--src/animation/backend/evaluateblendclipanimatorjob.cpp2
-rw-r--r--src/animation/frontend/qclock.cpp5
-rw-r--r--tests/auto/animation/animationutils/tst_animationutils.cpp108
5 files changed, 126 insertions, 9 deletions
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp
index 3f386d92a..a5656e230 100644
--- a/src/animation/backend/animationutils.cpp
+++ b/src/animation/backend/animationutils.cpp
@@ -80,7 +80,8 @@ ClipEvaluationData evaluationDataForClip(AnimationClip *clip,
animatorData.playbackRate, clip->duration(),
animatorData.loopCount, result.currentLoop);
result.isFinalFrame = isFinalFrame(result.localTime, clip->duration(),
- result.currentLoop, animatorData.loopCount);
+ result.currentLoop, animatorData.loopCount,
+ animatorData.playbackRate);
const bool hasNormalizedTime = isValidNormalizedTime(animatorData.normalizedLocalTime);
result.normalizedLocalTime = hasNormalizedTime ? animatorData.normalizedLocalTime
: result.localTime / clip->duration();
@@ -112,9 +113,10 @@ double localTimeFromElapsedTime(double t_current_local,
t_local = std::fmod(t_local, duration);
// Ensure we clamp to end of final loop
- if (int(loopNumber) == loopCount) {
+
+ if (int(loopNumber) == loopCount || int(loopNumber) < 0) {
loopNumber = loopCount - 1;
- t_local = duration;
+ t_local = playbackRate >= 0.0 ? duration : 0.0;
}
}
diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h
index 8c71e704a..0501d9598 100644
--- a/src/animation/backend/animationutils_p.h
+++ b/src/animation/backend/animationutils_p.h
@@ -318,11 +318,15 @@ AnimatorEvaluationData evaluationDataForAnimator(Animator animator,
inline bool isFinalFrame(double localTime,
double duration,
int currentLoop,
- int loopCount)
+ int loopCount,
+ double playbackRate)
{
- return (localTime >= duration &&
- loopCount != 0 &&
- currentLoop >= loopCount - 1);
+ // We must be on the final loop and
+ // - if playing forward, localTime must be equal or above the duration
+ // - if playing backward, localTime must be equal or below 0
+ if (playbackRate >= 0.0)
+ return (loopCount != 0 && currentLoop >= loopCount - 1 && localTime >= duration);
+ return (loopCount != 0 && currentLoop <= 0 && localTime <= 0);
}
inline bool isValidNormalizedTime(float t)
diff --git a/src/animation/backend/evaluateblendclipanimatorjob.cpp b/src/animation/backend/evaluateblendclipanimatorjob.cpp
index 765531902..8a5f1e533 100644
--- a/src/animation/backend/evaluateblendclipanimatorjob.cpp
+++ b/src/animation/backend/evaluateblendclipanimatorjob.cpp
@@ -129,7 +129,7 @@ void EvaluateBlendClipAnimatorJob::run()
blendedClipAnimator->setCurrentLoop(animatorData.currentLoop);
// Prepare the change record
- const bool finalFrame = isFinalFrame(localTime, duration, animatorData.currentLoop, animatorData.loopCount);
+ const bool finalFrame = isFinalFrame(localTime, duration, animatorData.currentLoop, animatorData.loopCount, animatorData.playbackRate);
const QVector<MappingData> mappingData = blendedClipAnimator->mappingData();
auto record = prepareAnimationRecord(blendedClipAnimator->peerId(),
mappingData,
diff --git a/src/animation/frontend/qclock.cpp b/src/animation/frontend/qclock.cpp
index f38c21807..519edd388 100644
--- a/src/animation/frontend/qclock.cpp
+++ b/src/animation/frontend/qclock.cpp
@@ -63,7 +63,10 @@ QClock::~QClock()
/*!
\property Qt3DAnimation::QClock::playbackRate
- The playback speed of the animation.
+ The playback speed of the animation. The playback speed can be negative.
+ When that is the case the animation will be played back from the current
+ normalized time value back to 0 and for the number of loops it had been
+ played for with a positive playback rate.
*/
double QClock::playbackRate() const
diff --git a/tests/auto/animation/animationutils/tst_animationutils.cpp b/tests/auto/animation/animationutils/tst_animationutils.cpp
index b19d74a25..76909d124 100644
--- a/tests/auto/animation/animationutils/tst_animationutils.cpp
+++ b/tests/auto/animation/animationutils/tst_animationutils.cpp
@@ -2059,6 +2059,114 @@ private Q_SLOTS:
QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 1")
<< handler << clip << animatorData << clipData;
}
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 1;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(1);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() * 0.5); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = false;
+
+ QTest::newRow("clip1.json, elapsedTime = duration / 2, loops = 1, current_loop = 1, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 1;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(1);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() + 1); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = true;
+
+ QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 1, current_loop = 1, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 2;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(0);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() + 1); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = true;
+
+ QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 0, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 2;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(1);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() * 2.0 + 1); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = true;
+
+ QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 1, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
}
void checkEvaluationDataForClip()