summaryrefslogtreecommitdiffstats
path: root/src/animation
diff options
context:
space:
mode:
authorChristian Strømme <christian.stromme@qt.io>2017-11-02 16:51:11 +0100
committerSean Harmer <sean.harmer@kdab.com>2018-01-23 18:23:29 +0000
commitf6699afc911da958347c6916c1eff3558ee8d431 (patch)
treee1718c9829abda29469a9955f2f275ada62713c1 /src/animation
parenta73937ac90169d77f2797633738fee27f71ad35a (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.cpp16
-rw-r--r--src/animation/backend/animationutils_p.h23
-rw-r--r--src/animation/backend/clipanimator.cpp18
-rw-r--r--src/animation/backend/clipanimator_p.h15
-rw-r--r--src/animation/backend/evaluateclipanimatorjob.cpp25
-rw-r--r--src/animation/backend/findrunningclipanimatorsjob.cpp9
-rw-r--r--src/animation/frontend/qabstractclipanimator.cpp26
-rw-r--r--src/animation/frontend/qabstractclipanimator.h4
-rw-r--r--src/animation/frontend/qabstractclipanimator_p.h2
-rw-r--r--src/animation/frontend/qclipanimator.cpp1
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;
}