diff options
Diffstat (limited to 'src/animation/backend')
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 ¤tLoop) +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 ¤tLoop) { 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 ¤tLoop) { - 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 ¤tLoop, - 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 ¤tLoop); - 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 ¤tLoop, - 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 ¤tLoop); + +Q_AUTOTEST_EXPORT +double phaseFromGlobalTime(double t_global, double t_start_global, + double playbackRate, double duration, + int loopCount, int ¤tLoop); + +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) |