diff options
Diffstat (limited to 'src/animation')
104 files changed, 8205 insertions, 1162 deletions
diff --git a/src/animation/animation.pro b/src/animation/animation.pro index 4443e3193..b7d08416c 100644 --- a/src/animation/animation.pro +++ b/src/animation/animation.pro @@ -15,7 +15,8 @@ include(backend/backend.pri) HEADERS += \ qt3danimation_global.h \ qt3danimation_global_p.h \ - animationlogging_p.h + animationlogging_p.h \ + job_common_p.h SOURCES += \ animationlogging.cpp diff --git a/src/animation/animationlogging.cpp b/src/animation/animationlogging.cpp index a822f0346..11bd47a23 100644 --- a/src/animation/animationlogging.cpp +++ b/src/animation/animationlogging.cpp @@ -44,10 +44,10 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { namespace Animation { -Q_LOGGING_CATEGORY(Backend, "Qt3D.Animation.Backend") -Q_LOGGING_CATEGORY(Frontend, "Qt3D.Animation.Frontend") -Q_LOGGING_CATEGORY(Jobs, "Qt3D.Animation.Jobs") -Q_LOGGING_CATEGORY(HandlerLogic, "Qt3D.Animation.Handler") +Q_LOGGING_CATEGORY(Backend, "Qt3D.Animation.Backend", QtWarningMsg) +Q_LOGGING_CATEGORY(Frontend, "Qt3D.Animation.Frontend", QtWarningMsg) +Q_LOGGING_CATEGORY(Jobs, "Qt3D.Animation.Jobs", QtWarningMsg) +Q_LOGGING_CATEGORY(HandlerLogic, "Qt3D.Animation.Handler", QtWarningMsg) } // namespace Animation } // namespace Qt3DAnimation 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/clipblendvalue_p.h b/src/animation/backend/clipblendvalue_p.h new file mode 100644 index 000000000..6da800f98 --- /dev/null +++ b/src/animation/backend/clipblendvalue_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** 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_CLIPBLENDVALUE_H +#define QT3DANIMATION_ANIMATION_CLIPBLENDVALUE_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 ClipBlendValue : public ClipBlendNode +{ +public: + ClipBlendValue(); + ~ClipBlendValue(); + + 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 +} // namespace Qt3DAnimation + + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_ANIMATION_CLIPBLENDVALUE_H 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/lerpclipblend_p.h b/src/animation/backend/lerpclipblend_p.h new file mode 100644 index 000000000..e09987759 --- /dev/null +++ b/src/animation/backend/lerpclipblend_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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_LERPCLIPBLEND_P_H +#define QT3DANIMATION_ANIMATION_LERPCLIPBLEND_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 LerpClipBlend : public ClipBlendNode +{ +public: + 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; +}; + +} // Animation + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#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) diff --git a/src/animation/frontend/frontend.pri b/src/animation/frontend/frontend.pri index 9eeeede97..9ea438395 100644 --- a/src/animation/frontend/frontend.pri +++ b/src/animation/frontend/frontend.pri @@ -1,35 +1,76 @@ HEADERS += \ $$PWD/qanimationaspect.h \ $$PWD/qanimationaspect_p.h \ - $$PWD/qanimationclip.h \ - $$PWD/qanimationclip_p.h \ + $$PWD/qabstractclipanimator.h \ + $$PWD/qabstractclipanimator_p.h \ + $$PWD/qabstractanimationclip.h \ + $$PWD/qabstractanimationclip_p.h \ $$PWD/qabstractclipblendnode.h \ $$PWD/qabstractclipblendnode_p.h \ $$PWD/qclipanimator.h \ $$PWD/qclipanimator_p.h \ $$PWD/qblendedclipanimator.h \ $$PWD/qblendedclipanimator_p.h \ - $$PWD/qconductedclipanimator.h \ - $$PWD/qconductedclipanimator_p.h \ $$PWD/qchannelmapper.h \ $$PWD/qchannelmapper_p.h \ $$PWD/qchannelmapping.h \ $$PWD/qchannelmapping_p.h \ - $$PWD/qlerpblend.h \ - $$PWD/qlerpblend_p.h \ $$PWD/qclipblendnodecreatedchange.h \ - $$PWD/qclipblendnodecreatedchange_p.h + $$PWD/qclipblendnodecreatedchange_p.h \ + $$PWD/qanimationcontroller.h \ + $$PWD/qanimationcontroller_p.h \ + $$PWD/qanimationgroup.h \ + $$PWD/qanimationgroup_p.h \ + $$PWD/qkeyframeanimation.h \ + $$PWD/qkeyframeanimation_p.h \ + $$PWD/qmorphinganimation.h \ + $$PWD/qmorphinganimation_p.h \ + $$PWD/qabstractanimation.h \ + $$PWD/qabstractanimation_p.h \ + $$PWD/qmorphtarget.h \ + $$PWD/qmorphtarget_p.h \ + $$PWD/qvertexblendanimation.h \ + $$PWD/qvertexblendanimation_p.h \ + $$PWD/qanimationcliploader.h \ + $$PWD/qanimationclip_p.h \ + $$PWD/qlerpclipblend.h \ + $$PWD/qlerpclipblend_p.h \ + $$PWD/qadditiveclipblend.h \ + $$PWD/qadditiveclipblend_p.h \ + $$PWD/qclipblendvalue.h \ + $$PWD/qclipblendvalue_p.h \ + $$PWD/qanimationclipdata.h \ + $$PWD/qchannel.h \ + $$PWD/qchannelcomponent.h \ + $$PWD/qkeyframe.h \ + $$PWD/qanimationclip.h \ + $$PWD/qanimationclip_p.h SOURCES += \ $$PWD/qanimationaspect.cpp \ - $$PWD/qanimationclip.cpp \ + $$PWD/qabstractclipanimator.cpp \ + $$PWD/qabstractanimationclip.cpp \ $$PWD/qabstractclipblendnode.cpp \ $$PWD/qclipanimator.cpp \ $$PWD/qblendedclipanimator.cpp \ - $$PWD/qconductedclipanimator.cpp \ $$PWD/qchannelmapper.cpp \ $$PWD/qchannelmapping.cpp \ - $$PWD/qlerpblend.cpp \ - $$PWD/qclipblendnodecreatedchange.cpp + $$PWD/qclipblendnodecreatedchange.cpp \ + $$PWD/qanimationcontroller.cpp \ + $$PWD/qanimationgroup.cpp \ + $$PWD/qkeyframeanimation.cpp \ + $$PWD/qmorphinganimation.cpp \ + $$PWD/qabstractanimation.cpp \ + $$PWD/qmorphtarget.cpp \ + $$PWD/qvertexblendanimation.cpp \ + $$PWD/qanimationcliploader.cpp \ + $$PWD/qlerpclipblend.cpp \ + $$PWD/qadditiveclipblend.cpp \ + $$PWD/qclipblendvalue.cpp \ + $$PWD/qanimationclipdata.cpp \ + $$PWD/qchannel.cpp \ + $$PWD/qchannelcomponent.cpp \ + $$PWD/qkeyframe.cpp \ + $$PWD/qanimationclip.cpp INCLUDEPATH += $$PWD diff --git a/src/animation/frontend/qabstractanimation.cpp b/src/animation/frontend/qabstractanimation.cpp new file mode 100644 index 000000000..efac3f5ec --- /dev/null +++ b/src/animation/frontend/qabstractanimation.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 "qabstractanimation.h" +#include "Qt3DAnimation/private/qabstractanimation_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +/*! + \class Qt3DAnimation::QAbstractAnimation + \brief An abstract base class for Qt3D animations + \inmodule Qt3DAnimation + \since 5.9 + \inherits QObject + + Qt3DAnimation::QAbstractAnimation is an abstract base class for all animations. + Qt3DAnimation::QAbstractAnimation can not be directly instantiated, but rather + through its subclasses. QAbstractAnimation specifies common properties + for all Qt3D animations, such as animation name and type, current position and animation + duration, while leaving the actual animating for the subclasses. +*/ + +/*! + \qmltype AbstractAnimation + \brief An abstract base type for Qt3D animations + \inqmlmodule Qt3D.Animation + \since 5.9 + \instantiates Qt3DAnimation::QAbstractAnimation + + AbstractAnimation is an abstract base type for all animations. + AbstractAnimation can not be directly instantiated, but rather + through its subtypes. AbstractAnimation specifies common properties + for all Qt3D animations, such as animation type, current position and animation + duration, while leaving the actual animating for the subtypes. +*/ +/*! + \enum QAbstractAnimation::AnimationType + + This enumeration specifies the type of the animation + \value KeyframeAnimation Simple keyframe animation implementation for QTransform + \value MorphingAnimation Blend-shape morphing animation + \value VertexBlendAnimation Vertex-blend animation +*/ +/*! + \property Qt3DAnimation::QAbstractAnimation::animationName + Holds the name of the animation. +*/ +/*! + \property Qt3DAnimation::QAbstractAnimation::animationType + Holds the type of the animation. +*/ +/*! + \property Qt3DAnimation::QAbstractAnimation::position + Holds the current position of the animation. +*/ +/*! + \property Qt3DAnimation::QAbstractAnimation::duration + Holds the duration of the animation. +*/ + +/*! + \qmlproperty string AbstractAnimation::animationName + Holds the name of the animation. +*/ +/*! + \qmlproperty enumeration AbstractAnimation::animationType + Holds the type of the animation. + \list + \li KeyframeAnimation + \li MorphingAnimation + \li VertexBlendAnimation + \endlist +*/ +/*! + \qmlproperty real AbstractAnimation::position + Holds the current position of the animation. +*/ +/*! + \qmlproperty real AbstractAnimation::duration + Holds the duration of the animation. +*/ + +QAbstractAnimationPrivate::QAbstractAnimationPrivate(QAbstractAnimation::AnimationType type) + : QObjectPrivate() + , m_animationType(type) + , m_position(0.0f) + , m_duration(0.0f) +{ + +} + +QAbstractAnimation::QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ + +} + +QString QAbstractAnimation::animationName() const +{ + Q_D(const QAbstractAnimation); + return d->m_animationName; +} + +QAbstractAnimation::AnimationType QAbstractAnimation::animationType() const +{ + Q_D(const QAbstractAnimation); + return d->m_animationType; +} + +float QAbstractAnimation::position() const +{ + Q_D(const QAbstractAnimation); + return d->m_position; +} + +float QAbstractAnimation::duration() const +{ + Q_D(const QAbstractAnimation); + return d->m_duration; +} + +void QAbstractAnimation::setAnimationName(const QString &name) +{ + Q_D(QAbstractAnimation); + if (name != d->m_animationName) { + d->m_animationName = name; + emit animationNameChanged(name); + } +} + +void QAbstractAnimation::setPosition(float position) +{ + Q_D(QAbstractAnimation); + if (!qFuzzyCompare(position, d->m_position)) { + d->m_position = position; + emit positionChanged(position); + } +} + +/*! + Sets the \a duration of the animation. +*/ +void QAbstractAnimation::setDuration(float duration) +{ + Q_D(QAbstractAnimation); + if (!qFuzzyCompare(duration, d->m_duration)) { + d->m_duration = duration; + emit durationChanged(duration); + } +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qabstractanimation.h b/src/animation/frontend/qabstractanimation.h new file mode 100644 index 000000000..0c6fe865e --- /dev/null +++ b/src/animation/frontend/qabstractanimation.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QABSTRACTANIMATION_H +#define QT3DANIMATION_QABSTRACTANIMATION_H + +#include <QtCore/qobject.h> +#include <QtCore/qvector.h> + +#include <Qt3DAnimation/qt3danimation_global.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAbstractAnimationPrivate; + +class QT3DANIMATIONSHARED_EXPORT QAbstractAnimation : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString animationName READ animationName WRITE setAnimationName NOTIFY animationNameChanged) + Q_PROPERTY(QAbstractAnimation::AnimationType animationType READ animationType CONSTANT) + Q_PROPERTY(float position READ position WRITE setPosition NOTIFY positionChanged) + Q_PROPERTY(float duration READ duration NOTIFY durationChanged) + +public: + enum AnimationType { + KeyframeAnimation = 1, + MorphingAnimation = 2, + VertexBlendAnimation = 3, + }; + Q_ENUM(AnimationType) + + QString animationName() const; + QAbstractAnimation::AnimationType animationType() const; + float position() const; + float duration() const; + +public Q_SLOTS: + void setAnimationName(const QString &name); + void setPosition(float position); + +protected: + explicit QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent = nullptr); + + void setDuration(float duration); + +Q_SIGNALS: + void animationNameChanged(const QString &name); + void positionChanged(float position); + void durationChanged(float duration); + +private: + Q_DECLARE_PRIVATE(QAbstractAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QABSTRACTANIMATION_H diff --git a/src/animation/frontend/qabstractanimation_p.h b/src/animation/frontend/qabstractanimation_p.h new file mode 100644 index 000000000..e1c412b01 --- /dev/null +++ b/src/animation/frontend/qabstractanimation_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QABSTRACTANIMATION_P_H +#define QT3DANIMATION_QABSTRACTANIMATION_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 <private/qobject_p.h> +#include <Qt3DAnimation/QAbstractAnimation> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class Q_AUTOTEST_EXPORT QAbstractAnimationPrivate : public QObjectPrivate +{ +public: + QAbstractAnimationPrivate(QAbstractAnimation::AnimationType type); + + QString m_animationName; + QAbstractAnimation::AnimationType m_animationType; + float m_position; + float m_duration; + + Q_DECLARE_PUBLIC(QAbstractAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QANIMATIONCONTROLLER_P_H diff --git a/src/animation/frontend/qabstractanimationclip.cpp b/src/animation/frontend/qabstractanimationclip.cpp new file mode 100644 index 000000000..7e6df4d70 --- /dev/null +++ b/src/animation/frontend/qabstractanimationclip.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** 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 "qabstractanimationclip.h" +#include "qabstractanimationclip_p.h" +#include <Qt3DCore/qpropertyupdatedchange.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +QAbstractAnimationClipPrivate::QAbstractAnimationClipPrivate() + : Qt3DCore::QNodePrivate() + , m_duration(0.0f) +{ +} + +void QAbstractAnimationClipPrivate::setDuration(float duration) +{ + if (qFuzzyCompare(duration, m_duration)) + return; + + Q_Q(QAbstractAnimationClip); + bool wasBlocked = q->blockNotifications(true); + m_duration = duration; + emit q->durationChanged(duration); + q->blockNotifications(wasBlocked); +} + +/*! + \class Qt3DAnimation::QAbstractAnimationClip + \inherits Qt3DCore::QNode + + \inmodule Qt3DAnimation + \since 5.9 + + \brief QAbstractAnimationClip is the base class for types providing key frame animation data. + + To utilise the key frame animation framework in the Qt 3D Animation module + the animator component in use needs to be provided with the key frame animation data. The + animation data is provided by one of the concrete subclasses of QAbstractAnimationClip: + + \list + \li Qt3DAnimation::QAnimationClip + \li Qt3DAnimation::QAnimationClipLoader + \endlist + + QAnimationClip should be used when you want to create the animation data + programmatically within your application. The actual data is set with a + QAnimationClipData value type. + + If you are loading baked animation data from a file, e.g. as created by an + artist, then use the QAnimationClipLoader class and set its \c source property. + + Once the animation clip has been populated with data using the above + methods, the read-only duration property will be updated by the Qt 3D Animation + backend. + + The typical usage of animation clips is: + + \code + auto animator = new QClipAnimator(); + auto clip = new QAnimationClipLoader(); + clip->setSource(QUrl::fromLocalFile("bounce.json")); + animator->setClip(clip); + animator->setChannelMapper(...); + animator->setRunning(true); + \endcode + + Animation clips are also used as the leaf node values in animation blend trees: + + \code + // Create leaf nodes of blend tree + auto slideClipValue = new QClipBlendValue( + new QAnimationClipLoader(QUrl::fromLocalFile("slide.json"))); + auto bounceClipValue = new QClipBlendValue( + new QAnimationClipLoader(QUrl::fromLocalFile("bounce.json"))); + + // Create blend tree inner node + auto additiveNode = new QAdditiveClipBlend(); + additiveNode->setBaseClip(slideClipValue); + additiveNode->setAdditiveClip(bounceClipValue); + additiveNode->setAdditiveFactor(0.5f); + + // Run the animator + auto animator = new QBlendedClipAnimator(); + animator->setBlendTree(additiveNode); + animator->setChannelMapper(...); + animator->setRunning(true); + \endcode + + \sa QAnimationClip, QAnimationClipLoader +*/ + +/*! + \internal +*/ +QAbstractAnimationClip::QAbstractAnimationClip(QAbstractAnimationClipPrivate &dd, + Qt3DCore::QNode *parent) + : Qt3DCore::QNode(dd, parent) +{ +} + +/*! + Destroys this animation clip. +*/ +QAbstractAnimationClip::~QAbstractAnimationClip() +{ +} + +/*! + \property QAbstractAnimationClip::duration + + Holds the duration of the animation clip in seconds. Gets updated once the + animation data is provided to Qt 3D using one of the concrete subclasses. +*/ +float QAbstractAnimationClip::duration() const +{ + Q_D(const QAbstractAnimationClip); + return d->m_duration; +} + +/*! + \internal +*/ +void QAbstractAnimationClip::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) +{ + Q_D(QAbstractAnimationClip); + if (change->type() == Qt3DCore::PropertyUpdated) { + Qt3DCore::QPropertyUpdatedChangePtr e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change); + if (e->propertyName() == QByteArrayLiteral("duration")) + d->setDuration(e->value().toFloat()); + } +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qlerpblend.h b/src/animation/frontend/qabstractanimationclip.h index 58fad7ff1..377725fa1 100644 --- a/src/animation/frontend/qlerpblend.h +++ b/src/animation/frontend/qabstractanimationclip.h @@ -34,45 +34,42 @@ ** ****************************************************************************/ -#ifndef QT3DANIMATION_QLERPBLEND_H -#define QT3DANIMATION_QLERPBLEND_H +#ifndef QT3DANIMATION_QABSTRACTANIMATIONCLIP_H +#define QT3DANIMATION_QABSTRACTANIMATIONCLIP_H -#include <Qt3DAnimation/qabstractclipblendnode.h> +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DCore/qnode.h> +#include <QtCore/qurl.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QLerpBlendPrivate; +class QAbstractAnimationClipPrivate; -class QT3DANIMATIONSHARED_EXPORT QLerpBlend : public QAbstractClipBlendNode +class QT3DANIMATIONSHARED_EXPORT QAbstractAnimationClip : public Qt3DCore::QNode { Q_OBJECT - Q_PROPERTY(float blendFactor READ blendFactor WRITE setBlendFactor NOTIFY blendFactorChanged) + Q_PROPERTY(float duration READ duration NOTIFY durationChanged) public: - explicit QLerpBlend(Qt3DCore::QNode *parent = nullptr); - ~QLerpBlend(); + ~QAbstractAnimationClip(); - float blendFactor() const; - -public Q_SLOTS: - void setBlendFactor(float blendFactor); + float duration() const; Q_SIGNALS: - void blendFactorChanged(float blendFactor); + void durationChanged(float duration); protected: - explicit QLerpBlend(QLerpBlendPrivate &dd, Qt3DCore::QNode *parent = nullptr); + QAbstractAnimationClip(QAbstractAnimationClipPrivate &dd, Qt3DCore::QNode *parent = nullptr); + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) Q_DECL_OVERRIDE; private: - Q_DECLARE_PRIVATE(QLerpBlend) - Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE; + Q_DECLARE_PRIVATE(QAbstractAnimationClip) }; -} // Qt3DAnimation +} // namespace Qt3DAnimation QT_END_NAMESPACE - -#endif // QT3DANIMATION_QLERPBLEND_H +#endif // QT3DANIMATION_QABSTRACTANIMATIONCLIP_H diff --git a/src/animation/backend/lerpblend_p.h b/src/animation/frontend/qabstractanimationclip_p.h index a28815349..8b74bee94 100644 --- a/src/animation/backend/lerpblend_p.h +++ b/src/animation/frontend/qabstractanimationclip_p.h @@ -34,8 +34,8 @@ ** ****************************************************************************/ -#ifndef QT3DANIMATION_ANIMATION_LERPBLEND_P_H -#define QT3DANIMATION_ANIMATION_LERPBLEND_P_H +#ifndef QT3DANIMATION_QABSTRACTANIMATIONCLIP_P_H +#define QT3DANIMATION_QABSTRACTANIMATIONCLIP_P_H // // W A R N I N G @@ -48,34 +48,29 @@ // We mean it. // -#include <Qt3DAnimation/private/clipblendnode_p.h> +#include <Qt3DCore/private/qnode_p.h> +#include "qabstractanimationclip.h" QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -namespace Animation { - -class Q_AUTOTEST_EXPORT LerpBlend : public ClipBlendNode +class QAbstractAnimationClipPrivate : public Qt3DCore::QNodePrivate { public: - LerpBlend(); - ~LerpBlend(); - - inline float blendFactor() const { return m_blendFactor; } + QAbstractAnimationClipPrivate(); - void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_FINAL; + Q_DECLARE_PUBLIC(QAbstractAnimationClip) -private: - void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; + void setDuration(float duration); - float m_blendFactor; + // Set from the backend + float m_duration; }; -} // Animation +} // namespace Qt3DAnimation -} // Qt3DAnimation QT_END_NAMESPACE -#endif // QT3DANIMATION_ANIMATION_LERPBLEND_P_H +#endif // QT3DANIMATION_QABSTRACTANIMATIONCLIP_P_H diff --git a/src/animation/frontend/qabstractclipanimator.cpp b/src/animation/frontend/qabstractclipanimator.cpp new file mode 100644 index 000000000..0d215b470 --- /dev/null +++ b/src/animation/frontend/qabstractclipanimator.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstractclipanimator.h" +#include "qabstractclipanimator_p.h" +#include <Qt3DAnimation/qchannelmapper.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +QAbstractClipAnimatorPrivate::QAbstractClipAnimatorPrivate() + : Qt3DCore::QComponentPrivate() + , m_mapper(nullptr) + , m_running(false) + , m_loops(1) +{ +} + +/*! + \qmltype AbsractClipAnimator + \instantiates Qt3DAnimation::QAbstractClipAnimator + \inqmlmodule Qt3D.Animation + \since 5.9 + + \brief AbstractClipAnimator is the base class for types providing animation playback + capabilities. + + Subclasses of AbstractClipAnimator can be aggregated by an Entity to + provide animation capabilities. The animator components provide an + interface for controlling the animation (e.g. start, stop). Each animator + type requires some form of animation data such as an AbstractAnimationClip + as well as a ChannelMapper which describes how the channels in the + animation clip should be mapped onto the properties of the objects you wish + to animate. + + The following subclasses are available: + + \list + \li Qt3D.Animation.ClipAnimator + \li Qt3D.Animation.BlendedClipAnimator + \endlist +*/ + +/*! + \class Qt3DAnimation::QAbstractClipAnimator + \inherits Qt3DCore::QComponent + + \inmodule Qt3DAnimation + \since 5.9 + + \brief QAbstractClipAnimator is the base class for types providing animation playback + capabilities. + + Subclasses of QAbstractClipAnimator can be aggregated by a QEntity to + provide animation capabilities. The animator components provide an + interface for controlling the animation (e.g. start, stop). Each animator + type requires some form of animation data such as a QAbstractAnimationClip + as well as a QChannelMapper which describes how the channels in the + animation clip should be mapped onto the properties of the objects you wish + to animate. + + The following subclasses are available: + + \list + \li Qt3DAnimation::QClipAnimator + \li Qt3DAnimation::QBlendedClipAnimator + \endlist +*/ + +QAbstractClipAnimator::QAbstractClipAnimator(Qt3DCore::QNode *parent) + : Qt3DCore::QComponent(*new QAbstractClipAnimatorPrivate, parent) +{ +} + +QAbstractClipAnimator::QAbstractClipAnimator(QAbstractClipAnimatorPrivate &dd, Qt3DCore::QNode *parent) + : Qt3DCore::QComponent(dd, parent) +{ +} + +QAbstractClipAnimator::~QAbstractClipAnimator() +{ +} + +/*! + \qmlproperty bool running + + This property holds whether the animation is currently running. +*/ + +/*! + \property running + + This property holds whether the animation is currently running. +*/ +bool QAbstractClipAnimator::isRunning() const +{ + Q_D(const QAbstractClipAnimator); + return d->m_running; +} + +/*! + \property ChannelMapper channelMapper + + This property holds the ChannelMapper that controls how the channels in + the animation clip map onto the properties of the target objects. +*/ + +/*! + \property channelMapper + + This property holds the QChannelMapper that controls how the channels in + the animation clip map onto the properties of the target objects. +*/ +QChannelMapper *QAbstractClipAnimator::channelMapper() const +{ + Q_D(const QAbstractClipAnimator); + return d->m_mapper; +} + +/*! + \qmlproperty int loops + + This property holds the number of times the animation should play. + + By default, loops is 1: the animation will play through once and then stop. + + If set to QAbstractClipAnimator::Infinite, the animation will continuously + repeat until it is explicitly stopped. +*/ + +/*! + \property loops + + This property holds the number of times the animation should play. + + By default, loops is 1: the animation will play through once and then stop. + + If set to QAbstractClipAnimator::Infinite, the animation will continuously + repeat until it is explicitly stopped. +*/ +int QAbstractClipAnimator::loopCount() const +{ + Q_D(const QAbstractClipAnimator); + return d->m_loops; +} + +void QAbstractClipAnimator::setRunning(bool running) +{ + Q_D(QAbstractClipAnimator); + if (d->m_running == running) + return; + + d->m_running = running; + emit runningChanged(running); +} + +void QAbstractClipAnimator::setChannelMapper(QChannelMapper *mapping) +{ + Q_D(QAbstractClipAnimator); + if (d->m_mapper == mapping) + return; + + if (d->m_mapper) + d->unregisterDestructionHelper(d->m_mapper); + + if (mapping && !mapping->parent()) + mapping->setParent(this); + d->m_mapper = mapping; + + // Ensures proper bookkeeping + if (d->m_mapper) + d->registerDestructionHelper(d->m_mapper, &QAbstractClipAnimator::setChannelMapper, d->m_mapper); + emit channelMapperChanged(mapping); +} + +void QAbstractClipAnimator::setLoopCount(int loops) +{ + Q_D(QAbstractClipAnimator); + if (d->m_loops == loops) + return; + + d->m_loops = loops; + emit loopCountChanged(loops); +} + +/*! + Starts the animation. +*/ +void QAbstractClipAnimator::start() +{ + setRunning(true); +} + +/*! + Stops the animation. +*/ +void QAbstractClipAnimator::stop() +{ + setRunning(false); +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qabstractclipanimator.h b/src/animation/frontend/qabstractclipanimator.h new file mode 100644 index 000000000..bd38fd68b --- /dev/null +++ b/src/animation/frontend/qabstractclipanimator.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DANIMATION_QABSTRACTCLIPANIMATOR_H +#define QT3DANIMATION_QABSTRACTCLIPANIMATOR_H + +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DCore/qcomponent.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAnimationClip; +class QChannelMapper; +class QAbstractClipAnimatorPrivate; + +class QT3DANIMATIONSHARED_EXPORT QAbstractClipAnimator : public Qt3DCore::QComponent +{ + Q_OBJECT + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(int loops READ loopCount WRITE setLoopCount NOTIFY loopCountChanged) + Q_PROPERTY(Qt3DAnimation::QChannelMapper *channelMapper READ channelMapper WRITE setChannelMapper NOTIFY channelMapperChanged) + +public: + enum Loops { Infinite = -1 }; + Q_ENUM(Loops) + + ~QAbstractClipAnimator(); + + bool isRunning() const; + Qt3DAnimation::QChannelMapper *channelMapper() const; + int loopCount() const; + +public Q_SLOTS: + void setRunning(bool running); + void setChannelMapper(Qt3DAnimation::QChannelMapper *channelMapper); + void setLoopCount(int loops); + + void start(); + void stop(); + +Q_SIGNALS: + void runningChanged(bool running); + void channelMapperChanged(Qt3DAnimation::QChannelMapper *channelMapper); + void loopCountChanged(int loops); + +protected: + explicit QAbstractClipAnimator(Qt3DCore::QNode *parent = nullptr); + QAbstractClipAnimator(QAbstractClipAnimatorPrivate &dd, Qt3DCore::QNode *parent = nullptr); + +private: + Q_DECLARE_PRIVATE(QAbstractClipAnimator) +}; + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QABSTRACTCLIPANIMATOR_H diff --git a/src/animation/frontend/qconductedclipanimator_p.h b/src/animation/frontend/qabstractclipanimator_p.h index c50247fa2..58553c8c1 100644 --- a/src/animation/frontend/qconductedclipanimator_p.h +++ b/src/animation/frontend/qabstractclipanimator_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QT3DANIMATION_QCONDUCTEDCLIPANIMATOR_P_H -#define QT3DANIMATION_QCONDUCTEDCLIPANIMATOR_P_H +#ifndef QT3DANIMATION_QABSTRACTCLIPANIMATOR_P_H +#define QT3DANIMATION_QABSTRACTCLIPANIMATOR_P_H // // W A R N I N G @@ -57,19 +57,25 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QConductedClipAnimatorPrivate : public Qt3DCore::QComponentPrivate +class QChannelMapper; + +class QAbstractClipAnimatorPrivate : public Qt3DCore::QComponentPrivate { public: - QConductedClipAnimatorPrivate(); + QAbstractClipAnimatorPrivate(); - Q_DECLARE_PUBLIC(QConductedClipAnimator) + Q_DECLARE_PUBLIC(QAbstractClipAnimator) - // TODO Add member variables + Qt3DAnimation::QChannelMapper *m_mapper; + bool m_running; + int m_loops; }; -struct QConductedClipAnimatorData +struct QAbstractClipAnimatorData { - // TODO: Add members that should be sent to the backend + Qt3DCore::QNodeId mapperId; + bool running; + int loops; }; } // namespace Qt3DAnimation @@ -77,4 +83,4 @@ struct QConductedClipAnimatorData QT_END_NAMESPACE -#endif // QT3DANIMATION_QCONDUCTEDCLIPANIMATOR_P_H +#endif // QT3DANIMATION_QABSTRACTCLIPANIMATOR_P_H diff --git a/src/animation/frontend/qabstractclipblendnode.cpp b/src/animation/frontend/qabstractclipblendnode.cpp index 4154b4223..9860e969f 100644 --- a/src/animation/frontend/qabstractclipblendnode.cpp +++ b/src/animation/frontend/qabstractclipblendnode.cpp @@ -36,9 +36,6 @@ #include "qabstractclipblendnode.h" #include "qabstractclipblendnode_p.h" -#include <Qt3DCore/qpropertynodeaddedchange.h> -#include <Qt3DCore/qpropertynoderemovedchange.h> -#include <Qt3DAnimation/qanimationclip.h> QT_BEGIN_NAMESPACE @@ -49,11 +46,82 @@ QAbstractClipBlendNodePrivate::QAbstractClipBlendNodePrivate() { } +/*! + \qmltype AbstractClipBlendNode + \instantiates Qt3DAnimation::QAbstractClipBlendNode + \inqmlmodule Qt3D.Animation + \since 5.9 + + \brief AbstractClipBlendNode is the base class for types used to construct animation blend + trees. + + Animation blend trees are used with a BlendedClipAnimator to dynamically blend a set of + animation clips together. The way in which the blending of animation clips is performed is + controlled by the structure of the blend tree and the properties on the nodes it contains. + + The leaf nodes in a blend tree are containers for the input animation clips. These clips can be + baked clips read from file via AnimationClipLoader, or they can be clips that you build within + your application with AnimatitonClip and AnimationClipData. To include a clip in your blend + tree, wrap it in a ClipBlendValue node. + + The interior nodes of a blend tree represent blending operations that will be applied to their + arguments which hold the input clips or even entire sub-trees of other blend tree nodes. + + At present, the Qt 3D Animation module provides the following blend tree node types: + + \list + \li Qt3D.Animation.ClipBlendValue + \li Qt3D.Animation.LerpClipBlend + \li Qt3D.Animation.QAdditiveClipBlend + \endlist + + Additional node types representing other blending operations will be added in the future. + + \sa BlendedClipAnimator +*/ + +/*! + \class Qt3DAnimation::QAbstractClipBlendNode + \inherits Qt3DCore::QNode + + \inmodule Qt3DAnimation + \since 5.9 + + \brief QAbstractClipBlendNode is the base class for types used to construct animation blend + trees. + + Animation blend trees are used with a QBlendedClipAnimator to dynamically blend a set of + animation clips together. The way in which the blending of animation clips is performed is + controlled by the structure of the blend tree and the properties on the nodes it contains. + + The leaf nodes in a blend tree are containers for the input animation clips. These clips can be + baked clips read from file via QAnimationClipLoader, or they can be clips that you build within + your application with QAnimatitonClip and QAnimationClipData. To include a clip in your blend + tree, wrap it in a QClipBlendValue node. + + The interior nodes of a blend tree represent blending operations that will be applied to their + arguments which hold the input clips or even entire sub-trees of other blend tree nodes. + + At present, the Qt 3D Animation module provides the following blend tree node types: + + \list + \li Qt3DAnimation::QClipBlendValue + \li Qt3DAnimation::QLerpClipBlend + \li Qt3DAnimation::QAdditiveClipBlend + \endlist + + Additional node types representing other blending operations will be added in the future. + + \sa QBlendedClipAnimator +*/ + +/*! \internal */ QAbstractClipBlendNode::QAbstractClipBlendNode(Qt3DCore::QNode *parent) : Qt3DCore::QNode(*new QAbstractClipBlendNodePrivate(), parent) { } +/*! \internal */ QAbstractClipBlendNode::QAbstractClipBlendNode(QAbstractClipBlendNodePrivate &dd, Qt3DCore::QNode *parent) : Qt3DCore::QNode(dd, parent) { @@ -63,62 +131,6 @@ QAbstractClipBlendNode::~QAbstractClipBlendNode() { } -void QAbstractClipBlendNode::addClip(QAnimationClip *clip) -{ - Q_D(QAbstractClipBlendNode); - if (!d->m_clips.contains(clip)) { - d->m_clips.push_back(clip); - - // Ensures proper bookkeeping - d->registerDestructionHelper(clip, &QAbstractClipBlendNode::removeClip, d->m_clips); - - // We need to add it as a child of the current node if it has been declared inline - // Or not previously added as a child of the current node so that - // 1) The backend gets notified about it's creation - // 2) When the current node is destroyed, it gets destroyed as well - if (!clip->parent()) - clip->setParent(this); - - if (d->m_changeArbiter != nullptr) { - const auto change = Qt3DCore::QPropertyNodeAddedChangePtr::create(id(), clip); - change->setPropertyName("clip"); - d->notifyObservers(change); - } - } -} - -void QAbstractClipBlendNode::removeClip(QAnimationClip *clip) -{ - Q_D(QAbstractClipBlendNode); - if (d->m_changeArbiter != nullptr) { - const auto change = Qt3DCore::QPropertyNodeRemovedChangePtr::create(id(), clip); - change->setPropertyName("clip"); - d->notifyObservers(change); - } - d->m_clips.removeOne(clip); - // Remove bookkeeping connection - d->unregisterDestructionHelper(clip); -} - -QVector<QAnimationClip *> QAbstractClipBlendNode::clips() const -{ - Q_D(const QAbstractClipBlendNode); - return d->m_clips; -} - -QAbstractClipBlendNode *QAbstractClipBlendNode::parentClipBlendNode() const -{ - QAbstractClipBlendNode *parentBlendClipNode = nullptr; - QNode *parentN = parentNode(); - while (parentN != nullptr) { - parentBlendClipNode = qobject_cast<QAbstractClipBlendNode *>(parentN); - if (parentBlendClipNode != nullptr) - break; - parentN = parentN->parentNode(); - } - return parentBlendClipNode; -} - } // Qt3DAnimation QT_END_NAMESPACE diff --git a/src/animation/frontend/qabstractclipblendnode.h b/src/animation/frontend/qabstractclipblendnode.h index 35714026c..d981bd7a5 100644 --- a/src/animation/frontend/qabstractclipblendnode.h +++ b/src/animation/frontend/qabstractclipblendnode.h @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QAnimationClip; +class QAbstractAnimationClip; class QAbstractClipBlendNodePrivate; class QT3DANIMATIONSHARED_EXPORT QAbstractClipBlendNode : public Qt3DCore::QNode @@ -53,16 +53,10 @@ class QT3DANIMATIONSHARED_EXPORT QAbstractClipBlendNode : public Qt3DCore::QNode public: ~QAbstractClipBlendNode(); - QVector<QAnimationClip *> clips() const; - QAbstractClipBlendNode *parentClipBlendNode() const; - void addClip(QAnimationClip *clip); - void removeClip(QAnimationClip *clip); - protected: explicit QAbstractClipBlendNode(Qt3DCore::QNode *parent = nullptr); explicit QAbstractClipBlendNode(QAbstractClipBlendNodePrivate &dd, Qt3DCore::QNode *parent = nullptr); - private: Q_DECLARE_PRIVATE(QAbstractClipBlendNode) }; diff --git a/src/animation/frontend/qabstractclipblendnode_p.h b/src/animation/frontend/qabstractclipblendnode_p.h index 42e10f18c..3066682a5 100644 --- a/src/animation/frontend/qabstractclipblendnode_p.h +++ b/src/animation/frontend/qabstractclipblendnode_p.h @@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QAnimationClip; +class QAbstractAnimationClip; class QAbstractClipBlendNode; class QAbstractClipBlendNodePrivate : public Qt3DCore::QNodePrivate @@ -63,7 +63,7 @@ public: QAbstractClipBlendNodePrivate(); Q_DECLARE_PUBLIC(QAbstractClipBlendNode) - QVector<QAnimationClip *> m_clips; + QVector<QAbstractAnimationClip *> m_clips; }; } // namespace Qt3DAnimation diff --git a/src/animation/frontend/qadditiveclipblend.cpp b/src/animation/frontend/qadditiveclipblend.cpp new file mode 100644 index 000000000..58ef5e577 --- /dev/null +++ b/src/animation/frontend/qadditiveclipblend.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** 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 "qadditiveclipblend.h" +#include "qadditiveclipblend_p.h" +#include <Qt3DAnimation/qclipblendnodecreatedchange.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + + +/*! + \qmltype AdditiveClipBlend + \instantiates Qt3DAnimation::QAdditiveClipBlend + \inqmlmodule Qt3D.Animation + + \since 5.9 + + \brief Performs an additive blend of two animation clips based on an additive factor. + + QAdditiveClipBlend can be useful to create advanced animation effects based on + individual animation clips. For example, if you: + + \list + \li set the baseClip property to a normal walk cycle animation clip and + \li set the additiveClip property to a shaking head difference clip, + \endlist + + then adjusting the additiveFactor property will control how much of the additiveClip gets added + on to the baseClip. This has he effect that with an additiveFactor of zero, this blend node will + yield the original walk cycle clip. With an additiveFactor of 1, it will yield the walk cycle + including a shaking head animation. + + The blending operation implemented by this class is: + + \badcode + resultClip = baseClip + additiveFactor * additiveClip + \endcode + + There is nothing stopping you from using values for the additiveFacor property outside the 0 to + 1 range, but please be aware that the input animation clips may not be authored in such a way + for this to make sense. + + \sa BlendedClipAnimator +*/ + +/*! + \class Qt3DAnimation::QAdditiveClipBlend + \inherits Qt3DAnimation::QAbstractClipBlendNode + + \inmodule Qt3DAnimation + \since 5.9 + + \brief Performs an additive blend of two animation clips based on an additive factor. + + QAdditiveClipBlend can be useful to create advanced animation effects based on + individual animation clips. For example, if you: + + \list + \li set the baseClip property to a normal walk cycle animation clip and + \li set the additiveClip property to a shaking head difference clip, + \endlist + + then adjusting the additiveFactor property will control how much of the additiveClip gets added + on to the baseClip. This has he effect that with an additiveFactor of zero, this blend node will + yield the original walk cycle clip. With an additiveFactor of 1, it will yield the walk cycle + including a shaking head animation. + + The blending operation implemented by this class is: + + \badcode + resultClip = baseClip + additiveFactor * additiveClip + \endcode + + There is nothing stopping you from using values for the additiveFacor property outside the 0 to + 1 range, but please be aware that the input animation clips may not be authored in such a way + for this to make sense. + + \sa QBlendedClipAnimator +*/ + +QAdditiveClipBlendPrivate::QAdditiveClipBlendPrivate() + : QAbstractClipBlendNodePrivate() + , m_baseClip(nullptr) + , m_additiveClip(nullptr) + , m_additiveFactor(0.0f) +{ +} + +QAdditiveClipBlend::QAdditiveClipBlend(Qt3DCore::QNode *parent) + : QAbstractClipBlendNode(*new QAdditiveClipBlendPrivate(), parent) +{ +} + +QAdditiveClipBlend::QAdditiveClipBlend(QAdditiveClipBlendPrivate &dd, Qt3DCore::QNode *parent) + : QAbstractClipBlendNode(dd, parent) +{ +} + +QAdditiveClipBlend::~QAdditiveClipBlend() +{ +} + +Qt3DCore::QNodeCreatedChangeBasePtr QAdditiveClipBlend::createNodeCreationChange() const +{ + Q_D(const QAdditiveClipBlend); + auto creationChange = QClipBlendNodeCreatedChangePtr<QAdditiveClipBlendData>::create(this); + QAdditiveClipBlendData &data = creationChange->data; + data.baseClipId = Qt3DCore::qIdForNode(d->m_baseClip); + data.additiveClipId = Qt3DCore::qIdForNode(d->m_additiveClip); + data.additiveFactor = d->m_additiveFactor; + return creationChange; +} + +/*! + \qmlproperty real AdditiveClipBlend::additiveFactor + + Specifies the blending factor, typically between 0 and 1, to control the blending of + two animation clips. +*/ +/*! + \property QAdditiveClipBlend::additiveFactor + + Specifies the blending factor, typically between 0 and 1, to control the blending of + two animation clips. + */ +float QAdditiveClipBlend::additiveFactor() const +{ + Q_D(const QAdditiveClipBlend); + return d->m_additiveFactor; +} + +/*! + \qmlproperty AbstractClipBlendNode baseClip + + This property holds the base animation clip. When the additiveFacgtor is zero the baseClip will + also be the resulting clip of this blend node. +*/ +/*! + \property baseClip + + This property holds the base animation clip. When the additiveFacgtor is zero the baseClip will + also be the resulting clip of this blend node. +*/ +QAbstractClipBlendNode *QAdditiveClipBlend::baseClip() const +{ + Q_D(const QAdditiveClipBlend); + return d->m_baseClip; +} + +/*! + \qmlproperty AbstractClipBlendNode additiveClip + + This property holds the additive clip to be blended with the baseClip. The amount of blending + is controlled by the additiveFactor property. +*/ +/*! + \property additiveClip + + This property holds the additive clip to be blended with the baseClip. The amount of blending + is controlled by the additiveFactor property. +*/ +QAbstractClipBlendNode *QAdditiveClipBlend::additiveClip() const +{ + Q_D(const QAdditiveClipBlend); + return d->m_additiveClip; +} + +void QAdditiveClipBlend::setAdditiveFactor(float additiveFactor) +{ + Q_D(QAdditiveClipBlend); + if (d->m_additiveFactor == additiveFactor) + return; + + d->m_additiveFactor = additiveFactor; + emit additiveFactorChanged(additiveFactor); +} + +void QAdditiveClipBlend::setBaseClip(QAbstractClipBlendNode *baseClip) +{ + Q_D(QAdditiveClipBlend); + if (d->m_baseClip == baseClip) + return; + + if (d->m_baseClip) + d->unregisterDestructionHelper(d->m_baseClip); + + if (baseClip && !baseClip->parent()) + baseClip->setParent(this); + d->m_baseClip = baseClip; + + // Ensures proper bookkeeping + if (d->m_baseClip) + d->registerDestructionHelper(d->m_baseClip, &QAdditiveClipBlend::setBaseClip, d->m_baseClip); + + emit baseClipChanged(baseClip); +} + +void QAdditiveClipBlend::setAdditiveClip(QAbstractClipBlendNode *additiveClip) +{ + Q_D(QAdditiveClipBlend); + if (d->m_additiveClip == additiveClip) + return; + + if (d->m_additiveClip) + d->unregisterDestructionHelper(d->m_additiveClip); + + if (additiveClip && !additiveClip->parent()) + additiveClip->setParent(this); + d->m_additiveClip = additiveClip; + + // Ensures proper bookkeeping + if (d->m_additiveClip) + d->registerDestructionHelper(d->m_additiveClip, &QAdditiveClipBlend::setAdditiveClip, d->m_additiveClip); + emit additiveClipChanged(additiveClip); +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qadditiveclipblend.h b/src/animation/frontend/qadditiveclipblend.h new file mode 100644 index 000000000..11731c702 --- /dev/null +++ b/src/animation/frontend/qadditiveclipblend.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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_QADDITIVECLIPBLEND_H +#define QT3DANIMATION_QADDITIVECLIPBLEND_H + +#include <Qt3DAnimation/qabstractclipblendnode.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAdditiveClipBlendPrivate; + +class QT3DANIMATIONSHARED_EXPORT QAdditiveClipBlend : public QAbstractClipBlendNode +{ + Q_OBJECT + Q_PROPERTY(Qt3DAnimation::QAbstractClipBlendNode *baseClip READ baseClip WRITE setBaseClip NOTIFY baseClipChanged) + Q_PROPERTY(Qt3DAnimation::QAbstractClipBlendNode *additiveClip READ additiveClip WRITE setAdditiveClip NOTIFY additiveClipChanged) + Q_PROPERTY(float additiveFactor READ additiveFactor WRITE setAdditiveFactor NOTIFY additiveFactorChanged) +public: + explicit QAdditiveClipBlend(Qt3DCore::QNode *parent = nullptr); + ~QAdditiveClipBlend(); + + float additiveFactor() const; + Qt3DAnimation::QAbstractClipBlendNode *baseClip() const; + Qt3DAnimation::QAbstractClipBlendNode *additiveClip() const; + +public Q_SLOTS: + void setAdditiveFactor(float additiveFactor); + void setBaseClip(Qt3DAnimation::QAbstractClipBlendNode *baseClip); + void setAdditiveClip(Qt3DAnimation::QAbstractClipBlendNode *additiveClip); + +Q_SIGNALS: + void additiveFactorChanged(float additiveFactor); + void baseClipChanged(Qt3DAnimation::QAbstractClipBlendNode *baseClip); + void additiveClipChanged(Qt3DAnimation::QAbstractClipBlendNode *additiveClip); + +protected: + explicit QAdditiveClipBlend(QAdditiveClipBlendPrivate &dd, Qt3DCore::QNode *parent = nullptr); + +private: + Q_DECLARE_PRIVATE(QAdditiveClipBlend) + Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE; +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QADDITIVECLIPBLEND_H diff --git a/src/animation/frontend/qadditiveclipblend_p.h b/src/animation/frontend/qadditiveclipblend_p.h new file mode 100644 index 000000000..0c408c9ff --- /dev/null +++ b/src/animation/frontend/qadditiveclipblend_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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_QADDITIVEBLEND_P_H +#define QT3DANIMATION_QADDITIVEBLEND_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/qabstractclipblendnode_p.h> +#include <Qt3DCore/qnodeid.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAdditiveClipBlend; + +class QAdditiveClipBlendPrivate : public QAbstractClipBlendNodePrivate +{ +public: + QAdditiveClipBlendPrivate(); + + Q_DECLARE_PUBLIC(QAdditiveClipBlend) + Qt3DAnimation::QAbstractClipBlendNode *m_baseClip; + Qt3DAnimation::QAbstractClipBlendNode *m_additiveClip; + float m_additiveFactor; +}; + +struct QAdditiveClipBlendData +{ + Qt3DCore::QNodeId baseClipId; + Qt3DCore::QNodeId additiveClipId; + float additiveFactor; +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QADDITIVEBLEND_P_H diff --git a/src/animation/frontend/qanimationaspect.cpp b/src/animation/frontend/qanimationaspect.cpp index 3310a8b77..00f97d5b6 100644 --- a/src/animation/frontend/qanimationaspect.cpp +++ b/src/animation/frontend/qanimationaspect.cpp @@ -39,17 +39,20 @@ #include "qanimationaspect.h" #include "qanimationaspect_p.h" -#include <Qt3DAnimation/qanimationclip.h> +#include <Qt3DAnimation/qabstractanimationclip.h> #include <Qt3DAnimation/qblendedclipanimator.h> #include <Qt3DAnimation/qclipanimator.h> -#include <Qt3DAnimation/qconductedclipanimator.h> #include <Qt3DAnimation/qchannelmapping.h> #include <Qt3DAnimation/qchannelmapper.h> -#include <Qt3DAnimation/qlerpblend.h> +#include <Qt3DAnimation/qlerpclipblend.h> +#include <Qt3DAnimation/qadditiveclipblend.h> +#include <Qt3DAnimation/qclipblendvalue.h> #include <Qt3DAnimation/private/handler_p.h> #include <Qt3DAnimation/private/managers_p.h> #include <Qt3DAnimation/private/nodefunctor_p.h> -#include <Qt3DAnimation/private/lerpblend_p.h> +#include <Qt3DAnimation/private/lerpclipblend_p.h> +#include <Qt3DAnimation/private/clipblendvalue_p.h> +#include <Qt3DAnimation/private/additiveclipblend_p.h> QT_BEGIN_NAMESPACE @@ -87,30 +90,33 @@ QAnimationAspect::QAnimationAspect(QAnimationAspectPrivate &dd, QObject *parent) { setObjectName(QStringLiteral("Animation Aspect")); Q_D(QAnimationAspect); - qRegisterMetaType<Qt3DAnimation::QAnimationClip*>(); + qRegisterMetaType<Qt3DAnimation::QAnimationClipLoader*>(); qRegisterMetaType<Qt3DAnimation::QChannelMapper*>(); - registerBackendType<QAnimationClip>( - QSharedPointer<Animation::NodeFunctor<Animation::AnimationClip, Animation::AnimationClipManager>>::create(d->m_handler.data(), - d->m_handler->animationClipManager())); + registerBackendType<QAbstractAnimationClip>( + QSharedPointer<Animation::NodeFunctor<Animation::AnimationClip, Animation::AnimationClipLoaderManager>>::create(d->m_handler.data(), + d->m_handler->animationClipLoaderManager())); registerBackendType<QClipAnimator>( QSharedPointer<Animation::NodeFunctor<Animation::ClipAnimator, Animation::ClipAnimatorManager>>::create(d->m_handler.data(), d->m_handler->clipAnimatorManager())); registerBackendType<QBlendedClipAnimator>( QSharedPointer<Animation::NodeFunctor<Animation::BlendedClipAnimator, Animation::BlendedClipAnimatorManager>>::create(d->m_handler.data(), d->m_handler->blendedClipAnimatorManager())); - registerBackendType<QConductedClipAnimator>( - QSharedPointer<Animation::NodeFunctor<Animation::ConductedClipAnimator, Animation::ConductedClipAnimatorManager>>::create(d->m_handler.data(), - d->m_handler->conductedClipAnimatorManager())); registerBackendType<QChannelMapping>( QSharedPointer<Animation::NodeFunctor<Animation::ChannelMapping, Animation::ChannelMappingManager>>::create(d->m_handler.data(), d->m_handler->channelMappingManager())); registerBackendType<QChannelMapper>( QSharedPointer<Animation::NodeFunctor<Animation::ChannelMapper, Animation::ChannelMapperManager>>::create(d->m_handler.data(), d->m_handler->channelMapperManager())); - registerBackendType<QLerpBlend>( - QSharedPointer<Animation::ClipBlendNodeFunctor<Animation::LerpBlend, Animation::ClipAnimatorManager>>::create(d->m_handler.data(), - d->m_handler->clipBlendNodeManager())); + registerBackendType<QLerpClipBlend>( + QSharedPointer<Animation::ClipBlendNodeFunctor<Animation::LerpClipBlend, Animation::ClipAnimatorManager>>::create(d->m_handler.data(), + d->m_handler->clipBlendNodeManager())); + registerBackendType<QAdditiveClipBlend>( + QSharedPointer<Animation::ClipBlendNodeFunctor<Animation::AdditiveClipBlend, Animation::ClipAnimatorManager>>::create(d->m_handler.data(), + d->m_handler->clipBlendNodeManager())); + registerBackendType<QClipBlendValue>( + QSharedPointer<Animation::ClipBlendNodeFunctor<Animation::ClipBlendValue, Animation::ClipAnimatorManager>>::create(d->m_handler.data(), + d->m_handler->clipBlendNodeManager())); } /*! \internal */ diff --git a/src/animation/frontend/qanimationclip.cpp b/src/animation/frontend/qanimationclip.cpp index 56fe3978c..c21d94b02 100644 --- a/src/animation/frontend/qanimationclip.cpp +++ b/src/animation/frontend/qanimationclip.cpp @@ -1,34 +1,37 @@ /**************************************************************************** ** ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). -** Contact: http://www.qt-project.org/legal +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL3$ +** $QT_BEGIN_LICENSE:LGPL$ ** 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. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 +** Foundation and appearing in the file LICENSE.LGPL3 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. +** will be met: https://www.gnu.org/licenses/lgpl-3.0.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. +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -36,37 +39,24 @@ #include "qanimationclip.h" #include "qanimationclip_p.h" -#include <Qt3DCore/qpropertyupdatedchange.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { QAnimationClipPrivate::QAnimationClipPrivate() - : Qt3DCore::QNodePrivate() - , m_duration(0.0f) + : QAbstractAnimationClipPrivate() + , m_clipData() { } -void QAnimationClipPrivate::setDuration(float duration) -{ - if (qFuzzyCompare(duration, m_duration)) - return; - - Q_Q(QAnimationClip); - bool wasBlocked = q->blockNotifications(true); - m_duration = duration; - emit q->durationChanged(duration); - q->blockNotifications(wasBlocked); -} - QAnimationClip::QAnimationClip(Qt3DCore::QNode *parent) - : Qt3DCore::QNode(*new QAnimationClipPrivate, parent) + : QAbstractAnimationClip(*new QAnimationClipPrivate, parent) { } QAnimationClip::QAnimationClip(QAnimationClipPrivate &dd, Qt3DCore::QNode *parent) - : Qt3DCore::QNode(dd, parent) + : QAbstractAnimationClip(dd, parent) { } @@ -74,44 +64,28 @@ QAnimationClip::~QAnimationClip() { } -QUrl QAnimationClip::source() const +QAnimationClipData QAnimationClip::clipData() const { Q_D(const QAnimationClip); - return d->m_source; + return d->m_clipData; } -float QAnimationClip::duration() const -{ - Q_D(const QAnimationClip); - return d->m_duration; -} - -void QAnimationClip::setSource(QUrl source) +void QAnimationClip::setClipData(const Qt3DAnimation::QAnimationClipData &clipData) { Q_D(QAnimationClip); - if (d->m_source == source) + if (d->m_clipData == clipData) return; - d->m_source = source; - emit sourceChanged(source); -} - -void QAnimationClip::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) -{ - Q_D(QAnimationClip); - if (change->type() == Qt3DCore::PropertyUpdated) { - Qt3DCore::QPropertyUpdatedChangePtr e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change); - if (e->propertyName() == QByteArrayLiteral("duration")) - d->setDuration(e->value().toFloat()); - } + d->m_clipData = clipData; + emit clipDataChanged(clipData); } Qt3DCore::QNodeCreatedChangeBasePtr QAnimationClip::createNodeCreationChange() const { - auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QAnimationClipData>::create(this); + auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QAnimationClipChangeData>::create(this); auto &data = creationChange->data; Q_D(const QAnimationClip); - data.source = d->m_source; + data.clipData = d->m_clipData; return creationChange; } diff --git a/src/animation/frontend/qanimationclip.h b/src/animation/frontend/qanimationclip.h index 55b1910fa..6e606c598 100644 --- a/src/animation/frontend/qanimationclip.h +++ b/src/animation/frontend/qanimationclip.h @@ -1,34 +1,37 @@ /**************************************************************************** ** ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). -** Contact: http://www.qt-project.org/legal +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL3$ +** $QT_BEGIN_LICENSE:LGPL$ ** 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. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 +** Foundation and appearing in the file LICENSE.LGPL3 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. +** will be met: https://www.gnu.org/licenses/lgpl-3.0.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. +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -37,9 +40,9 @@ #ifndef QT3DANIMATION_QANIMATIONCLIP_H #define QT3DANIMATION_QANIMATIONCLIP_H +#include <Qt3DAnimation/qabstractanimationclip.h> #include <Qt3DAnimation/qt3danimation_global.h> -#include <Qt3DCore/qnode.h> -#include <QtCore/qurl.h> +#include <Qt3DAnimation/qanimationclipdata.h> QT_BEGIN_NAMESPACE @@ -47,29 +50,25 @@ namespace Qt3DAnimation { class QAnimationClipPrivate; -class QT3DANIMATIONSHARED_EXPORT QAnimationClip : public Qt3DCore::QNode +class QT3DANIMATIONSHARED_EXPORT QAnimationClip : public QAbstractAnimationClip { Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - Q_PROPERTY(float duration READ duration NOTIFY durationChanged) + Q_PROPERTY(Qt3DAnimation::QAnimationClipData clipData READ clipData WRITE setClipData NOTIFY clipDataChanged) public: explicit QAnimationClip(Qt3DCore::QNode *parent = nullptr); ~QAnimationClip(); - QUrl source() const; - float duration() const; + Qt3DAnimation::QAnimationClipData clipData() const; public Q_SLOTS: - void setSource(QUrl source); + void setClipData(const Qt3DAnimation::QAnimationClipData &clipData); Q_SIGNALS: - void sourceChanged(QUrl source); - void durationChanged(float duration); + void clipDataChanged(Qt3DAnimation::QAnimationClipData clipData); protected: QAnimationClip(QAnimationClipPrivate &dd, Qt3DCore::QNode *parent = nullptr); - void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) Q_DECL_OVERRIDE; private: Q_DECLARE_PRIVATE(QAnimationClip) diff --git a/src/animation/frontend/qanimationclip_p.h b/src/animation/frontend/qanimationclip_p.h index 5e3df0e6e..514d568af 100644 --- a/src/animation/frontend/qanimationclip_p.h +++ b/src/animation/frontend/qanimationclip_p.h @@ -1,34 +1,37 @@ /**************************************************************************** ** ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). -** Contact: http://www.qt-project.org/legal +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL3$ +** $QT_BEGIN_LICENSE:LGPL$ ** 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. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 +** Foundation and appearing in the file LICENSE.LGPL3 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. +** will be met: https://www.gnu.org/licenses/lgpl-3.0.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. +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -48,32 +51,26 @@ // We mean it. // -#include <Qt3DCore/private/qnode_p.h> -#include "qanimationclip.h" +#include <Qt3DAnimation/private/qabstractanimationclip_p.h> +#include <Qt3DAnimation/qanimationclip.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QAnimationClipPrivate : public Qt3DCore::QNodePrivate +class QAnimationClipPrivate : public QAbstractAnimationClipPrivate { public: QAnimationClipPrivate(); Q_DECLARE_PUBLIC(QAnimationClip) - void setDuration(float duration); - - QUrl m_source; - - // Set from the backend - float m_duration; + QAnimationClipData m_clipData; }; -struct QAnimationClipData +struct QAnimationClipChangeData { - QUrl source; - bool running; + QAnimationClipData clipData; }; } // namespace Qt3DAnimation diff --git a/src/animation/frontend/qanimationclipdata.cpp b/src/animation/frontend/qanimationclipdata.cpp new file mode 100644 index 000000000..ef3b59012 --- /dev/null +++ b/src/animation/frontend/qanimationclipdata.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qanimationclipdata.h" + +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAnimationClipDataPrivate +{ +public: + QVector<QChannel> m_channels; + QString m_name; +}; + +QAnimationClipData::QAnimationClipData() + : d(new QAnimationClipDataPrivate) +{ +} + +QAnimationClipData::QAnimationClipData(const QAnimationClipData &rhs) + : d(new QAnimationClipDataPrivate) +{ + *d = *(rhs.d); +} + +QAnimationClipData &QAnimationClipData::operator=(const QAnimationClipData &rhs) +{ + if (this != &rhs) + *d = *(rhs.d); + return *this; +} + +QAnimationClipData::~QAnimationClipData() +{ +} + +void QAnimationClipData::setName(const QString &name) +{ + d->m_name = name; +} + +QString QAnimationClipData::name() const +{ + return d->m_name; +} + +int QAnimationClipData::channelCount() const +{ + return d->m_channels.size(); +} + +void QAnimationClipData::appendChannel(const QChannel &c) +{ + d->m_channels.append(c); +} + +void QAnimationClipData::insertChannel(int index, const QChannel &c) +{ + d->m_channels.insert(index, c); +} + +void QAnimationClipData::removeChannel(int index) +{ + d->m_channels.remove(index); +} + +void QAnimationClipData::clearChannels() +{ + d->m_channels.clear(); +} + +bool QAnimationClipData::isValid() const Q_DECL_NOTHROW +{ + // TODO: Perform more thorough checks + return !d->m_channels.isEmpty(); +} + +QAnimationClipData::const_iterator QAnimationClipData::begin() const Q_DECL_NOTHROW +{ + return d->m_channels.begin(); +} + +QAnimationClipData::const_iterator QAnimationClipData::end() const Q_DECL_NOTHROW +{ + return d->m_channels.end(); +} + + +bool operator==(const QAnimationClipData &lhs, const QAnimationClipData &rhs) Q_DECL_NOTHROW +{ + return lhs.d->m_name == rhs.d->m_name && + lhs.d->m_channels == rhs.d->m_channels; +} + +bool operator!=(const QAnimationClipData &lhs, const QAnimationClipData &rhs) Q_DECL_NOTHROW +{ + return lhs.d->m_name != rhs.d->m_name || + lhs.d->m_channels != rhs.d->m_channels; +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qanimationclipdata.h b/src/animation/frontend/qanimationclipdata.h new file mode 100644 index 000000000..605b17208 --- /dev/null +++ b/src/animation/frontend/qanimationclipdata.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DANIMATION_QANIMATIONCLIPDATA_H +#define QT3DANIMATION_QANIMATIONCLIPDATA_H + +#include <QtCore/qscopedpointer.h> +#include <QtCore/qstring.h> +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DAnimation/qchannel.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAnimationClipDataPrivate; + +class QT3DANIMATIONSHARED_EXPORT QAnimationClipData +{ +public: + QAnimationClipData(); + QAnimationClipData(const QAnimationClipData &); + QAnimationClipData &operator=(const QAnimationClipData &); + ~QAnimationClipData(); + + void setName(const QString &name); + QString name() const; + + int channelCount() const; + void appendChannel(const QChannel &c); + void insertChannel(int index, const QChannel &c); + void removeChannel(int index); + void clearChannels(); + + bool isValid() const Q_DECL_NOTHROW; + + // Iterator API + typedef const QChannel *const_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + const_iterator begin() const Q_DECL_NOTHROW; + const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); } + const_iterator end() const Q_DECL_NOTHROW; + const_iterator cend() const Q_DECL_NOTHROW { return end(); } + const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); } + const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); } + + friend bool operator==(const QAnimationClipData &, + const QAnimationClipData &) Q_DECL_NOTHROW; + friend bool operator!=(const QAnimationClipData &, + const QAnimationClipData &) Q_DECL_NOTHROW; + +private: + QScopedPointer<QAnimationClipDataPrivate> d; +}; + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Qt3DAnimation::QAnimationClipData) + +#endif // QT3DANIMATION_QANIMATIONCLIPDATA_H diff --git a/src/animation/frontend/qanimationcliploader.cpp b/src/animation/frontend/qanimationcliploader.cpp new file mode 100644 index 000000000..ed48438de --- /dev/null +++ b/src/animation/frontend/qanimationcliploader.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** 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 "qanimationcliploader.h" +#include "qanimationcliploader_p.h" +#include <Qt3DCore/qpropertyupdatedchange.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +QAnimationClipLoaderPrivate::QAnimationClipLoaderPrivate() + : QAbstractAnimationClipPrivate() + , m_source() + , m_status(QAnimationClipLoader::NotReady) +{ +} + +void QAnimationClipLoaderPrivate::setStatus(QAnimationClipLoader::Status status) +{ + Q_Q(QAnimationClipLoader); + if (status != m_status) { + m_status = status; + const bool blocked = q->blockNotifications(true); + emit q->statusChanged(m_status); + q->blockNotifications(blocked); + } +} + +/*! + \enum QAnimationClipLoader::Status + + This enum identifies the status of animation clip. + + \value NotReady The clip has not been loaded yet + \value Ready The clip was successfully loaded + \value Error An error occurred while loading the clip +*/ + +QAnimationClipLoader::QAnimationClipLoader(Qt3DCore::QNode *parent) + : QAbstractAnimationClip(*new QAnimationClipLoaderPrivate, parent) +{ +} + +QAnimationClipLoader::QAnimationClipLoader(const QUrl &source, + Qt3DCore::QNode *parent) + : QAbstractAnimationClip(*new QAnimationClipLoaderPrivate, parent) +{ + setSource(source); +} + +QAnimationClipLoader::QAnimationClipLoader(QAnimationClipLoaderPrivate &dd, Qt3DCore::QNode *parent) + : QAbstractAnimationClip(dd, parent) +{ +} + +QAnimationClipLoader::~QAnimationClipLoader() +{ +} + +QUrl QAnimationClipLoader::source() const +{ + Q_D(const QAnimationClipLoader); + return d->m_source; +} + +QAnimationClipLoader::Status QAnimationClipLoader::status() const +{ + Q_D(const QAnimationClipLoader); + return d->m_status; +} + +void QAnimationClipLoader::setSource(const QUrl &source) +{ + Q_D(QAnimationClipLoader); + if (d->m_source == source) + return; + + d->m_source = source; + emit sourceChanged(source); +} + +void QAnimationClipLoader::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) +{ + Q_D(QAnimationClipLoader); + if (change->type() == Qt3DCore::PropertyUpdated) { + const Qt3DCore::QPropertyUpdatedChangePtr e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change); + if (e->propertyName() == QByteArrayLiteral("status")) + d->setStatus(static_cast<QAnimationClipLoader::Status>(e->value().toInt())); + } +} + +Qt3DCore::QNodeCreatedChangeBasePtr QAnimationClipLoader::createNodeCreationChange() const +{ + auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QAnimationClipLoaderData>::create(this); + auto &data = creationChange->data; + Q_D(const QAnimationClipLoader); + data.source = d->m_source; + return creationChange; +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/backend/conductedclipanimator.cpp b/src/animation/frontend/qanimationcliploader.h index 32db1b7ea..20f0f14da 100644 --- a/src/animation/backend/conductedclipanimator.cpp +++ b/src/animation/frontend/qanimationcliploader.h @@ -34,47 +34,59 @@ ** ****************************************************************************/ -#include "conductedclipanimator_p.h" -#include <Qt3DAnimation/qconductedclipanimator.h> -#include <Qt3DAnimation/private/qconductedclipanimator_p.h> -#include <Qt3DCore/qpropertyupdatedchange.h> +#ifndef QT3DANIMATION_QANIMATIONCLIPLOADER_H +#define QT3DANIMATION_QANIMATIONCLIPLOADER_H + +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DAnimation/qabstractanimationclip.h> +#include <QtCore/qurl.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -namespace Animation { -ConductedClipAnimator::ConductedClipAnimator() - : BackendNode(ReadOnly) -{ -} +class QAnimationClipLoaderPrivate; -void ConductedClipAnimator::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) +class QT3DANIMATIONSHARED_EXPORT QAnimationClipLoader : public QAbstractAnimationClip { - const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QConductedClipAnimatorData>>(change); - const auto &data = typedChange->data; -} + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) -void ConductedClipAnimator::cleanup() -{ - setEnabled(false); - m_handler = nullptr; -} +public: + explicit QAnimationClipLoader(Qt3DCore::QNode *parent = nullptr); + explicit QAnimationClipLoader(const QUrl &source, + Qt3DCore::QNode *parent = nullptr); + ~QAnimationClipLoader(); + + enum Status { + NotReady = 0, + Ready, + Error + }; + Q_ENUM(Status) // LCOV_EXCL_LINE + + QUrl source() const; + Status status() const; + +public Q_SLOTS: + void setSource(const QUrl &source); + +Q_SIGNALS: + void sourceChanged(const QUrl &source); + void statusChanged(Status status); + +protected: + explicit QAnimationClipLoader(QAnimationClipLoaderPrivate &dd, Qt3DCore::QNode *parent = nullptr); + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) Q_DECL_OVERRIDE; + +private: + Q_DECLARE_PRIVATE(QAnimationClipLoader) + Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE; +}; -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 + +#endif // QT3DANIMATION_QANIMATIONCLIPLOADER_H diff --git a/src/animation/backend/conductedclipanimator_p.h b/src/animation/frontend/qanimationcliploader_p.h index d4534447e..70b21dbb4 100644 --- a/src/animation/backend/conductedclipanimator_p.h +++ b/src/animation/frontend/qanimationcliploader_p.h @@ -34,8 +34,8 @@ ** ****************************************************************************/ -#ifndef QT3DANIMATION_ANIMATION_CONDUCTEDCLIPANIMATOR_P_H -#define QT3DANIMATION_ANIMATION_CONDUCTEDCLIPANIMATOR_P_H +#ifndef QT3DANIMATION_QANIMATIONCLIPLOADER_P_H +#define QT3DANIMATION_QANIMATIONCLIPLOADER_P_H // // W A R N I N G @@ -48,32 +48,34 @@ // We mean it. // -#include <Qt3DAnimation/private/backendnode_p.h> +#include <Qt3DAnimation/private/qabstractanimationclip_p.h> +#include "qanimationcliploader.h" QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -namespace Animation { -class Handler; - -class Q_AUTOTEST_EXPORT ConductedClipAnimator : public BackendNode +class QAnimationClipLoaderPrivate : public QAbstractAnimationClipPrivate { public: - ConductedClipAnimator(); + QAnimationClipLoaderPrivate(); + + void setStatus(QAnimationClipLoader::Status status); - void cleanup(); + Q_DECLARE_PUBLIC(QAnimationClipLoader) - void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE; + QUrl m_source; + QAnimationClipLoader::Status m_status; +}; -private: - void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; +struct QAnimationClipLoaderData +{ + QUrl source; }; -} // namespace Animation } // namespace Qt3DAnimation QT_END_NAMESPACE -#endif // QT3DANIMATION_ANIMATION_CONDUCTEDCLIPANIMATOR_P_H +#endif // QT3DANIMATION_QANIMATIONCLIPLOADER_P_H diff --git a/src/animation/frontend/qanimationcontroller.cpp b/src/animation/frontend/qanimationcontroller.cpp new file mode 100644 index 000000000..d4c3c4005 --- /dev/null +++ b/src/animation/frontend/qanimationcontroller.cpp @@ -0,0 +1,390 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 "qanimationcontroller.h" +#include "qanimationgroup.h" + +#include <private/qanimationcontroller_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +/*! + \class Qt3DAnimation::QAnimationController + \brief A controller class for animations + \inmodule Qt3DAnimation + \since 5.9 + \inherits QObject + + Qt3DAnimation::QAnimationController class controls the selection and playback of animations. + The class can be used to find all animations from Qt3DCore::QEntity tree and create + \l {Qt3DAnimation::QAnimationGroup} {QAnimationGroups} from the animations with the same name. + The user can select which animation group is currently controlled with the animation + controller by setting the active animation. The animation position is then propagated to + that group after scaling and offsetting the provided position value with the + positionScale and positionOffset values. + + \note that the animation controller doesn't have internal timer, but instead the user + is responsible for updating the position property in timely manner. +*/ + +/*! + \qmltype AnimationController + \brief A controller type for animations + \inqmlmodule Qt3D.Animation + \since 5.9 + \instantiates Qt3DAnimation::QAnimationController + + AnimationController type controls the selection and playback of animations. + The type can be used to find all animations from Entity tree and create + \l {AnimationGroup} {AnimationGroups} from the animations with the same name. + The user can select which animation group is currently controlled with the animation + controller by setting the active animation. The animation position is then propagated to + that group after scaling and offsetting the provided position value with the + positionScale and positionOffset values. + + \note that the animation controller doesn't have internal timer, but instead the user + is responsible for updating the position property in timely manner. +*/ + +/*! + \property Qt3DAnimation::QAnimationController::activeAnimationGroup + Holds the currectly active animation group. +*/ +/*! + \property Qt3DAnimation::QAnimationController::position + Holds the current position of the animation. When the position is set, + it is scaled and offset with positionScale/positionOffset and propagated + to the active animation group. +*/ +/*! + \property Qt3DAnimation::QAnimationController::positionScale + Holds the position scale of the animation. +*/ +/*! + \property Qt3DAnimation::QAnimationController::positionOffset + Holds the position offset of the animation. +*/ +/*! + \property Qt3DAnimation::QAnimationController::entity + Holds the entity animations are gathered and grouped from. If the controller already + holds animations, they are cleared. +*/ +/*! + \property Qt3DAnimation::QAnimationController::recursive + Holds whether the recursively search the entity tree when gathering animations from the entity. + If set to true, the animations are searched also from the child entities of the entity. + If set to false, only the entity passed to the controller is searched. +*/ + +/*! + \qmlproperty int AnimationController::activeAnimationGroup + Holds the currectly active animation group. +*/ +/*! + \qmlproperty real AnimationController::position + Holds the current position of the animation. When the position is set, + it is scaled and offset with positionScale/positionOffset and propagated + to the active animation group. +*/ +/*! + \qmlproperty real AnimationController::positionScale + Holds the position scale of the animation. +*/ +/*! + \qmlproperty real AnimationController::positionOffset + Holds the position offset of the animation. +*/ +/*! + \qmlproperty Entity AnimationController::entity + Holds the entity animations are gathered and grouped from. If the controller already + holds animations, they are cleared. +*/ +/*! + \qmlproperty bool AnimationController::recursive + Holds whether the recursively search the entity tree when gathering animations from the entity. + If set to true, the animations are searched also from the child entities of the entity. + If set to false, only the entity passed to the controller is searched. +*/ +/*! + \qmlproperty list<AnimationGroup> AnimationController::animationGroups + Holds the list of animation groups in the controller. +*/ +/*! + \qmlmethod int Qt3D.Animation::AnimationController::getAnimationIndex(name) + Returns the index of the animation with \a name. Returns -1 if no AnimationGroup + with the given name is found. +*/ +/*! + \qmlmethod AnimationGroup Qt3D.Animation::AnimationController::getGroup(index) + Returns the AnimationGroup with the given \a index. +*/ + +QAnimationControllerPrivate::QAnimationControllerPrivate() + : QObjectPrivate() + , m_activeAnimationGroup(0) + , m_position(0.0f) + , m_scaledPosition(0.0f) + , m_positionScale(1.0f) + , m_positionOffset(0.0f) + , m_entity(nullptr) + , m_recursive(true) +{ + +} + +void QAnimationControllerPrivate::updatePosition(float position) +{ + m_position = position; + m_scaledPosition = scaledPosition(position); + if (m_activeAnimationGroup >= 0 && m_activeAnimationGroup < m_animationGroups.size()) + m_animationGroups[m_activeAnimationGroup]->setPosition(m_scaledPosition); +} + +float QAnimationControllerPrivate::scaledPosition(float position) const +{ + return m_positionScale * position + m_positionOffset; +} + +QAnimationGroup *QAnimationControllerPrivate::findGroup(const QString &name) +{ + for (QAnimationGroup *g : qAsConst(m_animationGroups)) { + if (g->name() == name) + return g; + } + return nullptr; +} + +void QAnimationControllerPrivate::extractAnimations() +{ + Q_Q(QAnimationController); + if (!m_entity) + return; + QList<Qt3DAnimation::QAbstractAnimation *> animations + = m_entity->findChildren<Qt3DAnimation::QAbstractAnimation *>(QString(), + m_recursive ? Qt::FindChildrenRecursively : Qt::FindDirectChildrenOnly); + if (animations.size() > 0) { + for (Qt3DAnimation::QAbstractAnimation *a : animations) { + QAnimationGroup *group = findGroup(a->animationName()); + if (!group) { + group = new QAnimationGroup(q); + group->setName(a->animationName()); + m_animationGroups.push_back(group); + } + group->addAnimation(a); + } + } +} +void QAnimationControllerPrivate::clearAnimations() +{ + for (Qt3DAnimation::QAnimationGroup *a : qAsConst(m_animationGroups)) + a->deleteLater(); + m_animationGroups.clear(); + m_activeAnimationGroup = 0; +} + +/*! + Constructs a new QAnimationController with \a parent. + */ +QAnimationController::QAnimationController(QObject *parent) + : QObject(*new QAnimationControllerPrivate, parent) +{ + +} + +/*! + Returns the list of animation groups the conroller is currently holding. + */ +QVector<QAnimationGroup *> QAnimationController::animationGroupList() +{ + Q_D(QAnimationController); + return d->m_animationGroups; +} + +int QAnimationController::activeAnimationGroup() const +{ + Q_D(const QAnimationController); + return d->m_activeAnimationGroup; +} + +float QAnimationController::position() const +{ + Q_D(const QAnimationController); + return d->m_position; +} + +float QAnimationController::positionScale() const +{ + Q_D(const QAnimationController); + return d->m_positionScale; +} + +float QAnimationController::positionOffset() const +{ + Q_D(const QAnimationController); + return d->m_positionOffset; +} + +Qt3DCore::QEntity *QAnimationController::entity() const +{ + Q_D(const QAnimationController); + return d->m_entity; +} + +bool QAnimationController::recursive() const +{ + Q_D(const QAnimationController); + return d->m_recursive; +} + +/*! + Sets the \a animationGroups for the controller. Old groups are cleared. + */ +void QAnimationController::setAnimationGroups(const QVector<Qt3DAnimation::QAnimationGroup *> &animationGroups) +{ + Q_D(QAnimationController); + d->m_animationGroups = animationGroups; + if (d->m_activeAnimationGroup >= d->m_animationGroups.size()) + d->m_activeAnimationGroup = 0; + d->updatePosition(d->m_position); +} + +/*! + Adds the given \a animationGroup to the controller. + */ +void QAnimationController::addAnimationGroup(Qt3DAnimation::QAnimationGroup *animationGroup) +{ + Q_D(QAnimationController); + if (!d->m_animationGroups.contains(animationGroup)) + d->m_animationGroups.push_back(animationGroup); +} + +/*! + Removes the given \a animationGroup from the controller. + */ +void QAnimationController::removeAnimationGroup(Qt3DAnimation::QAnimationGroup *animationGroup) +{ + Q_D(QAnimationController); + if (d->m_animationGroups.contains(animationGroup)) + d->m_animationGroups.removeAll(animationGroup); + if (d->m_activeAnimationGroup >= d->m_animationGroups.size()) + d->m_activeAnimationGroup = 0; +} + +void QAnimationController::setActiveAnimationGroup(int index) +{ + Q_D(QAnimationController); + if (d->m_activeAnimationGroup != index) { + d->m_activeAnimationGroup = index; + d->updatePosition(d->m_position); + emit activeAnimationGroupChanged(index); + } +} +void QAnimationController::setPosition(float position) +{ + Q_D(QAnimationController); + if (!qFuzzyCompare(d->m_scaledPosition, d->scaledPosition(position))) { + d->updatePosition(position); + emit positionChanged(position); + } +} + +void QAnimationController::setPositionScale(float scale) +{ + Q_D(QAnimationController); + if (!qFuzzyCompare(d->m_positionScale, scale)) { + d->m_positionScale = scale; + emit positionScaleChanged(scale); + } +} + +void QAnimationController::setPositionOffset(float offset) +{ + Q_D(QAnimationController); + if (!qFuzzyCompare(d->m_positionOffset, offset)) { + d->m_positionOffset = offset; + emit positionOffsetChanged(offset); + } +} + +void QAnimationController::setEntity(Qt3DCore::QEntity *entity) +{ + Q_D(QAnimationController); + if (d->m_entity != entity) { + d->clearAnimations(); + d->m_entity = entity; + d->extractAnimations(); + d->updatePosition(d->m_position); + emit entityChanged(entity); + } +} + +void QAnimationController::setRecursive(bool recursive) +{ + Q_D(QAnimationController); + if (d->m_recursive != recursive) { + d->m_recursive = recursive; + emit recursiveChanged(recursive); + } +} + +/*! + Returns the index of the animation with \a name. Returns -1 if no AnimationGroup + with the given name is found. +*/ +int QAnimationController::getAnimationIndex(const QString &name) const +{ + Q_D(const QAnimationController); + for (int i = 0; i < d->m_animationGroups.size(); ++i) { + if (d->m_animationGroups[i]->name() == name) + return i; + } + return -1; +} + +/*! + Returns the AnimationGroup with the given \a index. +*/ +QAnimationGroup *QAnimationController::getGroup(int index) const +{ + Q_D(const QAnimationController); + return d->m_animationGroups.at(index); +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qanimationcontroller.h b/src/animation/frontend/qanimationcontroller.h new file mode 100644 index 000000000..ae3272517 --- /dev/null +++ b/src/animation/frontend/qanimationcontroller.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QANIMATIONCONTROLLER_H +#define QT3DANIMATION_QANIMATIONCONTROLLER_H + +#include <Qt3DAnimation/qkeyframeanimation.h> +#include <Qt3DAnimation/qanimationgroup.h> +#include <Qt3DCore/qentity.h> + +#include <Qt3DAnimation/qt3danimation_global.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAnimationControllerPrivate; + +class QT3DANIMATIONSHARED_EXPORT QAnimationController : public QObject +{ + Q_OBJECT + Q_PROPERTY(int activeAnimationGroup READ activeAnimationGroup WRITE setActiveAnimationGroup NOTIFY activeAnimationGroupChanged) + Q_PROPERTY(float position READ position WRITE setPosition NOTIFY positionChanged) + Q_PROPERTY(float positionScale READ positionScale WRITE setPositionScale NOTIFY positionScaleChanged) + Q_PROPERTY(float positionOffset READ positionOffset WRITE setPositionOffset NOTIFY positionOffsetChanged) + Q_PROPERTY(Qt3DCore::QEntity *entity READ entity WRITE setEntity NOTIFY entityChanged) + Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged) + +public: + QAnimationController(QObject *parent = nullptr); + + QVector<Qt3DAnimation::QAnimationGroup *> animationGroupList(); + + int activeAnimationGroup() const; + float position() const; + float positionScale() const; + float positionOffset() const; + Qt3DCore::QEntity *entity() const; + bool recursive() const; + + void setAnimationGroups(const QVector<Qt3DAnimation::QAnimationGroup *> &animationGroups); + void addAnimationGroup(Qt3DAnimation::QAnimationGroup *animationGroups); + void removeAnimationGroup(Qt3DAnimation::QAnimationGroup *animationGroups); + + Q_INVOKABLE int getAnimationIndex(const QString &name) const; + Q_INVOKABLE Qt3DAnimation::QAnimationGroup *getGroup(int index) const; + +public Q_SLOTS: + void setActiveAnimationGroup(int index); + void setPosition(float position); + void setPositionScale(float scale); + void setPositionOffset(float offset); + void setEntity(Qt3DCore::QEntity *entity); + void setRecursive(bool recursive); + +Q_SIGNALS: + void activeAnimationGroupChanged(int index); + void positionChanged(float position); + void positionScaleChanged(float scale); + void positionOffsetChanged(float offset); + void entityChanged(Qt3DCore::QEntity *entity); + void recursiveChanged(bool recursive); + +private: + Q_DECLARE_PRIVATE(QAnimationController) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QANIMATIONCONTROLLER_H diff --git a/src/animation/frontend/qanimationcontroller_p.h b/src/animation/frontend/qanimationcontroller_p.h new file mode 100644 index 000000000..c06484911 --- /dev/null +++ b/src/animation/frontend/qanimationcontroller_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QANIMATIONCONTROLLER_P_H +#define QT3DANIMATION_QANIMATIONCONTROLLER_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 <private/qobject_p.h> +#include <Qt3DAnimation/QAnimationGroup> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAnimationControllerPrivate : public QObjectPrivate +{ +public: + QAnimationControllerPrivate(); + + QString m_name; + int m_activeAnimationGroup; + QVector<QAnimationGroup *> m_animationGroups; + float m_position; + float m_scaledPosition; + float m_positionScale; + float m_positionOffset; + Qt3DCore::QEntity *m_entity; + bool m_recursive; + + void updatePosition(float position); + void extractAnimations(); + void clearAnimations(); + QAnimationGroup *findGroup(const QString &name); + float scaledPosition(float position) const; + + Q_DECLARE_PUBLIC(QAnimationController) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QANIMATIONCONTROLLER_P_H diff --git a/src/animation/frontend/qanimationgroup.cpp b/src/animation/frontend/qanimationgroup.cpp new file mode 100644 index 000000000..365745662 --- /dev/null +++ b/src/animation/frontend/qanimationgroup.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 "qanimationgroup.h" +#include "Qt3DAnimation/private/qanimationgroup_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +/*! + \class Qt3DAnimation::QAnimationGroup + \brief A class grouping animations together + \inmodule Qt3DAnimation + \since 5.9 + \inherits QObject + + Qt3DAnimation::QAnimationGroup class is used to group multiple animations so that + they can act as one animation. The position set to the group is also set to + all animations in a group. The duration is the maximum of the individual animations. + The animations can be any supported animation type and do not have to have the same name. +*/ +/*! + \qmltype AnimationGroup + \brief A type grouping animations together + \inqmlmodule Qt3D.Animation + \since 5.9 + \inherits QObject + + AnimationGroup type is used to group multiple animations so that + they can act as one animation. The position set to the group is also set to + all animations in a group. The duration is the maximum of the individual animations. + The animations can be any supported animation type and do not have to have the same name. +*/ + +/*! + \property Qt3DAnimation::QAnimationGroup::name + Holds the name of the animation group. +*/ +/*! + \property Qt3DAnimation::QAnimationGroup::position + Holds the animation position. +*/ +/*! + \property Qt3DAnimation::QAnimationGroup::duration + Holds the maximum duration of the animations in the group. + \readonly +*/ + +/*! + \qmlproperty string AnimationGroup::name + Holds the name of the animation group. +*/ +/*! + \qmlproperty real AnimationGroup::position + Holds the animation position. +*/ +/*! + \qmlproperty real AnimationGroup::duration + Holds the maximum duration of the animations in the group. + \readonly +*/ +/*! + \qmlproperty list<AbstractAnimation> AnimationGroup::animations + Holds the list of animations in the animation group. +*/ + +QAnimationGroupPrivate::QAnimationGroupPrivate() + : QObjectPrivate() + , m_position(0.0f) + , m_duration(0.0f) +{ + +} + +void QAnimationGroupPrivate::updatePosition(float position) +{ + m_position = position; + for (QAbstractAnimation *aa : qAsConst(m_animations)) + aa->setPosition(position); +} + +/*! + Constructs an QAnimationGroup with \a parent. +*/ +QAnimationGroup::QAnimationGroup(QObject *parent) + : QObject(*new QAnimationGroupPrivate, parent) +{ + +} + +QString QAnimationGroup::name() const +{ + Q_D(const QAnimationGroup); + return d->m_name; +} + +/*! + Returns the list of animations in the group. + */ +QVector<Qt3DAnimation::QAbstractAnimation *> QAnimationGroup::animationList() +{ + Q_D(QAnimationGroup); + return d->m_animations; +} + +float QAnimationGroup::position() const +{ + Q_D(const QAnimationGroup); + return d->m_position; +} + +float QAnimationGroup::duration() const +{ + Q_D(const QAnimationGroup); + return d->m_duration; +} + +void QAnimationGroup::setName(const QString &name) +{ + Q_D(QAnimationGroup); + if (d->m_name != name) { + d->m_name = name; + emit nameChanged(name); + } +} + +/*! + Sets the \a animations to the group. Old animations are removed. + */ +void QAnimationGroup::setAnimations(const QVector<Qt3DAnimation::QAbstractAnimation *> &animations) +{ + Q_D(QAnimationGroup); + d->m_animations = animations; + d->m_duration = 0.0f; + for (const Qt3DAnimation::QAbstractAnimation *a : animations) + d->m_duration = qMax(d->m_duration, a->duration()); +} + +/*! + Adds new \a animation to the group. + */ +void QAnimationGroup::addAnimation(Qt3DAnimation::QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + if (!d->m_animations.contains(animation)) { + d->m_animations.push_back(animation); + d->m_duration = qMax(d->m_duration, animation->duration()); + } +} + +/*! + Removes \a animation from the group. + */ +void QAnimationGroup::removeAnimation(Qt3DAnimation::QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + if (!d->m_animations.contains(animation)) { + d->m_animations.removeAll(animation); + if (qFuzzyCompare(d->m_duration, animation->duration())) { + d->m_duration = 0.0f; + for (const Qt3DAnimation::QAbstractAnimation *a : d->m_animations) + d->m_duration = qMax(d->m_duration, a->duration()); + } + } +} + +void QAnimationGroup::setPosition(float position) +{ + Q_D(QAnimationGroup); + if (!qFuzzyCompare(d->m_position, position)) { + d->updatePosition(position); + emit positionChanged(position); + } +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qanimationgroup.h b/src/animation/frontend/qanimationgroup.h new file mode 100644 index 000000000..1e23c61ee --- /dev/null +++ b/src/animation/frontend/qanimationgroup.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QANIMATIONGROUP_H +#define QT3DANIMATION_QANIMATIONGROUP_H + +#include <qobject.h> + +#include <Qt3DAnimation/qabstractanimation.h> + +#include <Qt3DAnimation/qt3danimation_global.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAnimationGroupPrivate; + +class QT3DANIMATIONSHARED_EXPORT QAnimationGroup : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(float position READ position WRITE setPosition NOTIFY positionChanged) + Q_PROPERTY(float duration READ duration NOTIFY durationChanged) + +public: + explicit QAnimationGroup(QObject *parent = nullptr); + + QString name() const; + QVector<Qt3DAnimation::QAbstractAnimation *> animationList(); + float position() const; + float duration() const; + + void setAnimations(const QVector<Qt3DAnimation::QAbstractAnimation *> &animations); + void addAnimation(Qt3DAnimation::QAbstractAnimation *animation); + void removeAnimation(Qt3DAnimation::QAbstractAnimation *animation); + +public Q_SLOTS: + void setName(const QString &name); + void setPosition(float position); + +Q_SIGNALS: + void nameChanged(const QString &name); + void positionChanged(float position); + void durationChanged(float duration); + +private: + + Q_DECLARE_PRIVATE(QAnimationGroup) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QANIMATIONGROUP_H diff --git a/src/animation/frontend/qanimationgroup_p.h b/src/animation/frontend/qanimationgroup_p.h new file mode 100644 index 000000000..1e13952aa --- /dev/null +++ b/src/animation/frontend/qanimationgroup_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QANIMATIONGROUP_P_H +#define QT3DANIMATION_QANIMATIONGROUP_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 <private/qobject_p.h> +#include <Qt3DAnimation/QAnimationGroup> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QAnimationGroupPrivate : public QObjectPrivate +{ +public: + QAnimationGroupPrivate(); + + QString m_name; + QVector<Qt3DAnimation::QAbstractAnimation *> m_animations; + float m_position; + float m_duration; + + void updatePosition(float position); + + Q_DECLARE_PUBLIC(QAnimationGroup) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QANIMATIONGROUP_P_H diff --git a/src/animation/frontend/qblendedclipanimator.cpp b/src/animation/frontend/qblendedclipanimator.cpp index 8e0897b5c..e6b127bf1 100644 --- a/src/animation/frontend/qblendedclipanimator.cpp +++ b/src/animation/frontend/qblendedclipanimator.cpp @@ -46,21 +46,219 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { QBlendedClipAnimatorPrivate::QBlendedClipAnimatorPrivate() - : Qt3DCore::QComponentPrivate() + : Qt3DAnimation::QAbstractClipAnimatorPrivate() , m_blendTreeRoot(nullptr) - , m_mapper(nullptr) - , m_running(false) - , m_loops(1) { } +/*! + \qmltype BlendedClipAnimator + \instantiates Qt3DAnimation::QBlendedClipAnimator + \inqmlmodule Qt3D.Animation + \since 5.9 + + \brief BlendedClipAnimator is a component providing animation playback capabilities of a tree + of blend nodes. + + An instance of BlendedClipAnimator can be aggregated by an Entity to add the ability to play + back animation clips and to apply the calculated animation values to properties of QObjects. + + Whereas a ClipAnimator gets its animation data from a single animation clip, + BlendedClipAnimator can blend together multiple clips. The animation data is obtained by + evaluating a so called \e {blend tree}. A blend tree is a hierarchical tree structure where the + leaf nodes are value nodes that encapsulate an animation clip (AbstractAnimationClip); and the + internal nodes represent blending operations that operate on the nodes pointed to by their + operand properties. + + To associate a blend tree with a BlendedClipAnimator, set the animator's blendTree property to + point at the root node of your blend tree: + + \badcode + BlendedClipAnimator { + blendTree: AdditiveClipBlend { + .... + } + } + \endcode + + A blend tree can be constructed from the following node types: + + \note The blend node tree should only be edited when the animator is not running. + + \list + \li Qt3D.Animation.ClipBlendValue + \li Qt3D.Animation.LerpClipBlend + \li Qt3D.Animation.AdditiveClipBlend + \endlist + + Additional node types will be added over time. + + As an example consider the following blend tree: + + \badcode + Clip0---- + | + Lerp Node---- + | | + Clip1---- Additive Node + | + Clip2---- + \endcode + + This can be created and used as follows: + + \badcode + BlendedClipAnimator { + blendTree: AdditiveClipBlend { + baseClip: LerpClipBlend { + startClip: ClipBlendValue { + clip: AnimationClipLoader { source: "walk.json" } + } + + endClip: ClipBlendValue { + clip: AnimationClipLoader { source: "run.json" } + } + } + + additiveClip: ClipBlendValue { + clip: AnimationClipLoader { source: "wave-arm.json" } + } + } + + channelMapper: ChannelMapper {...} + running: true + } + \endcode + + By authoring a set of animation clips and blending between them dynamically at runtime with a + blend tree, we open up a huge set of possible resulting animations. As some simple examples of + the above blend tree, where alpha is the additive factor and beta is the lerp blend factor we + can get a 2D continuum of possible animations: + + \badcode + (alpha = 0, beta = 1) Running, No arm waving --- (alpha = 1, beta = 1) Running, Arm waving + | | + | | + | | + (alpha = 0, beta = 0) Walking, No arm waving --- (alpha = 0, beta = 1) Running, No arm waving + \endcode + + More complex blend trees offer even more flexibility for combining your animation clips. Note + that the values used to control the blend tree (alpha and beta above) are simple properties on + the blend nodes. This means, that these properties themselves can also be controlled by + the animation framework. +*/ + +/*! + \class Qt3DAnimation::QBlendedClipAnimator + \inherits Qt3DAnimation::QAbstractClipAnimator + + \inmodule Qt3DAnimation + \since 5.9 + + \brief QBlendedClipAnimator is a component providing animation playback capabilities of a tree + of blend nodes. + + An instance of QBlendedClipAnimator can be aggregated by a QEntity to add the ability to play + back animation clips and to apply the calculated animation values to properties of QObjects. + + Whereas a QClipAnimator gets its animation data from a single animation clip, + QBlendedClipAnimator can blend together multiple clips. The animation data is obtained by + evaluating a so called \e {blend tree}. A blend tree is a hierarchical tree structure where the + leaf nodes are value nodes that encapsulate an animation clip (QAbstractAnimationClip); and the + internal nodes represent blending operations that operate on the nodes pointed to by their + operand properties. + + To associate a blend tree with a QBlendedClipAnimator, set the animator's blendTree property to + point at the root node of your blend tree: + + \badcode + auto blendTreeRoot = new QAdditiveClipBlend(); + ... + auto animator = new QBlendedClipAnimator(); + animator->setBlendTree(blendTreeRoot); + \endcode + + A blend tree can be constructed from the following node types: + + \note The blend node tree should only be edited when the animator is not running. + + \list + \li Qt3DAnimation::QClipBlendValue + \li Qt3DAnimation::QLerpClipBlend + \li Qt3DAnimation::QAdditiveClipBlend + \endlist + + Additional node types will be added over time. + + As an example consider the following blend tree: + + \badcode + Clip0---- + | + Lerp Node---- + | | + Clip1---- Additive Node + | + Clip2---- + \endcode + + This can be created and used as follows: + + \code + // Create leaf nodes of blend tree + auto clip0 = new QClipBlendValue( + new QAnimationClipLoader(QUrl::fromLocalFile("walk.json"))); + auto clip1 = new QClipBlendValue( + new QAnimationClipLoader(QUrl::fromLocalFile("run.json"))); + auto clip2 = new QClipBlendValue( + new QAnimationClipLoader(QUrl::fromLocalFile("wave-arm.json"))); + + // Create blend tree inner nodes + auto lerpNode = new QLerpClipBlend(); + lerpNode->setStartClip(clip0); + lerpNode->setEndClip(clip1); + lerpNode->setBlendFactor(0.5f); // Half-walk, half-run + + auto additiveNode = new QAdditiveClipBlend(); + additiveNode->setBaseClip(lerpNode); // Comes from lerp sub-tree + additiveNode->setAdditiveClip(clip2); + additiveNode->setAdditiveFactor(1.0f); // Wave arm fully + + // Run the animator + auto animator = new QBlendedClipAnimator(); + animator->setBlendTree(additiveNode); + animator->setChannelMapper(...); + animator->setRunning(true); + \endcode + + By authoring a set of animation clips and blending between them dynamically at runtime with a + blend tree, we open up a huge set of possible resulting animations. As some simple examples of + the above blend tree, where alpha is the additive factor and beta is the lerp blend factor we + can get a 2D continuum of possible animations: + + \badcode + (alpha = 0, beta = 1) Running, No arm waving --- (alpha = 1, beta = 1) Running, Arm waving + | | + | | + | | + (alpha = 0, beta = 0) Walking, No arm waving --- (alpha = 0, beta = 1) Running, No arm waving + \endcode + + More complex blend trees offer even more flexibility for combining your animation clips. Note + that the values used to control the blend tree (alpha and beta above) are simple properties on + the blend nodes. This means, that these properties themselves can also be controlled by + the animation framework. + +*/ QBlendedClipAnimator::QBlendedClipAnimator(Qt3DCore::QNode *parent) - : Qt3DCore::QComponent(*new QBlendedClipAnimatorPrivate, parent) + : Qt3DAnimation::QAbstractClipAnimator(*new QBlendedClipAnimatorPrivate, parent) { } +/*! \internal */ QBlendedClipAnimator::QBlendedClipAnimator(QBlendedClipAnimatorPrivate &dd, Qt3DCore::QNode *parent) - : Qt3DCore::QComponent(dd, parent) + : Qt3DAnimation::QAbstractClipAnimator(dd, parent) { } @@ -68,28 +266,23 @@ QBlendedClipAnimator::~QBlendedClipAnimator() { } -QAbstractClipBlendNode *QBlendedClipAnimator::blendTree() const -{ - Q_D(const QBlendedClipAnimator); - return d->m_blendTreeRoot; -} +/*! + \qmlproperty AbstractClipBlendNode blendTree -bool QBlendedClipAnimator::isRunning() const -{ - Q_D(const QBlendedClipAnimator); - return d->m_running; -} + This property holds the root of the animation blend tree that will be evaluated before being + interpolated by the animator. +*/ -QChannelMapper *QBlendedClipAnimator::channelMapper() const -{ - Q_D(const QBlendedClipAnimator); - return d->m_mapper; -} +/*! + \property blendTree -int QBlendedClipAnimator::loops() const + This property holds the root of the animation blend tree that will be evaluated before being + interpolated by the animator. +*/ +QAbstractClipBlendNode *QBlendedClipAnimator::blendTree() const { Q_D(const QBlendedClipAnimator); - return d->m_loops; + return d->m_blendTreeRoot; } void QBlendedClipAnimator::setBlendTree(QAbstractClipBlendNode *blendTree) @@ -112,45 +305,7 @@ void QBlendedClipAnimator::setBlendTree(QAbstractClipBlendNode *blendTree) emit blendTreeChanged(blendTree); } -void QBlendedClipAnimator::setRunning(bool running) -{ - Q_D(QBlendedClipAnimator); - if (d->m_running == running) - return; - - d->m_running = running; - emit runningChanged(running); -} - -void QBlendedClipAnimator::setLoops(int loops) -{ - Q_D(QBlendedClipAnimator); - if (d->m_loops == loops) - return; - - d->m_loops = loops; - emit loopsChanged(loops); -} - - -void QBlendedClipAnimator::setChannelMapper(QChannelMapper *mapping) -{ - Q_D(QBlendedClipAnimator); - if (d->m_mapper == mapping) - return; - - if (d->m_mapper) - d->unregisterDestructionHelper(d->m_mapper); - - if (mapping && !mapping->parent()) - mapping->setParent(this); - d->m_mapper = mapping; - - if (d->m_mapper) - d->registerDestructionHelper(d->m_mapper, &QBlendedClipAnimator::setChannelMapper, d->m_mapper); - emit channelMapperChanged(mapping); -} - +/*! \internal */ Qt3DCore::QNodeCreatedChangeBasePtr QBlendedClipAnimator::createNodeCreationChange() const { auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QBlendedClipAnimatorData>::create(this); diff --git a/src/animation/frontend/qblendedclipanimator.h b/src/animation/frontend/qblendedclipanimator.h index be1189f4f..c227a05cc 100644 --- a/src/animation/frontend/qblendedclipanimator.h +++ b/src/animation/frontend/qblendedclipanimator.h @@ -41,47 +41,34 @@ #define QT3DANIMATION_QBLENDEDCLIPANIMATOR_H #include <Qt3DAnimation/qt3danimation_global.h> -#include <Qt3DCore/qcomponent.h> +#include <Qt3DAnimation/qabstractclipanimator.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QChannelMapper; class QAbstractClipBlendNode; class QBlendedClipAnimatorPrivate; -class QT3DANIMATIONSHARED_EXPORT QBlendedClipAnimator : public Qt3DCore::QComponent +class QT3DANIMATIONSHARED_EXPORT QBlendedClipAnimator : public Qt3DAnimation::QAbstractClipAnimator { Q_OBJECT Q_PROPERTY(Qt3DAnimation::QAbstractClipBlendNode *blendTree READ blendTree WRITE setBlendTree NOTIFY blendTreeChanged) - Q_PROPERTY(Qt3DAnimation::QChannelMapper *channelMapper READ channelMapper WRITE setChannelMapper NOTIFY channelMapperChanged) - Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged) - Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) public: explicit QBlendedClipAnimator(Qt3DCore::QNode *parent = nullptr); ~QBlendedClipAnimator(); - bool isRunning() const; - Qt3DAnimation::QChannelMapper *channelMapper() const; QAbstractClipBlendNode *blendTree() const; - int loops() const; public Q_SLOTS: - void setRunning(bool running); - void setChannelMapper(QChannelMapper *channelMapper); void setBlendTree(QAbstractClipBlendNode * blendTree); - void setLoops(int loops); Q_SIGNALS: void blendTreeChanged(QAbstractClipBlendNode * blendTree); - void runningChanged(bool running); - void channelMapperChanged(QChannelMapper *channelMapper); - void loopsChanged(int loops); protected: - QBlendedClipAnimator(QBlendedClipAnimatorPrivate &dd, Qt3DCore::QNode *parent = nullptr); + explicit QBlendedClipAnimator(QBlendedClipAnimatorPrivate &dd, Qt3DCore::QNode *parent = nullptr); private: Q_DECLARE_PRIVATE(QBlendedClipAnimator) diff --git a/src/animation/frontend/qblendedclipanimator_p.h b/src/animation/frontend/qblendedclipanimator_p.h index 47a8bbfd5..e874a528d 100644 --- a/src/animation/frontend/qblendedclipanimator_p.h +++ b/src/animation/frontend/qblendedclipanimator_p.h @@ -51,33 +51,26 @@ // We mean it. // -#include <Qt3DCore/private/qcomponent_p.h> +#include <Qt3DAnimation/private/qabstractclipanimator_p.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { class QAbstractClipBlendNode; -class QChannelMapper; -class QBlendedClipAnimatorPrivate : public Qt3DCore::QComponentPrivate +class QBlendedClipAnimatorPrivate : public Qt3DAnimation::QAbstractClipAnimatorPrivate { public: QBlendedClipAnimatorPrivate(); Q_DECLARE_PUBLIC(QBlendedClipAnimator) QAbstractClipBlendNode *m_blendTreeRoot; - QChannelMapper *m_mapper; - bool m_running; - int m_loops; }; -struct QBlendedClipAnimatorData +struct QBlendedClipAnimatorData : public QAbstractClipAnimatorData { Qt3DCore::QNodeId blendTreeRootId; - Qt3DCore::QNodeId mapperId; - bool running; - int loops; }; } // namespace Qt3DAnimation diff --git a/src/animation/frontend/qchannel.cpp b/src/animation/frontend/qchannel.cpp new file mode 100644 index 000000000..9c74fad09 --- /dev/null +++ b/src/animation/frontend/qchannel.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qchannel.h" + +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QChannelPrivate +{ +public: + QVector<QChannelComponent> m_channelComponents; + QString m_name; +}; + +QChannel::QChannel() + : d(new QChannelPrivate) +{ +} + +QChannel::QChannel(const QString &name) + : d(new QChannelPrivate) +{ + d->m_name = name; +} + +QChannel::QChannel(const QChannel &rhs) + : d(new QChannelPrivate) +{ + *d = *(rhs.d); +} + +QChannel &QChannel::operator=(const QChannel &rhs) +{ + if (this != &rhs) + *d = *(rhs.d); + return *this; +} + +QChannel::~QChannel() +{ +} + +void QChannel::setName(const QString &name) +{ + d->m_name = name; +} + +QString QChannel::name() const +{ + return d->m_name; +} + +int QChannel::channelComponentCount() const +{ + return d->m_channelComponents.size(); +} + +void QChannel::appendChannelComponent(const QChannelComponent &component) +{ + d->m_channelComponents.append(component); +} + +void QChannel::insertChannelComponent(int index, const QChannelComponent &component) +{ + d->m_channelComponents.insert(index, component); +} + +void QChannel::removeChannelComponent(int index) +{ + d->m_channelComponents.remove(index); +} + +void QChannel::clearChannelComponents() +{ + d->m_channelComponents.clear(); +} + +QChannel::const_iterator QChannel::begin() const Q_DECL_NOTHROW +{ + return d->m_channelComponents.begin(); +} + +QChannel::const_iterator QChannel::end() const Q_DECL_NOTHROW +{ + return d->m_channelComponents.end(); +} + +bool operator==(const QChannel &lhs, const QChannel &rhs) Q_DECL_NOTHROW +{ + return lhs.d->m_name == rhs.d->m_name && + lhs.d->m_channelComponents == rhs.d->m_channelComponents; +} + +bool operator!=(const QChannel &lhs, const QChannel &rhs) Q_DECL_NOTHROW +{ + return lhs.d->m_name != rhs.d->m_name || + lhs.d->m_channelComponents != rhs.d->m_channelComponents; +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qchannel.h b/src/animation/frontend/qchannel.h new file mode 100644 index 000000000..b94780689 --- /dev/null +++ b/src/animation/frontend/qchannel.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DANIMATION_QCHANNEL_H +#define QT3DANIMATION_QCHANNEL_H + +#include <QtCore/qscopedpointer.h> +#include <QtCore/qstring.h> +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DAnimation/qchannelcomponent.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QChannelPrivate; + +class QT3DANIMATIONSHARED_EXPORT QChannel +{ +public: + QChannel(); + explicit QChannel(const QString &name); + QChannel(const QChannel &); + QChannel &operator=(const QChannel &); + ~QChannel(); + + void setName(const QString &name); + QString name() const; + + int channelComponentCount() const; + void appendChannelComponent(const QChannelComponent &component); + void insertChannelComponent(int index, const QChannelComponent &component); + void removeChannelComponent(int index); + void clearChannelComponents(); + + // Iterator API + typedef const QChannelComponent *const_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + const_iterator begin() const Q_DECL_NOTHROW; + const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); } + const_iterator end() const Q_DECL_NOTHROW; + const_iterator cend() const Q_DECL_NOTHROW { return end(); } + const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); } + const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); } + + friend bool operator==(const QChannel &, + const QChannel &) Q_DECL_NOTHROW; + friend bool operator!=(const QChannel &, + const QChannel &) Q_DECL_NOTHROW; + +private: + QScopedPointer<QChannelPrivate> d; +}; + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QCHANNEL_H diff --git a/src/animation/frontend/qchannelcomponent.cpp b/src/animation/frontend/qchannelcomponent.cpp new file mode 100644 index 000000000..1ecc5520c --- /dev/null +++ b/src/animation/frontend/qchannelcomponent.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qchannelcomponent.h" + +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QChannelComponentPrivate +{ +public: + QVector<QKeyFrame> m_keyFrames; + QString m_name; +}; + +QChannelComponent::QChannelComponent() + : d(new QChannelComponentPrivate) +{ +} + +QChannelComponent::QChannelComponent(const QString &name) + : d(new QChannelComponentPrivate) +{ + d->m_name = name; +} + +QChannelComponent::QChannelComponent(const QChannelComponent &rhs) + : d(new QChannelComponentPrivate) +{ + *d = *(rhs.d); +} + +QChannelComponent &QChannelComponent::operator=(const QChannelComponent &rhs) +{ + if (this != &rhs) + *d = *(rhs.d); + return *this; +} + +QChannelComponent::~QChannelComponent() +{ +} + +void QChannelComponent::setName(const QString &name) +{ + d->m_name = name; +} + +QString QChannelComponent::name() const +{ + return d->m_name; +} + +int QChannelComponent::keyFrameCount() const +{ + return d->m_keyFrames.size(); +} + +void QChannelComponent::appendKeyFrame(const QKeyFrame &kf) +{ + d->m_keyFrames.append(kf); +} + +void QChannelComponent::insertKeyFrame(int index, const QKeyFrame &kf) +{ + d->m_keyFrames.insert(index, kf); +} + +void QChannelComponent::removeKeyFrame(int index) +{ + d->m_keyFrames.remove(index); +} + +void QChannelComponent::clearKeyFrames() +{ + d->m_keyFrames.clear(); +} + +QChannelComponent::const_iterator QChannelComponent::begin() const Q_DECL_NOTHROW +{ + return d->m_keyFrames.begin(); +} + +QChannelComponent::const_iterator QChannelComponent::end() const Q_DECL_NOTHROW +{ + return d->m_keyFrames.end(); +} + +bool operator==(const QChannelComponent &lhs, const QChannelComponent &rhs) Q_DECL_NOTHROW +{ + return lhs.d->m_name == rhs.d->m_name && + lhs.d->m_keyFrames == rhs.d->m_keyFrames; +} + +bool operator!=(const QChannelComponent &lhs, const QChannelComponent &rhs) Q_DECL_NOTHROW +{ + return lhs.d->m_name != rhs.d->m_name || + lhs.d->m_keyFrames != rhs.d->m_keyFrames; +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qchannelcomponent.h b/src/animation/frontend/qchannelcomponent.h new file mode 100644 index 000000000..922073a85 --- /dev/null +++ b/src/animation/frontend/qchannelcomponent.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DANIMATION_QCHANNELCOMPONENT_H +#define QT3DANIMATION_QCHANNELCOMPONENT_H + +#include <QtCore/qscopedpointer.h> +#include <QtCore/qstring.h> +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DAnimation/qkeyframe.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QChannelComponentPrivate; + +class QT3DANIMATIONSHARED_EXPORT QChannelComponent +{ +public: + QChannelComponent(); + explicit QChannelComponent(const QString &name); + QChannelComponent(const QChannelComponent &); + QChannelComponent &operator=(const QChannelComponent &); + ~QChannelComponent(); + + void setName(const QString &name); + QString name() const; + + int keyFrameCount() const; + void appendKeyFrame(const QKeyFrame &kf); + void insertKeyFrame(int index, const QKeyFrame &kf); + void removeKeyFrame(int index); + void clearKeyFrames(); + + // Iterator API + typedef const QKeyFrame *const_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + const_iterator begin() const Q_DECL_NOTHROW; + const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); } + const_iterator end() const Q_DECL_NOTHROW; + const_iterator cend() const Q_DECL_NOTHROW { return end(); } + const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); } + const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); } + + friend bool operator==(const QChannelComponent &, + const QChannelComponent &) Q_DECL_NOTHROW; + friend bool operator!=(const QChannelComponent &, + const QChannelComponent &) Q_DECL_NOTHROW; + +private: + QScopedPointer<QChannelComponentPrivate> d; +}; + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QCHANNELCOMPONENT_H diff --git a/src/animation/frontend/qchannelmapper.h b/src/animation/frontend/qchannelmapper.h index 399d92d5d..14319f29b 100644 --- a/src/animation/frontend/qchannelmapper.h +++ b/src/animation/frontend/qchannelmapper.h @@ -60,7 +60,7 @@ public: QVector<QChannelMapping *> mappings() const; protected: - QChannelMapper(QChannelMapperPrivate &dd, Qt3DCore::QNode *parent = nullptr); + explicit QChannelMapper(QChannelMapperPrivate &dd, Qt3DCore::QNode *parent = nullptr); private: Q_DECLARE_PRIVATE(QChannelMapper) diff --git a/src/animation/frontend/qchannelmapping.h b/src/animation/frontend/qchannelmapping.h index 0c4ca07f4..d768298fe 100644 --- a/src/animation/frontend/qchannelmapping.h +++ b/src/animation/frontend/qchannelmapping.h @@ -72,7 +72,7 @@ Q_SIGNALS: void propertyChanged(QString property); protected: - QChannelMapping(QChannelMappingPrivate &dd, Qt3DCore::QNode *parent = nullptr); + explicit QChannelMapping(QChannelMappingPrivate &dd, Qt3DCore::QNode *parent = nullptr); private: Q_DECLARE_PRIVATE(QChannelMapping) diff --git a/src/animation/frontend/qclipanimator.cpp b/src/animation/frontend/qclipanimator.cpp index e6c06c989..eb6c80aee 100644 --- a/src/animation/frontend/qclipanimator.cpp +++ b/src/animation/frontend/qclipanimator.cpp @@ -39,7 +39,7 @@ #include "qclipanimator.h" #include "qclipanimator_p.h" -#include <Qt3DAnimation/qanimationclip.h> +#include <Qt3DAnimation/qabstractanimationclip.h> #include <Qt3DAnimation/qchannelmapper.h> QT_BEGIN_NAMESPACE @@ -47,21 +47,66 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { QClipAnimatorPrivate::QClipAnimatorPrivate() - : Qt3DCore::QComponentPrivate() + : Qt3DAnimation::QAbstractClipAnimatorPrivate() , m_clip(nullptr) - , m_mapper(nullptr) - , m_running(false) - , m_loops(1) { } +/*! + \qmltype ClipAnimator + \instantiates Qt3DAnimation::QClipAnimator + \inqmlmodule Qt3D.Animation + \since 5.9 + + \brief ClipAnimator is a component providing simple animation playback capabilities. + + An instance of ClipAnimator can be aggregated by an Entity to add the ability to play back + animation clips and to apply the calculated animation values to properties of QObjects. + + The animation key frame data is provided via the clip property. This can be created + programmatically with AnimationClip or loaded from file with AnimationClipLoader. + + In order to apply the values played back from the channels of data in the animation clip, the + clip animator needs to have a ChannelMapper object assigned to the channelMapper property. + + The properties for controlling the animator are provided by the AbstractClipAnimator base + class. + + \sa AbstractClipAnimator, AbstractAnimationClip, ChannelMapper, BlendedClipAnimator +*/ + +/*! + \class Qt3DAnimation::QClipAnimator + \inherits Qt3DAnimation::QAbstractClipAnimator + + \inmodule Qt3DAnimation + \since 5.9 + + \brief QClipAnimator is a component providing simple animation playback capabilities. + + An instance of QClipAnimator can be aggregated by a QEntity to add the ability to play back + animation clips and to apply the calculated animation values to properties of QObjects. + + The animation key frame data is provided via the clip property. This can be created + programmatically with QAnimationClip or loaded from file with QAnimationClipLoader. + + In order to apply the values played back from the channels of data in the animation clip, the + clip animator needs to have a QChannelMapper object assigned to the channelMapper property. + + The properties for controlling the animator are provided by the QAbstractClipAnimator base + class. + + \sa QAbstractClipAnimator, QAbstractAnimationClip, QChannelMapper, QBlendedClipAnimator +*/ + QClipAnimator::QClipAnimator(Qt3DCore::QNode *parent) - : Qt3DCore::QComponent(*new QClipAnimatorPrivate, parent) + : Qt3DAnimation::QAbstractClipAnimator(*new QClipAnimatorPrivate, parent) { } +/*! \internal */ QClipAnimator::QClipAnimator(QClipAnimatorPrivate &dd, Qt3DCore::QNode *parent) - : Qt3DCore::QComponent(dd, parent) + : Qt3DAnimation::QAbstractClipAnimator(dd, parent) { } @@ -69,31 +114,26 @@ QClipAnimator::~QClipAnimator() { } -QAnimationClip *QClipAnimator::clip() const -{ - Q_D(const QClipAnimator); - return d->m_clip; -} +/*! + \qmlproperty AbstractAnimationClip clip -bool QClipAnimator::isRunning() const -{ - Q_D(const QClipAnimator); - return d->m_running; -} + This property holds the animation clip which contains the key frame data to be played back. + The key frame data can be specified in either an AnimationClip or AnimationClipLoader. +*/ -QChannelMapper *QClipAnimator::channelMapper() const -{ - Q_D(const QClipAnimator); - return d->m_mapper; -} +/*! + \property clip -int QClipAnimator::loops() const + This property holds the animation clip which contains the key frame data to be played back. + The key frame data can be specified in either a QAnimationClip or QAnimationClipLoader. +*/ +QAbstractAnimationClip *QClipAnimator::clip() const { Q_D(const QClipAnimator); - return d->m_loops; + return d->m_clip; } -void QClipAnimator::setClip(QAnimationClip *clip) +void QClipAnimator::setClip(QAbstractAnimationClip *clip) { Q_D(QClipAnimator); if (d->m_clip == clip) @@ -112,45 +152,7 @@ void QClipAnimator::setClip(QAnimationClip *clip) emit clipChanged(clip); } -void QClipAnimator::setRunning(bool running) -{ - Q_D(QClipAnimator); - if (d->m_running == running) - return; - - d->m_running = running; - emit runningChanged(running); -} - -void QClipAnimator::setChannelMapper(QChannelMapper *mapping) -{ - Q_D(QClipAnimator); - if (d->m_mapper == mapping) - return; - - if (d->m_mapper) - d->unregisterDestructionHelper(d->m_mapper); - - if (mapping && !mapping->parent()) - mapping->setParent(this); - d->m_mapper = mapping; - - // Ensures proper bookkeeping - if (d->m_mapper) - d->registerDestructionHelper(d->m_mapper, &QClipAnimator::setChannelMapper, d->m_mapper); - emit channelMapperChanged(mapping); -} - -void QClipAnimator::setLoops(int loops) -{ - Q_D(QClipAnimator); - if (d->m_loops == loops) - return; - - d->m_loops = loops; - emit loopsChanged(loops); -} - +/*! \internal */ Qt3DCore::QNodeCreatedChangeBasePtr QClipAnimator::createNodeCreationChange() const { auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QClipAnimatorData>::create(this); diff --git a/src/animation/frontend/qclipanimator.h b/src/animation/frontend/qclipanimator.h index 176d6fc0c..311ac4ab0 100644 --- a/src/animation/frontend/qclipanimator.h +++ b/src/animation/frontend/qclipanimator.h @@ -41,44 +41,32 @@ #define QT3DANIMATION_QCLIPANIMATOR_H #include <Qt3DAnimation/qt3danimation_global.h> -#include <Qt3DCore/qcomponent.h> +#include <Qt3DAnimation/qabstractclipanimator.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QAnimationClip; +class QAbstractAnimationClip; class QChannelMapper; class QClipAnimatorPrivate; -class QT3DANIMATIONSHARED_EXPORT QClipAnimator : public Qt3DCore::QComponent +class QT3DANIMATIONSHARED_EXPORT QClipAnimator : public Qt3DAnimation::QAbstractClipAnimator { Q_OBJECT - Q_PROPERTY(Qt3DAnimation::QAnimationClip *clip READ clip WRITE setClip NOTIFY clipChanged) - Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) - Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged) - Q_PROPERTY(Qt3DAnimation::QChannelMapper *channelMapper READ channelMapper WRITE setChannelMapper NOTIFY channelMapperChanged) + Q_PROPERTY(Qt3DAnimation::QAbstractAnimationClip *clip READ clip WRITE setClip NOTIFY clipChanged) public: explicit QClipAnimator(Qt3DCore::QNode *parent = nullptr); ~QClipAnimator(); - Qt3DAnimation::QAnimationClip *clip() const; - bool isRunning() const; - Qt3DAnimation::QChannelMapper *channelMapper() const; - int loops() const; + Qt3DAnimation::QAbstractAnimationClip *clip() const; public Q_SLOTS: - void setClip(Qt3DAnimation::QAnimationClip *clip); - void setRunning(bool running); - void setChannelMapper(Qt3DAnimation::QChannelMapper *channelMapper); - void setLoops(int loops); + void setClip(Qt3DAnimation::QAbstractAnimationClip *clip); Q_SIGNALS: - void clipChanged(Qt3DAnimation::QAnimationClip *clip); - void runningChanged(bool running); - void channelMapperChanged(Qt3DAnimation::QChannelMapper *channelMapper); - void loopsChanged(int loops); + void clipChanged(Qt3DAnimation::QAbstractAnimationClip *clip); protected: QClipAnimator(QClipAnimatorPrivate &dd, Qt3DCore::QNode *parent = nullptr); diff --git a/src/animation/frontend/qclipanimator_p.h b/src/animation/frontend/qclipanimator_p.h index 7bb9c526f..fe40ed442 100644 --- a/src/animation/frontend/qclipanimator_p.h +++ b/src/animation/frontend/qclipanimator_p.h @@ -51,34 +51,27 @@ // We mean it. // -#include <Qt3DCore/private/qcomponent_p.h> -#include <Qt3DAnimation/qanimationclip.h> +#include <Qt3DAnimation/private/qabstractclipanimator_p.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QChannelMapper; +class QAbstractAnimationClip; -class QClipAnimatorPrivate : public Qt3DCore::QComponentPrivate +class QClipAnimatorPrivate : public Qt3DAnimation::QAbstractClipAnimatorPrivate { public: QClipAnimatorPrivate(); Q_DECLARE_PUBLIC(QClipAnimator) - QAnimationClip *m_clip; - Qt3DAnimation::QChannelMapper *m_mapper; - bool m_running; - int m_loops; + QAbstractAnimationClip *m_clip; }; -struct QClipAnimatorData +struct QClipAnimatorData : public QAbstractClipAnimatorData { Qt3DCore::QNodeId clipId; - Qt3DCore::QNodeId mapperId; - bool running; - int loops; }; } // namespace Qt3DAnimation diff --git a/src/animation/frontend/qclipblendnodecreatedchange.cpp b/src/animation/frontend/qclipblendnodecreatedchange.cpp index 9e9104066..9251442bb 100644 --- a/src/animation/frontend/qclipblendnodecreatedchange.cpp +++ b/src/animation/frontend/qclipblendnodecreatedchange.cpp @@ -37,7 +37,7 @@ #include "qclipblendnodecreatedchange.h" #include "qclipblendnodecreatedchange_p.h" #include <Qt3DAnimation/qabstractclipblendnode.h> -#include <Qt3DAnimation/qanimationclip.h> +#include <Qt3DAnimation/qabstractanimationclip.h> QT_BEGIN_NAMESPACE @@ -45,8 +45,6 @@ namespace Qt3DAnimation { QClipBlendNodeCreatedChangeBasePrivate::QClipBlendNodeCreatedChangeBasePrivate(const QAbstractClipBlendNode *node) : Qt3DCore::QNodeCreatedChangeBasePrivate(node) - , m_parentClipBlendNodeId(Qt3DCore::qIdForNode(node->parentClipBlendNode())) - , m_clips(Qt3DCore::qIdsForNodes(node->clips())) { } @@ -59,19 +57,6 @@ QClipBlendNodeCreatedChangeBase::~QClipBlendNodeCreatedChangeBase() { } -Qt3DCore::QNodeId QClipBlendNodeCreatedChangeBase::parentClipBlendNodeId() const -{ - Q_D(const QClipBlendNodeCreatedChangeBase); - return d->m_parentClipBlendNodeId; -} - -Qt3DCore::QNodeIdVector QClipBlendNodeCreatedChangeBase::clips() const -{ - Q_D(const QClipBlendNodeCreatedChangeBase); - return d->m_clips; -} - - } // Qt3DAnimation QT_END_NAMESPACE diff --git a/src/animation/frontend/qclipblendnodecreatedchange.h b/src/animation/frontend/qclipblendnodecreatedchange.h index b07d080bd..01f17d7ca 100644 --- a/src/animation/frontend/qclipblendnodecreatedchange.h +++ b/src/animation/frontend/qclipblendnodecreatedchange.h @@ -53,9 +53,6 @@ public: explicit QClipBlendNodeCreatedChangeBase(const QAbstractClipBlendNode *node); ~QClipBlendNodeCreatedChangeBase(); - Qt3DCore::QNodeId parentClipBlendNodeId() const; - Qt3DCore::QNodeIdVector clips() const; - private: Q_DECLARE_PRIVATE(QClipBlendNodeCreatedChangeBase) }; diff --git a/src/animation/frontend/qclipblendnodecreatedchange_p.h b/src/animation/frontend/qclipblendnodecreatedchange_p.h index 0b853cd96..31342d3a8 100644 --- a/src/animation/frontend/qclipblendnodecreatedchange_p.h +++ b/src/animation/frontend/qclipblendnodecreatedchange_p.h @@ -60,9 +60,6 @@ class QClipBlendNodeCreatedChangeBasePrivate : public Qt3DCore::QNodeCreatedChan { public: QClipBlendNodeCreatedChangeBasePrivate(const QAbstractClipBlendNode *node); - - Qt3DCore::QNodeId m_parentClipBlendNodeId; - Qt3DCore::QNodeIdVector m_clips; }; } // Qt3DAnimation diff --git a/src/animation/frontend/qclipblendvalue.cpp b/src/animation/frontend/qclipblendvalue.cpp new file mode 100644 index 000000000..d329f6722 --- /dev/null +++ b/src/animation/frontend/qclipblendvalue.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipblendvalue.h" +#include "qclipblendvalue_p.h" +#include <Qt3DAnimation/qabstractanimationclip.h> +#include <Qt3DAnimation/qclipblendnodecreatedchange.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +QClipBlendValuePrivate::QClipBlendValuePrivate() + : QAbstractClipBlendNodePrivate() + , m_clip(nullptr) +{ +} + +QClipBlendValue::QClipBlendValue(Qt3DCore::QNode *parent) + : QAbstractClipBlendNode(*new QClipBlendValuePrivate(), parent) +{ +} + +QClipBlendValue::QClipBlendValue(Qt3DAnimation::QAbstractAnimationClip *clip, + Qt3DCore::QNode *parent) + : QAbstractClipBlendNode(*new QClipBlendValuePrivate(), parent) +{ + setClip(clip); +} + +QClipBlendValue::QClipBlendValue(QClipBlendValuePrivate &dd, Qt3DCore::QNode *parent) + : QAbstractClipBlendNode(dd, parent) +{ +} + +QClipBlendValue::~QClipBlendValue() +{ +} + +Qt3DAnimation::QAbstractAnimationClip *QClipBlendValue::clip() const +{ + Q_D(const QClipBlendValue); + return d->m_clip; +} + +void QClipBlendValue::setClip(Qt3DAnimation::QAbstractAnimationClip *clip) +{ + Q_D(QClipBlendValue); + if (d->m_clip == clip) + return; + + if (d->m_clip) + d->unregisterDestructionHelper(d->m_clip); + + if (clip && !clip->parent()) + clip->setParent(this); + d->m_clip = clip; + + // Ensures proper bookkeeping + if (d->m_clip) + d->registerDestructionHelper(d->m_clip, &QClipBlendValue::setClip, d->m_clip); + emit clipChanged(clip); +} + +Qt3DCore::QNodeCreatedChangeBasePtr QClipBlendValue::createNodeCreationChange() const +{ + Q_D(const QClipBlendValue); + auto creationChange = QClipBlendNodeCreatedChangePtr<QClipBlendValueData>::create(this); + QClipBlendValueData &data = creationChange->data; + data.clipId = Qt3DCore::qIdForNode(d->m_clip); + return creationChange; +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qclipblendvalue.h b/src/animation/frontend/qclipblendvalue.h new file mode 100644 index 000000000..b18bfb57b --- /dev/null +++ b/src/animation/frontend/qclipblendvalue.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DANIMATION_QCLIPBLENDVALUE_H +#define QT3DANIMATION_QCLIPBLENDVALUE_H + +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DAnimation/qabstractclipblendnode.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QClipBlendValuePrivate; + +class QT3DANIMATIONSHARED_EXPORT QClipBlendValue : public Qt3DAnimation::QAbstractClipBlendNode +{ + Q_OBJECT + Q_PROPERTY(Qt3DAnimation::QAbstractAnimationClip *clip READ clip WRITE setClip NOTIFY clipChanged) + +public: + explicit QClipBlendValue(Qt3DCore::QNode *parent = nullptr); + explicit QClipBlendValue(Qt3DAnimation::QAbstractAnimationClip *clip, + Qt3DCore::QNode *parent = nullptr); + ~QClipBlendValue(); + + Qt3DAnimation::QAbstractAnimationClip *clip() const; + +public Q_SLOTS: + void setClip(Qt3DAnimation::QAbstractAnimationClip *clip); + +Q_SIGNALS: + void clipChanged(Qt3DAnimation::QAbstractAnimationClip *clip); + +protected: + explicit QClipBlendValue(QClipBlendValuePrivate &dd, Qt3DCore::QNode *parent = nullptr); + +private: + Q_DECLARE_PRIVATE(QClipBlendValue) + Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE; +}; + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QCLIPBLENDVALUE_H diff --git a/src/animation/frontend/qconductedclipanimator.h b/src/animation/frontend/qclipblendvalue_p.h index 4364f6774..7a397a1b8 100644 --- a/src/animation/frontend/qconductedclipanimator.h +++ b/src/animation/frontend/qclipblendvalue_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. @@ -37,40 +37,44 @@ ** ****************************************************************************/ -#ifndef QT3DANIMATION_QCONDUCTEDCLIPANIMATOR_H -#define QT3DANIMATION_QCONDUCTEDCLIPANIMATOR_H +#ifndef QT3DANIMATION_QCLIPBLENDNODE_P_H +#define QT3DANIMATION_QCLIPBLENDNODE_P_H -#include <Qt3DAnimation/qt3danimation_global.h> -#include <Qt3DCore/qcomponent.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/qabstractclipblendnode_p.h> +#include <Qt3DAnimation/qclipblendvalue.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QConductedClipAnimatorPrivate; - -class QT3DANIMATIONSHARED_EXPORT QConductedClipAnimator : public Qt3DCore::QComponent +class QClipBlendValuePrivate : public QAbstractClipBlendNodePrivate { - Q_OBJECT - // TODO: Add property declarations public: - explicit QConductedClipAnimator(Qt3DCore::QNode *parent = nullptr); - ~QConductedClipAnimator(); - -public Q_SLOTS: + QClipBlendValuePrivate(); -Q_SIGNALS: + Q_DECLARE_PUBLIC(QClipBlendValue) -protected: - QConductedClipAnimator(QConductedClipAnimatorPrivate &dd, Qt3DCore::QNode *parent = nullptr); + Qt3DAnimation::QAbstractAnimationClip *m_clip; +}; -private: - Q_DECLARE_PRIVATE(QConductedClipAnimator) - Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE; +struct QClipBlendValueData +{ + Qt3DCore::QNodeId clipId; }; } // namespace Qt3DAnimation QT_END_NAMESPACE -#endif // QT3DANIMATION_QCONDUCTEDCLIPANIMATOR_H +#endif // QT3DANIMATION_QCLIPBLENDNODE_P_H diff --git a/src/animation/frontend/qconductedclipanimator.cpp b/src/animation/frontend/qkeyframe.cpp index 29bdefe37..b3d339bf4 100644 --- a/src/animation/frontend/qconductedclipanimator.cpp +++ b/src/animation/frontend/qkeyframe.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. @@ -36,40 +36,13 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "qconductedclipanimator.h" -#include "qconductedclipanimator_p.h" + +#include "qkeyframe.h" QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -QConductedClipAnimatorPrivate::QConductedClipAnimatorPrivate() - : Qt3DCore::QComponentPrivate() -{ -} - -QConductedClipAnimator::QConductedClipAnimator(Qt3DCore::QNode *parent) - : Qt3DCore::QComponent(*new QConductedClipAnimatorPrivate, parent) -{ -} - -QConductedClipAnimator::QConductedClipAnimator(QConductedClipAnimatorPrivate &dd, Qt3DCore::QNode *parent) - : Qt3DCore::QComponent(dd, parent) -{ -} - -QConductedClipAnimator::~QConductedClipAnimator() -{ -} - -Qt3DCore::QNodeCreatedChangeBasePtr QConductedClipAnimator::createNodeCreationChange() const -{ - auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QConductedClipAnimatorData>::create(this); - auto &data = creationChange->data; - Q_D(const QConductedClipAnimator); - // TODO: Send data members in creation change - return creationChange; -} } // namespace Qt3DAnimation diff --git a/src/animation/frontend/qkeyframe.h b/src/animation/frontend/qkeyframe.h new file mode 100644 index 000000000..7eabbeb17 --- /dev/null +++ b/src/animation/frontend/qkeyframe.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DANIMATION_QKEYFRAME_H +#define QT3DANIMATION_QKEYFRAME_H + +#include <QtGui/qvector2d.h> +#include <Qt3DAnimation/qt3danimation_global.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QT3DANIMATIONSHARED_EXPORT QKeyFrame +{ +public: + enum InterpolationType : quint8 { + ConstantInterpolation, + LinearInterpolation, + BezierInterpolation + }; + + Q_DECL_CONSTEXPR QKeyFrame() Q_DECL_NOTHROW + : m_coordinates() + , m_leftControlPoint() + , m_rightControlPoint() + , m_interpolationType(BezierInterpolation) + { + } + + Q_DECL_CONSTEXPR explicit QKeyFrame(QVector2D coords) Q_DECL_NOTHROW + : m_coordinates(coords) + , m_leftControlPoint() + , m_rightControlPoint() + , m_interpolationType(LinearInterpolation) + { + } + + Q_DECL_CONSTEXPR explicit QKeyFrame(QVector2D coords, + QVector2D lh, + QVector2D rh) Q_DECL_NOTHROW + : m_coordinates(coords) + , m_leftControlPoint(lh) + , m_rightControlPoint(rh) + , m_interpolationType(BezierInterpolation) + { + } + + void setCoordinates(QVector2D coords) Q_DECL_NOTHROW + { + m_coordinates = coords; + } + + Q_DECL_CONSTEXPR QVector2D coordinates() const Q_DECL_NOTHROW + { + return m_coordinates; + } + + void setLeftControlPoint(QVector2D lh) Q_DECL_NOTHROW + { + m_leftControlPoint = lh; + } + + Q_DECL_CONSTEXPR QVector2D leftControlPoint() const Q_DECL_NOTHROW + { + return m_leftControlPoint; + } + + void setRightControlPoint(QVector2D rh) Q_DECL_NOTHROW + { + m_rightControlPoint = rh; + } + + Q_DECL_CONSTEXPR QVector2D rightControlPoint() const Q_DECL_NOTHROW + { + return m_rightControlPoint; + } + + void setInterpolationType(InterpolationType interp) Q_DECL_NOTHROW + { + m_interpolationType = interp; + } + + Q_DECL_CONSTEXPR InterpolationType interpolationType() const Q_DECL_NOTHROW + { + return m_interpolationType; + } + + friend inline bool operator==(const QKeyFrame &, const QKeyFrame &) Q_DECL_NOTHROW; + friend inline bool operator!=(const QKeyFrame &, const QKeyFrame &) Q_DECL_NOTHROW; + +private: + QVector2D m_coordinates; + QVector2D m_leftControlPoint; + QVector2D m_rightControlPoint; + InterpolationType m_interpolationType; +}; + +QT3D_DECLARE_TYPEINFO(Qt3DAnimation, QKeyFrame, Q_PRIMITIVE_TYPE) + +inline bool operator==(const QKeyFrame &lhs, const QKeyFrame &rhs) Q_DECL_NOTHROW +{ + if (lhs.m_interpolationType != rhs.m_interpolationType) + return false; + + if (lhs.m_interpolationType == QKeyFrame::BezierInterpolation) { + return lhs.m_coordinates == rhs.m_coordinates && + lhs.m_leftControlPoint == rhs.m_leftControlPoint && + lhs.m_rightControlPoint == rhs.m_rightControlPoint; + } + + return lhs.m_coordinates == rhs.m_coordinates; +} + +inline bool operator!=(const QKeyFrame &lhs, const QKeyFrame &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QKEYFRAME_H diff --git a/src/animation/frontend/qkeyframeanimation.cpp b/src/animation/frontend/qkeyframeanimation.cpp new file mode 100644 index 000000000..597b34cee --- /dev/null +++ b/src/animation/frontend/qkeyframeanimation.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 "qkeyframeanimation.h" +#include "Qt3DAnimation/private/qkeyframeanimation_p.h" + +#include <cmath> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +/*! + \class Qt3DAnimation::QKeyframeAnimation + \brief A class implementing simple keyframe animation to a QTransform + \inmodule Qt3DAnimation + \since 5.9 + \inherits Qt3DAnimation::QAbstractAnimation + + A Qt3DAnimation::QKeyframeAnimation class implements simple keyframe animation + that can be used to animate \l QTransform. The keyframes consists of multiple + timed QTransforms, which are interpolated and applied to the target \l QTransform. + \l QEasingCurve is used between keyframes to control the interpolator. RepeatMode + can be set for when the position set to the QKeyframeAnimation is below or above + the values defined in the keyframe positions. +*/ + +/*! + \qmltype KeyframeAnimation + \brief A type implementing simple keyframe animation to a Transform + \inqmlmodule Qt3D.Animation + \since 5.9 + \inherits AbstractAnimation + \instantiates Qt3DAnimation::QKeyframeAnimation + + A KeyframeAnimation type implements simple keyframe animation + that can be used to animate \l Transform. The keyframes consists of multiple + timed \l {Qt3D.Core::Transform}s, which are interpolated and applied + to the target Transform. EasingCurve is used between keyframes to control + the interpolator. RepeatMode can be set for when the position set to the + KeyframeAnimation is less or or greater than the values defined in the keyframe positions. +*/ + +/*! + \property Qt3DAnimation::QKeyframeAnimation::framePositions + Holds the positions of the keyframes. Each position in the list specifies the position + of the corresponding keyframe with the same index. The values must be in an ascending order. + Values can be positive or negative and do not have any predefined unit. +*/ +/*! + \property Qt3DAnimation::QKeyframeAnimation::target + Holds the target QTransform the animation is applied to. +*/ +/*! + \property Qt3DAnimation::QKeyframeAnimation::easing + Holds the easing curve of the interpolator between keyframes. +*/ +/*! + \property Qt3DAnimation::QKeyframeAnimation::targetName + Holds the name of the target transform. This is a convenience property making it + easier to match the target transform to the keyframe animation. The name + is usually same as the name of the parent entity of the target transform, but + does not have to be. +*/ +/*! + \property Qt3DAnimation::QKeyframeAnimation::startMode + Holds the repeat mode for the position values less than the first frame position. +*/ +/*! + \property Qt3DAnimation::QKeyframeAnimation::endMode + Holds the repeat mode for the position values greater than the last frame position. +*/ +/*! + \enum QKeyframeAnimation::RepeatMode + + This enumeration specifies how position values outside keyframe values are handled. + \value None The animation is not applied to the target transform. + \value Constant The edge keyframe value is used. + \value Repeat The animation is repeated. +*/ +/*! + \qmlproperty list<real> KeyframeAnimation::framePositions + Holds the positions of the keyframes. Each position in the list specifies the position + of the corresponding keyframe. The values must be in an ascending order. Values can + be positive or negative and do not have any predefined unit. +*/ +/*! + \qmlproperty Transform KeyframeAnimation::target + Holds the target Transform the animation is applied to. +*/ +/*! + \qmlproperty EasingCurve KeyframeAnimation::easing + Holds the easing curve of the interpolator between keyframes. +*/ +/*! + \qmlproperty string KeyframeAnimation::targetName + Holds the name of the target transform. This is a convenience property making it + easier to match the target transform to the keyframe animation. The name + is usually same as the name of the parent entity of the target transform, but + does not have to be. +*/ +/*! + \qmlproperty enumeration KeyframeAnimation::startMode + Holds the repeat mode for the position values less than the first frame position. + \list + \li None + \li Constant + \li Repeat + \endlist +*/ +/*! + \qmlproperty enumeration KeyframeAnimation::endMode + Holds the repeat mode for the position values greater than the last frame position. + \list + \li None + \li Constant + \li Repeat + \endlist +*/ +/*! + \qmlproperty list<Transform> KeyframeAnimation::keyframes + Holds the list of keyframes in the keyframe animation. +*/ + +QKeyframeAnimationPrivate::QKeyframeAnimationPrivate() + : QAbstractAnimationPrivate(QAbstractAnimation::KeyframeAnimation) + , m_target(nullptr) + , m_minposition(0.0f) + , m_maxposition(0.0f) + , m_startMode(QKeyframeAnimation::Constant) + , m_endMode(QKeyframeAnimation::Constant) +{ + +} + +/*! + Constructs an QKeyframeAnimation with \a parent. +*/ +QKeyframeAnimation::QKeyframeAnimation(QObject *parent) + : QAbstractAnimation(*new QKeyframeAnimationPrivate(), parent) +{ + Q_D(QKeyframeAnimation); + d->m_positionConnection = QObject::connect(this, &QAbstractAnimation::positionChanged, + this, &QKeyframeAnimation::updateAnimation); +} + + +void QKeyframeAnimation::setFramePositions(const QVector<float> &positions) +{ + Q_D(QKeyframeAnimation); + d->m_framePositions = positions; + d->m_position = -1.0f; + if (d->m_framePositions.size() == 0) { + d->m_minposition = d->m_maxposition = 0.0f; + return; + } + d->m_minposition = d->m_framePositions.first(); + d->m_maxposition = d->m_framePositions.last(); + float lastPos = d->m_minposition; + for (float p : qAsConst(d->m_framePositions)) { + if (p < lastPos || p > d->m_maxposition) + qWarning() << "positions not ordered correctly"; + lastPos = p; + } + setDuration(d->m_maxposition); +} + +/*! + Sets the \a keyframes of the animation. Old keyframes are cleared. + */ +void QKeyframeAnimation::setKeyframes(const QVector<Qt3DCore::QTransform *> &keyframes) +{ + Q_D(QKeyframeAnimation); + d->m_keyframes = keyframes; +} + +// slerp which allows long path +QQuaternion lslerp(QQuaternion q1, QQuaternion q2, float t) +{ + QQuaternion ret; + // Handle the easy cases first. + if (t <= 0.0f) + return q1; + else if (t >= 1.0f) + return q2; + + float cos = qBound(-1.0f, QQuaternion::dotProduct(q1, q2), 1.0f); + float angle = std::acos(cos); + float sin = std::sin(angle); + if (!qFuzzyIsNull(sin)) { + float a = std::sin((1.0 - t) * angle) / sin; + float b = std::sin(t * angle) / sin; + ret = (q1 * a + q2 * b).normalized(); + } else { + ret = q1 * (1.0f-t) + q2 * t; + } + return ret; +} + +void QKeyframeAnimationPrivate::calculateFrame(float position) +{ + if (m_target && m_framePositions.size() > 0 + && m_keyframes.size() == m_framePositions.size()) { + if (position < m_minposition) { + if (m_startMode == QKeyframeAnimation::None) { + return; + } else if (m_startMode == QKeyframeAnimation::Constant) { + m_target->setRotation(m_keyframes.first()->rotation()); + m_target->setScale3D(m_keyframes.first()->scale3D()); + m_target->setTranslation(m_keyframes.first()->translation()); + return; + } else { + // must be repeat + position = std::fmod(-(position - m_minposition), m_maxposition - m_minposition) + + m_minposition; + } + } else if (position >= m_maxposition) { + if (m_endMode == QKeyframeAnimation::None) { + return; + } else if (m_endMode == QKeyframeAnimation::Constant) { + m_target->setRotation(m_keyframes.last()->rotation()); + m_target->setScale3D(m_keyframes.last()->scale3D()); + m_target->setTranslation(m_keyframes.last()->translation()); + return; + } else { + // must be repeat + position = std::fmod(position - m_minposition, m_maxposition - m_minposition) + + m_minposition; + } + } + if (position >= m_minposition && position < m_maxposition) { + for (int i = 0; i < m_framePositions.size() - 1; i++) { + if (position >= m_framePositions.at(i) + && position < m_framePositions.at(i+1)) { + float ip = (position - m_framePositions.at(i)) + / (m_framePositions.at(i+1) - m_framePositions.at(i)); + float eIp = m_easing.valueForProgress(ip); + float eIip = 1.0f - eIp; + + Qt3DCore::QTransform *a = m_keyframes.at(i); + Qt3DCore::QTransform *b = m_keyframes.at(i+1); + + QVector3D s = a->scale3D() * eIip + b->scale3D() * eIp; + QVector3D t = a->translation() * eIip + b->translation() * eIp; + QQuaternion r = QQuaternion::slerp(a->rotation(), b->rotation(), eIp); + + m_target->setRotation(r); + m_target->setScale3D(s); + m_target->setTranslation(t); + return; + } + } + } + } +} + +void QKeyframeAnimation::updateAnimation(float position) +{ + Q_D(QKeyframeAnimation); + d->calculateFrame(position); +} + +QVector<float> QKeyframeAnimation::framePositions() const +{ + Q_D(const QKeyframeAnimation); + return d->m_framePositions; +} + +/*! + Returns the list of keyframes. + */ +QVector<Qt3DCore::QTransform *> QKeyframeAnimation::keyframeList() const +{ + Q_D(const QKeyframeAnimation); + return d->m_keyframes; +} + +void QKeyframeAnimation::setTarget(Qt3DCore::QTransform *target) +{ + Q_D(QKeyframeAnimation); + if (d->m_target != target) { + d->m_target = target; + emit targetChanged(d->m_target); + d->m_position = -1.0f; + + if (target) { + d->m_baseScale = target->scale3D(); + d->m_baseTranslation = target->translation(); + d->m_baseRotation = target->rotation(); + } + } +} + +QKeyframeAnimation::RepeatMode QKeyframeAnimation::startMode() const +{ + Q_D(const QKeyframeAnimation); + return d->m_startMode; +} + +QKeyframeAnimation::RepeatMode QKeyframeAnimation::endMode() const +{ + Q_D(const QKeyframeAnimation); + return d->m_endMode; +} + +void QKeyframeAnimation::setEasing(const QEasingCurve &easing) +{ + Q_D(QKeyframeAnimation); + if (d->m_easing != easing) { + d->m_easing = easing; + emit easingChanged(easing); + } +} + +void QKeyframeAnimation::setTargetName(const QString &name) +{ + Q_D(QKeyframeAnimation); + if (d->m_targetName != name) { + d->m_targetName = name; + emit targetNameChanged(name); + } +} + +void QKeyframeAnimation::setStartMode(QKeyframeAnimation::RepeatMode mode) +{ + Q_D(QKeyframeAnimation); + if (d->m_startMode != mode) { + d->m_startMode = mode; + emit startModeChanged(mode); + } +} + +void QKeyframeAnimation::setEndMode(QKeyframeAnimation::RepeatMode mode) +{ + Q_D(QKeyframeAnimation); + if (mode != d->m_endMode) { + d->m_endMode = mode; + emit endModeChanged(mode); + } +} + +/*! + Adds new \a keyframe at the end of the animation. The QTransform can + be added to the animation multiple times. + */ +void QKeyframeAnimation::addKeyframe(Qt3DCore::QTransform *keyframe) +{ + Q_D(QKeyframeAnimation); + d->m_keyframes.push_back(keyframe); +} + +/*! + Removes a \a keyframe from the animation. If the same QTransform + is set as keyframe multiple times, all occurrences are removed. + */ +void QKeyframeAnimation::removeKeyframe(Qt3DCore::QTransform *keyframe) +{ + Q_D(QKeyframeAnimation); + d->m_keyframes.removeAll(keyframe); +} + +QString QKeyframeAnimation::targetName() const +{ + Q_D(const QKeyframeAnimation); + return d->m_targetName; +} + +QEasingCurve QKeyframeAnimation::easing() const +{ + Q_D(const QKeyframeAnimation); + return d->m_easing; +} + +Qt3DCore::QTransform *QKeyframeAnimation::target() const +{ + Q_D(const QKeyframeAnimation); + return d->m_target; +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qkeyframeanimation.h b/src/animation/frontend/qkeyframeanimation.h new file mode 100644 index 000000000..19cb192b4 --- /dev/null +++ b/src/animation/frontend/qkeyframeanimation.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QKEYFRAMEANIMATION_H +#define QT3DANIMATION_QKEYFRAMEANIMATION_H + +#include <Qt3DCore/qtransform.h> + +#include <Qt3DAnimation/qabstractanimation.h> +#include <Qt3DAnimation/qt3danimation_global.h> + +#include <QtCore/qeasingcurve.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QKeyframeAnimationPrivate; + +class QT3DANIMATIONSHARED_EXPORT QKeyframeAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(QVector<float> framePositions READ framePositions WRITE setFramePositions NOTIFY framePositionsChanged) + Q_PROPERTY(Qt3DCore::QTransform *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + Q_PROPERTY(QString targetName READ targetName WRITE setTargetName NOTIFY targetNameChanged) + Q_PROPERTY(QKeyframeAnimation::RepeatMode startMode READ startMode WRITE setStartMode NOTIFY startModeChanged) + Q_PROPERTY(QKeyframeAnimation::RepeatMode endMode READ endMode WRITE setEndMode NOTIFY endModeChanged) + +public: + explicit QKeyframeAnimation(QObject *parent = nullptr); + + enum RepeatMode + { + None, + Constant, + Repeat, + }; + Q_ENUM(RepeatMode) + + QVector<float> framePositions() const; + QVector<Qt3DCore::QTransform *> keyframeList() const; + Qt3DCore::QTransform *target() const; + QEasingCurve easing() const; + QString targetName() const; + RepeatMode startMode() const; + RepeatMode endMode() const; + + void setKeyframes(const QVector<Qt3DCore::QTransform *> &keyframes); + void addKeyframe(Qt3DCore::QTransform *keyframe); + void removeKeyframe(Qt3DCore::QTransform *keyframe); + +public Q_SLOTS: + void setFramePositions(const QVector<float> &positions); + void setTarget(Qt3DCore::QTransform *target); + void setEasing(const QEasingCurve &easing); + void setTargetName(const QString &name); + void setStartMode(RepeatMode mode); + void setEndMode(RepeatMode mode); + +Q_SIGNALS: + void framePositionsChanged(const QVector<float> &positions); + void targetChanged(Qt3DCore::QTransform *target); + void easingChanged(const QEasingCurve &easing); + void targetNameChanged(const QString &name); + void startModeChanged(QKeyframeAnimation::RepeatMode startMode); + void endModeChanged(QKeyframeAnimation::RepeatMode endMode); + +private: + void updateAnimation(float position); + + Q_DECLARE_PRIVATE(QKeyframeAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QKEYFRAMEANIMATION_H diff --git a/src/animation/frontend/qkeyframeanimation_p.h b/src/animation/frontend/qkeyframeanimation_p.h new file mode 100644 index 000000000..b9b1f8585 --- /dev/null +++ b/src/animation/frontend/qkeyframeanimation_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QKEYFRAMEANIMATION_P_H +#define QT3DANIMATION_QKEYFRAMEANIMATION_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 <private/qobject_p.h> +#include <Qt3DAnimation/qkeyframeanimation.h> +#include <private/qabstractanimation_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QKeyframeAnimationPrivate : public QAbstractAnimationPrivate +{ +public: + QKeyframeAnimationPrivate(); + + void calculateFrame(float position); + + QVector<float> m_framePositions; + QVector<Qt3DCore::QTransform *> m_keyframes; + Qt3DCore::QTransform *m_target; + QEasingCurve m_easing; + QString m_animationName; + QString m_targetName; + float m_minposition; + float m_maxposition; + QKeyframeAnimation::RepeatMode m_startMode; + QKeyframeAnimation::RepeatMode m_endMode; + QVector3D m_baseScale; + QVector3D m_baseTranslation; + QQuaternion m_baseRotation; + QMetaObject::Connection m_positionConnection; + + Q_DECLARE_PUBLIC(QKeyframeAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QKEYFRAMEANIMATION_P_H diff --git a/src/animation/frontend/qlerpblend.cpp b/src/animation/frontend/qlerpclipblend.cpp index 661c70ef0..3b26adbaa 100644 --- a/src/animation/frontend/qlerpblend.cpp +++ b/src/animation/frontend/qlerpclipblend.cpp @@ -34,9 +34,8 @@ ** ****************************************************************************/ -#include "qlerpblend.h" -#include "qlerpblend_p.h" -#include <Qt3DAnimation/qanimationclip.h> +#include "qlerpclipblend.h" +#include "qlerpclipblend_p.h" #include <Qt3DAnimation/qclipblendnodecreatedchange.h> QT_BEGIN_NAMESPACE @@ -45,7 +44,7 @@ namespace Qt3DAnimation { /*! \qmltype LerpBlend - \instantiates Qt3DAnimation::QLerpBlend + \instantiates Qt3DAnimation::QLerpClipBlend \inqmlmodule Qt3D.Animation \brief Performs a linear interpolation of two animation clips based on a @@ -65,7 +64,7 @@ namespace Qt3DAnimation { */ /*! - \class Qt3DAnimation::QLerpBlend + \class Qt3DAnimation::QLerpClipBlend \inmodule Qt3DAnimation \inherits Qt3DAnimation::QAbstractClipBlendNode @@ -74,7 +73,7 @@ namespace Qt3DAnimation { \since 5.9 - QLerpBlend can be useful to create advanced animation effects based on + QLerpClipBlend can be useful to create advanced animation effects based on individual animation clips. For instance, given a player character,, lerp blending could be used to combine a walking animation clip with an injured animation clip based on a blend factor that increases the more the player @@ -85,31 +84,35 @@ namespace Qt3DAnimation { \sa QBlendedClipAnimator */ -QLerpBlendPrivate::QLerpBlendPrivate() +QLerpClipBlendPrivate::QLerpClipBlendPrivate() : QAbstractClipBlendNodePrivate() + , m_startClip(nullptr) + , m_endClip(nullptr) , m_blendFactor(0.0f) { } -QLerpBlend::QLerpBlend(Qt3DCore::QNode *parent) - : QAbstractClipBlendNode(*new QLerpBlendPrivate(), parent) +QLerpClipBlend::QLerpClipBlend(Qt3DCore::QNode *parent) + : QAbstractClipBlendNode(*new QLerpClipBlendPrivate(), parent) { } -QLerpBlend::QLerpBlend(QLerpBlendPrivate &dd, Qt3DCore::QNode *parent) +QLerpClipBlend::QLerpClipBlend(QLerpClipBlendPrivate &dd, Qt3DCore::QNode *parent) : QAbstractClipBlendNode(dd, parent) { } -QLerpBlend::~QLerpBlend() +QLerpClipBlend::~QLerpClipBlend() { } -Qt3DCore::QNodeCreatedChangeBasePtr QLerpBlend::createNodeCreationChange() const +Qt3DCore::QNodeCreatedChangeBasePtr QLerpClipBlend::createNodeCreationChange() const { - Q_D(const QLerpBlend); - auto creationChange = QClipBlendNodeCreatedChangePtr<QLerpBlendData>::create(this); - QLerpBlendData &data = creationChange->data; + Q_D(const QLerpClipBlend); + auto creationChange = QClipBlendNodeCreatedChangePtr<QLerpClipBlendData>::create(this); + QLerpClipBlendData &data = creationChange->data; + data.startClipId = Qt3DCore::qIdForNode(d->m_startClip); + data.endClipId = Qt3DCore::qIdForNode(d->m_endClip); data.blendFactor = d->m_blendFactor; return creationChange; } @@ -121,20 +124,61 @@ Qt3DCore::QNodeCreatedChangeBasePtr QLerpBlend::createNodeCreationChange() const two animation clips. */ /*! - \property QLerpBlend::blendFactor + \property QLerpClipBlend::blendFactor Specifies the blending factor between 0 and 1 to control the blending of two animation clips. */ -float QLerpBlend::blendFactor() const +float QLerpClipBlend::blendFactor() const { - Q_D(const QLerpBlend); + Q_D(const QLerpClipBlend); return d->m_blendFactor; } -void QLerpBlend::setBlendFactor(float blendFactor) +/*! + \qmlproperty AbstractClipBlendNode LerpClipBlend::startClip + + Holds the sub-tree that should be used as the start clip for this + lerp blend node. That is, the clip returned by this blend node when + the blendFactor is set to a value of 0. +*/ +/*! + \property QLerpClipBlend::startClip + + Holds the sub-tree that should be used as the start clip for this + lerp blend node. That is, the clip returned by this blend node when + the blendFactor is set to a value of 0. +*/ +Qt3DAnimation::QAbstractClipBlendNode *QLerpClipBlend::startClip() const { - Q_D(QLerpBlend); + Q_D(const QLerpClipBlend); + return d->m_startClip; +} + +/*! + \qmlproperty AbstractClipBlendNode LerpClipBlend::endClip + + Holds the sub-tree that should be used as the end clip for this + lerp blend node. That is, the clip returned by this blend node when + the blendFactor is set to a value of 1. +*/ +/*! + \property QLerpClipBlend::endClip + + Holds the sub-tree that should be used as the start clip for this + lerp blend node. That is, the clip returned by this blend node when + the blendFactor is set to a value of 1. +*/ +Qt3DAnimation::QAbstractClipBlendNode *QLerpClipBlend::endClip() const +{ + Q_D(const QLerpClipBlend); + return d->m_endClip; +} + +void QLerpClipBlend::setBlendFactor(float blendFactor) +{ + Q_D(QLerpClipBlend); + if (d->m_blendFactor == blendFactor) return; @@ -142,6 +186,44 @@ void QLerpBlend::setBlendFactor(float blendFactor) emit blendFactorChanged(blendFactor); } +void QLerpClipBlend::setStartClip(Qt3DAnimation::QAbstractClipBlendNode *startClip) +{ + Q_D(QLerpClipBlend); + if (d->m_startClip == startClip) + return; + + if (d->m_startClip) + d->unregisterDestructionHelper(d->m_startClip); + + if (startClip && !startClip->parent()) + startClip->setParent(this); + d->m_startClip = startClip; + + // Ensures proper bookkeeping + if (d->m_startClip) + d->registerDestructionHelper(d->m_startClip, &QLerpClipBlend::setStartClip, d->m_startClip); + emit startClipChanged(startClip); +} + +void QLerpClipBlend::setEndClip(Qt3DAnimation::QAbstractClipBlendNode *endClip) +{ + Q_D(QLerpClipBlend); + if (d->m_endClip == endClip) + return; + + if (d->m_endClip) + d->unregisterDestructionHelper(d->m_endClip); + + if (endClip && !endClip->parent()) + endClip->setParent(this); + d->m_endClip = endClip; + + // Ensures proper bookkeeping + if (d->m_endClip) + d->registerDestructionHelper(d->m_endClip, &QLerpClipBlend::setEndClip, d->m_endClip); + emit endClipChanged(endClip); +} + /*! \qmlproperty list<AnimationClip> LerpBlend::clips @@ -152,19 +234,19 @@ void QLerpBlend::setBlendFactor(float blendFactor) /*! - \fn void QLerpBlend::addClip(QAnimationClip *clip); + \fn void QLerpClipBlend::addClip(QAbstractAnimationClip *clip); Adds a \a clip to the blending node's clips list. \note Only the two first AnimationClip are used, subsequent ones are ignored */ /*! - \fn void QLerpBlend::removeClip(QAnimationClip *clip); + \fn void QLerpClipBlend::removeClip(QAbstractAnimationClip *clip); Removes a \a clip from the blending node's clips list. */ /*! - \fn QVector<QAnimationClip *> QLerpBlend::clips() const; + \fn QVector<QAbstractAnimationClip *> QLerpClipBlend::clips() const; Returns the list of QAnimationClip against which the blending is performed. */ diff --git a/src/animation/frontend/qlerpclipblend.h b/src/animation/frontend/qlerpclipblend.h new file mode 100644 index 000000000..f6707a5a5 --- /dev/null +++ b/src/animation/frontend/qlerpclipblend.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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_QLERPCLIPBLEND_H +#define QT3DANIMATION_QLERPCLIPBLEND_H + +#include <Qt3DAnimation/qabstractclipblendnode.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QLerpClipBlendPrivate; + +class QT3DANIMATIONSHARED_EXPORT QLerpClipBlend : public QAbstractClipBlendNode +{ + Q_OBJECT + Q_PROPERTY(Qt3DAnimation::QAbstractClipBlendNode *startClip READ startClip WRITE setStartClip NOTIFY startClipChanged) + Q_PROPERTY(Qt3DAnimation::QAbstractClipBlendNode *endClip READ endClip WRITE setEndClip NOTIFY endClipChanged) + Q_PROPERTY(float blendFactor READ blendFactor WRITE setBlendFactor NOTIFY blendFactorChanged) + +public: + explicit QLerpClipBlend(Qt3DCore::QNode *parent = nullptr); + ~QLerpClipBlend(); + + float blendFactor() const; + Qt3DAnimation::QAbstractClipBlendNode *startClip() const; + Qt3DAnimation::QAbstractClipBlendNode *endClip() const; + +public Q_SLOTS: + void setBlendFactor(float blendFactor); + void setStartClip(Qt3DAnimation::QAbstractClipBlendNode * startClip); + void setEndClip(Qt3DAnimation::QAbstractClipBlendNode * endClip); + +Q_SIGNALS: + void blendFactorChanged(float blendFactor); + void startClipChanged(Qt3DAnimation::QAbstractClipBlendNode * startClip); + void endClipChanged(Qt3DAnimation::QAbstractClipBlendNode * endClip); + +protected: + explicit QLerpClipBlend(QLerpClipBlendPrivate &dd, Qt3DCore::QNode *parent = nullptr); + +private: + Q_DECLARE_PRIVATE(QLerpClipBlend) + Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const Q_DECL_OVERRIDE; +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + + +#endif // QT3DANIMATION_QLERPCLIPBLEND_H diff --git a/src/animation/frontend/qlerpblend_p.h b/src/animation/frontend/qlerpclipblend_p.h index 035e6cf79..eee597059 100644 --- a/src/animation/frontend/qlerpblend_p.h +++ b/src/animation/frontend/qlerpclipblend_p.h @@ -50,25 +50,29 @@ #include <Qt3DAnimation/private/qabstractclipblendnode_p.h> #include <Qt3DCore/qnodeid.h> -#include "qlerpblend.h" +#include "qlerpclipblend.h" QT_BEGIN_NAMESPACE namespace Qt3DAnimation { -class QLerpBlend; +class QLerpClipBlend; -class QLerpBlendPrivate : public QAbstractClipBlendNodePrivate +class QLerpClipBlendPrivate : public QAbstractClipBlendNodePrivate { public: - QLerpBlendPrivate(); + QLerpClipBlendPrivate(); - Q_DECLARE_PUBLIC(QLerpBlend) + Q_DECLARE_PUBLIC(QLerpClipBlend) + QAbstractClipBlendNode *m_startClip; + QAbstractClipBlendNode *m_endClip; float m_blendFactor; }; -struct QLerpBlendData +struct QLerpClipBlendData { + Qt3DCore::QNodeId startClipId; + Qt3DCore::QNodeId endClipId; float blendFactor; }; diff --git a/src/animation/frontend/qmorphinganimation.cpp b/src/animation/frontend/qmorphinganimation.cpp new file mode 100644 index 000000000..3824b8d64 --- /dev/null +++ b/src/animation/frontend/qmorphinganimation.cpp @@ -0,0 +1,453 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 "qmorphinganimation.h" +#include <private/qmorphinganimation_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +/*! + \class Qt3DAnimation::QMorphingAnimation + \brief A class implementing blend-shape morphing animation + \inmodule Qt3DAnimation + \since 5.9 + \inherits Qt3DAnimation::QAbstractAnimation + + A Qt3DAnimation::QMorphingAnimation class implements blend-shape morphing animation + to a target \l {Qt3DRender::QGeometryRenderer}{QGeometryRenderer}. The QMorphingAnimation + sets the correct \l {Qt3DRender::QAttribute}{QAttributes} from the + \l {Qt3DAnimation::QMorphTarget}{morph targets} to the target + \l {Qt3DRender::QGeometryRenderer::geometry} {QGeometryRenderer::geometry} and calculates + interpolator for the current position. The actual blending between the attributes must + be implemented in the material. Qt3DAnimation::QMorphPhongMaterial implements material + with morphing support for phong lighting model. The blending happens between + 2 attributes - 'base' and 'target'. The names for the base and target attributes are taken from + the morph target names, where the base attribute retains the name it already has and the + target attribute name gets 'Target' appended to the name. The interpolator can be + set as a \l {Qt3DRender::QParameter}{QParameter} to the used material. + All morph targets in the animation should contain the attributes with same names as those + in the base geometry. + +*/ +/*! + \qmltype MorphingAnimation + \brief A type implementing blend-shape morphing animation + \inqmlmodule Qt3D.Animation + \since 5.9 + \inherits AbstractAnimation + \instantiates Qt3DAnimation::QMorphingAnimation + + A MorphingAnimation type implements blend-shape morphing animation + to a target \l GeometryRenderer. The MorphingAnimation sets the correct + \l {Attribute}{Attributes} from the morph targets to the target + \l {Qt3D.Render::GeometryRenderer::geometry}{GeometryRenderer::geometry} and calculates + interpolator for the current position. The actual blending between the attributes must + be implemented in the material. MorphPhongMaterial implements material + with morphing support for phong lighting model. The blending happens between + 2 attributes - 'base' and 'target'. The names for the base and target attributes are taken from + the morph target names, where the base attribute retains the name it already has and the + target attribute name gets 'Target' appended to the name. All morph targets in the animation + should contain the attributes with same names as those in the base geometry. + +*/ +/*! + \property Qt3DAnimation::QMorphingAnimation::targetPositions + Holds the position values of the morph target. Each position in the list specifies the position + of the corresponding morph target with the same index. The values must be in an ascending order. + Values can be positive or negative and do not have any predefined unit. +*/ +/*! + \property Qt3DAnimation::QMorphingAnimation::interpolator + Holds the interpolator between base and target attributes. + \readonly +*/ +/*! + \property Qt3DAnimation::QMorphingAnimation::target + Holds the target QGeometryRenderer the morphing animation is applied to. +*/ +/*! + \property Qt3DAnimation::QMorphingAnimation::targetName + Holds the name of the target geometry. This is a convenience property making it + easier to match the target geometry to the morphing animation. The name + is usually same as the name of the parent entity of the target QGeometryRenderer, but + does not have to be. +*/ +/*! + \property Qt3DAnimation::QMorphingAnimation::method + Holds the morphing method. The default is Relative. +*/ +/*! + \property Qt3DAnimation::QMorphingAnimation::easing + Holds the easing curve of the interpolator between morph targets. +*/ +/*! + \enum Qt3DAnimation::QMorphingAnimation::Method + + This enumeration specifies the morphing method. + \value Normalized The blending should use the normalized formula; + V' = Vbase * (1.0 - sum(Wi)) + sum[Vi * Wi] + \value Relative The blending should use the relative formula; + V' = Vbase + sum[Vi * Wi] +*/ + +/*! + \qmlproperty list<real> MorphingAnimation::targetPositions + Holds the position values of the morph target. Each position in the list specifies the position + of the corresponding morph target with the same index. The values must be in an ascending order. + Values can be positive or negative and do not have any predefined unit. +*/ +/*! + \qmlproperty real MorphingAnimation::interpolator + Holds the interpolator between base and target attributes. + \readonly +*/ +/*! + \qmlproperty GeometryRenderer MorphingAnimation::target + Holds the target GeometryRenderer the morphing animation is applied to. +*/ +/*! + \qmlproperty string MorphingAnimation::targetName + Holds the name of the target geometry. This is a convenience property making it + easier to match the target geometry to the morphing animation. The name + is usually same as the name of the parent entity of the target GeometryRenderer, but + does not have to be. +*/ +/*! + \qmlproperty enumeration MorphingAnimation::method + Holds the morphing method. The default is Relative. + \list + \li Normalized + \li Relative + \endlist +*/ +/*! + \qmlproperty EasingCurve MorphingAnimation::easing + Holds the easing curve of the interpolator between morph targets. +*/ +/*! + \qmlproperty list<MorphTarget> MorphingAnimation::morphTargets + Holds the list of morph targets in the morphing animation. +*/ + +QMorphingAnimationPrivate::QMorphingAnimationPrivate() + : QAbstractAnimationPrivate(QAbstractAnimation::MorphingAnimation) + , m_minposition(0.0f) + , m_maxposition(0.0f) + , m_flattened(nullptr) + , m_method(QMorphingAnimation::Relative) + , m_interpolator(0.0f) + , m_target(nullptr) + , m_currentTarget(nullptr) +{ + +} + +QMorphingAnimationPrivate::~QMorphingAnimationPrivate() +{ + for (QVector<float> *weights : qAsConst(m_weights)) + delete weights; +} + +void QMorphingAnimationPrivate::updateAnimation(float position) +{ + Q_Q(QMorphingAnimation); + if (!m_target || !m_target->geometry()) + return; + + QVector<int> relevantValues; + float sum = 0.0f; + float interpolator = 0.0f; + m_morphKey.resize(m_morphTargets.size()); + + // calculate morph key + if (position < m_minposition) { + m_morphKey = *m_weights.first(); + } else if (position >= m_maxposition) { + m_morphKey = *m_weights.last(); + } else { + for (int i = 0; i < m_targetPositions.size() - 1; ++i) { + if (position >= m_targetPositions.at(i) && position < m_targetPositions.at(i + 1)) { + interpolator = (position - m_targetPositions.at(i)) + / (m_targetPositions.at(i + 1) - m_targetPositions.at(i)); + interpolator = m_easing.valueForProgress(interpolator); + float iip = 1.0f - interpolator; + + for (int j = 0; j < m_morphTargets.size(); ++j) { + m_morphKey[j] = interpolator * m_weights.at(i + 1)->at(j) + + iip * m_weights.at(i)->at(j); + } + } + } + } + + // check relevant values + for (int j = 0; j < m_morphKey.size(); ++j) { + sum += m_morphKey[j]; + if (!qFuzzyIsNull(m_morphKey[j])) + relevantValues.push_back(j); + } + + if (relevantValues.size() == 0 || qFuzzyIsNull(sum)) { + // only base is used + interpolator = 0.0f; + } else if (relevantValues.size() == 1) { + // one morph target has non-zero weight + setTargetInterpolated(relevantValues[0]); + interpolator = sum; + } else { + // more than one morph target has non-zero weight + // flatten morph targets to one + qWarning() << Q_FUNC_INFO << "Flattening required"; + } + + // Relative method uses negative interpolator, normalized uses positive + if (m_method == QMorphingAnimation::Relative) + interpolator = -interpolator; + + if (!qFuzzyCompare(interpolator, m_interpolator)) { + m_interpolator = interpolator; + emit q->interpolatorChanged(m_interpolator); + } +} + +void QMorphingAnimationPrivate::setTargetInterpolated(int morphTarget) +{ + QMorphTarget *target = m_morphTargets[morphTarget]; + Qt3DRender::QGeometry *geometry = m_target->geometry(); + + // remove attributes from previous frame + if (m_currentTarget && (target != m_currentTarget)) { + const QVector<Qt3DRender::QAttribute *> targetAttributes = m_currentTarget->attributeList(); + for (int i = 0; i < targetAttributes.size(); ++i) + geometry->removeAttribute(targetAttributes.at(i)); + } + + const QVector<Qt3DRender::QAttribute *> targetAttributes = target->attributeList(); + + // add attributes from current frame to the geometry + if (target != m_currentTarget) { + for (int i = 0; i < m_attributeNames.size(); ++i) { + QString targetName = m_attributeNames.at(i); + targetName.append(QLatin1String("Target")); + targetAttributes[i]->setName(targetName); + geometry->addAttribute(targetAttributes.at(i)); + } + } + m_currentTarget = target; +} + +/*! + Construct a new QMorphingAnimation with \a parent. + */ +QMorphingAnimation::QMorphingAnimation(QObject *parent) + : QAbstractAnimation(*new QMorphingAnimationPrivate, parent) +{ + Q_D(QMorphingAnimation); + d->m_positionConnection = QObject::connect(this, &QAbstractAnimation::positionChanged, + this, &QMorphingAnimation::updateAnimation); +} + +QVector<float> QMorphingAnimation::targetPositions() const +{ + Q_D(const QMorphingAnimation); + return d->m_targetPositions; +} + +float QMorphingAnimation::interpolator() const +{ + Q_D(const QMorphingAnimation); + return d->m_interpolator; +} + +Qt3DRender::QGeometryRenderer *QMorphingAnimation::target() const +{ + Q_D(const QMorphingAnimation); + return d->m_target; +} + +QString QMorphingAnimation::targetName() const +{ + Q_D(const QMorphingAnimation); + return d->m_targetName; +} + +QMorphingAnimation::Method QMorphingAnimation::method() const +{ + Q_D(const QMorphingAnimation); + return d->m_method; +} + +QEasingCurve QMorphingAnimation::easing() const +{ + Q_D(const QMorphingAnimation); + return d->m_easing; +} + +/*! + Set morph \a targets to animation. Old targets are cleared. +*/ +void QMorphingAnimation::setMorphTargets(const QVector<Qt3DAnimation::QMorphTarget *> &targets) +{ + Q_D(QMorphingAnimation); + d->m_morphTargets = targets; + d->m_attributeNames = targets[0]->attributeNames(); + d->m_position = -1.0f; +} + +/*! + Add new morph \a target at the end of the animation. +*/ +void QMorphingAnimation::addMorphTarget(Qt3DAnimation::QMorphTarget *target) +{ + Q_D(QMorphingAnimation); + if (!d->m_morphTargets.contains(target)) { + d->m_morphTargets.push_back(target); + d->m_position = -1.0f; + if (d->m_attributeNames.empty()) + d->m_attributeNames = target->attributeNames(); + } +} + +/*! + Remove morph \a target from the animation. +*/ +void QMorphingAnimation::removeMorphTarget(Qt3DAnimation::QMorphTarget *target) +{ + Q_D(QMorphingAnimation); + d->m_morphTargets.removeAll(target); + d->m_position = -1.0f; +} + +void QMorphingAnimation::setTargetPositions(const QVector<float> &targetPositions) +{ + Q_D(QMorphingAnimation); + d->m_targetPositions = targetPositions; + emit targetPositionsChanged(targetPositions); + d->m_minposition = targetPositions.first(); + d->m_maxposition = targetPositions.last(); + setDuration(d->m_targetPositions.last()); + if (d->m_weights.size() < targetPositions.size()) { + d->m_weights.resize(targetPositions.size()); + for (int i = 0; i < d->m_weights.size(); ++i) { + if (d->m_weights[i] == nullptr) + d->m_weights[i] = new QVector<float>(); + } + } + d->m_position = -1.0f; +} + +void QMorphingAnimation::setTarget(Qt3DRender::QGeometryRenderer *target) +{ + Q_D(QMorphingAnimation); + if (d->m_target != target) { + d->m_position = -1.0f; + d->m_target = target; + emit targetChanged(target); + } +} + +/*! + Sets morph \a weights at \a positionIndex. +*/ +void QMorphingAnimation::setWeights(int positionIndex, const QVector<float> &weights) +{ + Q_D(QMorphingAnimation); + if (d->m_weights.size() < positionIndex) + d->m_weights.resize(positionIndex + 1); + if (d->m_weights[positionIndex] == nullptr) + d->m_weights[positionIndex] = new QVector<float>(); + *d->m_weights[positionIndex] = weights; + d->m_position = -1.0f; +} + +/*! + Return morph weights at \a positionIndex. +*/ +QVector<float> QMorphingAnimation::getWeights(int positionIndex) +{ + Q_D(QMorphingAnimation); + return *d->m_weights[positionIndex]; +} + +/*! + Return morph target list. +*/ +QVector<Qt3DAnimation::QMorphTarget *> QMorphingAnimation::morphTargetList() +{ + Q_D(QMorphingAnimation); + return d->m_morphTargets; +} + +void QMorphingAnimation::setTargetName(const QString name) +{ + Q_D(QMorphingAnimation); + if (d->m_targetName != name) { + d->m_targetName = name; + emit targetNameChanged(name); + } +} + +void QMorphingAnimation::setMethod(QMorphingAnimation::Method method) +{ + Q_D(QMorphingAnimation); + if (d->m_method != method) { + d->m_method = method; + d->m_position = -1.0f; + emit methodChanged(method); + } +} + +void QMorphingAnimation::setEasing(const QEasingCurve &easing) +{ + Q_D(QMorphingAnimation); + if (d->m_easing != easing) { + d->m_easing = easing; + d->m_position = -1.0f; + emit easingChanged(easing); + } +} + +void QMorphingAnimation::updateAnimation(float position) +{ + Q_D(QMorphingAnimation); + d->updateAnimation(position); +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qmorphinganimation.h b/src/animation/frontend/qmorphinganimation.h new file mode 100644 index 000000000..2944fad91 --- /dev/null +++ b/src/animation/frontend/qmorphinganimation.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QMORPHINGANIMATION_H +#define QT3DANIMATION_QMORPHINGANIMATION_H + +#include <Qt3DRender/qgeometryrenderer.h> + +#include <Qt3DAnimation/qabstractanimation.h> +#include <Qt3DAnimation/qmorphtarget.h> +#include <Qt3DAnimation/qt3danimation_global.h> + +#include <QtCore/qeasingcurve.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QMorphingAnimationPrivate; + +class QT3DANIMATIONSHARED_EXPORT QMorphingAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(QVector<float> targetPositions READ targetPositions WRITE setTargetPositions NOTIFY targetPositionsChanged) + Q_PROPERTY(float interpolator READ interpolator NOTIFY interpolatorChanged) + Q_PROPERTY(Qt3DRender::QGeometryRenderer *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString targetName READ targetName WRITE setTargetName NOTIFY targetNameChanged) + Q_PROPERTY(QMorphingAnimation::Method method READ method WRITE setMethod NOTIFY methodChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + +public: + enum Method + { + Normalized, + Relative + }; + Q_ENUM(Method) + + explicit QMorphingAnimation(QObject *parent = nullptr); + + QVector<float> targetPositions() const; + float interpolator() const; + Qt3DRender::QGeometryRenderer *target() const; + QString targetName() const; + QMorphingAnimation::Method method() const; + QEasingCurve easing() const; + + void setMorphTargets(const QVector<Qt3DAnimation::QMorphTarget *> &targets); + void addMorphTarget(Qt3DAnimation::QMorphTarget *target); + void removeMorphTarget(Qt3DAnimation::QMorphTarget *target); + + void setWeights(int positionIndex, const QVector<float> &weights); + QVector<float> getWeights(int positionIndex); + + QVector<Qt3DAnimation::QMorphTarget *> morphTargetList(); + +public Q_SLOTS: + void setTargetPositions(const QVector<float> &targetPositions); + void setTarget(Qt3DRender::QGeometryRenderer *target); + void setTargetName(const QString name); + void setMethod(QMorphingAnimation::Method method); + void setEasing(const QEasingCurve &easing); + +Q_SIGNALS: + void targetPositionsChanged(const QVector<float> &targetPositions); + void interpolatorChanged(float interpolator); + void targetChanged(Qt3DRender::QGeometryRenderer *target); + void targetNameChanged(const QString &name); + void methodChanged(QMorphingAnimation::Method method); + void easingChanged(const QEasingCurve &easing); + +private: + + void updateAnimation(float position); + + Q_DECLARE_PRIVATE(QMorphingAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QMORPHINGANIMATION_H diff --git a/src/animation/frontend/qmorphinganimation_p.h b/src/animation/frontend/qmorphinganimation_p.h new file mode 100644 index 000000000..c306f3309 --- /dev/null +++ b/src/animation/frontend/qmorphinganimation_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QMORPHINGANIMATION_P_H +#define QT3DANIMATION_QMORPHINGANIMATION_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 <QtCore/qeasingcurve.h> +#include <Qt3DAnimation/qmorphinganimation.h> + +#include <private/qobject_p.h> +#include <private/qabstractanimation_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QMorphingAnimationPrivate : public QAbstractAnimationPrivate +{ +public: + QMorphingAnimationPrivate(); + ~QMorphingAnimationPrivate(); + + void updateAnimation(float position); + void setTargetInterpolated(int morphTarget); + + float m_minposition; + float m_maxposition; + QVector<float> m_targetPositions; + QVector<QVector<float>*> m_weights; + QVector<float> m_morphKey; + QStringList m_attributeNames; + QVector<Qt3DAnimation::QMorphTarget *> m_morphTargets; + QMorphTarget *m_flattened; + QMorphingAnimation::Method m_method; + QEasingCurve m_easing; + float m_interpolator; + Qt3DRender::QGeometryRenderer *m_target; + QString m_targetName; + + QMorphTarget *m_currentTarget; + + QMetaObject::Connection m_positionConnection; + + Q_DECLARE_PUBLIC(QMorphingAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QMORPHINGANIMATION_P_H diff --git a/src/animation/frontend/qmorphtarget.cpp b/src/animation/frontend/qmorphtarget.cpp new file mode 100644 index 000000000..9dc30b8ba --- /dev/null +++ b/src/animation/frontend/qmorphtarget.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 "qmorphtarget.h" +#include "Qt3DAnimation/private/qmorphtarget_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +/*! + \class Qt3DAnimation::QMorphTarget + \brief A class providing morph targets to blend-shape animation + \inmodule Qt3DAnimation + \since 5.9 + \inherits QObject + + A Qt3DAnimation::QMorphTarget class is a convenience class, which provides a list + of \l {Qt3DRender::QAttribute} {QAttributes}, which the QMorphingAnimation uses + to animate geometry. A QMorphTarget can also be created based on existing + \l Qt3DRender::QGeometry. + +*/ +/*! + \qmltype MorphTarget + \brief A type providing morph targets to blend-shape animation + \inqmlmodule Qt3D.Animation + \since 5.9 + \inherits QtObject + \instantiates Qt3DAnimation::QMorphTarget + + A MorphTarget type is a convenience type, which provides a list + of \l {Qt3D.Render::Attribute} {Attributes}, which the MorphingAnimation uses + to animate geometry. A MorphTarget can also be created based on existing + \l {Qt3D.Render::Geometry}{Geometry}. + +*/ + +/*! + \property Qt3DAnimation::QMorphTarget::attributeNames + Holds a list of attribute names contained in the morph target. + \readonly +*/ + +/*! + \qmlproperty list<string> MorphTarget::attributeNames + Holds a list of attribute names contained in the morph target. + \readonly +*/ +/*! + \qmlproperty list<Attribute> MorphTarget::attributes + Holds the list of attributes in the morph target. +*/ +/*! + \qmlmethod MorphTarget Qt3D.Animation::MorphTarget::fromGeometry(geometry, stringList) + Returns a morph target based on the attributes defined by the given stringList from + the given geometry. +*/ + +QMorphTargetPrivate::QMorphTargetPrivate() + : QObjectPrivate() +{ + +} + +void QMorphTargetPrivate::updateAttributeNames() +{ + m_attributeNames.clear(); + for (const Qt3DRender::QAttribute *attr : qAsConst(m_targetAttributes)) + m_attributeNames.push_back(attr->name()); +} + +/*! + Constructs a QMorphTarget with given \a parent. +*/ +QMorphTarget::QMorphTarget(QObject *parent) + : QObject(*new QMorphTargetPrivate, parent) +{ + +} + +/*! + Returns a list of attributes contained in the morph target. +*/ +QVector<Qt3DRender::QAttribute *> QMorphTarget::attributeList() const +{ + Q_D(const QMorphTarget); + return d->m_targetAttributes; +} + +QStringList QMorphTarget::attributeNames() const +{ + Q_D(const QMorphTarget); + return d->m_attributeNames; +} + +/*! + Sets \a attributes to the morph target. Old attributes are cleared. +*/ +void QMorphTarget::setAttributes(const QVector<Qt3DRender::QAttribute *> &attributes) +{ + Q_D(QMorphTarget); + d->m_targetAttributes = attributes; + d->m_attributeNames.clear(); + for (const Qt3DRender::QAttribute *attr : attributes) + d->m_attributeNames.push_back(attr->name()); + + emit attributeNamesChanged(d->m_attributeNames); +} + +/*! + Adds an \a attribute the morph target. An attribute with the same + name must not have been added previously to the morph target. +*/ +void QMorphTarget::addAttribute(Qt3DRender::QAttribute *attribute) +{ + Q_D(QMorphTarget); + for (const Qt3DRender::QAttribute *attr : qAsConst(d->m_targetAttributes)) { + if (attr->name() == attribute->name()) + return; + } + d->m_targetAttributes.push_back(attribute); + d->m_attributeNames.push_back(attribute->name()); + emit attributeNamesChanged(d->m_attributeNames); +} + +/*! + Removes an \a attribute from the morph target. +*/ +void QMorphTarget::removeAttribute(Qt3DRender::QAttribute *attribute) +{ + Q_D(QMorphTarget); + if (d->m_targetAttributes.contains(attribute)) { + d->m_targetAttributes.removeAll(attribute); + d->updateAttributeNames(); + emit attributeNamesChanged(d->m_attributeNames); + } +} + +/*! + Returns a morph target based on the \a attributes in the given \a geometry. +*/ +QMorphTarget *QMorphTarget::fromGeometry(Qt3DRender::QGeometry *geometry, const QStringList &attributes) +{ + QMorphTarget *target = new QMorphTarget(); + for (Qt3DRender::QAttribute *attr : geometry->attributes()) { + if (attributes.contains(attr->name())) + target->addAttribute(attr); + } + return target; +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qmorphtarget.h b/src/animation/frontend/qmorphtarget.h new file mode 100644 index 000000000..0435924e1 --- /dev/null +++ b/src/animation/frontend/qmorphtarget.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QMORPHTARGET_H +#define QT3DANIMATION_QMORPHTARGET_H + +#include <Qt3DRender/qattribute.h> +#include <Qt3DRender/qgeometry.h> + +#include <QtCore/qstringlist.h> + +#include <Qt3DAnimation/qt3danimation_global.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QMorphTargetPrivate; + +class QT3DANIMATIONSHARED_EXPORT QMorphTarget : public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList attributeNames READ attributeNames NOTIFY attributeNamesChanged) + +public: + explicit QMorphTarget(QObject *parent = nullptr); + + QVector<Qt3DRender::QAttribute *> attributeList() const; + QStringList attributeNames() const; + + void setAttributes(const QVector<Qt3DRender::QAttribute *> &attributes); + void addAttribute(Qt3DRender::QAttribute *attribute); + void removeAttribute(Qt3DRender::QAttribute *attribute); + + Q_INVOKABLE static QMorphTarget *fromGeometry(Qt3DRender::QGeometry *geometry, + const QStringList &attributes); + +Q_SIGNALS: + void attributeNamesChanged(const QStringList &attributeNames); + +private: + + Q_DECLARE_PRIVATE(QMorphTarget) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QMORPHTARGET_H diff --git a/src/animation/frontend/qmorphtarget_p.h b/src/animation/frontend/qmorphtarget_p.h new file mode 100644 index 000000000..1fc3734ae --- /dev/null +++ b/src/animation/frontend/qmorphtarget_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QMORPHTARGET_P_H +#define QT3DANIMATION_QMORPHTARGET_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/qmorphtarget.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QMorphTargetPrivate : public QObjectPrivate +{ +public: + QMorphTargetPrivate(); + + void updateAttributeNames(); + + QStringList m_attributeNames; + QVector<Qt3DRender::QAttribute *> m_targetAttributes; + + Q_DECLARE_PUBLIC(QMorphTarget) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QMORPHTARGET_P_H diff --git a/src/animation/frontend/qvertexblendanimation.cpp b/src/animation/frontend/qvertexblendanimation.cpp new file mode 100644 index 000000000..3ddd83bf0 --- /dev/null +++ b/src/animation/frontend/qvertexblendanimation.cpp @@ -0,0 +1,339 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 "qvertexblendanimation.h" + +#include <private/qvertexblendanimation_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +/*! + \class Qt3DAnimation::QVertexBlendAnimation + \brief A class implementing vertex-blend morphing animation + \inmodule Qt3DAnimation + \since 5.9 + \inherits Qt3DAnimation::QAbstractAnimation + + A Qt3DAnimation::QVertexBlendAnimation class implements vertex-blend morphing animation + to a target \l {Qt3DRender::QGeometryRenderer}{QGeometryRenderer}. The QVertexBlendAnimation + sets the correct \l {Qt3DRender::QAttribute}{QAttributes} from the + \l {Qt3DAnimation::QMorphTarget}{morph targets} to the target + \l {Qt3DRender::QGeometryRenderer::geometry} {QGeometryRenderer::geometry} and calculates + interpolator for the current position. Unlike with QMorphingAnimation, where the blending is + controller with blend weights, the blending occurs between sequential morph targets. + The actual blending between the attributes must be implemented in the material. + Qt3DAnimation::QMorphPhongMaterial implements material with morphing support for phong + lighting model. The blending happens between 2 attributes - 'base' and 'target'. + The names for the base and target attributes are taken from the morph target names, + where the base attribute retains the name it already has and the target attribute name + gets 'Target' appended to the name. The interpolator can be set as + a \l {Qt3DRender::QParameter}{QParameter} to the used material. + All morph targets in the animation should contain the attributes with same names as those + in the base geometry. + +*/ +/*! + \qmltype VertexBlendAnimation + \brief A type implementing vertex-blend morphing animation + \inqmlmodule Qt3D.Animation + \since 5.9 + \inherits AbstractAnimation + \instantiates Qt3DAnimation::QVertexBlendAnimation + + A VertexBlendAnimation type implements vertex-blend morphing animation + to a target \l GeometryRenderer. The VertexBlendAnimation sets the correct + \l {Attribute}{Attributes} from the morph targets to the target + \l {Qt3D.Render::GeometryRenderer::geometry}{GeometryRenderer::geometry} and calculates + interpolator for the current position. Unlike with MorphingAnimation, where the blending is + controller with blend weights, the blending occurs between sequential morph targets. + The actual blending between the attributes must be implemented in the material. + MorphPhongMaterial implements material with morphing support for phong lighting model. + The blending happens between 2 attributes - 'base' and 'target'. The names for the base + and target attributes are taken from the morph target names, where the base attribute + retains the name it already has and the target attribute name gets 'Target' appended to + the name. All morph targets in the animation should contain the attributes with same names + as those in the base geometry. + +*/ +/*! + \property Qt3DAnimation::QVertexBlendAnimation::targetPositions + Holds the position values of the morph target. Each position in the list specifies the position + of the corresponding morph target with the same index. The values must be in an ascending order. + Values can be positive or negative and do not have any predefined unit. +*/ +/*! + \property Qt3DAnimation::QVertexBlendAnimation::interpolator + Holds the interpolator between base and target attributes. + \readonly +*/ +/*! + \property Qt3DAnimation::QVertexBlendAnimation::target + Holds the target QGeometryRenderer the morphing animation is applied to. +*/ +/*! + \property Qt3DAnimation::QVertexBlendAnimation::targetName + Holds the name of the target geometry. This is a convenience property making it + easier to match the target geometry to the morphing animation. The name + is usually same as the name of the parent entity of the target QGeometryRenderer, but + does not have to be. +*/ + +/*! + \qmlproperty list<real> VertexBlendAnimation::targetPositions + Holds the position values of the morph target. Each position in the list specifies the position + of the corresponding morph target with the same index. The values must be in an ascending order. + Values can be positive or negative and do not have any predefined unit. +*/ +/*! + \qmlproperty real VertexBlendAnimation::interpolator + Holds the interpolator between base and target attributes. + \readonly +*/ +/*! + \qmlproperty GeometryRenderer VertexBlendAnimation::target + Holds the target GeometryRenderer the morphing animation is applied to. +*/ +/*! + \qmlproperty string VertexBlendAnimation::targetName + Holds the name of the target geometry. This is a convenience property making it + easier to match the target geometry to the morphing animation. The name + is usually same as the name of the parent entity of the target GeometryRenderer, but + does not have to be. +*/ +/*! + \qmlproperty list<MorphTarget> VertexBlendAnimation::morphTargets + Holds the list of \l {MorphTarget}{morph targets} added to the animation. +*/ + +QVertexBlendAnimationPrivate::QVertexBlendAnimationPrivate() + : QAbstractAnimationPrivate(QAbstractAnimation::VertexBlendAnimation) + , m_interpolator(0.0f) + , m_target(nullptr) + , m_currentBase(nullptr) + , m_currentTarget(nullptr) +{ + +} + +void QVertexBlendAnimationPrivate::getAttributesInPosition(float position, int *target0, + int *target1, float *interpolator) +{ + if (position < m_targetPositions.first()) { + *target0 = 0; + *target1 = qMin(1, m_targetPositions.size()); + *interpolator = 0.0f; + } else if (position > m_targetPositions.last()) { + *target0 = qMax(m_targetPositions.size() - 2, 0); + *target1 = qMax(m_targetPositions.size() - 1, 0); + *interpolator = 1.0f; + } else { + for (int i = 0; i < m_targetPositions.size() - 1; i++) { + if (position >= m_targetPositions[i] && position < m_targetPositions[i + 1]) { + *target0 = i; + *target1 = i + 1; + float a = (position - m_targetPositions[i]) + / (m_targetPositions[i + 1] - m_targetPositions[i]); + *interpolator = a; + } + } + } +} + +void QVertexBlendAnimationPrivate::updateAnimation(float position) +{ + Q_Q(QVertexBlendAnimation); + if (!m_target || !m_target->geometry()) + return; + + Qt3DAnimation::QMorphTarget *base; + Qt3DAnimation::QMorphTarget *target; + int target0, target1; + float interpolator; + getAttributesInPosition(position, &target0, &target1, &interpolator); + + base = m_morphTargets.at(target0); + target = m_morphTargets.at(target1); + + Qt3DRender::QGeometry *geometry = m_target->geometry(); + + // remove attributes from previous frame + if ((m_currentBase && (base != m_currentBase)) + || (m_currentTarget && (target != m_currentTarget))) { + const QVector<Qt3DRender::QAttribute *> baseAttributes = m_currentBase->attributeList(); + const QVector<Qt3DRender::QAttribute *> targetAttributes = m_currentTarget->attributeList(); + for (int i = 0; i < baseAttributes.size(); ++i) { + geometry->removeAttribute(baseAttributes.at(i)); + geometry->removeAttribute(targetAttributes.at(i)); + } + } + + const QVector<Qt3DRender::QAttribute *> baseAttributes = base->attributeList(); + const QVector<Qt3DRender::QAttribute *> targetAttributes = target->attributeList(); + const QStringList attributeNames = base->attributeNames(); + + // add attributes from current frame to the geometry + if (base != m_currentBase || target != m_currentTarget) { + for (int i = 0; i < baseAttributes.size(); ++i) { + const QString baseName = attributeNames.at(i); + QString targetName = baseName; + targetName.append(QLatin1String("Target")); + + baseAttributes[i]->setName(baseName); + geometry->addAttribute(baseAttributes.at(i)); + targetAttributes[i]->setName(targetName); + geometry->addAttribute(targetAttributes.at(i)); + } + } + m_currentBase = base; + m_currentTarget = target; + + if (!qFuzzyCompare(interpolator, m_interpolator)) { + m_interpolator = interpolator; + emit q->interpolatorChanged(interpolator); + } +} + +/*! + Construct a new QVertexBlendAnimation with \a parent. + */ +QVertexBlendAnimation::QVertexBlendAnimation(QObject *parent) + : QAbstractAnimation(*new QVertexBlendAnimationPrivate, parent) +{ + Q_D(QVertexBlendAnimation); + d->m_positionConnection = QObject::connect(this, &QAbstractAnimation::positionChanged, + this, &QVertexBlendAnimation::updateAnimation); +} + +QVector<float> QVertexBlendAnimation::targetPositions() const +{ + Q_D(const QVertexBlendAnimation); + return d->m_targetPositions; +} + +float QVertexBlendAnimation::interpolator() const +{ + Q_D(const QVertexBlendAnimation); + return d->m_interpolator; +} + +Qt3DRender::QGeometryRenderer *QVertexBlendAnimation::target() const +{ + Q_D(const QVertexBlendAnimation); + return d->m_target; +} + +QString QVertexBlendAnimation::targetName() const +{ + Q_D(const QVertexBlendAnimation); + return d->m_targetName; +} + +/*! + Set morph \a targets to animation. Old targets are cleared. +*/ +void QVertexBlendAnimation::setMorphTargets(const QVector<Qt3DAnimation::QMorphTarget *> &targets) +{ + Q_D(QVertexBlendAnimation); + d->m_morphTargets = targets; +} + +/*! + Add new morph \a target at the end of the animation. +*/ +void QVertexBlendAnimation::addMorphTarget(Qt3DAnimation::QMorphTarget *target) +{ + Q_D(QVertexBlendAnimation); + if (!d->m_morphTargets.contains(target)) + d->m_morphTargets.push_back(target); +} + +/*! + Remove morph \a target from the animation. +*/ +void QVertexBlendAnimation::removeMorphTarget(Qt3DAnimation::QMorphTarget *target) +{ + Q_D(QVertexBlendAnimation); + d->m_morphTargets.removeAll(target); +} + +void QVertexBlendAnimation::setTargetPositions(const QVector<float> &targetPositions) +{ + Q_D(QVertexBlendAnimation); + if (d->m_targetPositions == targetPositions) + return; + d->m_targetPositions = targetPositions; + emit targetPositionsChanged(targetPositions); + setDuration(d->m_targetPositions.last()); +} + +void QVertexBlendAnimation::setTarget(Qt3DRender::QGeometryRenderer *target) +{ + Q_D(QVertexBlendAnimation); + if (d->m_target != target) { + d->m_target = target; + emit targetChanged(target); + } +} + +/*! + Return morph target list. +*/ +QVector<Qt3DAnimation::QMorphTarget *> QVertexBlendAnimation::morphTargetList() +{ + Q_D(QVertexBlendAnimation); + return d->m_morphTargets; +} + +void QVertexBlendAnimation::setTargetName(const QString name) +{ + Q_D(QVertexBlendAnimation); + if (d->m_targetName != name) { + d->m_targetName = name; + emit targetNameChanged(name); + } +} + +void QVertexBlendAnimation::updateAnimation(float position) +{ + Q_D(QVertexBlendAnimation); + d->updateAnimation(position); +} + +} // Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qvertexblendanimation.h b/src/animation/frontend/qvertexblendanimation.h new file mode 100644 index 000000000..a7da2bcda --- /dev/null +++ b/src/animation/frontend/qvertexblendanimation.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QVERTEXBLENDANIMATION_H +#define QT3DANIMATION_QVERTEXBLENDANIMATION_H + +#include <Qt3DRender/qgeometryrenderer.h> +#include <Qt3DAnimation/qabstractanimation.h> +#include <Qt3DAnimation/qmorphtarget.h> + +#include <Qt3DAnimation/qt3danimation_global.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QVertexBlendAnimationPrivate; + +class QT3DANIMATIONSHARED_EXPORT QVertexBlendAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(QVector<float> targetPositions READ targetPositions WRITE setTargetPositions NOTIFY targetPositionsChanged) + Q_PROPERTY(float interpolator READ interpolator NOTIFY interpolatorChanged) + Q_PROPERTY(Qt3DRender::QGeometryRenderer *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString targetName READ targetName WRITE setTargetName NOTIFY targetNameChanged) + +public: + explicit QVertexBlendAnimation(QObject *parent = nullptr); + + QVector<float> targetPositions() const; + float interpolator() const; + Qt3DRender::QGeometryRenderer *target() const; + QString targetName() const; + + void setMorphTargets(const QVector<Qt3DAnimation::QMorphTarget *> &targets); + void addMorphTarget(Qt3DAnimation::QMorphTarget *target); + void removeMorphTarget(Qt3DAnimation::QMorphTarget *target); + + QVector<Qt3DAnimation::QMorphTarget *> morphTargetList(); + +public Q_SLOTS: + void setTargetPositions(const QVector<float> &targetPositions); + void setTarget(Qt3DRender::QGeometryRenderer *target); + void setTargetName(const QString name); + +Q_SIGNALS: + void targetPositionsChanged(const QVector<float> &targetPositions); + void interpolatorChanged(float interpolator); + void targetChanged(Qt3DRender::QGeometryRenderer *target); + void targetNameChanged(const QString &name); + +private: + + void updateAnimation(float position); + + Q_DECLARE_PRIVATE(QVertexBlendAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QVERTEXBLENDANIMATION_H diff --git a/src/animation/frontend/qvertexblendanimation_p.h b/src/animation/frontend/qvertexblendanimation_p.h new file mode 100644 index 000000000..8f2609fc8 --- /dev/null +++ b/src/animation/frontend/qvertexblendanimation_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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_QVERTEXBLENDANIMATION_P_H +#define QT3DANIMATION_QVERTEXBLENDANIMATION_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/qvertexblendanimation.h> + +#include <private/qobject_p.h> +#include <private/qabstractanimation_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QVertexBlendAnimationPrivate : public QAbstractAnimationPrivate +{ +public: + QVertexBlendAnimationPrivate(); + + void getAttributesInPosition(float position, int *target0, int *target1, float *interpolator); + void updateAnimation(float position); + + QVector<float> m_targetPositions; + QVector<Qt3DAnimation::QMorphTarget *> m_morphTargets; + float m_interpolator; + Qt3DRender::QGeometryRenderer *m_target; + QString m_targetName; + QMorphTarget *m_currentBase; + QMorphTarget *m_currentTarget; + + QMetaObject::Connection m_positionConnection; + + Q_DECLARE_PUBLIC(QVertexBlendAnimation) +}; + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QVertexBlendANIMATION_P_H diff --git a/src/animation/job_common_p.h b/src/animation/job_common_p.h new file mode 100644 index 000000000..882246a42 --- /dev/null +++ b/src/animation/job_common_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DANIMATION_ANIMATION_JOB_COMMON_P_H +#define QT3DANIMATION_ANIMATION_JOB_COMMON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/private/qaspectjob_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +namespace Animation { + +namespace JobTypes { + +enum JobType { + BuildBlendTree = 8192, + EvaluateBlendClipAnimator, + EvaluateClipAnimator, + LoadAnimationClip, + FindRunningClipAnimator +}; + +} // JobTypes + +} // Animation + +} // Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_ANIMATION_JOB_COMMON_P_H |