diff options
author | Christian Strømme <christian.stromme@qt.io> | 2017-11-02 16:51:11 +0100 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2018-01-23 18:23:29 +0000 |
commit | f6699afc911da958347c6916c1eff3558ee8d431 (patch) | |
tree | e1718c9829abda29469a9955f2f275ada62713c1 /src/animation | |
parent | a73937ac90169d77f2797633738fee27f71ad35a (diff) |
Add support for setting a time index in the ClipAnimator
This makes it possible to seek the animation clip to a specific position
in the time-line. The index is a real value between 0 and 1, where 0.0 is
the start of the clip and 1.0 is at the end of the clip
Change-Id: Ic8c57d04e4f1e0a7628713e057b5b8a41a400c5a
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Diffstat (limited to 'src/animation')
-rw-r--r-- | src/animation/backend/animationutils.cpp | 16 | ||||
-rw-r--r-- | src/animation/backend/animationutils_p.h | 23 | ||||
-rw-r--r-- | src/animation/backend/clipanimator.cpp | 18 | ||||
-rw-r--r-- | src/animation/backend/clipanimator_p.h | 15 | ||||
-rw-r--r-- | src/animation/backend/evaluateclipanimatorjob.cpp | 25 | ||||
-rw-r--r-- | src/animation/backend/findrunningclipanimatorsjob.cpp | 9 | ||||
-rw-r--r-- | src/animation/frontend/qabstractclipanimator.cpp | 26 | ||||
-rw-r--r-- | src/animation/frontend/qabstractclipanimator.h | 4 | ||||
-rw-r--r-- | src/animation/frontend/qabstractclipanimator_p.h | 2 | ||||
-rw-r--r-- | src/animation/frontend/qclipanimator.cpp | 1 |
10 files changed, 125 insertions, 14 deletions
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp index 44bf31854..c6147fe7e 100644 --- a/src/animation/backend/animationutils.cpp +++ b/src/animation/backend/animationutils.cpp @@ -109,6 +109,9 @@ ClipEvaluationData evaluationDataForClip(AnimationClip *clip, animatorData.loopCount, result.currentLoop); result.isFinalFrame = isFinalFrame(result.localTime, clip->duration(), result.currentLoop, animatorData.loopCount); + const bool hasNormalizedTime = isValidNormalizedTime(animatorData.normalizedLocalTime); + result.normalizedLocalTime = hasNormalizedTime ? animatorData.normalizedLocalTime + : result.localTime / clip->duration(); return result; } @@ -333,7 +336,8 @@ QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float> QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId, const QVector<MappingData> &mappingDataVec, const QVector<float> &channelResults, - bool finalFrame) + bool finalFrame, + float normalizedLocalTime) { QVector<Qt3DCore::QSceneChangePtr> changes; QVarLengthArray<Skeleton *, 4> dirtySkeletons; @@ -390,6 +394,15 @@ QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId anim for (const auto skeleton : dirtySkeletons) skeleton->sendLocalPoses(); + if (isValidNormalizedTime(normalizedLocalTime)) { + auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(animatorId); + e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll); + e->setPropertyName("normalizedTime"); + e->setValue(normalizedLocalTime); + Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isIntermediate = !finalFrame; + changes.push_back(e); + } + // If it's the final frame, notify the frontend that we've stopped if (finalFrame) { auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(animatorId); @@ -398,6 +411,7 @@ QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId anim e->setValue(false); changes.push_back(e); } + return changes; } diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h index 94038eecc..66e417a2a 100644 --- a/src/animation/backend/animationutils_p.h +++ b/src/animation/backend/animationutils_p.h @@ -113,11 +113,13 @@ struct AnimatorEvaluationData int loopCount; int currentLoop; double playbackRate; + float normalizedLocalTime; }; struct ClipEvaluationData { int currentLoop; + float normalizedLocalTime; double localTime; bool isFinalFrame; }; @@ -248,15 +250,23 @@ inline constexpr double toSecs(qint64 nsecs) { return nsecs / 1.0e9; } inline qint64 toNsecs(double seconds) { return qRound64(seconds * 1.0e9); } template<typename Animator> -AnimatorEvaluationData evaluationDataForAnimator(Animator animator, Clock* clock, qint64 nsSincePreviousFrame) +AnimatorEvaluationData evaluationDataForAnimator(Animator animator, + Clock* clock, + qint64 nsSincePreviousFrame) { + const bool seeking = animator->isSeeking(); AnimatorEvaluationData data; data.loopCount = animator->loops(); data.currentLoop = animator->currentLoop(); - data.playbackRate = clock != nullptr ? clock->playbackRate() : 1.0; + // The playback-rate is always 1.0 when seeking + data.playbackRate = ((clock != nullptr) && !seeking) ? clock->playbackRate() : 1.0; // Convert global time from nsec to sec data.elapsedTime = toSecs(nsSincePreviousFrame); - data.currentTime = animator->lastLocalTime(); + // When seeking we base it on the current time being at the start of the clip + data.currentTime = seeking ? 0.0 : animator->lastLocalTime(); + // If we're not seeking the local normalized time will be calculate in + // evaluationDataForClip(). + data.normalizedLocalTime = seeking ? animator->normalizedLocalTime() : -1.0; return data; } @@ -270,6 +280,11 @@ inline bool isFinalFrame(double localTime, currentLoop >= loopCount - 1); } +inline bool isValidNormalizedTime(float t) +{ + return !(t < 0.0f) && !(t > 1.0f); +} + Q_AUTOTEST_EXPORT int componentsForType(int type); @@ -300,7 +315,7 @@ Q_AUTOTEST_EXPORT QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId, const QVector<MappingData> &mappingDataVec, const QVector<float> &channelResults, - bool finalFrame); + bool finalFrame, float normalizedLocalTime = -1.0f); Q_AUTOTEST_EXPORT QVector<AnimationCallbackAndValue> prepareCallbacks(const QVector<MappingData> &mappingDataVec, diff --git a/src/animation/backend/clipanimator.cpp b/src/animation/backend/clipanimator.cpp index b294d57a2..acb3c8170 100644 --- a/src/animation/backend/clipanimator.cpp +++ b/src/animation/backend/clipanimator.cpp @@ -60,6 +60,8 @@ ClipAnimator::ClipAnimator() , m_lastLocalTime(0.0) , m_mappingData() , m_currentLoop(0) + , m_normalizedLocalTime(-1.0f) + , m_lastNormalizedLocalTime(-1.0f) { } @@ -72,6 +74,7 @@ void ClipAnimator::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr m_clockId = data.clockId; m_running = data.running; m_loops = data.loops; + m_normalizedLocalTime = data.normalizedTime; setDirty(Handler::ClipAnimatorDirty); } @@ -106,6 +109,13 @@ void ClipAnimator::setRunning(bool running) setDirty(Handler::ClipAnimatorDirty); } +void ClipAnimator::setNormalizedLocalTime(float normalizedTime) +{ + m_normalizedLocalTime = normalizedTime; + if (isValidNormalizedTime(normalizedTime)) + setDirty(Handler::ClipAnimatorDirty); +} + void ClipAnimator::cleanup() { setEnabled(false); @@ -116,6 +126,7 @@ void ClipAnimator::cleanup() m_running = false; m_loops = 1; m_clipFormat = ClipFormat(); + m_normalizedLocalTime = m_lastNormalizedLocalTime = -1.0f; } void ClipAnimator::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) @@ -133,6 +144,8 @@ void ClipAnimator::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) setRunning(change->value().toBool()); else if (change->propertyName() == QByteArrayLiteral("loops")) m_loops = change->value().toInt(); + else if (change->propertyName() == QByteArrayLiteral("normalizedTime")) + setNormalizedLocalTime(change->value().toFloat()); break; } @@ -183,6 +196,11 @@ void ClipAnimator::setLastLocalTime(double lastLocalTime) m_lastLocalTime = lastLocalTime; } +void ClipAnimator::setLastNormalizedLocalTime(float normalizedTime) +{ + m_lastNormalizedLocalTime = normalizedTime; +} + } // namespace Animation } // namespace Qt3DAnimation diff --git a/src/animation/backend/clipanimator_p.h b/src/animation/backend/clipanimator_p.h index 28c0ae51d..f4c04a4bf 100644 --- a/src/animation/backend/clipanimator_p.h +++ b/src/animation/backend/clipanimator_p.h @@ -77,12 +77,14 @@ public: bool isRunning() const { return m_running; } void setLoops(int loops) { m_loops = loops; } int loops() const { return m_loops; } + void setNormalizedLocalTime(float normalizedLocalTime); + float normalizedLocalTime() const { return m_normalizedLocalTime; } void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override; void setHandler(Handler *handler) { m_handler = handler; } // Called by jobs - bool canRun() const { return !m_clipId.isNull() && !m_mapperId.isNull() && m_running; } + bool canRun() const { return !m_clipId.isNull() && !m_mapperId.isNull(); } void setMappingData(const QVector<MappingData> &mappingData) { m_mappingData = mappingData; } QVector<MappingData> mappingData() const { return m_mappingData; } @@ -105,6 +107,14 @@ public: double lastLocalTime() const; void setLastLocalTime(double lastLocalTime); + float lastNormalizedLocalTime() { return m_lastNormalizedLocalTime; } + void setLastNormalizedLocalTime(float normalizedLocalTime); + bool isSeeking() const + { + return isValidNormalizedTime(m_normalizedLocalTime) + && !qFuzzyCompare(m_lastNormalizedLocalTime, m_normalizedLocalTime); + } + private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final; @@ -121,6 +131,9 @@ private: int m_currentLoop; ClipFormat m_clipFormat; + + float m_normalizedLocalTime; + float m_lastNormalizedLocalTime; }; } // namespace Animation diff --git a/src/animation/backend/evaluateclipanimatorjob.cpp b/src/animation/backend/evaluateclipanimatorjob.cpp index 15953b83a..74f9012d7 100644 --- a/src/animation/backend/evaluateclipanimatorjob.cpp +++ b/src/animation/backend/evaluateclipanimatorjob.cpp @@ -58,21 +58,31 @@ void EvaluateClipAnimatorJob::run() ClipAnimator *clipAnimator = m_handler->clipAnimatorManager()->data(m_clipAnimatorHandle); Q_ASSERT(clipAnimator); - if (!clipAnimator->isRunning()) + const bool running = clipAnimator->isRunning(); + const bool seeking = clipAnimator->isSeeking(); + if (!running && !seeking) { + m_handler->setClipAnimatorRunning(m_clipAnimatorHandle, false); return; + } - qint64 globalTimeNS = m_handler->simulationTime(); - qint64 nsSincePreviousFrame = clipAnimator->nsSincePreviousFrame(globalTimeNS); + const qint64 globalTimeNS = m_handler->simulationTime(); Clock *clock = m_handler->clockManager()->lookupResource(clipAnimator->clockId()); // Evaluate the fcurves AnimationClip *clip = m_handler->animationClipLoaderManager()->lookupResource(clipAnimator->clipId()); Q_ASSERT(clip); + + const qint64 nsSincePreviousFrame = seeking ? toNsecs(clip->duration() * clipAnimator->normalizedLocalTime()) + : clipAnimator->nsSincePreviousFrame(globalTimeNS); + // Prepare for evaluation (convert global time to local time ....) - const AnimatorEvaluationData animatorEvaluationData = evaluationDataForAnimator(clipAnimator, clock, nsSincePreviousFrame); + const AnimatorEvaluationData animatorEvaluationData = evaluationDataForAnimator(clipAnimator, + clock, + nsSincePreviousFrame); + const ClipEvaluationData preEvaluationDataForClip = evaluationDataForClip(clip, animatorEvaluationData); - const ClipResults rawClipResults = evaluateClipAtLocalTime(clip, preEvaluationDataForClip.localTime); + const ClipResults rawClipResults = evaluateClipAtPhase(clip, preEvaluationDataForClip.normalizedLocalTime); // Reformat the clip results into the layout used by this animator/blend tree const ClipFormat clipFormat = clipAnimator->clipFormat(); @@ -84,12 +94,15 @@ void EvaluateClipAnimatorJob::run() clipAnimator->setCurrentLoop(preEvaluationDataForClip.currentLoop); clipAnimator->setLastGlobalTimeNS(globalTimeNS); clipAnimator->setLastLocalTime(preEvaluationDataForClip.localTime); + clipAnimator->setLastNormalizedLocalTime(preEvaluationDataForClip.normalizedLocalTime); + clipAnimator->setNormalizedLocalTime(-1.0f); // Re-set to something invalid. // Prepare property changes (if finalFrame it also prepares the change for the running property for the frontend) const QVector<Qt3DCore::QSceneChangePtr> changes = preparePropertyChanges(clipAnimator->peerId(), clipAnimator->mappingData(), formattedClipResults, - preEvaluationDataForClip.isFinalFrame); + preEvaluationDataForClip.isFinalFrame, + preEvaluationDataForClip.normalizedLocalTime); // Send the property changes clipAnimator->sendPropertyChanges(changes); diff --git a/src/animation/backend/findrunningclipanimatorsjob.cpp b/src/animation/backend/findrunningclipanimatorsjob.cpp index 291f369f6..a9b0a85ed 100644 --- a/src/animation/backend/findrunningclipanimatorsjob.cpp +++ b/src/animation/backend/findrunningclipanimatorsjob.cpp @@ -65,10 +65,15 @@ void FindRunningClipAnimatorsJob::run() for (const auto &clipAnimatorHandle : qAsConst(m_clipAnimatorHandles)) { ClipAnimator *clipAnimator = clipAnimatorManager->data(clipAnimatorHandle); Q_ASSERT(clipAnimator); + const bool canRun = clipAnimator->canRun(); - m_handler->setClipAnimatorRunning(clipAnimatorHandle, canRun); + const bool running = clipAnimator->isRunning(); + const bool seeking = clipAnimator->isSeeking(); + m_handler->setClipAnimatorRunning(clipAnimatorHandle, canRun && (seeking || running)); - if (!canRun) + // TODO: Actually check if this is needed first, currently we re-build this every time + // canRun (or the normalized time) is true. + if (!canRun && !(seeking || running)) continue; // The clip animator needs to know how to map fcurve values through to properties on QNodes. diff --git a/src/animation/frontend/qabstractclipanimator.cpp b/src/animation/frontend/qabstractclipanimator.cpp index 9cdffee92..a56b349e7 100644 --- a/src/animation/frontend/qabstractclipanimator.cpp +++ b/src/animation/frontend/qabstractclipanimator.cpp @@ -53,6 +53,7 @@ QAbstractClipAnimatorPrivate::QAbstractClipAnimatorPrivate() , m_clock(nullptr) , m_running(false) , m_loops(1) + , m_normalizedTime(0.0f) { } @@ -195,6 +196,17 @@ QClock *QAbstractClipAnimator::clock() const return d->m_clock; } +/*! + \property int QAbstractClipAnimator::normalizedTime + + This property holds the clips normalized time. +*/ +float QAbstractClipAnimator::normalizedTime() const +{ + Q_D(const QAbstractClipAnimator); + return d->m_normalizedTime; +} + void QAbstractClipAnimator::setRunning(bool running) { Q_D(QAbstractClipAnimator); @@ -252,6 +264,20 @@ void QAbstractClipAnimator::setClock(QClock *clock) emit clockChanged(clock); } +void QAbstractClipAnimator::setNormalizedTime(float timeFraction) +{ + Q_D(QAbstractClipAnimator); + const float adjustedFraction = qBound(0.0f, timeFraction, 1.0f); + if (!qFuzzyCompare(timeFraction, adjustedFraction)) + qWarning("time fraction value clipped to : %f", double(adjustedFraction)); + + if (qFuzzyCompare(d->m_normalizedTime, adjustedFraction)) + return; + + d->m_normalizedTime = adjustedFraction; + emit normalizedTimeChanged(adjustedFraction); +} + /*! Starts the animation. */ diff --git a/src/animation/frontend/qabstractclipanimator.h b/src/animation/frontend/qabstractclipanimator.h index 9e8d3c457..08bf1f5dc 100644 --- a/src/animation/frontend/qabstractclipanimator.h +++ b/src/animation/frontend/qabstractclipanimator.h @@ -59,6 +59,7 @@ class QT3DANIMATIONSHARED_EXPORT QAbstractClipAnimator : public Qt3DCore::QCompo Q_PROPERTY(int loops READ loopCount WRITE setLoopCount NOTIFY loopCountChanged) Q_PROPERTY(Qt3DAnimation::QChannelMapper *channelMapper READ channelMapper WRITE setChannelMapper NOTIFY channelMapperChanged) Q_PROPERTY(Qt3DAnimation::QClock *clock READ clock WRITE setClock NOTIFY clockChanged) + Q_PROPERTY(float normalizedTime READ normalizedTime WRITE setNormalizedTime NOTIFY normalizedTimeChanged) public: enum Loops { Infinite = -1 }; @@ -70,12 +71,14 @@ public: Qt3DAnimation::QChannelMapper *channelMapper() const; int loopCount() const; Qt3DAnimation::QClock *clock() const; + float normalizedTime() const; public Q_SLOTS: void setRunning(bool running); void setChannelMapper(Qt3DAnimation::QChannelMapper *channelMapper); void setLoopCount(int loops); void setClock(Qt3DAnimation::QClock *clock); + void setNormalizedTime(float timeFraction); void start(); void stop(); @@ -85,6 +88,7 @@ Q_SIGNALS: void channelMapperChanged(Qt3DAnimation::QChannelMapper *channelMapper); void loopCountChanged(int loops); void clockChanged(Qt3DAnimation::QClock *clock); + void normalizedTimeChanged(float index); protected: explicit QAbstractClipAnimator(Qt3DCore::QNode *parent = nullptr); diff --git a/src/animation/frontend/qabstractclipanimator_p.h b/src/animation/frontend/qabstractclipanimator_p.h index 4b0ef3339..e65ee160c 100644 --- a/src/animation/frontend/qabstractclipanimator_p.h +++ b/src/animation/frontend/qabstractclipanimator_p.h @@ -71,6 +71,7 @@ public: Qt3DAnimation::QClock *m_clock; bool m_running; int m_loops; + float m_normalizedTime; }; struct QAbstractClipAnimatorData @@ -78,6 +79,7 @@ struct QAbstractClipAnimatorData Qt3DCore::QNodeId mapperId; Qt3DCore::QNodeId clockId; bool running; + float normalizedTime; int loops; }; diff --git a/src/animation/frontend/qclipanimator.cpp b/src/animation/frontend/qclipanimator.cpp index 5383e2c9b..c14715494 100644 --- a/src/animation/frontend/qclipanimator.cpp +++ b/src/animation/frontend/qclipanimator.cpp @@ -163,6 +163,7 @@ Qt3DCore::QNodeCreatedChangeBasePtr QClipAnimator::createNodeCreationChange() co data.clockId = Qt3DCore::qIdForNode(d->m_clock); data.running = d->m_running; data.loops = d->m_loops; + data.normalizedTime = d->m_normalizedTime; return creationChange; } |