summaryrefslogtreecommitdiffstats
path: root/src/animation/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/animation/backend')
-rw-r--r--src/animation/backend/additiveclipblend.cpp101
-rw-r--r--src/animation/backend/additiveclipblend_p.h110
-rw-r--r--src/animation/backend/animationclip.cpp159
-rw-r--r--src/animation/backend/animationclip_p.h48
-rw-r--r--src/animation/backend/animationutils.cpp431
-rw-r--r--src/animation/backend/animationutils_p.h193
-rw-r--r--src/animation/backend/backend.pri18
-rw-r--r--src/animation/backend/bezierevaluator.cpp2
-rw-r--r--src/animation/backend/blendedclipanimator.cpp6
-rw-r--r--src/animation/backend/blendedclipanimator_p.h10
-rw-r--r--src/animation/backend/buildblendtreesjob.cpp126
-rw-r--r--src/animation/backend/channelmapper_p.h1
-rw-r--r--src/animation/backend/channelmapping_p.h9
-rw-r--r--src/animation/backend/clipanimator.cpp5
-rw-r--r--src/animation/backend/clipanimator_p.h9
-rw-r--r--src/animation/backend/clipblendnode.cpp156
-rw-r--r--src/animation/backend/clipblendnode_p.h32
-rw-r--r--src/animation/backend/clipblendnodevisitor.cpp114
-rw-r--r--src/animation/backend/clipblendnodevisitor_p.h24
-rw-r--r--src/animation/backend/clipblendvalue.cpp116
-rw-r--r--src/animation/backend/clipblendvalue_p.h (renamed from src/animation/backend/conductedclipanimator_p.h)41
-rw-r--r--src/animation/backend/conductedclipanimator.cpp80
-rw-r--r--src/animation/backend/evaluateblendclipanimatorjob.cpp142
-rw-r--r--src/animation/backend/evaluateblendclipanimatorjob_p.h3
-rw-r--r--src/animation/backend/evaluateclipanimatorjob.cpp28
-rw-r--r--src/animation/backend/fcurve.cpp63
-rw-r--r--src/animation/backend/fcurve_p.h29
-rw-r--r--src/animation/backend/findrunningclipanimatorsjob.cpp6
-rw-r--r--src/animation/backend/handle_types_p.h2
-rw-r--r--src/animation/backend/handler.cpp7
-rw-r--r--src/animation/backend/handler_p.h17
-rw-r--r--src/animation/backend/keyframe_p.h10
-rw-r--r--src/animation/backend/lerpclipblend.cpp (renamed from src/animation/backend/lerpblend.cpp)48
-rw-r--r--src/animation/backend/lerpclipblend_p.h (renamed from src/animation/backend/lerpblend_p.h)36
-rw-r--r--src/animation/backend/loadanimationclipjob.cpp4
-rw-r--r--src/animation/backend/managers_p.h16
36 files changed, 1518 insertions, 684 deletions
diff --git a/src/animation/backend/additiveclipblend.cpp b/src/animation/backend/additiveclipblend.cpp
new file mode 100644
index 000000000..ac8b849e2
--- /dev/null
+++ b/src/animation/backend/additiveclipblend.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "additiveclipblend_p.h"
+#include <Qt3DAnimation/qclipblendnodecreatedchange.h>
+#include <Qt3DAnimation/qadditiveclipblend.h>
+#include <Qt3DAnimation/private/qadditiveclipblend_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DAnimation {
+
+namespace Animation {
+
+AdditiveClipBlend::AdditiveClipBlend()
+ : ClipBlendNode(ClipBlendNode::AdditiveBlendType)
+ , m_baseClipId()
+ , m_additiveClipId()
+ , m_additiveFactor(0.0f)
+{
+}
+
+AdditiveClipBlend::~AdditiveClipBlend()
+{
+}
+
+void AdditiveClipBlend::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
+{
+ if (e->type() == Qt3DCore::PropertyUpdated) {
+ Qt3DCore::QPropertyUpdatedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
+ if (change->propertyName() == QByteArrayLiteral("additiveFactor"))
+ m_additiveFactor = change->value().toFloat();
+ else if (change->propertyName() == QByteArrayLiteral("baseClip"))
+ m_baseClipId = change->value().value<Qt3DCore::QNodeId>();
+ else if (change->propertyName() == QByteArrayLiteral("additiveClip"))
+ m_additiveClipId = change->value().value<Qt3DCore::QNodeId>();
+ }
+}
+
+ClipResults AdditiveClipBlend::doBlend(const QVector<ClipResults> &blendData) const
+{
+ Q_ASSERT(blendData.size() == 2);
+ Q_ASSERT(blendData[0].size() == blendData[1].size());
+ const int elementCount = blendData.first().size();
+ ClipResults blendResults(elementCount);
+
+ for (int i = 0; i < elementCount; ++i)
+ blendResults[i] = blendData[0][i] + m_additiveFactor * blendData[1][i];
+
+ return blendResults;
+}
+
+void AdditiveClipBlend::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
+{
+ ClipBlendNode::initializeFromPeer(change);
+ const auto creationChangeData = qSharedPointerCast<Qt3DAnimation::QClipBlendNodeCreatedChange<Qt3DAnimation::QAdditiveClipBlendData>>(change);
+ const Qt3DAnimation::QAdditiveClipBlendData cloneData = creationChangeData->data;
+ m_baseClipId = cloneData.baseClipId;
+ m_additiveClipId = cloneData.additiveClipId;
+ m_additiveFactor = cloneData.additiveFactor;
+}
+
+} // Animation
+
+} // Qt3DAnimation
+
+QT_END_NAMESPACE
diff --git a/src/animation/backend/additiveclipblend_p.h b/src/animation/backend/additiveclipblend_p.h
new file mode 100644
index 000000000..b3fe240ed
--- /dev/null
+++ b/src/animation/backend/additiveclipblend_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DANIMATION_ANIMATION_ADDITIVECLIPBLEND_P_H
+#define QT3DANIMATION_ANIMATION_ADDITIVECLIPBLEND_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DAnimation/private/clipblendnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DAnimation {
+
+namespace Animation {
+
+class Q_AUTOTEST_EXPORT AdditiveClipBlend : public ClipBlendNode
+{
+public:
+ AdditiveClipBlend();
+ ~AdditiveClipBlend();
+
+ inline Qt3DCore::QNodeId baseClipId() const { return m_baseClipId; }
+ void setBaseClipId(Qt3DCore::QNodeId baseClipId) { m_baseClipId = baseClipId; } // For unit tests
+
+ inline Qt3DCore::QNodeId additiveClipId() const { return m_additiveClipId; }
+ void setAdditiveClipId(Qt3DCore::QNodeId additiveClipId) { m_additiveClipId = additiveClipId; } // For unit tests
+
+ inline float additiveFactor() const { return m_additiveFactor; }
+ void setAdditiveFactor(float additiveFactor) { m_additiveFactor = additiveFactor; } // For unit tests
+
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_FINAL;
+
+ inline QVector<Qt3DCore::QNodeId> allDependencyIds() const Q_DECL_OVERRIDE
+ {
+ return currentDependencyIds();
+ }
+
+ inline QVector<Qt3DCore::QNodeId> currentDependencyIds() const Q_DECL_OVERRIDE
+ {
+ return { m_baseClipId, m_additiveClipId };
+ }
+
+ inline double duration() const Q_DECL_OVERRIDE
+ {
+ ClipBlendNode *node = clipBlendNodeManager()->lookupNode(m_baseClipId);
+ Q_ASSERT(node);
+ return node->duration();
+ }
+
+protected:
+ ClipResults doBlend(const QVector<ClipResults> &blendData) const Q_DECL_FINAL;
+
+private:
+ void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL;
+
+ Qt3DCore::QNodeId m_baseClipId;
+ Qt3DCore::QNodeId m_additiveClipId;
+ float m_additiveFactor;
+};
+
+} // Animation
+
+} // Qt3DAnimation
+
+QT_END_NAMESPACE
+
+#endif // QT3DANIMATION_ANIMATION_ADDITIVECLIPBLEND_P_H
diff --git a/src/animation/backend/animationclip.cpp b/src/animation/backend/animationclip.cpp
index 3265a8fc5..6a3c3c15b 100644
--- a/src/animation/backend/animationclip.cpp
+++ b/src/animation/backend/animationclip.cpp
@@ -36,11 +36,12 @@
#include "animationclip_p.h"
#include <Qt3DAnimation/qanimationclip.h>
+#include <Qt3DAnimation/qanimationcliploader.h>
#include <Qt3DAnimation/private/qanimationclip_p.h>
+#include <Qt3DAnimation/private/qanimationcliploader_p.h>
#include <Qt3DAnimation/private/animationlogging_p.h>
#include <Qt3DRender/private/qurlhelper_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
-#include <Qt3DCore/private/qpropertyupdatedchangebase_p.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qfile.h>
@@ -56,20 +57,36 @@ namespace Animation {
AnimationClip::AnimationClip()
: BackendNode(ReadWrite)
, m_source()
+ , m_status(QAnimationClipLoader::NotReady)
+ , m_clipData()
+ , m_dataType(Unknown)
, m_name()
- , m_objectName()
- , m_channelGroups()
+ , m_channels()
, m_duration(0.0f)
{
}
void AnimationClip::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
{
- const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QAnimationClipData>>(change);
- const auto &data = typedChange->data;
- m_source = data.source;
- if (!m_source.isEmpty())
- setDirty(Handler::AnimationClipDirty);
+ const auto loaderTypedChange = qSharedPointerDynamicCast<Qt3DCore::QNodeCreatedChange<QAnimationClipLoaderData>>(change);
+ if (loaderTypedChange) {
+ const auto &data = loaderTypedChange->data;
+ m_dataType = File;
+ m_source = data.source;
+ if (!m_source.isEmpty())
+ setDirty(Handler::AnimationClipDirty);
+ return;
+ }
+
+ const auto clipTypedChange = qSharedPointerDynamicCast<Qt3DCore::QNodeCreatedChange<QAnimationClipChangeData>>(change);
+ if (clipTypedChange) {
+ const auto &data = clipTypedChange->data;
+ m_dataType = Data;
+ m_clipData = data.clipData;
+ if (m_clipData.isValid())
+ setDirty(Handler::AnimationClipDirty);
+ return;
+ }
}
void AnimationClip::cleanup()
@@ -77,20 +94,41 @@ void AnimationClip::cleanup()
setEnabled(false);
m_handler = nullptr;
m_source.clear();
- m_channelGroups.clear();
+ m_clipData.clearChannels();
+ m_status = QAnimationClipLoader::NotReady;
+ m_dataType = Unknown;
+ m_channels.clear();
m_duration = 0.0f;
clearData();
}
+void AnimationClip::setStatus(QAnimationClipLoader::Status status)
+{
+ if (status != m_status) {
+ m_status = status;
+ Qt3DCore::QPropertyUpdatedChangePtr e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll);
+ e->setPropertyName("status");
+ e->setValue(QVariant::fromValue(m_status));
+ notifyObservers(e);
+ }
+}
+
void AnimationClip::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
{
switch (e->type()) {
case Qt3DCore::PropertyUpdated: {
const auto change = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
if (change->propertyName() == QByteArrayLiteral("source")) {
+ Q_ASSERT(m_dataType == File);
m_source = change->value().toUrl();
setDirty(Handler::AnimationClipDirty);
+ } else if (change->propertyName() == QByteArrayLiteral("clipData")) {
+ Q_ASSERT(m_dataType == Data);
+ m_clipData = change->value().value<Qt3DAnimation::QAnimationClipData>();
+ if (m_clipData.isValid())
+ setDirty(Handler::AnimationClipDirty);
}
break;
}
@@ -110,11 +148,45 @@ void AnimationClip::loadAnimation()
qCDebug(Jobs) << Q_FUNC_INFO << m_source;
clearData();
+ // Load the data
+ switch (m_dataType) {
+ case File:
+ loadAnimationFromUrl();
+ break;
+
+ case Data:
+ loadAnimationFromData();
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ }
+
+ // Update the duration
+ const float t = findDuration();
+ setDuration(t);
+
+ m_channelComponentCount = findChannelComponentCount();
+
+ // If using a loader inform the frontend of the status change
+ if (m_source.isEmpty()) {
+ if (qFuzzyIsNull(t) || m_channelComponentCount == 0)
+ setStatus(QAnimationClipLoader::Error);
+ else
+ setStatus(QAnimationClipLoader::Ready);
+ }
+
+ qCDebug(Jobs) << "Loaded animation data:" << *this;
+}
+
+void AnimationClip::loadAnimationFromUrl()
+{
// TODO: Handle remote files
QString filePath = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(m_source);
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Could not find animation clip:" << filePath;
+ setStatus(QAnimationClipLoader::Error);
return;
}
@@ -127,28 +199,29 @@ void AnimationClip::loadAnimation()
qCDebug(Jobs) << "Found" << animationsArray.size() << "animations:";
for (int i = 0; i < animationsArray.size(); ++i) {
QJsonObject animation = animationsArray.at(i).toObject();
- qCDebug(Jobs) << "\tName:" << animation[QLatin1String("action")].toString()
- << "Object:" << animation[QLatin1String("object")].toString();
+ qCDebug(Jobs) << "Animation Name:" << animation[QLatin1String("animationName")].toString();
}
// For now just load the first animation
+ // TODO: Allow loading a named animation from within the file analogous to QMesh
QJsonObject animation = animationsArray.at(0).toObject();
- m_name = animation[QLatin1String("action")].toString();
- m_objectName = animation[QLatin1String("object")].toString();
- QJsonArray groupsArray = animation[QLatin1String("groups")].toArray();
- const int groupCount = groupsArray.size();
- m_channelGroups.resize(groupCount);
- for (int i = 0; i < groupCount; ++i) {
- const QJsonObject group = groupsArray.at(i).toObject();
- m_channelGroups[i].read(group);
+ m_name = animation[QLatin1String("animationName")].toString();
+ QJsonArray channelsArray = animation[QLatin1String("channels")].toArray();
+ const int channelCount = channelsArray.size();
+ m_channels.resize(channelCount);
+ for (int i = 0; i < channelCount; ++i) {
+ const QJsonObject group = channelsArray.at(i).toObject();
+ m_channels[i].read(group);
}
+}
- const float t = findDuration();
- setDuration(t);
-
- m_channelCount = findChannelCount();
-
- qCDebug(Jobs) << "Loaded animation data:" << *this;
+void AnimationClip::loadAnimationFromData()
+{
+ // Reformat data from QAnimationClipData to backend format
+ m_channels.resize(m_clipData.channelCount());
+ int i = 0;
+ for (const auto &frontendChannel : qAsConst(m_clipData))
+ m_channels[i++].setFromQChannel(frontendChannel);
}
void AnimationClip::setDuration(float duration)
@@ -161,44 +234,52 @@ void AnimationClip::setDuration(float duration)
// Send a change to the frontend
auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll);
- Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isFinal = true;
e->setPropertyName("duration");
e->setValue(m_duration);
notifyObservers(e);
}
+int AnimationClip::channelIndex(const QString &channelName) const
+{
+ const int channelCount = m_channels.size();
+ for (int i = 0; i < channelCount; ++i) {
+ if (m_channels[i].name == channelName)
+ return i;
+ }
+ return -1;
+}
+
/*!
\internal
- Given the index of a channel group, \a channelGroupIndex, calculates
- the base index of the first channel in this group. For example, if
+ Given the index of a channel, \a channelIndex, calculates
+ the base index of the first channelComponent in this group. For example, if
there are two channel groups each with 3 channels and you request
the channelBaseIndex(1), the return value will be 3. Indices 0-2 are
for the first group, so the first channel of the second group occurs
at index 3.
*/
-int AnimationClip::channelBaseIndex(int channelGroupIndex) const
+int AnimationClip::channelComponentBaseIndex(int channelIndex) const
{
int index = 0;
- for (int i = 0; i < channelGroupIndex; ++i)
- index += m_channelGroups[i].channels.size();
+ for (int i = 0; i < channelIndex; ++i)
+ index += m_channels[i].channelComponents.size();
return index;
}
void AnimationClip::clearData()
{
m_name.clear();
- m_objectName.clear();
- m_channelGroups.clear();
+ m_channels.clear();
}
float AnimationClip::findDuration()
{
// Iterate over the contained fcurves and find the longest one
double tMax = 0.0;
- for (const ChannelGroup &channelGroup : qAsConst(m_channelGroups)) {
- for (const Channel &channel : qAsConst(channelGroup.channels)) {
- const float t = channel.fcurve.endTime();
+ for (const Channel &channel : qAsConst(m_channels)) {
+ for (const ChannelComponent &channelComponent : qAsConst(channel.channelComponents)) {
+ const float t = channelComponent.fcurve.endTime();
if (t > tMax)
tMax = t;
}
@@ -206,11 +287,11 @@ float AnimationClip::findDuration()
return tMax;
}
-int AnimationClip::findChannelCount()
+int AnimationClip::findChannelComponentCount()
{
int channelCount = 0;
- for (const ChannelGroup &channelGroup : qAsConst(m_channelGroups))
- channelCount += channelGroup.channels.size();
+ for (const Channel &channel : qAsConst(m_channels))
+ channelCount += channel.channelComponents.size();
return channelCount;
}
diff --git a/src/animation/backend/animationclip_p.h b/src/animation/backend/animationclip_p.h
index cfd8558c7..7ff79c01a 100644
--- a/src/animation/backend/animationclip_p.h
+++ b/src/animation/backend/animationclip_p.h
@@ -49,6 +49,8 @@
//
#include <Qt3DAnimation/private/backendnode_p.h>
+#include <Qt3DAnimation/qanimationclipdata.h>
+#include <Qt3DAnimation/qanimationcliploader.h>
#include <Qt3DAnimation/private/fcurve_p.h>
#include <QtCore/qurl.h>
@@ -67,32 +69,52 @@ public:
void cleanup();
void setSource(const QUrl &source) { m_source = source; }
QUrl source() const { return m_source; }
+ void setStatus(QAnimationClipLoader::Status status);
+ QAnimationClipLoader::Status status() const { return m_status; }
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
QString name() const { return m_name; }
- QString objectName() const { return m_objectName; }
- const QVector<ChannelGroup> &channelGroups() const { return m_channelGroups; }
+ const QVector<Channel> &channels() const { return m_channels; }
// Called from jobs
void loadAnimation();
void setDuration(float duration);
float duration() const { return m_duration; }
- int channelCount() const { return m_channelCount; }
- int channelBaseIndex(int channelGroupIndex) const;
+ int channelIndex(const QString &channelName) const;
+ int channelCount() const { return m_channelComponentCount; }
+ int channelComponentBaseIndex(int channelGroupIndex) const;
+
+ // Allow unit tests to set the data type
+#if !defined(QT_BUILD_INTERNAL)
+private:
+#endif
+ enum ClipDataType {
+ Unknown,
+ File,
+ Data
+ };
+#if defined(QT_BUILD_INTERNAL)
+public:
+ void setDataType(ClipDataType dataType) { m_dataType = dataType; }
+#endif
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL;
+ void loadAnimationFromUrl();
+ void loadAnimationFromData();
void clearData();
float findDuration();
- int findChannelCount();
+ int findChannelComponentCount();
QUrl m_source;
+ QAnimationClipLoader::Status m_status;
+ QAnimationClipData m_clipData;
+ ClipDataType m_dataType;
QString m_name;
- QString m_objectName;
- QVector<ChannelGroup> m_channelGroups;
+ QVector<Channel> m_channels;
float m_duration;
- int m_channelCount;
+ int m_channelComponentCount;
};
#ifndef QT_NO_DEBUG_STREAM
@@ -101,12 +123,12 @@ inline QDebug operator<<(QDebug dbg, const AnimationClip &animationClip)
QDebugStateSaver saver(dbg);
dbg << "QNodeId =" << animationClip.peerId() << endl
<< "Name =" << animationClip.name() << endl
- << "Object Name =" << animationClip.objectName() << endl
- << "Channel Groups:" << endl;
+ << "Duration: " << animationClip.duration() << endl
+ << "Channels:" << endl;
- const QVector<ChannelGroup> channelGroups = animationClip.channelGroups();
- for (const auto channelGroup : channelGroups) {
- dbg << channelGroup;
+ const QVector<Channel> channels = animationClip.channels();
+ for (const auto &channel : channels) {
+ dbg << channel;
}
return dbg;
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp
index 65a455144..1f675f271 100644
--- a/src/animation/backend/animationutils.cpp
+++ b/src/animation/backend/animationutils.cpp
@@ -37,6 +37,9 @@
#include "animationutils_p.h"
#include <Qt3DAnimation/private/handler_p.h>
#include <Qt3DAnimation/private/managers_p.h>
+#include <Qt3DAnimation/private/clipblendnode_p.h>
+#include <Qt3DAnimation/private/clipblendnodevisitor_p.h>
+#include <Qt3DAnimation/private/clipblendvalue_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/private/qpropertyupdatedchangebase_p.h>
#include <QtGui/qvector2d.h>
@@ -46,17 +49,60 @@
#include <QtCore/qvariant.h>
#include <Qt3DAnimation/private/animationlogging_p.h>
+#include <numeric>
+
QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
namespace Animation {
-double AnimationUtils::localTimeFromGlobalTime(double t_global,
- double t_start_global,
- double playbackRate,
- double duration,
- int loopCount,
- int &currentLoop)
+int componentsForType(int type)
+{
+ int componentCount = 1;
+ switch (type) {
+ case QVariant::Double:
+ componentCount = 1;
+ break;
+
+ case QVariant::Vector2D:
+ componentCount = 2;
+ break;
+
+ case QVariant::Vector3D:
+ componentCount = 3;
+ break;
+
+ case QVariant::Vector4D:
+ case QVariant::Quaternion:
+ componentCount = 4;
+ break;
+
+ default:
+ qWarning() << "Unhandled animation type";
+ }
+
+ return componentCount;
+}
+
+ClipEvaluationData evaluationDataForClip(AnimationClip *clip,
+ const AnimatorEvaluationData &animatorData)
+{
+ // global time values expected in seconds
+ ClipEvaluationData result;
+ result.localTime = localTimeFromGlobalTime(animatorData.globalTime, animatorData.startTime,
+ animatorData.playbackRate, clip->duration(),
+ animatorData.loopCount, result.currentLoop);
+ result.isFinalFrame = isFinalFrame(result.localTime, clip->duration(),
+ result.currentLoop, animatorData.loopCount);
+ return result;
+}
+
+double localTimeFromGlobalTime(double t_global,
+ double t_start_global,
+ double playbackRate,
+ double duration,
+ int loopCount,
+ int &currentLoop)
{
double t_local = playbackRate * (t_global - t_start_global);
double loopNumber = 0;
@@ -89,60 +135,48 @@ double AnimationUtils::localTimeFromGlobalTime(double t_global,
return t_local;
}
-QVector<int> AnimationUtils::channelsToIndices(const ChannelGroup &channelGroup, int dataType, int offset)
+double phaseFromGlobalTime(double t_global, double t_start_global,
+ double playbackRate, double duration,
+ int loopCount, int &currentLoop)
{
- static const QStringList standardSuffixes = (QStringList()
- << QLatin1String("x")
- << QLatin1String("y")
- << QLatin1String("z")
- << QLatin1String("w"));
- static const QStringList quaternionSuffixes = (QStringList()
- << QLatin1String("w")
- << QLatin1String("x")
- << QLatin1String("y")
- << QLatin1String("z"));
+ const double t_local = localTimeFromGlobalTime(t_global, t_start_global, playbackRate,
+ duration, loopCount, currentLoop);
+ return t_local / duration;
+}
+
+ComponentIndices channelComponentsToIndices(const Channel &channel, int dataType, int offset)
+{
+#if defined Q_COMPILER_UNIFORM_INIT
+ static const QVector<char> standardSuffixes = { 'X', 'Y', 'Z', 'W' };
+ static const QVector<char> quaternionSuffixes = { 'W', 'X', 'Y', 'Z' };
+#else
+ static const QVector<char> standardSuffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
+ static const QVector<char> quaternionSuffixes = (QVector<char>() << 'W' << 'X' << 'Y' << 'Z');
+#endif
if (dataType != QVariant::Quaternion)
- return channelsToIndicesHelper(channelGroup, dataType, offset, standardSuffixes);
+ return channelComponentsToIndicesHelper(channel, dataType, offset, standardSuffixes);
else
- return channelsToIndicesHelper(channelGroup, dataType, offset, quaternionSuffixes);
-
+ return channelComponentsToIndicesHelper(channel, dataType, offset, quaternionSuffixes);
}
-QVector<int> AnimationUtils::channelsToIndicesHelper(const ChannelGroup &channelGroup, int dataType, int offset, const QStringList &suffixes)
+ComponentIndices channelComponentsToIndicesHelper(const Channel &channel,
+ int dataType,
+ int offset,
+ const QVector<char> &suffixes)
{
- int expectedChannelCount = 1;
- switch (dataType) {
- case QVariant::Double:
- expectedChannelCount = 1;
- break;
-
- case QVariant::Vector2D:
- expectedChannelCount = 2;
- break;
-
- case QVariant::Vector3D:
- expectedChannelCount = 3;
- break;
-
- case QVariant::Vector4D:
- case QVariant::Quaternion:
- expectedChannelCount = 4;
- break;
-
- default:
- qWarning() << "Unhandled animation type";
- }
-
- const int foundChannelCount = channelGroup.channels.size();
- if (foundChannelCount != expectedChannelCount) {
- qWarning() << "Data type expects" << expectedChannelCount
- << "but found" << foundChannelCount << "channels in the animation clip";
+ const int expectedComponentCount = componentsForType(dataType);
+ const int actualComponentCount = channel.channelComponents.size();
+ if (actualComponentCount != expectedComponentCount) {
+ qWarning() << "Data type expects" << expectedComponentCount
+ << "but found" << actualComponentCount << "components in the animation clip";
}
- QVector<int> indices(expectedChannelCount);
- for (int i = 0; i < expectedChannelCount; ++i) {
- int index = suffixes.indexOf(channelGroup.channels[i].name);
+ ComponentIndices indices(expectedComponentCount);
+ for (int i = 0; i < expectedComponentCount; ++i) {
+ const QString &componentName = channel.channelComponents[i].name;
+ char suffix = componentName.at(componentName.length() - 1).toLatin1();
+ int index = suffixes.indexOf(suffix);
if (index != -1)
indices[i] = index + offset;
else
@@ -151,57 +185,35 @@ QVector<int> AnimationUtils::channelsToIndicesHelper(const ChannelGroup &channel
return indices;
}
-QVector<float> AnimationUtils::evaluateAtGlobalTime(AnimationClip *clip,
- qint64 globalTime,
- qint64 startTime,
- int loopCount,
- int &currentLoop,
- bool &finalFrame)
-{
- // Calculate local time from global time
- const double t_global = double(globalTime) / 1.0e9;
- const double t_start_global = double(startTime) / 1.0e9;
- const double playbackRate = 1.0; // Assume standard playback rate for now
- const double duration = clip->duration();
-
- const double localTime = localTimeFromGlobalTime(t_global, t_start_global,
- playbackRate, duration,
- loopCount, currentLoop);
- return AnimationUtils::evaluateAtLocalTime(clip, localTime,
- currentLoop, loopCount,
- finalFrame);
-}
-
-QVector<float> AnimationUtils::evaluateAtLocalTime(AnimationClip *clip, float localTime,
- int currentLoop, int loopCount,
- bool &finalFrame)
+ClipResults evaluateClipAtLocalTime(AnimationClip *clip, float localTime)
{
QVector<float> channelResults;
Q_ASSERT(clip);
- // TODO: Uncomment when we add loopCount property
- if (localTime >= clip->duration()
- && loopCount != 0
- && currentLoop == loopCount - 1)
- finalFrame = true;
-
// Ensure we have enough storage to hold the evaluations
channelResults.resize(clip->channelCount());
// Iterate over channels and evaluate the fcurves
- const QVector<ChannelGroup> &channelGroups = clip->channelGroups();
+ const QVector<Channel> &channels = clip->channels();
int i = 0;
- for (const ChannelGroup &channelGroup : channelGroups) {
- for (const auto channel : qAsConst(channelGroup.channels))
- channelResults[i++] = channel.fcurve.evaluateAtTime(localTime);
+ for (const Channel &channel : channels) {
+ for (const auto &channelComponent : qAsConst(channel.channelComponents))
+ channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime);
}
return channelResults;
}
-QVector<Qt3DCore::QSceneChangePtr> AnimationUtils::preparePropertyChanges(Qt3DCore::QNodeId peerId,
- const QVector<MappingData> &mappingDataVec,
- const QVector<float> &channelResults,
- bool finalFrame)
+ClipResults evaluateClipAtPhase(AnimationClip *clip, float phase)
+{
+ // Calculate the clip local time from the phase and clip duration
+ const double localTime = phase * clip->duration();
+ return evaluateClipAtLocalTime(clip, localTime);
+}
+
+QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId,
+ const QVector<MappingData> &mappingDataVec,
+ const QVector<float> &channelResults,
+ bool finalFrame)
{
QVector<Qt3DCore::QSceneChangePtr> changes;
// Iterate over the mappings
@@ -212,7 +224,7 @@ QVector<Qt3DCore::QSceneChangePtr> AnimationUtils::preparePropertyChanges(Qt3DCo
e->setPropertyName(mappingData.propertyName);
// Handle intermediate updates vs final flag properly
- Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isFinal = finalFrame;
+ Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isIntermediate = !finalFrame;
// Build the new value from the channel/fcurve evaluation results
QVariant v;
@@ -269,21 +281,24 @@ QVector<Qt3DCore::QSceneChangePtr> AnimationUtils::preparePropertyChanges(Qt3DCo
// If it's the final frame, notify the frontend that we've stopped
if (finalFrame) {
- auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId);
+ auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(animatorId);
e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll);
e->setPropertyName("running");
e->setValue(false);
- Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isFinal = true;
changes.push_back(e);
}
return changes;
}
-QVector<AnimationUtils::MappingData> AnimationUtils::buildPropertyMappings(Handler *handler, const AnimationClip *clip, const ChannelMapper *mapper)
+//TODO: Remove this and use new implementation below for both the unblended
+// and blended animation cases.
+QVector<MappingData> buildPropertyMappings(Handler *handler,
+ const AnimationClip *clip,
+ const ChannelMapper *mapper)
{
QVector<MappingData> mappingDataVec;
ChannelMappingManager *mappingManager = handler->channelMappingManager();
- const QVector<ChannelGroup> &channelGroups = clip->channelGroups();
+ const QVector<Channel> &channels = clip->channels();
// Iterate over the mappings in the mapper object
for (const Qt3DCore::QNodeId mappingId : mapper->mappingIds()) {
@@ -308,13 +323,13 @@ QVector<AnimationUtils::MappingData> AnimationUtils::buildPropertyMappings(Handl
const QString channelName = mapping->channelName();
int channelGroupIndex = 0;
bool foundMatch = false;
- for (const ChannelGroup &channelGroup : channelGroups) {
- if (channelGroup.name == channelName) {
+ for (const Channel &channel : channels) {
+ if (channel.name == channelName) {
foundMatch = true;
- const int channelBaseIndex = clip->channelBaseIndex(channelGroupIndex);
+ const int channelBaseIndex = clip->channelComponentBaseIndex(channelGroupIndex);
// Within this group, match channel names with index ordering
- mappingData.channelIndices = channelsToIndices(channelGroup, mappingData.type, channelBaseIndex);
+ mappingData.channelIndices = channelComponentsToIndices(channel, mappingData.type, channelBaseIndex);
// Store the mapping data
mappingDataVec.push_back(mappingData);
@@ -330,6 +345,226 @@ QVector<AnimationUtils::MappingData> AnimationUtils::buildPropertyMappings(Handl
return mappingDataVec;
}
+QVector<MappingData> buildPropertyMappings(const QVector<ChannelMapping*> &channelMappings,
+ const QVector<ChannelNameAndType> &channelNamesAndTypes,
+ const QVector<ComponentIndices> &channelComponentIndices)
+{
+ QVector<MappingData> mappingDataVec;
+ mappingDataVec.reserve(channelMappings.size());
+
+ // Iterate over the mappings
+ for (const auto mapping : channelMappings) {
+ // Populate the data we need, easy stuff first
+ MappingData mappingData;
+ mappingData.targetId = mapping->targetId();
+ mappingData.propertyName = mapping->propertyName();
+ mappingData.type = mapping->type();
+
+ if (mappingData.type == static_cast<int>(QVariant::Invalid)) {
+ qWarning() << "Unknown type for node id =" << mappingData.targetId
+ << "and property =" << mapping->property();
+ continue;
+ }
+
+ // Try to find matching channel name and type
+ const ChannelNameAndType nameAndType = { mapping->channelName(), mapping->type() };
+ const int index = channelNamesAndTypes.indexOf(nameAndType);
+ if (index != -1) {
+ // We got one!
+ mappingData.channelIndices = channelComponentIndices[index];
+ mappingDataVec.push_back(mappingData);
+ }
+ }
+
+ return mappingDataVec;
+}
+
+QVector<ChannelNameAndType> buildRequiredChannelsAndTypes(Handler *handler,
+ const ChannelMapper *mapper)
+{
+ ChannelMappingManager *mappingManager = handler->channelMappingManager();
+ const QVector<Qt3DCore::QNodeId> mappingIds = mapper->mappingIds();
+
+ // Reserve enough storage assuming each mapping is for a different channel.
+ // May be overkill but avoids potential for multiple allocations
+ QVector<ChannelNameAndType> namesAndTypes;
+ namesAndTypes.reserve(mappingIds.size());
+
+ // Iterate through the mappings and add ones not already used by an earlier mapping.
+ // We could add them all then sort and remove duplicates. However, our approach has the
+ // advantage of keeping the blend tree format more consistent with the mapping
+ // orderings which will have better cache locality when generating events.
+ for (const Qt3DCore::QNodeId mappingId : mappingIds) {
+ // Get the mapping object
+ ChannelMapping *mapping = mappingManager->lookupResource(mappingId);
+ Q_ASSERT(mapping);
+
+ // Get the name and type
+ const ChannelNameAndType nameAndType{ mapping->channelName(), mapping->type() };
+
+ // Add if not already contained
+ if (!namesAndTypes.contains(nameAndType))
+ namesAndTypes.push_back(nameAndType);
+ }
+
+ return namesAndTypes;
+}
+
+QVector<ComponentIndices> assignChannelComponentIndices(const QVector<ChannelNameAndType> &namesAndTypes)
+{
+ QVector<ComponentIndices> channelComponentIndices;
+ channelComponentIndices.reserve(namesAndTypes.size());
+
+ int baseIndex = 0;
+ for (const auto &entry : namesAndTypes) {
+ // Populate indices in order
+ const int componentCount = componentsForType(entry.type);
+ ComponentIndices indices(componentCount);
+ std::iota(indices.begin(), indices.end(), baseIndex);
+
+ // Append to the results
+ channelComponentIndices.push_back(indices);
+
+ // Increment baseIndex
+ baseIndex += componentCount;
+ }
+
+ return channelComponentIndices;
+}
+
+QVector<Qt3DCore::QNodeId> gatherValueNodesToEvaluate(Handler *handler,
+ Qt3DCore::QNodeId blendTreeRootId)
+{
+ Q_ASSERT(handler);
+ Q_ASSERT(blendTreeRootId.isNull() == false);
+
+ // We need the ClipBlendNodeManager to be able to lookup nodes from their Ids
+ ClipBlendNodeManager *nodeManager = handler->clipBlendNodeManager();
+
+ // Visit the tree in a pre-order manner and collect the dependencies
+ QVector<Qt3DCore::QNodeId> clipIds;
+ ClipBlendNodeVisitor visitor(nodeManager,
+ ClipBlendNodeVisitor::PreOrder,
+ ClipBlendNodeVisitor::VisitOnlyDependencies);
+
+ auto func = [&clipIds, nodeManager] (ClipBlendNode *blendNode) {
+ const auto dependencyIds = blendNode->currentDependencyIds();
+ for (const auto dependencyId : dependencyIds) {
+ // Look up the blend node and if it's a value type (clip),
+ // add it to the set of value node ids that need to be evaluated
+ ClipBlendNode *node = nodeManager->lookupNode(dependencyId);
+ if (node && node->blendType() == ClipBlendNode::ValueType)
+ clipIds.append(dependencyId);
+ }
+ };
+ visitor.traverse(blendTreeRootId, func);
+
+ // Sort and remove duplicates
+ std::sort(clipIds.begin(), clipIds.end());
+ auto last = std::unique(clipIds.begin(), clipIds.end());
+ clipIds.erase(last, clipIds.end());
+ return clipIds;
+}
+
+ComponentIndices generateClipFormatIndices(const QVector<ChannelNameAndType> &targetChannels,
+ const QVector<ComponentIndices> &targetIndices,
+ const AnimationClip *clip)
+{
+ Q_ASSERT(targetChannels.size() == targetIndices.size());
+
+ // Reserve enough storage for all the format indices
+ int indexCount = 0;
+ for (const auto targetIndexVec : targetIndices)
+ indexCount += targetIndexVec.size();
+ ComponentIndices format;
+ format.resize(indexCount);
+
+
+ // Iterate through the target channels
+ const int channelCount = targetChannels.size();
+ auto formatIt = format.begin();
+ for (int i = 0; i < channelCount; ++i) {
+ // Find the index of the channel from the clip
+ const ChannelNameAndType &targetChannel = targetChannels[i];
+ const int clipChannelIndex = clip->channelIndex(targetChannel.name);
+
+ // TODO: Ensure channel in the clip has enough components to map to the type.
+ // Requires some improvements to the clip data structure first.
+ // TODO: I don't think we need the targetIndices, only the number of components
+ // for each target channel. Check once blend tree is complete.
+ const int componentCount = targetIndices[i].size();
+
+ if (clipChannelIndex != -1) {
+ // Found a matching channel in the clip. Get the base channel
+ // component index and populate the format indices for this channel.
+ const int baseIndex = clip->channelComponentBaseIndex(clipChannelIndex);
+ std::iota(formatIt, formatIt + componentCount, baseIndex);
+
+ } else {
+ // No such channel in this clip. We'll use default values when
+ // mapping from the clip to the formatted clip results.
+ std::fill(formatIt, formatIt + componentCount, -1);
+ }
+
+ formatIt += componentCount;
+ }
+
+ return format;
+}
+
+ClipResults formatClipResults(const ClipResults &rawClipResults,
+ const ComponentIndices &format)
+{
+ // Resize the output to match the number of indices
+ const int elementCount = format.size();
+ ClipResults formattedClipResults(elementCount);
+
+ // Perform a gather operation to format the data
+ // TODO: For large numbers of components do this in parallel with
+ // for e.g. a parallel_for() like construct
+ for (int i = 0; i < elementCount; ++i) {
+ const float value = format[i] != -1 ? rawClipResults[format[i]] : 0.0f;
+ formattedClipResults[i] = value;
+ }
+
+ return formattedClipResults;
+}
+
+ClipResults evaluateBlendTree(Handler *handler,
+ BlendedClipAnimator *animator,
+ Qt3DCore::QNodeId blendTreeRootId)
+{
+ Q_ASSERT(handler);
+ Q_ASSERT(blendTreeRootId.isNull() == false);
+ const Qt3DCore::QNodeId animatorId = animator->peerId();
+
+ // We need the ClipBlendNodeManager to be able to lookup nodes from their Ids
+ ClipBlendNodeManager *nodeManager = handler->clipBlendNodeManager();
+
+ // Visit the tree in a post-order manner and for each interior node call
+ // blending function. We only need to visit the nodes that affect the blend
+ // tree at this time.
+ ClipBlendNodeVisitor visitor(nodeManager,
+ ClipBlendNodeVisitor::PostOrder,
+ ClipBlendNodeVisitor::VisitOnlyDependencies);
+
+ // TODO: When jobs can spawn other jobs we could evaluate subtrees of
+ // the blend tree in parallel. Since it's just a dependency tree, it maps
+ // simply onto the dependencies between jobs.
+ auto func = [animatorId] (ClipBlendNode *blendNode) {
+ // Look up the blend node and if it's an interior node, perform
+ // the blend operation
+ if (blendNode->blendType() != ClipBlendNode::ValueType)
+ blendNode->blend(animatorId);
+ };
+ visitor.traverse(blendTreeRootId, func);
+
+ // The clip results stored in the root node for this animator
+ // now represent the result of the blend tree evaluation
+ ClipBlendNode *blendTreeRootNode = nodeManager->lookupNode(blendTreeRootId);
+ Q_ASSERT(blendTreeRootNode);
+ return blendTreeRootNode->clipResults(animatorId);
+}
} // Animation
} // Qt3DAnimation
diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h
index e7f019aee..f8fcbafa7 100644
--- a/src/animation/backend/animationutils_p.h
+++ b/src/animation/backend/animationutils_p.h
@@ -57,69 +57,150 @@ QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
namespace Animation {
-class ChannelGroup;
+struct Channel;
+class BlendedClipAnimator;
class Handler;
class AnimationClip;
class ChannelMapper;
+class ChannelMapping;
-class Q_AUTOTEST_EXPORT AnimationUtils
+typedef QVector<int> ComponentIndices;
+
+struct MappingData
+{
+ Qt3DCore::QNodeId targetId;
+ const char *propertyName;
+ int type;
+ ComponentIndices channelIndices;
+};
+
+struct AnimatorEvaluationData
{
-public:
- struct MappingData {
- Qt3DCore::QNodeId targetId;
- const char *propertyName;
- int type;
- int channelBaseIndex;
- QVector<int> channelIndices;
- };
-
- struct BlendingMappingData {
- Qt3DCore::QNodeId targetId;
- const char *propertyName;
- int type;
- QVector<int> channelIndicesClip1;
- QVector<int> channelIndicesClip2;
-
- enum BlendAction {
- NoBlending, // Use the channel from Clip1 only
- ClipBlending, // Blending 2 clips sharing the same channel
- };
- BlendAction blendAction;
- };
-
- static double localTimeFromGlobalTime(double t_global, double t_start_global,
- double playbackRate, double duration,
- int loopCount, int &currentLoop);
- static QVector<int> channelsToIndices(const ChannelGroup &channelGroup,
- int dataType,
- int offset = 0);
- static QVector<int> channelsToIndicesHelper(const ChannelGroup &channelGroup,
- int dataType,
- int offset,
- const QStringList &suffixes);
- static QVector<float> evaluateAtGlobalTime(AnimationClip *clip,
- qint64 globalTime,
- qint64 startTime,
- int loopCount,
- int &currentLoop,
- bool &finalFrame);
- static QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId peerId,
- const QVector<MappingData> &mappingData,
- const QVector<float> &channelResults,
- bool finalFrame);
- static QVector<MappingData> buildPropertyMappings(Handler *handler,
- const AnimationClip *clip,
- const ChannelMapper *mapper);
-
-
-private:
- static QVector<float> evaluateAtLocalTime(AnimationClip *clip,
- float localTime,
- int currentLoop,
- int loopCount,
- bool &finalFrame);
+ double globalTime;
+ double startTime;
+ int loopCount;
+ double playbackRate;
};
+struct ClipEvaluationData
+{
+ int currentLoop;
+ double localTime;
+ bool isFinalFrame;
+};
+
+typedef QVector<float> ClipResults;
+
+struct ChannelNameAndType
+{
+ QString name;
+ int type;
+
+ bool operator==(const ChannelNameAndType &rhs) const
+ {
+ return name == rhs.name && type == rhs.type;
+ }
+};
+
+template<typename Animator>
+AnimatorEvaluationData evaluationDataForAnimator(Animator animator, qint64 globalTime)
+{
+ AnimatorEvaluationData data;
+ data.loopCount = animator->loops();
+ data.playbackRate = 1.0; // should be a property on the animator
+ // Convert global time from nsec to sec
+ data.startTime = double(animator->startTime()) / 1.0e9;
+ data.globalTime = double(globalTime) / 1.0e9;
+ return data;
+}
+
+inline bool isFinalFrame(double localTime,
+ double duration,
+ int currentLoop,
+ int loopCount)
+{
+ return (localTime >= duration &&
+ loopCount != 0 &&
+ currentLoop >= loopCount - 1);
+}
+
+Q_AUTOTEST_EXPORT
+int componentsForType(int type);
+
+Q_AUTOTEST_EXPORT
+ClipEvaluationData evaluationDataForClip(AnimationClip *clip,
+ const AnimatorEvaluationData &animatorData);
+
+Q_AUTOTEST_EXPORT
+ComponentIndices channelComponentsToIndices(const Channel &channel,
+ int dataType,
+ int offset = 0);
+
+Q_AUTOTEST_EXPORT
+ComponentIndices channelComponentsToIndicesHelper(const Channel &channelGroup,
+ int dataType,
+ int offset,
+ const QVector<char> &suffixes);
+
+Q_AUTOTEST_EXPORT
+ClipResults evaluateClipAtLocalTime(AnimationClip *clip,
+ float localTime);
+
+Q_AUTOTEST_EXPORT
+ClipResults evaluateClipAtPhase(AnimationClip *clip,
+ float phase);
+
+Q_AUTOTEST_EXPORT
+QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId,
+ const QVector<MappingData> &mappingData,
+ const QVector<float> &channelResults,
+ bool finalFrame);
+
+Q_AUTOTEST_EXPORT
+QVector<MappingData> buildPropertyMappings(Handler *handler,
+ const AnimationClip *clip,
+ const ChannelMapper *mapper);
+
+Q_AUTOTEST_EXPORT
+QVector<MappingData> buildPropertyMappings(const QVector<ChannelMapping *> &channelMappings,
+ const QVector<ChannelNameAndType> &channelNamesAndTypes,
+ const QVector<ComponentIndices> &channelComponentIndices);
+
+Q_AUTOTEST_EXPORT
+QVector<ChannelNameAndType> buildRequiredChannelsAndTypes(Handler *handler,
+ const ChannelMapper *mapper);
+
+Q_AUTOTEST_EXPORT
+QVector<ComponentIndices> assignChannelComponentIndices(const QVector<ChannelNameAndType> &namesAndTypes);
+
+Q_AUTOTEST_EXPORT
+double localTimeFromGlobalTime(double t_global, double t_start_global,
+ double playbackRate, double duration,
+ int loopCount, int &currentLoop);
+
+Q_AUTOTEST_EXPORT
+double phaseFromGlobalTime(double t_global, double t_start_global,
+ double playbackRate, double duration,
+ int loopCount, int &currentLoop);
+
+Q_AUTOTEST_EXPORT
+QVector<Qt3DCore::QNodeId> gatherValueNodesToEvaluate(Handler *handler,
+ Qt3DCore::QNodeId blendTreeRootId);
+
+Q_AUTOTEST_EXPORT
+ComponentIndices generateClipFormatIndices(const QVector<ChannelNameAndType> &targetChannels,
+ const QVector<ComponentIndices> &targetIndices,
+ const AnimationClip *clip);
+
+Q_AUTOTEST_EXPORT
+ClipResults formatClipResults(const ClipResults &rawClipResults,
+ const ComponentIndices &format);
+
+Q_AUTOTEST_EXPORT
+ClipResults evaluateBlendTree(Handler *handler,
+ BlendedClipAnimator *animator,
+ Qt3DCore::QNodeId blendNodeId);
+
} // Animation
} // Qt3DAnimation
diff --git a/src/animation/backend/backend.pri b/src/animation/backend/backend.pri
index b0b0feaf0..2bc72e1d3 100644
--- a/src/animation/backend/backend.pri
+++ b/src/animation/backend/backend.pri
@@ -2,7 +2,6 @@
INCLUDEPATH += $$PWD
HEADERS += \
- $$PWD/animationclip_p.h \
$$PWD/handle_types_p.h \
$$PWD/handler_p.h \
$$PWD/nodefunctor_p.h \
@@ -13,7 +12,6 @@ HEADERS += \
$$PWD/functionrangefinder_p.h \
$$PWD/clipanimator_p.h \
$$PWD/blendedclipanimator_p.h \
- $$PWD/conductedclipanimator_p.h \
$$PWD/backendnode_p.h \
$$PWD/loadanimationclipjob_p.h \
$$PWD/channelmapping_p.h \
@@ -21,21 +19,22 @@ HEADERS += \
$$PWD/findrunningclipanimatorsjob_p.h \
$$PWD/evaluateclipanimatorjob_p.h \
$$PWD/clipblendnode_p.h \
- $$PWD/lerpblend_p.h \
$$PWD/clipblendnodevisitor_p.h \
$$PWD/animationutils_p.h \
$$PWD/buildblendtreesjob_p.h \
- $$PWD/evaluateblendclipanimatorjob_p.h
+ $$PWD/evaluateblendclipanimatorjob_p.h \
+ $$PWD/lerpclipblend_p.h \
+ $$PWD/additiveclipblend_p.h \
+ $$PWD/clipblendvalue_p.h \
+ $$PWD/animationclip_p.h
SOURCES += \
- $$PWD/animationclip.cpp \
$$PWD/handler.cpp \
$$PWD/fcurve.cpp \
$$PWD/bezierevaluator.cpp \
$$PWD/functionrangefinder.cpp \
$$PWD/clipanimator.cpp \
$$PWD/blendedclipanimator.cpp \
- $$PWD/conductedclipanimator.cpp \
$$PWD/backendnode.cpp \
$$PWD/loadanimationclipjob.cpp \
$$PWD/channelmapping.cpp \
@@ -43,9 +42,12 @@ SOURCES += \
$$PWD/findrunningclipanimatorsjob.cpp \
$$PWD/evaluateclipanimatorjob.cpp \
$$PWD/clipblendnode.cpp \
- $$PWD/lerpblend.cpp \
$$PWD/managers.cpp \
$$PWD/clipblendnodevisitor.cpp \
$$PWD/animationutils.cpp \
$$PWD/buildblendtreesjob.cpp \
- $$PWD/evaluateblendclipanimatorjob.cpp
+ $$PWD/evaluateblendclipanimatorjob.cpp \
+ $$PWD/lerpclipblend.cpp \
+ $$PWD/additiveclipblend.cpp \
+ $$PWD/clipblendvalue.cpp \
+ $$PWD/animationclip.cpp
diff --git a/src/animation/backend/bezierevaluator.cpp b/src/animation/backend/bezierevaluator.cpp
index 6ed8b1aa2..9d81a2f2f 100644
--- a/src/animation/backend/bezierevaluator.cpp
+++ b/src/animation/backend/bezierevaluator.cpp
@@ -83,7 +83,7 @@ float BezierEvaluator::valueForTime(float time) const
{
const float u = parameterForTime(time);
- // Calulate powers of u and (1-u) that we need
+ // Calculate powers of u and (1-u) that we need
const float u2 = u * u;
const float u3 = u2 * u;
const float mu = 1.0f - u;
diff --git a/src/animation/backend/blendedclipanimator.cpp b/src/animation/backend/blendedclipanimator.cpp
index 1cb774f72..08789a5f9 100644
--- a/src/animation/backend/blendedclipanimator.cpp
+++ b/src/animation/backend/blendedclipanimator.cpp
@@ -74,7 +74,6 @@ void BlendedClipAnimator::cleanup()
m_startGlobalTime = 0;
m_currentLoop = 0;
m_loops = 1;
- m_mappingData.clear();
}
void BlendedClipAnimator::setBlendTreeRootId(Qt3DCore::QNodeId blendTreeId)
@@ -95,11 +94,6 @@ void BlendedClipAnimator::setRunning(bool running)
setDirty(Handler::BlendedClipAnimatorDirty);
}
-void BlendedClipAnimator::setMappingData(const QVector<AnimationUtils::BlendingMappingData> mappingData)
-{
- m_mappingData = mappingData;
-}
-
void BlendedClipAnimator::sendPropertyChanges(const QVector<Qt3DCore::QSceneChangePtr> &changes)
{
for (const Qt3DCore::QSceneChangePtr &change : changes)
diff --git a/src/animation/backend/blendedclipanimator_p.h b/src/animation/backend/blendedclipanimator_p.h
index d0db13e53..4421cb43a 100644
--- a/src/animation/backend/blendedclipanimator_p.h
+++ b/src/animation/backend/blendedclipanimator_p.h
@@ -77,17 +77,18 @@ public:
void setMapperId(Qt3DCore::QNodeId mapperId);
void setRunning(bool running);
- void setMappingData(const QVector<AnimationUtils::BlendingMappingData> mappingData);
- QVector<AnimationUtils::BlendingMappingData> mappingData() const { return m_mappingData; }
-
void setStartTime(qint64 globalTime) { m_startGlobalTime = globalTime; }
qint64 startTime() const { return m_startGlobalTime; }
+ void setLoops(int loops) { m_loops = loops; }
int loops() const { return m_loops; }
int currentLoop() const { return m_currentLoop; }
void setCurrentLoop(int currentLoop) { m_currentLoop = currentLoop; }
+ void setMappingData(const QVector<MappingData> &mappingData) { m_mappingData = mappingData; }
+ QVector<MappingData> mappingData() const { return m_mappingData; }
+
void sendPropertyChanges(const QVector<Qt3DCore::QSceneChangePtr> &changes);
private:
@@ -97,9 +98,10 @@ private:
bool m_running;
qint64 m_startGlobalTime;
- QVector<AnimationUtils::BlendingMappingData> m_mappingData;
int m_currentLoop;
int m_loops;
+
+ QVector<MappingData> m_mappingData;
};
} // namespace Animation
diff --git a/src/animation/backend/buildblendtreesjob.cpp b/src/animation/backend/buildblendtreesjob.cpp
index a8e969a39..fe56099a2 100644
--- a/src/animation/backend/buildblendtreesjob.cpp
+++ b/src/animation/backend/buildblendtreesjob.cpp
@@ -39,7 +39,9 @@
#include <Qt3DAnimation/private/managers_p.h>
#include <Qt3DAnimation/private/clipblendnodevisitor_p.h>
#include <Qt3DAnimation/private/clipblendnode_p.h>
-#include <Qt3DAnimation/private/lerpblend_p.h>
+#include <Qt3DAnimation/private/clipblendvalue_p.h>
+#include <Qt3DAnimation/private/lerpclipblend_p.h>
+#include <Qt3DAnimation/private/job_common_p.h>
QT_BEGIN_NAMESPACE
@@ -49,7 +51,7 @@ namespace Animation {
BuildBlendTreesJob::BuildBlendTreesJob()
: Qt3DCore::QAspectJob()
{
- // TO DO: Add Profiler ID
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::BuildBlendTree, 0);
}
void BuildBlendTreesJob::setBlendedClipAnimators(const QVector<HBlendedClipAnimator> &blendedClipAnimatorHandles)
@@ -57,84 +59,68 @@ void BuildBlendTreesJob::setBlendedClipAnimators(const QVector<HBlendedClipAnima
m_blendedClipAnimatorHandles = blendedClipAnimatorHandles;
}
+// Note this job is run once for all stopped blended animators that have been marked dirty
+// We assume that the structure of blend node tree does not change once a BlendClipAnimator has been set to running
void BuildBlendTreesJob::run()
{
- for (const HBlendedClipAnimator blendedClipAnimatorHandle : m_blendedClipAnimatorHandles) {
- // Retrive BlendTree node
+ for (const HBlendedClipAnimator blendedClipAnimatorHandle : qAsConst(m_blendedClipAnimatorHandles)) {
+ // Retrieve BlendTree node
BlendedClipAnimator *blendClipAnimator = m_handler->blendedClipAnimatorManager()->data(blendedClipAnimatorHandle);
Q_ASSERT(blendClipAnimator);
- // TO DO: Add support for tree of blend nodes
- // For now assumes only one
-
const bool canRun = blendClipAnimator->canRun();
m_handler->setBlendedClipAnimatorRunning(blendedClipAnimatorHandle, canRun);
- // Check if blend clip can run and if so build blend tree
- if (canRun) {
- const ChannelMapper *mapper = m_handler->channelMapperManager()->lookupResource(blendClipAnimator->mapperId());
- Q_ASSERT(mapper);
- ClipBlendNode *node = m_handler->clipBlendNodeManager()->lookupNode(blendClipAnimator->blendTreeRootId());
-
- const Qt3DCore::QNodeIdVector clipIds = node->clipIds();
- // There must be 2 clips
- if (clipIds.size() != 2) {
- qWarning() << "A Blend Tree requires exactly 2 clips";
- return;
- }
-
- // Retrieve Animation clips
- const AnimationClip *clip1 = m_handler->animationClipManager()->lookupResource(clipIds.first());
- const AnimationClip *clip2 = m_handler->animationClipManager()->lookupResource(clipIds.last());
-
- // Build mappings for the 2 clips
- const QVector<AnimationUtils::MappingData> mappingDataClip1 = AnimationUtils::buildPropertyMappings(m_handler, clip1, mapper);
- const QVector<AnimationUtils::MappingData> mappingDataClip2 = AnimationUtils::buildPropertyMappings(m_handler, clip2, mapper);
-
- // We can only blend channels that are in both clips
- // If a channel is present in one clip and not the other, we use 100% of its value (no blending)
- QVector<AnimationUtils::BlendingMappingData> blendingMappingData;
- const int mappingInClip1Size = mappingDataClip1.size();
- blendingMappingData.reserve(mappingInClip1Size);
-
- // Find mappings that are in both vectors and build mappingData out of that
- for (const AnimationUtils::MappingData &mappingDataInClip1 : mappingDataClip1) {
- AnimationUtils::BlendingMappingData mappingData;
- mappingData.channelIndicesClip1 = mappingDataInClip1.channelIndices;
- mappingData.propertyName = mappingDataInClip1.propertyName;
- mappingData.targetId = mappingDataInClip1.targetId;
- mappingData.type = mappingDataInClip1.type;
- mappingData.blendAction = AnimationUtils::BlendingMappingData::NoBlending;
- blendingMappingData.push_back(mappingData);
- }
-
- for (const AnimationUtils::MappingData &mappingDataInClip2 : mappingDataClip2) {
- bool sharedChannel = false;
- for (int i = 0; i < mappingInClip1Size; ++i) {
- AnimationUtils::BlendingMappingData &mappingDataInClip1 = blendingMappingData[i];
- if ((strcmp(mappingDataInClip1.propertyName, mappingDataInClip2.propertyName) == 0) &&
- mappingDataInClip1.targetId == mappingDataInClip2.targetId &&
- mappingDataInClip1.type == mappingDataInClip2.type) {
- // We have a channel shared in both clips
- mappingDataInClip1.channelIndicesClip2 = mappingDataInClip2.channelIndices;
- mappingDataInClip1.blendAction = AnimationUtils::BlendingMappingData::ClipBlending;
- sharedChannel = true;
- break;
- }
- }
- if (!sharedChannel) { // We have a channel defined in only one of the clips
- AnimationUtils::BlendingMappingData mappingData;
- mappingData.channelIndicesClip2 = mappingDataInClip2.channelIndices;
- mappingData.propertyName = mappingDataInClip2.propertyName;
- mappingData.targetId = mappingDataInClip2.targetId;
- mappingData.type = mappingDataInClip2.type;
- mappingData.blendAction = AnimationUtils::BlendingMappingData::NoBlending;
- blendingMappingData.push_back(mappingData);
- }
- }
+ if (!canRun)
+ continue;
+
+ // Build the format for clip results that should be used by nodes in the blend
+ // tree when used with this animator
+ const ChannelMapper *mapper = m_handler->channelMapperManager()->lookupResource(blendClipAnimator->mapperId());
+ Q_ASSERT(mapper);
+ QVector<ChannelNameAndType> channelNamesAndTypes
+ = buildRequiredChannelsAndTypes(m_handler, mapper);
+ QVector<ComponentIndices> channelComponentIndices
+ = assignChannelComponentIndices(channelNamesAndTypes);
+
+ // Find the leaf value nodes of the blend tree and for each of them
+ // create a set of format indices that can later be used to map the
+ // raw ClipResults resulting from evaluating an animation clip to the
+ // layout used by the blend tree for this animator
+ const QVector<Qt3DCore::QNodeId> valueNodeIds
+ = gatherValueNodesToEvaluate(m_handler, blendClipAnimator->blendTreeRootId());
+ for (const auto valueNodeId : valueNodeIds) {
+ ClipBlendValue *valueNode
+ = static_cast<ClipBlendValue *>(m_handler->clipBlendNodeManager()->lookupNode(valueNodeId));
+ Q_ASSERT(valueNode);
+
+ const Qt3DCore::QNodeId clipId = valueNode->clipId();
+ const AnimationClip *clip = m_handler->animationClipLoaderManager()->lookupResource(clipId);
+ Q_ASSERT(clip);
+
+ const ComponentIndices formatIndices
+ = generateClipFormatIndices(channelNamesAndTypes,
+ channelComponentIndices,
+ clip);
+ valueNode->setFormatIndices(blendClipAnimator->peerId(), formatIndices);
+ }
- blendClipAnimator->setMappingData(blendingMappingData);
+ // Finally, build the mapping data vector for this blended clip animator. This
+ // gets used during the final stage of evaluation when sending the property changes
+ // out to the targets of the animation. We do the costly work once up front.
+ const QVector<Qt3DCore::QNodeId> channelMappingIds = mapper->mappingIds();
+ QVector<ChannelMapping *> channelMappings;
+ channelMappings.reserve(channelMappingIds.size());
+ for (const auto mappingId : channelMappingIds) {
+ ChannelMapping *mapping = m_handler->channelMappingManager()->lookupResource(mappingId);
+ Q_ASSERT(mapping);
+ channelMappings.push_back(mapping);
}
+ const QVector<MappingData> mappingDataVec
+ = buildPropertyMappings(channelMappings,
+ channelNamesAndTypes,
+ channelComponentIndices);
+ blendClipAnimator->setMappingData(mappingDataVec);
}
}
diff --git a/src/animation/backend/channelmapper_p.h b/src/animation/backend/channelmapper_p.h
index 66654d56d..710de01ab 100644
--- a/src/animation/backend/channelmapper_p.h
+++ b/src/animation/backend/channelmapper_p.h
@@ -71,6 +71,7 @@ public:
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
+ void setMappingIds(const QVector<Qt3DCore::QNodeId> &mappingIds) { m_mappingIds = mappingIds; }
QVector<Qt3DCore::QNodeId> mappingIds() const { return m_mappingIds; }
private:
diff --git a/src/animation/backend/channelmapping_p.h b/src/animation/backend/channelmapping_p.h
index 4d6d80bf2..93cf5efed 100644
--- a/src/animation/backend/channelmapping_p.h
+++ b/src/animation/backend/channelmapping_p.h
@@ -70,10 +70,19 @@ public:
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
+ void setChannelName(const QString &channelName) { m_channelName = channelName; }
QString channelName() const { return m_channelName; }
+
+ void setTargetId(Qt3DCore::QNodeId targetId) { m_targetId = targetId; }
Qt3DCore::QNodeId targetId() const { return m_targetId; }
+
+ void setProperty(const QString &property) { m_property = property; }
QString property() const { return m_property; }
+
+ void setType(int type) { m_type = type; }
int type() const { return m_type; }
+
+ void setPropertyName(const char *propertyName) { m_propertyName = propertyName; }
const char *propertyName() const { return m_propertyName; }
private:
diff --git a/src/animation/backend/clipanimator.cpp b/src/animation/backend/clipanimator.cpp
index ff3cb1b12..65fd0f57f 100644
--- a/src/animation/backend/clipanimator.cpp
+++ b/src/animation/backend/clipanimator.cpp
@@ -123,11 +123,6 @@ void ClipAnimator::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
QBackendNode::sceneChangeEvent(e);
}
-void ClipAnimator::setMappingData(const QVector<AnimationUtils::MappingData> mappingData)
-{
- m_mappingData = mappingData;
-}
-
void ClipAnimator::sendPropertyChanges(const QVector<Qt3DCore::QSceneChangePtr> &changes)
{
for (const Qt3DCore::QSceneChangePtr &change : changes)
diff --git a/src/animation/backend/clipanimator_p.h b/src/animation/backend/clipanimator_p.h
index 577d18d7b..da9109cfb 100644
--- a/src/animation/backend/clipanimator_p.h
+++ b/src/animation/backend/clipanimator_p.h
@@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
namespace Animation {
-class ChannelGroup;
+struct Channel;
class Handler;
class Q_AUTOTEST_EXPORT ClipAnimator : public BackendNode
@@ -73,6 +73,7 @@ public:
void setRunning(bool running);
bool isRunning() const { return m_running; }
+ void setLoops(int loops) { m_loops = loops; }
int loops() const { return m_loops; }
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
@@ -80,8 +81,8 @@ public:
// Called by jobs
bool canRun() const { return !m_clipId.isNull() && !m_mapperId.isNull() && m_running; }
- void setMappingData(const QVector<AnimationUtils::MappingData> mappingData);
- QVector<AnimationUtils::MappingData> mappingData() const { return m_mappingData; }
+ void setMappingData(const QVector<MappingData> &mappingData) { m_mappingData = mappingData; }
+ QVector<MappingData> mappingData() const { return m_mappingData; }
void setStartTime(qint64 globalTime) { m_startGlobalTime = globalTime; }
qint64 startTime() const { return m_startGlobalTime; }
@@ -101,7 +102,7 @@ private:
// Working state
qint64 m_startGlobalTime;
- QVector<AnimationUtils::MappingData> m_mappingData;
+ QVector<MappingData> m_mappingData;
int m_currentLoop;
};
diff --git a/src/animation/backend/clipblendnode.cpp b/src/animation/backend/clipblendnode.cpp
index a763f7f48..94a956837 100644
--- a/src/animation/backend/clipblendnode.cpp
+++ b/src/animation/backend/clipblendnode.cpp
@@ -36,7 +36,7 @@
#include "clipblendnode_p.h"
#include <Qt3DAnimation/qclipblendnodecreatedchange.h>
-#include <Qt3DAnimation/qanimationclip.h>
+#include <Qt3DAnimation/qabstractanimationclip.h>
#include <Qt3DCore/qpropertynoderemovedchange.h>
#include <Qt3DCore/qpropertynodeaddedchange.h>
@@ -57,107 +57,101 @@ ClipBlendNode::~ClipBlendNode()
{
}
-Qt3DCore::QNodeId ClipBlendNode::parentId() const
-{
- return m_parentId;
-}
-
-Qt3DCore::QNodeIdVector ClipBlendNode::childrenIds() const
-{
- return m_childrenIds;
-}
-
-Qt3DCore::QNodeIdVector ClipBlendNode::clipIds() const
-{
- return m_clipIds;
-}
-
-void ClipBlendNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
-{
- switch (e->type()) {
-
- case Qt3DCore::PropertyValueAdded: {
- Qt3DCore::QPropertyNodeAddedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeAddedChange>(e);
- if (change->metaObject()->inherits(&QAbstractClipBlendNode::staticMetaObject))
- addChildId(change->addedNodeId());
- else if (change->metaObject()->inherits(&QAnimationClip::staticMetaObject))
- m_clipIds.push_back(change->addedNodeId());
- break;
- }
-
- case Qt3DCore::PropertyValueRemoved: {
- Qt3DCore::QPropertyNodeRemovedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeRemovedChange>(e);
- if (change->metaObject()->inherits(&QAbstractClipBlendNode::staticMetaObject))
- removeChildId(change->removedNodeId());
- else if (change->metaObject()->inherits(&QAnimationClip::staticMetaObject))
- m_clipIds.removeOne(change->removedNodeId());
- break;
- }
-
- default:
- break;
- }
-
- Qt3DCore::QBackendNode::sceneChangeEvent(e);
-}
-
void ClipBlendNode::setClipBlendNodeManager(ClipBlendNodeManager *manager)
{
m_manager = manager;
}
-ClipBlendNodeManager *ClipBlendNode::clipBlendNodeManager() const
+void ClipBlendNode::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
{
- return m_manager;
+ Q_UNUSED(change);
}
-void ClipBlendNode::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
+ClipBlendNode::BlendType Animation::ClipBlendNode::blendType() const
{
- const auto creationChange = qSharedPointerCast<QClipBlendNodeCreatedChangeBase>(change);
- setParentId(creationChange->parentClipBlendNodeId());
- m_clipIds = creationChange->clips();
+ return m_blendType;
}
-void ClipBlendNode::setParentId(Qt3DCore::QNodeId parentId)
+void ClipBlendNode::setClipResults(Qt3DCore::QNodeId animatorId, const ClipResults &clipResults)
{
- if (parentId != m_parentId) {
- // We already had a parent, tell it to abandon us
- if (!m_parentId.isNull()) {
- ClipBlendNode *parent = m_manager->lookupNode(m_parentId);
- if (parent != nullptr)
- parent->m_childrenIds.removeAll(peerId());
- }
- m_parentId = parentId;
- ClipBlendNode *parent = m_manager->lookupNode(m_parentId);
- if (parent != nullptr && !parent->m_childrenIds.contains(peerId()))
- parent->m_childrenIds.append(peerId());
+ // Do we already have an entry for this animator?
+ const int animatorIndex = m_animatorIds.indexOf(animatorId);
+ if (animatorIndex == -1) {
+ // Nope, add it
+ m_animatorIds.push_back(animatorId);
+ m_clipResults.push_back(clipResults);
+ } else {
+ m_clipResults[animatorIndex] = clipResults;
}
}
-void ClipBlendNode::addChildId(Qt3DCore::QNodeId childId)
+ClipResults ClipBlendNode::clipResults(Qt3DCore::QNodeId animatorId) const
{
- if (!m_childrenIds.contains(childId)) {
- ClipBlendNode *child = m_manager->lookupNode(childId);
- if (child != nullptr) {
- m_childrenIds.push_back(childId);
- child->m_parentId = peerId();
- }
- }
+ const int animatorIndex = m_animatorIds.indexOf(animatorId);
+ if (animatorIndex != -1)
+ return m_clipResults[animatorIndex];
+ return ClipResults();
}
-void ClipBlendNode::removeChildId(Qt3DCore::QNodeId childId)
+/*!
+ \fn QVector<Qt3DCore::QNodeId> ClipBlendNode::currentDependencyIds() const
+ \internal
+
+ Each subclass of ClipBlendNode must implement this function such that it
+ returns a vector of the ids of ClipBlendNodes upon which is it dependent
+ in order to be able to evaluate given its current internal state.
+
+ For example, a subclass implementing a simple lerp blend between two
+ other nodes, would always return the ids of the nodes between which it
+ is lerping.
+
+ A more generalised lerp node that is capable of lerping between a
+ series of nodes would return the ids of the two nodes that correspond
+ to the blend values which sandwich the currently set blend value.
+
+ The animation handler will submit a job that uses this function to
+ build a list of clips that must be evaluated in order to later
+ evaluate the entire blend tree. In this way, the clips can all be
+ evaluated in one pass, and the tree in a subsequent pass.
+*/
+
+/*!
+ \fn QVector<Qt3DCore::QNodeId> ClipBlendNode::allDependencyIds() const
+ \internal
+
+ Similar to currentDependencyIds() but returns the ids of all potential
+ dependency nodes, not just those that are dependencies given the current
+ internal state. For example a generalised lerp node would return the ids
+ of all nodes that can participate in the lerp for any value of the blend
+ parameter. Not just those bounding the current blend value.
+*/
+
+/*!
+ \internal
+
+ Fetches the ClipResults from the nodes listed in the dependencyIds
+ and passes them to the doBlend() virtual function which should be
+ implemented in subclasses to perform the actual blend operation.
+ The results are then inserted into the clip results for this blend
+ node indexed by the \a animatorId.
+*/
+void ClipBlendNode::blend(Qt3DCore::QNodeId animatorId)
{
- if (m_childrenIds.contains(childId)) {
- ClipBlendNode *child = m_manager->lookupNode(childId);
- if (child != nullptr)
- child->m_parentId = Qt3DCore::QNodeId();
- m_childrenIds.removeAll(childId);
+ // Obtain the clip results from each of the dependencies
+ const QVector<Qt3DCore::QNodeId> dependencyNodeIds = currentDependencyIds();
+ const int dependencyCount = dependencyNodeIds.size();
+ QVector<ClipResults> blendData;
+ blendData.reserve(dependencyCount);
+ for (const auto dependencyId : dependencyNodeIds) {
+ ClipBlendNode *dependencyNode = clipBlendNodeManager()->lookupNode(dependencyId);
+ ClipResults blendDataElement = dependencyNode->clipResults(animatorId);
+ blendData.push_back(blendDataElement);
}
-}
-ClipBlendNode::BlendType Animation::ClipBlendNode::blendType() const
-{
- return m_blendType;
+ // Ask the blend node to perform the actual blend operation on the data
+ // from the dependencies
+ ClipResults blendedResults = doBlend(blendData);
+ setClipResults(animatorId, blendedResults);
}
} // Animation
diff --git a/src/animation/backend/clipblendnode_p.h b/src/animation/backend/clipblendnode_p.h
index d3af9301b..17eeed9b3 100644
--- a/src/animation/backend/clipblendnode_p.h
+++ b/src/animation/backend/clipblendnode_p.h
@@ -67,35 +67,37 @@ public:
enum BlendType {
NoneBlendType,
- LerpBlendType
+ LerpBlendType,
+ AdditiveBlendType,
+ ValueType
};
void setClipBlendNodeManager(ClipBlendNodeManager *manager);
+ inline ClipBlendNodeManager *clipBlendNodeManager() const { return m_manager; }
- ClipBlendNodeManager *clipBlendNodeManager() const;
BlendType blendType() const;
- Qt3DCore::QNodeId parentId() const;
- Qt3DCore::QNodeIdVector childrenIds() const;
- Qt3DCore::QNodeIdVector clipIds() const;
- void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
+ void blend(Qt3DCore::QNodeId animatorId);
+
+ void setClipResults(Qt3DCore::QNodeId animatorId, const ClipResults &clipResults);
+ ClipResults clipResults(Qt3DCore::QNodeId animatorId) const;
+
+ virtual QVector<Qt3DCore::QNodeId> allDependencyIds() const = 0;
+ virtual QVector<Qt3DCore::QNodeId> currentDependencyIds() const = 0;
+ virtual double duration() const = 0;
protected:
explicit ClipBlendNode(BlendType blendType);
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_OVERRIDE;
+ virtual ClipResults doBlend(const QVector<ClipResults> &blendData) const = 0;
private:
- void setParentId(Qt3DCore::QNodeId parentId);
- void addChildId(Qt3DCore::QNodeId childId);
- void removeChildId(Qt3DCore::QNodeId childId);
-
- // Can either contain clips or nothing (tree of other blend nodes)
- Qt3DCore::QNodeIdVector m_clipIds;
-
- Qt3DCore::QNodeId m_parentId;
- Qt3DCore::QNodeIdVector m_childrenIds;
ClipBlendNodeManager *m_manager;
BlendType m_blendType;
+
+ // Store the results of evaluations indexed by animator id
+ QVector<Qt3DCore::QNodeId> m_animatorIds;
+ QVector<ClipResults> m_clipResults;
};
template<typename Backend, typename Frontend>
diff --git a/src/animation/backend/clipblendnodevisitor.cpp b/src/animation/backend/clipblendnodevisitor.cpp
index 9940bd1f8..5e63a4e79 100644
--- a/src/animation/backend/clipblendnodevisitor.cpp
+++ b/src/animation/backend/clipblendnodevisitor.cpp
@@ -41,34 +41,130 @@
QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
-
namespace Animation {
-ClipBlendNodeVisitor::ClipBlendNodeVisitor(ClipBlendNodeManager *manager)
+/*!
+ \class ClipBlendNodeVisitor
+ \internal
+ Visits a blend tree in either pre- or post-order manner, optionally taking care
+ to only visit the nodes that are dependencies of evaluating the blend
+ tree.
+*/
+ClipBlendNodeVisitor::ClipBlendNodeVisitor(ClipBlendNodeManager *manager,
+ TraversalOrder order,
+ NodeFilter filter)
: m_manager(manager)
+ , m_order(order)
+ , m_filter(filter)
{
}
-void ClipBlendNodeVisitor::traverse(Qt3DCore::QNodeId rootId, const VisitFunction &visitFunction) const
+void ClipBlendNodeVisitor::traverse(Qt3DCore::QNodeId rootId,
+ const VisitFunction &visitFunction) const
{
ClipBlendNode *node = m_manager->lookupNode(rootId);
- if (node != nullptr)
- visit(node, visitFunction);
+ if (node != nullptr) {
+ switch (m_order) {
+ case PreOrder: {
+ switch (m_filter) {
+ case VisitAllNodes:
+ visitPreOrderAllNodes(node, visitFunction);
+ break;
+
+ case VisitOnlyDependencies:
+ visitPreOrderDependencyNodes(node, visitFunction);
+ break;
+ }
+
+ break;
+ }
+
+ case PostOrder: {
+ switch (m_filter) {
+ case VisitAllNodes:
+ visitPostOrderAllNodes(node, visitFunction);
+ break;
+
+ case VisitOnlyDependencies:
+ visitPostOrderDependencyNodes(node, visitFunction);
+ break;
+ }
+
+ break;
+ }
+ }
+ }
}
-void ClipBlendNodeVisitor::visit(ClipBlendNode *node, const VisitFunction &visitFunction) const
+/*!
+ \internal
+ Leaf to root traversal (Pre-order traversal) visiting all nodes even if
+ they will not participate in current evaluation of the blend tree.
+*/
+void ClipBlendNodeVisitor::visitPreOrderAllNodes(ClipBlendNode *node,
+ const VisitFunction &visitFunction) const
{
visitFunction(node);
- const Qt3DCore::QNodeIdVector childIds = node->childrenIds();
+ const Qt3DCore::QNodeIdVector childIds = node->allDependencyIds();
for (const Qt3DCore::QNodeId childId: childIds) {
ClipBlendNode *childNode = m_manager->lookupNode(childId);
if (childNode != nullptr)
- visit(childNode, visitFunction);
+ visitPreOrderAllNodes(childNode, visitFunction);
}
}
-} // Animation
+/*!
+ \internal
+ Leaf to root traversal (Post-order traversal) visiting all nodes even if
+ they will not participate in current evaluation of the blend tree.
+*/
+void ClipBlendNodeVisitor::visitPostOrderAllNodes(ClipBlendNode *node,
+ const VisitFunction &visitFunction) const
+{
+ const Qt3DCore::QNodeIdVector childIds = node->allDependencyIds();
+ for (const Qt3DCore::QNodeId childId: childIds) {
+ ClipBlendNode *childNode = m_manager->lookupNode(childId);
+ if (childNode != nullptr)
+ visitPostOrderAllNodes(childNode, visitFunction);
+ }
+ visitFunction(node);
+}
+
+/*!
+ \internal
+ Leaf to root traversal (Pre-order traversal) visiting only nodes required
+ to evaluate the blend tree given its current state.
+*/
+void ClipBlendNodeVisitor::visitPreOrderDependencyNodes(ClipBlendNode *node,
+ const VisitFunction &visitFunction) const
+{
+ visitFunction(node);
+ const Qt3DCore::QNodeIdVector childIds = node->currentDependencyIds();
+ for (const Qt3DCore::QNodeId childId: childIds) {
+ ClipBlendNode *childNode = m_manager->lookupNode(childId);
+ if (childNode != nullptr)
+ visitPreOrderDependencyNodes(childNode, visitFunction);
+ }
+}
+
+/*!
+ \internal
+ Leaf to root traversal (Post-order traversal) visiting only nodes required
+ to evaluate the blend tree given its current state.
+*/
+void ClipBlendNodeVisitor::visitPostOrderDependencyNodes(ClipBlendNode *node,
+ const VisitFunction &visitFunction) const
+{
+ const Qt3DCore::QNodeIdVector childIds = node->currentDependencyIds();
+ for (const Qt3DCore::QNodeId childId: childIds) {
+ ClipBlendNode *childNode = m_manager->lookupNode(childId);
+ if (childNode != nullptr)
+ visitPostOrderDependencyNodes(childNode, visitFunction);
+ }
+ visitFunction(node);
+}
+} // Animation
} // Qt3DAnimation
QT_END_NAMESPACE
diff --git a/src/animation/backend/clipblendnodevisitor_p.h b/src/animation/backend/clipblendnodevisitor_p.h
index d619c7132..1bedb206a 100644
--- a/src/animation/backend/clipblendnodevisitor_p.h
+++ b/src/animation/backend/clipblendnodevisitor_p.h
@@ -55,7 +55,6 @@
QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
-
namespace Animation {
class ClipBlendNodeManager;
@@ -66,18 +65,35 @@ using VisitFunction = std::function<void (ClipBlendNode *)>;
class Q_AUTOTEST_EXPORT ClipBlendNodeVisitor
{
public:
- explicit ClipBlendNodeVisitor(ClipBlendNodeManager *manager);
+ enum TraversalOrder {
+ PreOrder,
+ PostOrder
+ };
+
+ enum NodeFilter {
+ VisitAllNodes,
+ VisitOnlyDependencies
+ };
+
+ explicit ClipBlendNodeVisitor(ClipBlendNodeManager *manager,
+ TraversalOrder order = PostOrder,
+ NodeFilter filter = VisitAllNodes);
void traverse(Qt3DCore::QNodeId rootId, const VisitFunction &visitFunction) const;
private:
- void visit(ClipBlendNode *node, const VisitFunction &visitFunction) const;
+ void visitPreOrderAllNodes(ClipBlendNode *node, const VisitFunction &visitFunction) const;
+ void visitPostOrderAllNodes(ClipBlendNode *node, const VisitFunction &visitFunction) const;
+
+ void visitPreOrderDependencyNodes(ClipBlendNode *node, const VisitFunction &visitFunction) const;
+ void visitPostOrderDependencyNodes(ClipBlendNode *node, const VisitFunction &visitFunction) const;
ClipBlendNodeManager *m_manager;
+ TraversalOrder m_order;
+ NodeFilter m_filter;
};
} // Animation
-
} // Qt3DAnimation
QT_END_NAMESPACE
diff --git a/src/animation/backend/clipblendvalue.cpp b/src/animation/backend/clipblendvalue.cpp
new file mode 100644
index 000000000..2d4ed99d2
--- /dev/null
+++ b/src/animation/backend/clipblendvalue.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "clipblendvalue_p.h"
+#include <Qt3DAnimation/qclipblendnodecreatedchange.h>
+#include <Qt3DAnimation/qclipblendvalue.h>
+#include <Qt3DAnimation/private/qclipblendvalue_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DAnimation {
+namespace Animation {
+
+ClipBlendValue::ClipBlendValue()
+ : ClipBlendNode(ValueType)
+{
+}
+
+ClipBlendValue::~ClipBlendValue()
+{
+}
+
+void ClipBlendValue::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
+{
+ ClipBlendNode::initializeFromPeer(change);
+ const auto creationChange
+ = qSharedPointerCast<QClipBlendNodeCreatedChange<QClipBlendValueData>>(change);
+ const Qt3DAnimation::QClipBlendValueData data = creationChange->data;
+ m_clipId = data.clipId;
+}
+
+void ClipBlendValue::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
+{
+ if (e->type() == Qt3DCore::PropertyUpdated) {
+ Qt3DCore::QPropertyUpdatedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
+ if (change->propertyName() == QByteArrayLiteral("clip"))
+ m_clipId = change->value().value<Qt3DCore::QNodeId>();
+ }
+}
+
+ClipResults ClipBlendValue::doBlend(const QVector<ClipResults> &blendData) const
+{
+ // Should never be called for the value node
+ Q_UNUSED(blendData);
+ Q_UNREACHABLE();
+ return ClipResults();
+}
+
+double ClipBlendValue::duration() const
+{
+ if (m_clipId.isNull())
+ return 0.0;
+ AnimationClip *clip = m_handler->animationClipLoaderManager()->lookupResource(m_clipId);
+ Q_ASSERT(clip);
+ return clip->duration();
+}
+
+void ClipBlendValue::setFormatIndices(Qt3DCore::QNodeId animatorId, const ComponentIndices &formatIndices)
+{
+ // Do we already have an entry for this animator?
+ const int animatorIndex = m_animatorIds.indexOf(animatorId);
+ if (animatorIndex == -1) {
+ // Nope, add it
+ m_animatorIds.push_back(animatorId);
+ m_formatIndicies.push_back(formatIndices);
+ } else {
+ m_formatIndicies[animatorIndex] = formatIndices;
+ }
+}
+
+ComponentIndices ClipBlendValue::formatIndices(Qt3DCore::QNodeId animatorId)
+{
+ const int animatorIndex = m_animatorIds.indexOf(animatorId);
+ if (animatorIndex != -1)
+ return m_formatIndicies[animatorIndex];
+ return ComponentIndices();
+}
+
+} // namespace Animation
+} // namespace Qt3DAnimation
+
+QT_END_NAMESPACE
diff --git a/src/animation/backend/conductedclipanimator_p.h b/src/animation/backend/clipblendvalue_p.h
index d4534447e..6da800f98 100644
--- a/src/animation/backend/conductedclipanimator_p.h
+++ b/src/animation/backend/clipblendvalue_p.h
@@ -34,8 +34,8 @@
**
****************************************************************************/
-#ifndef QT3DANIMATION_ANIMATION_CONDUCTEDCLIPANIMATOR_P_H
-#define QT3DANIMATION_ANIMATION_CONDUCTEDCLIPANIMATOR_P_H
+#ifndef QT3DANIMATION_ANIMATION_CLIPBLENDVALUE_H
+#define QT3DANIMATION_ANIMATION_CLIPBLENDVALUE_H
//
// W A R N I N G
@@ -48,26 +48,49 @@
// We mean it.
//
-#include <Qt3DAnimation/private/backendnode_p.h>
+#include <Qt3DAnimation/private/clipblendnode_p.h>
QT_BEGIN_NAMESPACE
namespace Qt3DAnimation {
namespace Animation {
-class Handler;
-
-class Q_AUTOTEST_EXPORT ConductedClipAnimator : public BackendNode
+class Q_AUTOTEST_EXPORT ClipBlendValue : public ClipBlendNode
{
public:
- ConductedClipAnimator();
+ ClipBlendValue();
+ ~ClipBlendValue();
- void cleanup();
+ inline Qt3DCore::QNodeId clipId() const { return m_clipId; }
+ void setClipId(Qt3DCore::QNodeId clipId) { m_clipId = clipId; } // For unit tests
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE;
+ inline QVector<Qt3DCore::QNodeId> allDependencyIds() const Q_DECL_OVERRIDE
+ {
+ return currentDependencyIds();
+ }
+
+ inline QVector<Qt3DCore::QNodeId> currentDependencyIds() const Q_DECL_OVERRIDE
+ {
+ return { m_clipId };
+ }
+
+ double duration() const Q_DECL_OVERRIDE;
+
+ void setFormatIndices(Qt3DCore::QNodeId animatorId, const ComponentIndices &formatIndices);
+ ComponentIndices formatIndices(Qt3DCore::QNodeId animatorId);
+
+protected:
+ ClipResults doBlend(const QVector<ClipResults> &blendData) const Q_DECL_OVERRIDE;
+
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL;
+
+ Qt3DCore::QNodeId m_clipId;
+
+ QVector<Qt3DCore::QNodeId> m_animatorIds;
+ QVector<ComponentIndices> m_formatIndicies;
};
} // namespace Animation
@@ -76,4 +99,4 @@ private:
QT_END_NAMESPACE
-#endif // QT3DANIMATION_ANIMATION_CONDUCTEDCLIPANIMATOR_P_H
+#endif // QT3DANIMATION_ANIMATION_CLIPBLENDVALUE_H
diff --git a/src/animation/backend/conductedclipanimator.cpp b/src/animation/backend/conductedclipanimator.cpp
deleted file mode 100644
index 32db1b7ea..000000000
--- a/src/animation/backend/conductedclipanimator.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt3D module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "conductedclipanimator_p.h"
-#include <Qt3DAnimation/qconductedclipanimator.h>
-#include <Qt3DAnimation/private/qconductedclipanimator_p.h>
-#include <Qt3DCore/qpropertyupdatedchange.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace Qt3DAnimation {
-namespace Animation {
-
-ConductedClipAnimator::ConductedClipAnimator()
- : BackendNode(ReadOnly)
-{
-}
-
-void ConductedClipAnimator::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
-{
- const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QConductedClipAnimatorData>>(change);
- const auto &data = typedChange->data;
-}
-
-void ConductedClipAnimator::cleanup()
-{
- setEnabled(false);
- m_handler = nullptr;
-}
-
-void ConductedClipAnimator::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
-{
- switch (e->type()) {
- case Qt3DCore::PropertyUpdated: {
- break;
- }
-
- default:
- break;
- }
- QBackendNode::sceneChangeEvent(e);
-}
-
-} // namespace Animation
-} // namespace Qt3DAnimation
-
-QT_END_NAMESPACE
diff --git a/src/animation/backend/evaluateblendclipanimatorjob.cpp b/src/animation/backend/evaluateblendclipanimatorjob.cpp
index e15a419b1..76d24a4d3 100644
--- a/src/animation/backend/evaluateblendclipanimatorjob.cpp
+++ b/src/animation/backend/evaluateblendclipanimatorjob.cpp
@@ -39,7 +39,10 @@
#include <Qt3DAnimation/private/managers_p.h>
#include <Qt3DAnimation/private/animationlogging_p.h>
#include <Qt3DAnimation/private/animationutils_p.h>
-#include <Qt3DAnimation/private/lerpblend_p.h>
+#include <Qt3DAnimation/private/clipblendvalue_p.h>
+#include <Qt3DAnimation/private/lerpclipblend_p.h>
+#include <Qt3DAnimation/private/clipblendnodevisitor_p.h>
+#include <Qt3DAnimation/private/job_common_p.h>
QT_BEGIN_NAMESPACE
@@ -49,110 +52,71 @@ namespace Animation {
EvaluateBlendClipAnimatorJob::EvaluateBlendClipAnimatorJob()
: Qt3DCore::QAspectJob()
{
- // TO DO: Add Profiler ID
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::EvaluateBlendClipAnimator, 0);
}
void EvaluateBlendClipAnimatorJob::run()
{
- const qint64 globalTime = m_handler->simulationTime();
- //qDebug() << Q_FUNC_INFO << "t_global =" << globalTime;
-
+ // Find the set of clips that need to be evaluated by querying each node
+ // in the blend tree.
+ // TODO: We should be able to cache this for each blend animator and only
+ // update when a node indicates its dependencies have changed as a result
+ // of blend factors changing
BlendedClipAnimator *blendedClipAnimator = m_handler->blendedClipAnimatorManager()->data(m_blendClipAnimatorHandle);
- Q_ASSERT(blendedClipAnimator);
-
- // TO DO: Right now we are doing LERP but refactor to handle other types
- ClipBlendNode *blendNode = m_handler->clipBlendNodeManager()->lookupNode(blendedClipAnimator->blendTreeRootId());
- Q_ASSERT(blendNode->blendType() == ClipBlendNode::LerpBlendType);
- LerpBlend *lerpBlendNode = static_cast<LerpBlend *>(blendNode);
-
- const Qt3DCore::QNodeIdVector clipIds = lerpBlendNode->clipIds();
+ Qt3DCore::QNodeId blendTreeRootId = blendedClipAnimator->blendTreeRootId();
+ const QVector<Qt3DCore::QNodeId> valueNodeIdsToEvaluate = gatherValueNodesToEvaluate(m_handler, blendTreeRootId);
- bool globalFinalFrame = false;
+ // Calculate the resulting duration of the blend tree based upon its current state
+ ClipBlendNodeManager *blendNodeManager = m_handler->clipBlendNodeManager();
+ ClipBlendNode *blendTreeRootNode = blendNodeManager->lookupNode(blendTreeRootId);
+ Q_ASSERT(blendTreeRootNode);
+ const double duration = blendTreeRootNode->duration();
- // Evaluate the fcurves for both clip
- AnimationClip *clip1 = m_handler->animationClipManager()->lookupResource(clipIds.first());
- AnimationClip *clip2 = m_handler->animationClipManager()->lookupResource(clipIds.last());
- Q_ASSERT(clip1 && clip2);
- bool finalFrame1 = false;
- bool finalFrame2 = false;
+ // Calculate the phase given the blend tree duration and global time
+ const qint64 globalTime = m_handler->simulationTime();
+ const AnimatorEvaluationData animatorData = evaluationDataForAnimator(blendedClipAnimator, globalTime);
int currentLoop = 0;
- const QVector<float> channelResultsClip1 = AnimationUtils::evaluateAtGlobalTime(clip1,
- globalTime,
- blendedClipAnimator->startTime(),
- blendedClipAnimator->loops(),
- currentLoop,
- finalFrame1);
- const QVector<float> channelResultsClip2 = AnimationUtils::evaluateAtGlobalTime(clip2,
- globalTime,
- blendedClipAnimator->startTime(),
- blendedClipAnimator->loops(),
- currentLoop,
- finalFrame2);
- globalFinalFrame = (finalFrame1 && finalFrame2);
-
- blendedClipAnimator->setCurrentLoop(currentLoop);
-
- // Perform blending between the two clips
- const float blendFactor = lerpBlendNode->blendFactor();
-
- QVector<AnimationUtils::MappingData> blendedMappingData;
- QVector<float> blendedValues;
-
- // Build a combined vector of blended value
- const QVector<AnimationUtils::BlendingMappingData> blendingMappingData = blendedClipAnimator->mappingData();
- for (const AnimationUtils::BlendingMappingData &mapping : blendingMappingData) {
-
- AnimationUtils::MappingData finalMapping;
- finalMapping.type = mapping.type;
- finalMapping.targetId = mapping.targetId;
- finalMapping.propertyName = mapping.propertyName;
-
- switch (mapping.blendAction) {
- case AnimationUtils::BlendingMappingData::ClipBlending: {
- Q_ASSERT(mapping.channelIndicesClip1.size() == mapping.channelIndicesClip2.size());
- for (int i = 0, m = mapping.channelIndicesClip1.size(); i < m; ++i) {
- const float value1 = channelResultsClip1.at(mapping.channelIndicesClip1[i]);
- const float value2 = channelResultsClip2.at(mapping.channelIndicesClip2[i]);
- const float blendedValue = ((1.0f - blendFactor) * value1) + (blendFactor * value2);
- finalMapping.channelIndices.push_back(blendedValues.size());
- blendedValues.push_back(blendedValue);
- }
- break;
- }
- case AnimationUtils::BlendingMappingData::NoBlending: {
- const bool useClip1 = !mapping.channelIndicesClip1.empty();
- const QVector<int> channelIndices = useClip1 ? mapping.channelIndicesClip1 : mapping.channelIndicesClip2;
- const QVector<float> values = useClip1 ? channelResultsClip1 : channelResultsClip2;
- for (int i = 0, m = channelIndices.size(); i < m; ++i) {
- const float value = values.at(channelIndices[i]);
- finalMapping.channelIndices.push_back(blendedValues.size());
- blendedValues.push_back(value);
- }
- break;
- }
- default:
- Q_UNREACHABLE();
- break;
- }
-
- blendedMappingData.push_back(finalMapping);
+ const double phase = phaseFromGlobalTime(animatorData.globalTime,
+ animatorData.startTime,
+ animatorData.playbackRate,
+ duration,
+ animatorData.loopCount,
+ currentLoop);
+
+ // Iterate over the value nodes of the blend tree, evaluate the
+ // contained animation clips at the current phase and store the results
+ // in the animator indexed by node.
+ AnimationClipLoaderManager *clipLoaderManager = m_handler->animationClipLoaderManager();
+ for (const auto valueNodeId : valueNodeIdsToEvaluate) {
+ ClipBlendValue *valueNode = static_cast<ClipBlendValue *>(blendNodeManager->lookupNode(valueNodeId));
+ Q_ASSERT(valueNode);
+ AnimationClip *clip = clipLoaderManager->lookupResource(valueNode->clipId());
+ Q_ASSERT(clip);
+
+ ClipResults rawClipResults = evaluateClipAtPhase(clip, phase);
+
+ // Reformat the clip results into the layout used by this animator/blend tree
+ ComponentIndices format = valueNode->formatIndices(blendedClipAnimator->peerId());
+ ClipResults formattedClipResults = formatClipResults(rawClipResults, format);
+ valueNode->setClipResults(blendedClipAnimator->peerId(), formattedClipResults);
}
- if (globalFinalFrame)
- blendedClipAnimator->setRunning(false);
+ // Evaluate the blend tree
+ ClipResults blendedResults = evaluateBlendTree(m_handler, blendedClipAnimator, blendTreeRootId);
- // Prepare property changes (if finalFrame it also prepares the change for the running property for the frontend)
- const QVector<Qt3DCore::QSceneChangePtr> changes = AnimationUtils::preparePropertyChanges(blendedClipAnimator->peerId(),
- blendedMappingData,
- blendedValues,
- globalFinalFrame);
+ const double localTime = phase * duration;
+ const bool finalFrame = isFinalFrame(localTime, duration, currentLoop, animatorData.loopCount);
+ // Prepare the property change events
+ const QVector<MappingData> mappingData = blendedClipAnimator->mappingData();
+ const QVector<Qt3DCore::QSceneChangePtr> changes = preparePropertyChanges(blendedClipAnimator->peerId(),
+ mappingData,
+ blendedResults,
+ finalFrame);
// Send the property changes
blendedClipAnimator->sendPropertyChanges(changes);
-
}
-
} // Animation
} // Qt3DAnimation
diff --git a/src/animation/backend/evaluateblendclipanimatorjob_p.h b/src/animation/backend/evaluateblendclipanimatorjob_p.h
index 07db68fdc..a7822c8f9 100644
--- a/src/animation/backend/evaluateblendclipanimatorjob_p.h
+++ b/src/animation/backend/evaluateblendclipanimatorjob_p.h
@@ -50,6 +50,8 @@
#include <Qt3DCore/qaspectjob.h>
#include <Qt3DAnimation/private/handle_types_p.h>
+#include <Qt3DAnimation/private/animationutils_p.h>
+#include <Qt3DAnimation/private/blendedclipanimator_p.h>
QT_BEGIN_NAMESPACE
@@ -57,6 +59,7 @@ namespace Qt3DAnimation {
namespace Animation {
class Handler;
+class ClipBlendNode;
class EvaluateBlendClipAnimatorJob : public Qt3DCore::QAspectJob
{
diff --git a/src/animation/backend/evaluateclipanimatorjob.cpp b/src/animation/backend/evaluateclipanimatorjob.cpp
index e547ec441..e89405d63 100644
--- a/src/animation/backend/evaluateclipanimatorjob.cpp
+++ b/src/animation/backend/evaluateclipanimatorjob.cpp
@@ -39,6 +39,7 @@
#include <Qt3DAnimation/private/managers_p.h>
#include <Qt3DAnimation/private/animationlogging_p.h>
#include <Qt3DAnimation/private/animationutils_p.h>
+#include <Qt3DAnimation/private/job_common_p.h>
QT_BEGIN_NAMESPACE
@@ -48,6 +49,7 @@ namespace Animation {
EvaluateClipAnimatorJob::EvaluateClipAnimatorJob()
: Qt3DCore::QAspectJob()
{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::EvaluateClipAnimator, 0);
}
void EvaluateClipAnimatorJob::run()
@@ -61,27 +63,23 @@ void EvaluateClipAnimatorJob::run()
Q_ASSERT(clipAnimator);
// Evaluate the fcurves
- AnimationClip *clip = m_handler->animationClipManager()->lookupResource(clipAnimator->clipId());
+ AnimationClip *clip = m_handler->animationClipLoaderManager()->lookupResource(clipAnimator->clipId());
Q_ASSERT(clip);
- bool finalFrame = false;
- int currentLoop = 0;
- const QVector<float> channelResults = AnimationUtils::evaluateAtGlobalTime(clip,
- globalTime,
- clipAnimator->startTime(),
- clipAnimator->loops(),
- currentLoop,
- finalFrame);
+ // Prepare for evaluation (convert global time to local time ....)
+ const AnimatorEvaluationData animatorEvaluationData = evaluationDataForAnimator(clipAnimator, globalTime);
+ const ClipEvaluationData preEvaluationDataForClip = evaluationDataForClip(clip, animatorEvaluationData);
+ const ClipResults channelResults = evaluateClipAtLocalTime(clip, preEvaluationDataForClip.localTime);
- if (finalFrame)
+ if (preEvaluationDataForClip.isFinalFrame)
clipAnimator->setRunning(false);
- clipAnimator->setCurrentLoop(currentLoop);
+ clipAnimator->setCurrentLoop(preEvaluationDataForClip.currentLoop);
// Prepare property changes (if finalFrame it also prepares the change for the running property for the frontend)
- const QVector<Qt3DCore::QSceneChangePtr> changes = AnimationUtils::preparePropertyChanges(clipAnimator->peerId(),
- clipAnimator->mappingData(),
- channelResults,
- finalFrame);
+ const QVector<Qt3DCore::QSceneChangePtr> changes = preparePropertyChanges(clipAnimator->peerId(),
+ clipAnimator->mappingData(),
+ channelResults,
+ preEvaluationDataForClip.isFinalFrame);
// Send the property changes
clipAnimator->sendPropertyChanges(changes);
diff --git a/src/animation/backend/fcurve.cpp b/src/animation/backend/fcurve.cpp
index ce16fb429..809949472 100644
--- a/src/animation/backend/fcurve.cpp
+++ b/src/animation/backend/fcurve.cpp
@@ -92,25 +92,25 @@ void FCurve::read(const QJsonObject &json)
{
clearKeyframes();
- const QJsonArray keyframeArray = json[QLatin1String("keyframes")].toArray();
+ const QJsonArray keyframeArray = json[QLatin1String("keyFrames")].toArray();
const int keyframeCount = keyframeArray.size();
for (int i = 0; i < keyframeCount; ++i) {
const QJsonObject keyframeData = keyframeArray.at(i).toObject();
// Extract the keyframe local time and value
- const QJsonArray keyframeCoords = keyframeData[QLatin1String("co")].toArray();
+ const QJsonArray keyframeCoords = keyframeData[QLatin1String("coords")].toArray();
float localTime = keyframeCoords.at(0).toDouble();
Keyframe keyframe;
- keyframe.interpolation = Keyframe::Bezier;
+ keyframe.interpolation = QKeyFrame::BezierInterpolation;
keyframe.value = keyframeCoords.at(1).toDouble();
- const QJsonArray leftHandle = keyframeData[QLatin1String("handle_left")].toArray();
+ const QJsonArray leftHandle = keyframeData[QLatin1String("leftHandle")].toArray();
keyframe.leftControlPoint[0] = leftHandle.at(0).toDouble();
keyframe.leftControlPoint[1] = leftHandle.at(1).toDouble();
- const QJsonArray rightHandle = keyframeData[QLatin1String("handle_right")].toArray();
+ const QJsonArray rightHandle = keyframeData[QLatin1String("rightHandle")].toArray();
keyframe.rightControlPoint[0] = rightHandle.at(0).toDouble();
keyframe.rightControlPoint[1] = rightHandle.at(1).toDouble();
@@ -121,25 +121,60 @@ void FCurve::read(const QJsonObject &json)
// back so they do not interset.
}
-void Channel::read(const QJsonObject &json)
+void FCurve::setFromQChannelComponent(const QChannelComponent &qcc)
+{
+ clearKeyframes();
+
+ for (const auto &frontendKeyFrame : qcc) {
+ // Extract the keyframe local time and value
+ const float localTime = frontendKeyFrame.coordinates()[0];
+
+ Keyframe keyFrame;
+ keyFrame.interpolation = frontendKeyFrame.interpolationType();
+ keyFrame.value = frontendKeyFrame.coordinates()[1];
+ keyFrame.leftControlPoint = frontendKeyFrame.leftControlPoint();
+ keyFrame.rightControlPoint = frontendKeyFrame.rightControlPoint();
+ appendKeyframe(localTime, keyFrame);
+ }
+
+ // TODO: Ensure beziers have no loops or cusps by scaling the control points
+ // back so they do not interset.
+}
+
+void ChannelComponent::read(const QJsonObject &json)
{
- name = json[QLatin1String("name")].toString();
+ name = json[QLatin1String("channelComponentName")].toString();
fcurve.read(json);
}
-void ChannelGroup::read(const QJsonObject &json)
+void ChannelComponent::setFromQChannelComponent(const QChannelComponent &qcc)
{
- name = json[QLatin1String("group")].toString();
- const QJsonArray channelsArray = json[QLatin1String("channels")].toArray();
- const int channelCount = channelsArray.size();
- channels.resize(channelCount);
+ name = qcc.name();
+ fcurve.setFromQChannelComponent(qcc);
+}
+
+void Channel::read(const QJsonObject &json)
+{
+ name = json[QLatin1String("channelName")].toString();
+ const QJsonArray channelComponentsArray = json[QLatin1String("channelComponents")].toArray();
+ const int channelCount = channelComponentsArray.size();
+ channelComponents.resize(channelCount);
for (int i = 0; i < channelCount; ++i) {
- const QJsonObject channel = channelsArray.at(i).toObject();
- channels[i].read(channel);
+ const QJsonObject channel = channelComponentsArray.at(i).toObject();
+ channelComponents[i].read(channel);
}
}
+void Channel::setFromQChannel(const QChannel &qch)
+{
+ name = qch.name();
+ channelComponents.resize(qch.channelComponentCount());
+ int i = 0;
+ for (const auto &frontendChannelComponent : qch)
+ channelComponents[i++].setFromQChannelComponent(frontendChannelComponent);
+}
+
} // namespace Animation
} // namespace Qt3DAnimation
diff --git a/src/animation/backend/fcurve_p.h b/src/animation/backend/fcurve_p.h
index def33941e..7ab1593d1 100644
--- a/src/animation/backend/fcurve_p.h
+++ b/src/animation/backend/fcurve_p.h
@@ -50,6 +50,10 @@
#include "keyframe_p.h"
#include "functionrangefinder_p.h"
+
+#include <Qt3DAnimation/qchannel.h>
+#include <Qt3DAnimation/qchannelcomponent.h>
+#include <Qt3DAnimation/qkeyframe.h>
#include <QtCore/qvector.h>
#ifndef QT_NO_DEBUG_STREAM
@@ -81,6 +85,7 @@ public:
float evaluateAtTime(float localTime) const;
void read(const QJsonObject &json);
+ void setFromQChannelComponent(const QChannelComponent &qcc);
private:
QVector<float> m_localTimes;
@@ -106,41 +111,43 @@ inline QDebug operator<<(QDebug dbg, const FCurve &fcurve)
}
#endif
-struct Channel
+struct ChannelComponent
{
QString name;
FCurve fcurve;
void read(const QJsonObject &json);
+ void setFromQChannelComponent(const QChannelComponent &qcc);
};
#ifndef QT_NO_DEBUG_STREAM
-inline QDebug operator<<(QDebug dbg, const Channel &channel)
+inline QDebug operator<<(QDebug dbg, const ChannelComponent &channelComponent)
{
QDebugStateSaver saver(dbg);
- dbg << "Channel Name: " << channel.name << endl
- << "Fcurve:" << channel.fcurve << endl;
+ dbg << "Channel Component Name: " << channelComponent.name << endl
+ << "FCurve:" << channelComponent.fcurve << endl;
return dbg;
}
#endif
-struct ChannelGroup
+struct Channel
{
QString name;
- QVector<Channel> channels;
+ QVector<ChannelComponent> channelComponents;
void read(const QJsonObject &json);
+ void setFromQChannel(const QChannel &qch);
};
#ifndef QT_NO_DEBUG_STREAM
-inline QDebug operator<<(QDebug dbg, const ChannelGroup &channelGroup)
+inline QDebug operator<<(QDebug dbg, const Channel &channel)
{
QDebugStateSaver saver(dbg);
- dbg << "Name: " << channelGroup.name << endl
- << "Channels:" << channelGroup.channels.size() << endl;
+ dbg << "Channel Name: " << channel.name << endl
+ << "Channels:" << channel.channelComponents.size() << endl;
- for (const auto channel : qAsConst(channelGroup.channels)) {
- dbg << channel;
+ for (const auto &channelComponent : qAsConst(channel.channelComponents)) {
+ dbg << channelComponent;
}
return dbg;
}
diff --git a/src/animation/backend/findrunningclipanimatorsjob.cpp b/src/animation/backend/findrunningclipanimatorsjob.cpp
index 3b2d9d4f5..80739a02b 100644
--- a/src/animation/backend/findrunningclipanimatorsjob.cpp
+++ b/src/animation/backend/findrunningclipanimatorsjob.cpp
@@ -39,6 +39,7 @@
#include <Qt3DAnimation/private/managers_p.h>
#include <Qt3DAnimation/private/animationlogging_p.h>
#include <Qt3DAnimation/private/animationutils_p.h>
+#include <Qt3DAnimation/private/job_common_p.h>
QT_BEGIN_NAMESPACE
@@ -48,6 +49,7 @@ namespace Animation {
FindRunningClipAnimatorsJob::FindRunningClipAnimatorsJob()
: Qt3DCore::QAspectJob()
{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::FindRunningClipAnimator, 0);
}
void FindRunningClipAnimatorsJob::setDirtyClipAnimators(const QVector<HClipAnimator> &clipAnimatorHandles)
@@ -71,10 +73,10 @@ void FindRunningClipAnimatorsJob::run()
// TODO: Should be possible to parallelise this with the fcurve evaluation as
// sending the property change events doesn't happen until after evaluation
if (canRun) {
- const AnimationClip *clip = m_handler->animationClipManager()->lookupResource(clipAnimator->clipId());
+ const AnimationClip *clip = m_handler->animationClipLoaderManager()->lookupResource(clipAnimator->clipId());
const ChannelMapper *mapper = m_handler->channelMapperManager()->lookupResource(clipAnimator->mapperId());
Q_ASSERT(clip && mapper);
- const QVector<AnimationUtils::MappingData> mappingData = AnimationUtils::buildPropertyMappings(m_handler, clip, mapper);
+ const QVector<MappingData> mappingData = buildPropertyMappings(m_handler, clip, mapper);
clipAnimator->setMappingData(mappingData);
}
}
diff --git a/src/animation/backend/handle_types_p.h b/src/animation/backend/handle_types_p.h
index f2c6774d2..f6f77eb20 100644
--- a/src/animation/backend/handle_types_p.h
+++ b/src/animation/backend/handle_types_p.h
@@ -61,14 +61,12 @@ namespace Animation {
class AnimationClip;
class ClipAnimator;
class BlendedClipAnimator;
-class ConductedClipAnimator;
class ChannelMapping;
class ChannelMapper;
typedef Qt3DCore::QHandle<AnimationClip, 16> HAnimationClip;
typedef Qt3DCore::QHandle<ClipAnimator, 16> HClipAnimator;
typedef Qt3DCore::QHandle<BlendedClipAnimator, 12> HBlendedClipAnimator;
-typedef Qt3DCore::QHandle<ConductedClipAnimator, 8> HConductedClipAnimator;
typedef Qt3DCore::QHandle<ChannelMapping, 16> HChannelMapping;
typedef Qt3DCore::QHandle<ChannelMapper, 16> HChannelMapper;
diff --git a/src/animation/backend/handler.cpp b/src/animation/backend/handler.cpp
index 6dea5d6f3..04c46b40a 100644
--- a/src/animation/backend/handler.cpp
+++ b/src/animation/backend/handler.cpp
@@ -42,6 +42,8 @@
#include <Qt3DAnimation/private/buildblendtreesjob_p.h>
#include <Qt3DAnimation/private/evaluateblendclipanimatorjob_p.h>
#include <Qt3DAnimation/private/animationlogging_p.h>
+#include <Qt3DAnimation/private/buildblendtreesjob_p.h>
+#include <Qt3DAnimation/private/evaluateblendclipanimatorjob_p.h>
QT_BEGIN_NAMESPACE
@@ -49,10 +51,9 @@ namespace Qt3DAnimation {
namespace Animation {
Handler::Handler()
- : m_animationClipManager(new AnimationClipManager)
+ : m_animationClipLoaderManager(new AnimationClipLoaderManager)
, m_clipAnimatorManager(new ClipAnimatorManager)
, m_blendedClipAnimatorManager(new BlendedClipAnimatorManager)
- , m_conductedClipAnimatorManager(new ConductedClipAnimatorManager)
, m_channelMappingManager(new ChannelMappingManager)
, m_channelMapperManager(new ChannelMapperManager)
, m_clipBlendNodeManager(new ClipBlendNodeManager)
@@ -74,7 +75,7 @@ void Handler::setDirty(DirtyFlag flag, Qt3DCore::QNodeId nodeId)
{
switch (flag) {
case AnimationClipDirty: {
- const auto handle = m_animationClipManager->lookupHandle(nodeId);
+ const auto handle = m_animationClipLoaderManager->lookupHandle(nodeId);
m_dirtyAnimationClips.push_back(handle);
break;
}
diff --git a/src/animation/backend/handler_p.h b/src/animation/backend/handler_p.h
index 3abaae215..52892f9d0 100644
--- a/src/animation/backend/handler_p.h
+++ b/src/animation/backend/handler_p.h
@@ -50,8 +50,6 @@
#include <QtGlobal>
#include <Qt3DAnimation/private/handle_types_p.h>
-#include <Qt3DAnimation/private/buildblendtreesjob_p.h>
-#include <Qt3DAnimation/private/evaluateblendclipanimatorjob_p.h>
#include <Qt3DCore/qaspectjob.h>
#include <Qt3DCore/qnodeid.h>
#include <QtCore/qscopedpointer.h>
@@ -66,13 +64,11 @@ namespace Qt3DAnimation {
namespace Animation {
class AnimationClip;
-class AnimationClipManager;
+class AnimationClipLoaderManager;
class ClipAnimator;
class ClipAnimatorManager;
class BlendedClipAnimator;
class BlendedClipAnimatorManager;
-class ConductedClipAnimator;
-class ConductedClipAnimatorManager;
class ChannelMapping;
class ChannelMappingManager;
class ChannelMapper;
@@ -82,6 +78,11 @@ class ClipBlendNodeManager;
class FindRunningClipAnimatorsJob;
class LoadAnimationClipJob;
class EvaluateClipAnimatorJob;
+class BuildBlendTreesJob;
+class EvaluateBlendClipAnimatorJob;
+
+using BuildBlendTreesJobPtr = QSharedPointer<BuildBlendTreesJob>;
+using EvaluateBlendClipAnimatorJobPtr = QSharedPointer<EvaluateBlendClipAnimatorJob>;
class Q_AUTOTEST_EXPORT Handler
{
@@ -106,10 +107,9 @@ public:
void setBlendedClipAnimatorRunning(const HBlendedClipAnimator &handle, bool running);
QVector<HBlendedClipAnimator> runningBlenndedClipAnimators() const { return m_runningBlendedClipAnimators; }
- AnimationClipManager *animationClipManager() const Q_DECL_NOTHROW { return m_animationClipManager.data(); }
+ AnimationClipLoaderManager *animationClipLoaderManager() const Q_DECL_NOTHROW { return m_animationClipLoaderManager.data(); }
ClipAnimatorManager *clipAnimatorManager() const Q_DECL_NOTHROW { return m_clipAnimatorManager.data(); }
BlendedClipAnimatorManager *blendedClipAnimatorManager() const Q_DECL_NOTHROW { return m_blendedClipAnimatorManager.data(); }
- ConductedClipAnimatorManager *conductedClipAnimatorManager() const Q_DECL_NOTHROW { return m_conductedClipAnimatorManager.data(); }
ChannelMappingManager *channelMappingManager() const Q_DECL_NOTHROW { return m_channelMappingManager.data(); }
ChannelMapperManager *channelMapperManager() const Q_DECL_NOTHROW { return m_channelMapperManager.data(); }
ClipBlendNodeManager *clipBlendNodeManager() const Q_DECL_NOTHROW { return m_clipBlendNodeManager.data(); }
@@ -117,10 +117,9 @@ public:
QVector<Qt3DCore::QAspectJobPtr> jobsToExecute(qint64 time);
private:
- QScopedPointer<AnimationClipManager> m_animationClipManager;
+ QScopedPointer<AnimationClipLoaderManager> m_animationClipLoaderManager;
QScopedPointer<ClipAnimatorManager> m_clipAnimatorManager;
QScopedPointer<BlendedClipAnimatorManager> m_blendedClipAnimatorManager;
- QScopedPointer<ConductedClipAnimatorManager> m_conductedClipAnimatorManager;
QScopedPointer<ChannelMappingManager> m_channelMappingManager;
QScopedPointer<ChannelMapperManager> m_channelMapperManager;
QScopedPointer<ClipBlendNodeManager> m_clipBlendNodeManager;
diff --git a/src/animation/backend/keyframe_p.h b/src/animation/backend/keyframe_p.h
index 550c9fde0..9d2c3bc47 100644
--- a/src/animation/backend/keyframe_p.h
+++ b/src/animation/backend/keyframe_p.h
@@ -48,6 +48,7 @@
// We mean it.
//
+#include <Qt3DAnimation/qkeyframe.h>
#include <QtGui/qvector2d.h>
QT_BEGIN_NAMESPACE
@@ -57,13 +58,6 @@ namespace Animation {
struct Keyframe
{
- enum Interpolation {
- Constant,
- Linear,
- Bezier
- // TODO: Add other easing types
- };
-
inline bool operator==(const Keyframe &rhs) const
{
return value == rhs.value
@@ -75,7 +69,7 @@ struct Keyframe
float value; // Value (time is stored separately in FCurve)
QVector2D leftControlPoint; // Bezier control point (time, value)
QVector2D rightControlPoint; // Bezier control point (time, value)
- Interpolation interpolation; // Method to use for evaluation between this Keyframe and the next
+ QKeyFrame::InterpolationType interpolation; // Method to use for evaluation between this Keyframe and the next
};
} // namespace Animation
diff --git a/src/animation/backend/lerpblend.cpp b/src/animation/backend/lerpclipblend.cpp
index 29261aadf..eef0072f9 100644
--- a/src/animation/backend/lerpblend.cpp
+++ b/src/animation/backend/lerpclipblend.cpp
@@ -34,9 +34,9 @@
**
****************************************************************************/
-#include "lerpblend_p.h"
+#include "lerpclipblend_p.h"
#include <Qt3DAnimation/qclipblendnodecreatedchange.h>
-#include <Qt3DAnimation/private/qlerpblend_p.h>
+#include <Qt3DAnimation/private/qlerpclipblend_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
QT_BEGIN_NAMESPACE
@@ -45,33 +45,65 @@ namespace Qt3DAnimation {
namespace Animation {
-LerpBlend::LerpBlend()
+LerpClipBlend::LerpClipBlend()
: ClipBlendNode(ClipBlendNode::LerpBlendType)
+ , m_startClipId()
+ , m_endClipId()
, m_blendFactor(0.0f)
{
}
-LerpBlend::~LerpBlend()
+LerpClipBlend::~LerpClipBlend()
{
}
-void LerpBlend::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
+void LerpClipBlend::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
{
if (e->type() == Qt3DCore::PropertyUpdated) {
Qt3DCore::QPropertyUpdatedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
if (change->propertyName() == QByteArrayLiteral("blendFactor"))
m_blendFactor = change->value().toFloat();
+ else if (change->propertyName() == QByteArrayLiteral("startClip"))
+ m_startClipId = change->value().value<Qt3DCore::QNodeId>();
+ else if (change->propertyName() == QByteArrayLiteral("endClip"))
+ m_endClipId = change->value().value<Qt3DCore::QNodeId>();
}
}
-void LerpBlend::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
+ClipResults LerpClipBlend::doBlend(const QVector<ClipResults> &blendData) const
+{
+ Q_ASSERT(blendData.size() == 2);
+ Q_ASSERT(blendData[0].size() == blendData[1].size());
+ const int elementCount = blendData.first().size();
+ ClipResults blendResults(elementCount);
+
+ for (int i = 0; i < elementCount; ++i)
+ blendResults[i] = (1.0f - m_blendFactor) * blendData[0][i] + (m_blendFactor * blendData[1][i]);
+
+ return blendResults;
+}
+
+void LerpClipBlend::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
{
ClipBlendNode::initializeFromPeer(change);
- const auto creationChangeData = qSharedPointerCast<Qt3DAnimation::QClipBlendNodeCreatedChange<Qt3DAnimation::QLerpBlendData>>(change);
- const Qt3DAnimation::QLerpBlendData cloneData = creationChangeData->data;
+ const auto creationChangeData = qSharedPointerCast<Qt3DAnimation::QClipBlendNodeCreatedChange<Qt3DAnimation::QLerpClipBlendData>>(change);
+ const Qt3DAnimation::QLerpClipBlendData cloneData = creationChangeData->data;
+ m_startClipId = cloneData.startClipId;
+ m_endClipId = cloneData.endClipId;
m_blendFactor = cloneData.blendFactor;
}
+double LerpClipBlend::duration() const
+{
+ ClipBlendNode *startNode = clipBlendNodeManager()->lookupNode(m_startClipId);
+ const double startNodeDuration = startNode ? startNode->duration() : 0.0;
+
+ ClipBlendNode *endNode = clipBlendNodeManager()->lookupNode(m_endClipId);
+ const double endNodeDuration = endNode ? endNode->duration() : 0.0;
+
+ return (1.0f - m_blendFactor) * startNodeDuration + m_blendFactor * endNodeDuration;
+}
+
} // Animation
} // Qt3DAnimation
diff --git a/src/animation/backend/lerpblend_p.h b/src/animation/backend/lerpclipblend_p.h
index a28815349..e09987759 100644
--- a/src/animation/backend/lerpblend_p.h
+++ b/src/animation/backend/lerpclipblend_p.h
@@ -34,8 +34,8 @@
**
****************************************************************************/
-#ifndef QT3DANIMATION_ANIMATION_LERPBLEND_P_H
-#define QT3DANIMATION_ANIMATION_LERPBLEND_P_H
+#ifndef QT3DANIMATION_ANIMATION_LERPCLIPBLEND_P_H
+#define QT3DANIMATION_ANIMATION_LERPCLIPBLEND_P_H
//
// W A R N I N G
@@ -56,19 +56,43 @@ namespace Qt3DAnimation {
namespace Animation {
-class Q_AUTOTEST_EXPORT LerpBlend : public ClipBlendNode
+class Q_AUTOTEST_EXPORT LerpClipBlend : public ClipBlendNode
{
public:
- LerpBlend();
- ~LerpBlend();
+ LerpClipBlend();
+ ~LerpClipBlend();
inline float blendFactor() const { return m_blendFactor; }
+ void setBlendFactor(float blendFactor) { m_blendFactor = blendFactor; } // For unit tests
+
+ inline Qt3DCore::QNodeId startClipId() const { return m_startClipId; }
+ void setStartClipId(Qt3DCore::QNodeId startClipId) { m_startClipId = startClipId; } // For unit tests
+
+ inline Qt3DCore::QNodeId endClipId() const { return m_endClipId; }
+ void setEndClipId(Qt3DCore::QNodeId endClipId) { m_endClipId = endClipId; } // For unit tests
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_FINAL;
+ inline QVector<Qt3DCore::QNodeId> allDependencyIds() const Q_DECL_OVERRIDE
+ {
+ return currentDependencyIds();
+ }
+
+ inline QVector<Qt3DCore::QNodeId> currentDependencyIds() const Q_DECL_OVERRIDE
+ {
+ return { m_startClipId, m_endClipId };
+ }
+
+ double duration() const Q_DECL_OVERRIDE;
+
+protected:
+ ClipResults doBlend(const QVector<ClipResults> &blendData) const Q_DECL_FINAL;
+
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL;
+ Qt3DCore::QNodeId m_startClipId;
+ Qt3DCore::QNodeId m_endClipId;
float m_blendFactor;
};
@@ -78,4 +102,4 @@ private:
QT_END_NAMESPACE
-#endif // QT3DANIMATION_ANIMATION_LERPBLEND_P_H
+#endif // QT3DANIMATION_ANIMATION_LERPCLIPBLEND_P_H
diff --git a/src/animation/backend/loadanimationclipjob.cpp b/src/animation/backend/loadanimationclipjob.cpp
index c94435398..c0201e0e9 100644
--- a/src/animation/backend/loadanimationclipjob.cpp
+++ b/src/animation/backend/loadanimationclipjob.cpp
@@ -39,6 +39,7 @@
#include <Qt3DAnimation/private/animationclip_p.h>
#include <Qt3DAnimation/private/handler_p.h>
#include <Qt3DAnimation/private/managers_p.h>
+#include <Qt3DAnimation/private/job_common_p.h>
QT_BEGIN_NAMESPACE
@@ -49,6 +50,7 @@ LoadAnimationClipJob::LoadAnimationClipJob()
: Qt3DCore::QAspectJob()
, m_animationClipHandles()
{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::LoadAnimationClip, 0);
}
void LoadAnimationClipJob::addDirtyAnimationClips(const QVector<HAnimationClip> &animationClipHandles)
@@ -67,7 +69,7 @@ void LoadAnimationClipJob::clearDirtyAnimationClips()
void LoadAnimationClipJob::run()
{
Q_ASSERT(m_handler);
- AnimationClipManager *animationClipManager = m_handler->animationClipManager();
+ AnimationClipLoaderManager *animationClipManager = m_handler->animationClipLoaderManager();
for (const auto animationClipHandle : qAsConst(m_animationClipHandles)) {
AnimationClip *animationClip = animationClipManager->data(animationClipHandle);
animationClip->loadAnimation();
diff --git a/src/animation/backend/managers_p.h b/src/animation/backend/managers_p.h
index 42484921f..f99ed0698 100644
--- a/src/animation/backend/managers_p.h
+++ b/src/animation/backend/managers_p.h
@@ -56,7 +56,6 @@
#include <Qt3DAnimation/private/animationclip_p.h>
#include <Qt3DAnimation/private/blendedclipanimator_p.h>
#include <Qt3DAnimation/private/clipanimator_p.h>
-#include <Qt3DAnimation/private/conductedclipanimator_p.h>
#include <Qt3DAnimation/private/channelmapping_p.h>
#include <Qt3DAnimation/private/channelmapper_p.h>
#include <Qt3DCore/private/qresourcemanager_p.h>
@@ -68,14 +67,14 @@ namespace Animation {
class ClipBlendNode;
-class AnimationClipManager : public Qt3DCore::QResourceManager<
+class AnimationClipLoaderManager : public Qt3DCore::QResourceManager<
AnimationClip,
Qt3DCore::QNodeId,
16,
Qt3DCore::ArrayAllocatingPolicy>
{
public:
- AnimationClipManager() {}
+ AnimationClipLoaderManager() {}
};
class ClipAnimatorManager : public Qt3DCore::QResourceManager<
@@ -98,16 +97,6 @@ public:
BlendedClipAnimatorManager() {}
};
-class ConductedClipAnimatorManager : public Qt3DCore::QResourceManager<
- ConductedClipAnimator,
- Qt3DCore::QNodeId,
- 8,
- Qt3DCore::ArrayAllocatingPolicy>
-{
-public:
- ConductedClipAnimatorManager() {}
-};
-
class ChannelMappingManager : public Qt3DCore::QResourceManager<
ChannelMapping,
Qt3DCore::QNodeId,
@@ -149,7 +138,6 @@ private:
Q_DECLARE_RESOURCE_INFO(Qt3DAnimation::Animation::AnimationClip, Q_REQUIRES_CLEANUP)
Q_DECLARE_RESOURCE_INFO(Qt3DAnimation::Animation::ClipAnimator, Q_REQUIRES_CLEANUP)
Q_DECLARE_RESOURCE_INFO(Qt3DAnimation::Animation::BlendedClipAnimator, Q_REQUIRES_CLEANUP)
-Q_DECLARE_RESOURCE_INFO(Qt3DAnimation::Animation::ConductedClipAnimator, Q_REQUIRES_CLEANUP)
Q_DECLARE_RESOURCE_INFO(Qt3DAnimation::Animation::ChannelMapping, Q_REQUIRES_CLEANUP)
Q_DECLARE_RESOURCE_INFO(Qt3DAnimation::Animation::ChannelMapper, Q_REQUIRES_CLEANUP)