diff options
author | Sean Harmer <sean.harmer@kdab.com> | 2017-01-28 15:22:19 +0000 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2017-01-29 13:59:24 +0000 |
commit | 9efbf9abd5452dd2c05250449efe1abf5887f481 (patch) | |
tree | 8ee8731491f9de28e451935cbc00034e372bf94c | |
parent | ddc85a1244c49fbe1873fa2c83e74dfb485d3598 (diff) |
Add loops property to ClipAnimator and implement logic for it
Change-Id: Ied67635f202e01c626177b4869b77db5bd3e80d4
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
-rw-r--r-- | src/animation/backend/clipanimator.cpp | 35 | ||||
-rw-r--r-- | src/animation/backend/clipanimator_p.h | 6 | ||||
-rw-r--r-- | src/animation/frontend/qclipanimator.cpp | 18 | ||||
-rw-r--r-- | src/animation/frontend/qclipanimator.h | 4 | ||||
-rw-r--r-- | src/animation/frontend/qclipanimator_p.h | 2 | ||||
-rw-r--r-- | tests/auto/animation/clipanimator/tst_clipanimator.cpp | 14 | ||||
-rw-r--r-- | tests/auto/animation/qclipanimator/tst_qclipanimator.cpp | 46 | ||||
-rw-r--r-- | tests/manual/animation-keyframe-simple/main.qml | 2 |
8 files changed, 121 insertions, 6 deletions
diff --git a/src/animation/backend/clipanimator.cpp b/src/animation/backend/clipanimator.cpp index 6b5dbbf33..ac6ca6710 100644 --- a/src/animation/backend/clipanimator.cpp +++ b/src/animation/backend/clipanimator.cpp @@ -39,6 +39,7 @@ #include <Qt3DAnimation/private/qclipanimator_p.h> #include <Qt3DAnimation/private/animationclip_p.h> #include <Qt3DAnimation/private/managers_p.h> +#include <Qt3DAnimation/private/animationlogging_p.h> #include <Qt3DCore/qpropertyupdatedchange.h> #include <Qt3DCore/private/qpropertyupdatedchangebase_p.h> @@ -48,6 +49,7 @@ #include <QtGui/qquaternion.h> #include <QtCore/qvariant.h> +#include <QtCore/qmath.h> QT_BEGIN_NAMESPACE @@ -59,6 +61,7 @@ ClipAnimator::ClipAnimator() , m_clipId() , m_mapperId() , m_running(false) + , m_loops(1) , m_startGlobalTime(0) , m_channelResults() , m_mappingData() @@ -74,6 +77,7 @@ void ClipAnimator::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr m_clipId = data.clipId; m_mapperId = data.mapperId; m_running = data.running; + m_loops = data.loops; setDirty(Handler::ClipAnimatorDirty); } @@ -106,6 +110,7 @@ void ClipAnimator::cleanup() m_clipId = Qt3DCore::QNodeId(); m_mapperId = Qt3DCore::QNodeId(); m_running = false; + m_loops = 1; } void ClipAnimator::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) @@ -119,6 +124,8 @@ void ClipAnimator::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) setMapperId(change->value().value<Qt3DCore::QNodeId>()); else if (change->propertyName() == QByteArrayLiteral("running")) setRunning(change->value().toBool()); + else if (change->propertyName() == QByteArrayLiteral("loops")) + m_loops = change->value().toInt(); break; } @@ -192,11 +199,10 @@ void ClipAnimator::evaluateAtGlobalTime(qint64 globalTime) const double t_start_global = double(m_startGlobalTime) / 1.0e9; const double playbackRate = 1.0; // Assume standard playback rate for now const double duration = clip->duration(); - const int loopCount = 1; // TODO: Make property on QClipAnimator const double localTime = localTimeFromGlobalTime(t_global, t_start_global, playbackRate, duration, - loopCount); + m_loops, &m_currentLoop); evaluateAtLocalTime(localTime); } @@ -206,8 +212,11 @@ void ClipAnimator::evaluateAtLocalTime(float localTime) Q_ASSERT(clip); // TODO: Uncomment when we add loopCount property - if (localTime >= clip->duration() && /*loopCount != 0 &&*/ m_currentLoop == 0 /*loopCount - 1*/) + if (localTime >= clip->duration() + && m_loops != 0 + && m_currentLoop == m_loops - 1) { m_finalFrame = true; + } // Ensure we have enough storage to hold the evaluations m_channelResults.resize(clip->channelCount()); @@ -300,19 +309,37 @@ void ClipAnimator::sendPropertyChanges() double ClipAnimator::localTimeFromGlobalTime(double t_global, double t_start_global, double playbackRate, double duration, - int loopCount) + int loopCount, int *currentLoop) { double t_local = playbackRate * (t_global - t_start_global); + double loopNumber = 0; if (loopCount == 1) { t_local = qBound(0.0, t_local, duration); } else if (loopCount == 0) { // Loops forever + (void) std::modf(t_local / duration, &loopNumber); t_local = std::fmod(t_local, duration); } else { // N loops t_local = qBound(0.0, t_local, double(loopCount) * duration); + (void) std::modf(t_local / duration, &loopNumber); t_local = std::fmod(t_local, duration); + + // Ensure we clamp to end of final loop + if (loopNumber == loopCount) { + loopNumber = loopCount - 1; + t_local = duration; + } } + + qCDebug(Jobs) << "t_global - t_start =" << t_global - t_start_global + << "current loop =" << loopNumber + << "t =" << t_local + << "duration =" << duration; + + if (currentLoop != nullptr) + *currentLoop = loopNumber; + return t_local; } diff --git a/src/animation/backend/clipanimator_p.h b/src/animation/backend/clipanimator_p.h index 91403b918..f5c5ae03d 100644 --- a/src/animation/backend/clipanimator_p.h +++ b/src/animation/backend/clipanimator_p.h @@ -72,6 +72,7 @@ public: void setRunning(bool running); bool isRunning() const { return m_running; } + int loops() const { return m_loops; } void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE; @@ -92,7 +93,7 @@ public: static double localTimeFromGlobalTime(double t_global, double t_start_global, double playbackRate, double duration, - int loopCount); + int loopCount, int *currentLoop = nullptr); static QVector<int> channelsToIndices(const ChannelGroup &channelGroup, int dataType, @@ -106,10 +107,13 @@ private: int offset, const QStringList &suffixes); + // Mirror of frontend properties Qt3DCore::QNodeId m_clipId; Qt3DCore::QNodeId m_mapperId; bool m_running; + int m_loops; + // Working state qint64 m_startGlobalTime; QVector<float> m_channelResults; diff --git a/src/animation/frontend/qclipanimator.cpp b/src/animation/frontend/qclipanimator.cpp index 485f13091..e6c06c989 100644 --- a/src/animation/frontend/qclipanimator.cpp +++ b/src/animation/frontend/qclipanimator.cpp @@ -51,6 +51,7 @@ QClipAnimatorPrivate::QClipAnimatorPrivate() , m_clip(nullptr) , m_mapper(nullptr) , m_running(false) + , m_loops(1) { } @@ -86,6 +87,12 @@ QChannelMapper *QClipAnimator::channelMapper() const return d->m_mapper; } +int QClipAnimator::loops() const +{ + Q_D(const QClipAnimator); + return d->m_loops; +} + void QClipAnimator::setClip(QAnimationClip *clip) { Q_D(QClipAnimator); @@ -134,6 +141,16 @@ void QClipAnimator::setChannelMapper(QChannelMapper *mapping) emit channelMapperChanged(mapping); } +void QClipAnimator::setLoops(int loops) +{ + Q_D(QClipAnimator); + if (d->m_loops == loops) + return; + + d->m_loops = loops; + emit loopsChanged(loops); +} + Qt3DCore::QNodeCreatedChangeBasePtr QClipAnimator::createNodeCreationChange() const { auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QClipAnimatorData>::create(this); @@ -142,6 +159,7 @@ Qt3DCore::QNodeCreatedChangeBasePtr QClipAnimator::createNodeCreationChange() co data.clipId = Qt3DCore::qIdForNode(d->m_clip); data.mapperId = Qt3DCore::qIdForNode(d->m_mapper); data.running = d->m_running; + data.loops = d->m_loops; return creationChange; } diff --git a/src/animation/frontend/qclipanimator.h b/src/animation/frontend/qclipanimator.h index ab5d5e541..176d6fc0c 100644 --- a/src/animation/frontend/qclipanimator.h +++ b/src/animation/frontend/qclipanimator.h @@ -56,6 +56,7 @@ class QT3DANIMATIONSHARED_EXPORT QClipAnimator : public Qt3DCore::QComponent Q_OBJECT Q_PROPERTY(Qt3DAnimation::QAnimationClip *clip READ clip WRITE setClip NOTIFY clipChanged) Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged) Q_PROPERTY(Qt3DAnimation::QChannelMapper *channelMapper READ channelMapper WRITE setChannelMapper NOTIFY channelMapperChanged) public: @@ -65,16 +66,19 @@ public: Qt3DAnimation::QAnimationClip *clip() const; bool isRunning() const; Qt3DAnimation::QChannelMapper *channelMapper() const; + int loops() const; public Q_SLOTS: void setClip(Qt3DAnimation::QAnimationClip *clip); void setRunning(bool running); void setChannelMapper(Qt3DAnimation::QChannelMapper *channelMapper); + void setLoops(int loops); Q_SIGNALS: void clipChanged(Qt3DAnimation::QAnimationClip *clip); void runningChanged(bool running); void channelMapperChanged(Qt3DAnimation::QChannelMapper *channelMapper); + void loopsChanged(int loops); protected: QClipAnimator(QClipAnimatorPrivate &dd, Qt3DCore::QNode *parent = nullptr); diff --git a/src/animation/frontend/qclipanimator_p.h b/src/animation/frontend/qclipanimator_p.h index c158c12d8..7bb9c526f 100644 --- a/src/animation/frontend/qclipanimator_p.h +++ b/src/animation/frontend/qclipanimator_p.h @@ -70,6 +70,7 @@ public: QAnimationClip *m_clip; Qt3DAnimation::QChannelMapper *m_mapper; bool m_running; + int m_loops; }; struct QClipAnimatorData @@ -77,6 +78,7 @@ struct QClipAnimatorData Qt3DCore::QNodeId clipId; Qt3DCore::QNodeId mapperId; bool running; + int loops; }; } // namespace Qt3DAnimation diff --git a/tests/auto/animation/clipanimator/tst_clipanimator.cpp b/tests/auto/animation/clipanimator/tst_clipanimator.cpp index b19f60da2..77f067973 100644 --- a/tests/auto/animation/clipanimator/tst_clipanimator.cpp +++ b/tests/auto/animation/clipanimator/tst_clipanimator.cpp @@ -52,6 +52,7 @@ private Q_SLOTS: auto clip = new Qt3DAnimation::QAnimationClip(); animator.setClip(clip); + animator.setLoops(10); // WHEN simulateInitialization(&animator, &backendAnimator); @@ -61,6 +62,7 @@ private Q_SLOTS: QCOMPARE(backendAnimator.isEnabled(), animator.isEnabled()); QCOMPARE(backendAnimator.clipId(), clip->id()); QCOMPARE(backendAnimator.isRunning(), animator.isRunning()); + QCOMPARE(backendAnimator.loops(), animator.loops()); } void checkInitialAndCleanedUpState() @@ -75,12 +77,14 @@ private Q_SLOTS: QCOMPARE(backendAnimator.isEnabled(), false); QCOMPARE(backendAnimator.clipId(), Qt3DCore::QNodeId()); QCOMPARE(backendAnimator.isRunning(), false); + QCOMPARE(backendAnimator.loops(), 1); // GIVEN Qt3DAnimation::QClipAnimator animator; auto clip = new Qt3DAnimation::QAnimationClip(); animator.setClip(clip); animator.setRunning(true); + animator.setLoops(25); // WHEN simulateInitialization(&animator, &backendAnimator); @@ -91,6 +95,7 @@ private Q_SLOTS: QCOMPARE(backendAnimator.clipId(), Qt3DCore::QNodeId()); QCOMPARE(backendAnimator.isEnabled(), false); QCOMPARE(backendAnimator.isRunning(), false); + QCOMPARE(backendAnimator.loops(), 1); } void checkPropertyChanges() @@ -128,6 +133,15 @@ private Q_SLOTS: // THEN QCOMPARE(backendAnimator.isRunning(), true); + + // WHEN + updateChange.reset(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId())); + updateChange->setPropertyName("loops"); + updateChange->setValue(64); + backendAnimator.sceneChangeEvent(updateChange); + + // THEN + QCOMPARE(backendAnimator.loops(), 64); } }; diff --git a/tests/auto/animation/qclipanimator/tst_qclipanimator.cpp b/tests/auto/animation/qclipanimator/tst_qclipanimator.cpp index 59195606d..d210d4365 100644 --- a/tests/auto/animation/qclipanimator/tst_qclipanimator.cpp +++ b/tests/auto/animation/qclipanimator/tst_qclipanimator.cpp @@ -59,6 +59,7 @@ private Q_SLOTS: // THEN QCOMPARE(animator.clip(), static_cast<Qt3DAnimation::QAnimationClip *>(nullptr)); QCOMPARE(animator.channelMapper(), static_cast<Qt3DAnimation::QChannelMapper *>(nullptr)); + QCOMPARE(animator.loops(), 1); } void checkPropertyChanges() @@ -107,6 +108,26 @@ private Q_SLOTS: QCOMPARE(animator.channelMapper(), newValue); QCOMPARE(spy.count(), 0); } + + { + // WHEN + QSignalSpy spy(&animator, SIGNAL(loopsChanged(int))); + const int newValue = 5; + animator.setLoops(newValue); + + // THEN + QVERIFY(spy.isValid()); + QCOMPARE(animator.loops(), newValue); + QCOMPARE(spy.count(), 1); + + // WHEN + spy.clear(); + animator.setLoops(newValue); + + // THEN + QCOMPARE(animator.loops(), newValue); + QCOMPARE(spy.count(), 0); + } } void checkCreationData() @@ -138,6 +159,7 @@ private Q_SLOTS: QCOMPARE(animator.metaObject(), creationChangeData->metaObject()); QCOMPARE(animator.clip()->id(), data.clipId); QCOMPARE(animator.channelMapper()->id(), data.mapperId); + QCOMPARE(animator.loops(), data.loops); } // WHEN @@ -218,6 +240,30 @@ private Q_SLOTS: QCOMPARE(arbiter.events.size(), 0); } + { + // WHEN + animator.setLoops(10); + QCoreApplication::processEvents(); + + // THEN + QCOMPARE(arbiter.events.size(), 1); + auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>(); + QCOMPARE(change->propertyName(), "loops"); + QCOMPARE(change->type(), Qt3DCore::PropertyUpdated); + QCOMPARE(change->value().toInt(), animator.loops()); + + arbiter.events.clear(); + } + + { + // WHEN + animator.setLoops(10); + QCoreApplication::processEvents(); + + // THEN + QCOMPARE(arbiter.events.size(), 0); + } + } }; diff --git a/tests/manual/animation-keyframe-simple/main.qml b/tests/manual/animation-keyframe-simple/main.qml index 38b898b93..ad9e59659 100644 --- a/tests/manual/animation-keyframe-simple/main.qml +++ b/tests/manual/animation-keyframe-simple/main.qml @@ -38,7 +38,7 @@ DefaultSceneEntity { }, ClipAnimator { id: animator - + loops: 3 onRunningChanged: console.log("running = " + running) clip: AnimationClip { |