diff options
22 files changed, 588 insertions, 69 deletions
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp index 2bfca2a33..e33a31b7b 100644 --- a/src/animation/backend/animationutils.cpp +++ b/src/animation/backend/animationutils.cpp @@ -225,81 +225,90 @@ ClipResults evaluateClipAtPhase(AnimationClip *clip, float phase) return evaluateClipAtLocalTime(clip, localTime); } -QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId, - const QVector<MappingData> &mappingDataVec, - const QVector<float> &channelResults, - bool finalFrame) +QVariant buildPropertyValue(const MappingData &mappingData, const QVector<float> &channelResults) { - QVector<Qt3DCore::QSceneChangePtr> changes; - // Iterate over the mappings - for (const MappingData &mappingData : mappingDataVec) { - // Construct a property update change, set target, property and delivery options - auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(mappingData.targetId); - e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll); - e->setPropertyName(mappingData.propertyName); + QVariant v; - // Handle intermediate updates vs final flag properly - Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isIntermediate = !finalFrame; + switch (mappingData.type) { + case QMetaType::Float: + case QVariant::Double: { + v = QVariant::fromValue(channelResults[mappingData.channelIndices[0]]); + break; + } - // Build the new value from the channel/fcurve evaluation results - QVariant v; - switch (mappingData.type) { - case QMetaType::Float: - case QVariant::Double: { - v = QVariant::fromValue(channelResults[mappingData.channelIndices[0]]); - break; - } + case QVariant::Vector2D: { + const QVector2D vector(channelResults[mappingData.channelIndices[0]], + channelResults[mappingData.channelIndices[1]]); + v = QVariant::fromValue(vector); + break; + } - case QVariant::Vector2D: { - const QVector2D vector(channelResults[mappingData.channelIndices[0]], - channelResults[mappingData.channelIndices[1]]); - v = QVariant::fromValue(vector); - break; - } + case QVariant::Vector3D: { + const QVector3D vector(channelResults[mappingData.channelIndices[0]], + channelResults[mappingData.channelIndices[1]], + channelResults[mappingData.channelIndices[2]]); + v = QVariant::fromValue(vector); + break; + } - case QVariant::Vector3D: { - const QVector3D vector(channelResults[mappingData.channelIndices[0]], - channelResults[mappingData.channelIndices[1]], - channelResults[mappingData.channelIndices[2]]); - v = QVariant::fromValue(vector); - break; - } + case QVariant::Vector4D: { + const QVector4D vector(channelResults[mappingData.channelIndices[0]], + channelResults[mappingData.channelIndices[1]], + channelResults[mappingData.channelIndices[2]], + channelResults[mappingData.channelIndices[3]]); + v = QVariant::fromValue(vector); + break; + } - case QVariant::Vector4D: { - const QVector4D vector(channelResults[mappingData.channelIndices[0]], - channelResults[mappingData.channelIndices[1]], - channelResults[mappingData.channelIndices[2]], - channelResults[mappingData.channelIndices[3]]); - v = QVariant::fromValue(vector); - break; - } + case QVariant::Quaternion: { + QQuaternion q(channelResults[mappingData.channelIndices[0]], + channelResults[mappingData.channelIndices[1]], + channelResults[mappingData.channelIndices[2]], + channelResults[mappingData.channelIndices[3]]); + q.normalize(); + v = QVariant::fromValue(q); + break; + } - case QVariant::Quaternion: { - QQuaternion q(channelResults[mappingData.channelIndices[0]], - channelResults[mappingData.channelIndices[1]], - channelResults[mappingData.channelIndices[2]], - channelResults[mappingData.channelIndices[3]]); - q.normalize(); - v = QVariant::fromValue(q); - break; - } + case QVariant::Color: { + const QColor color = QColor::fromRgbF(channelResults[mappingData.channelIndices[0]], + channelResults[mappingData.channelIndices[1]], + channelResults[mappingData.channelIndices[2]]); + v = QVariant::fromValue(color); + break; + } - case QVariant::Color: { - const QColor color = QColor::fromRgbF(channelResults[mappingData.channelIndices[0]], - channelResults[mappingData.channelIndices[1]], - channelResults[mappingData.channelIndices[2]]); - v = QVariant::fromValue(color); - break; - } + default: + qWarning() << "Unhandled animation type" << mappingData.type; + break; + } + + return v; +} - default: - qWarning() << "Unhandled animation type"; +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 + for (const MappingData &mappingData : mappingDataVec) { + if (!mappingData.propertyName) continue; + // Build the new value from the channel/fcurve evaluation results + const QVariant v = buildPropertyValue(mappingData, channelResults); + if (v.isValid()) { + // Construct a property update change, set target, property and delivery options + auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(mappingData.targetId); + e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll); + e->setPropertyName(mappingData.propertyName); + // Handle intermediate updates vs final flag properly + Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isIntermediate = !finalFrame; + // Assign new value and send + e->setValue(v); + changes.push_back(e); } - - // Assign new value and send - e->setValue(v); - changes.push_back(e); } @@ -314,6 +323,25 @@ QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId anim return changes; } +QVector<AnimationCallbackAndValue> prepareCallbacks(const QVector<MappingData> &mappingDataVec, + const QVector<float> &channelResults) +{ + QVector<AnimationCallbackAndValue> callbacks; + for (const MappingData &mappingData : mappingDataVec) { + if (!mappingData.callback) + continue; + const QVariant v = buildPropertyValue(mappingData, channelResults); + if (v.isValid()) { + AnimationCallbackAndValue callback; + callback.callback = mappingData.callback; + callback.flags = mappingData.callbackFlags; + callback.value = v; + callbacks.append(callback); + } + } + return callbacks; +} + //TODO: Remove this and use new implementation below for both the unblended // and blended animation cases. QVector<MappingData> buildPropertyMappings(Handler *handler, @@ -336,10 +364,13 @@ QVector<MappingData> buildPropertyMappings(Handler *handler, mappingData.targetId = mapping->targetId(); mappingData.propertyName = mapping->propertyName(); mappingData.type = mapping->type(); + mappingData.callback = mapping->callback(); + mappingData.callbackFlags = mapping->callbackFlags(); if (mappingData.type == static_cast<int>(QVariant::Invalid)) { qWarning() << "Unknown type for node id =" << mappingData.targetId - << "and property =" << mapping->property(); + << "and property =" << mapping->property() + << "and callback =" << mapping->callback(); continue; } @@ -384,10 +415,13 @@ QVector<MappingData> buildPropertyMappings(const QVector<ChannelMapping*> &chann mappingData.targetId = mapping->targetId(); mappingData.propertyName = mapping->propertyName(); mappingData.type = mapping->type(); + mappingData.callback = mapping->callback(); + mappingData.callbackFlags = mapping->callbackFlags(); if (mappingData.type == static_cast<int>(QVariant::Invalid)) { qWarning() << "Unknown type for node id =" << mappingData.targetId - << "and property =" << mapping->property(); + << "and property =" << mapping->property() + << "and callback =" << mapping->callback(); continue; } diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h index f8fcbafa7..6f00fc632 100644 --- a/src/animation/backend/animationutils_p.h +++ b/src/animation/backend/animationutils_p.h @@ -49,12 +49,14 @@ // #include <Qt3DAnimation/private/qt3danimation_global_p.h> +#include <Qt3DAnimation/qanimationcallback.h> #include <Qt3DCore/qnodeid.h> #include <Qt3DCore/qscenechange.h> QT_BEGIN_NAMESPACE namespace Qt3DAnimation { +class QAnimationCallback; namespace Animation { struct Channel; @@ -70,6 +72,8 @@ struct MappingData { Qt3DCore::QNodeId targetId; const char *propertyName; + QAnimationCallback *callback; + QAnimationCallback::Flags callbackFlags; int type; ComponentIndices channelIndices; }; @@ -102,6 +106,13 @@ struct ChannelNameAndType } }; +struct AnimationCallbackAndValue +{ + QAnimationCallback *callback; + QAnimationCallback::Flags flags; + QVariant value; +}; + template<typename Animator> AnimatorEvaluationData evaluationDataForAnimator(Animator animator, qint64 globalTime) { @@ -152,11 +163,15 @@ ClipResults evaluateClipAtPhase(AnimationClip *clip, Q_AUTOTEST_EXPORT QVector<Qt3DCore::QSceneChangePtr> preparePropertyChanges(Qt3DCore::QNodeId animatorId, - const QVector<MappingData> &mappingData, + const QVector<MappingData> &mappingDataVec, const QVector<float> &channelResults, bool finalFrame); Q_AUTOTEST_EXPORT +QVector<AnimationCallbackAndValue> prepareCallbacks(const QVector<MappingData> &mappingDataVec, + const QVector<float> &channelResults); + +Q_AUTOTEST_EXPORT QVector<MappingData> buildPropertyMappings(Handler *handler, const AnimationClip *clip, const ChannelMapper *mapper); diff --git a/src/animation/backend/blendedclipanimator.cpp b/src/animation/backend/blendedclipanimator.cpp index 08789a5f9..46b3b87fa 100644 --- a/src/animation/backend/blendedclipanimator.cpp +++ b/src/animation/backend/blendedclipanimator.cpp @@ -37,6 +37,7 @@ #include "blendedclipanimator_p.h" #include <Qt3DAnimation/qblendedclipanimator.h> #include <Qt3DAnimation/private/qblendedclipanimator_p.h> +#include <Qt3DAnimation/private/qanimationcallbacktrigger_p.h> #include <Qt3DCore/qpropertyupdatedchange.h> QT_BEGIN_NAMESPACE @@ -100,6 +101,21 @@ void BlendedClipAnimator::sendPropertyChanges(const QVector<Qt3DCore::QSceneChan notifyObservers(change); } +void BlendedClipAnimator::sendCallbacks(const QVector<AnimationCallbackAndValue> &callbacks) +{ + for (const AnimationCallbackAndValue &callback : callbacks) { + if (callback.flags.testFlag(QAnimationCallback::OnThreadPool)) { + callback.callback->valueChanged(callback.value); + } else { + auto e = QAnimationCallbackTriggerPtr::create(peerId()); + e->setCallback(callback.callback); + e->setValue(callback.value); + e->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes); + notifyObservers(e); + } + } +} + Qt3DCore::QNodeId BlendedClipAnimator::blendTreeRootId() const { return m_blendTreeRootId; diff --git a/src/animation/backend/blendedclipanimator_p.h b/src/animation/backend/blendedclipanimator_p.h index 4421cb43a..e6311bc59 100644 --- a/src/animation/backend/blendedclipanimator_p.h +++ b/src/animation/backend/blendedclipanimator_p.h @@ -90,6 +90,7 @@ public: QVector<MappingData> mappingData() const { return m_mappingData; } void sendPropertyChanges(const QVector<Qt3DCore::QSceneChangePtr> &changes); + void sendCallbacks(const QVector<AnimationCallbackAndValue> &callbacks); private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; diff --git a/src/animation/backend/channelmapping.cpp b/src/animation/backend/channelmapping.cpp index 0fd0f2714..82e6a95a8 100644 --- a/src/animation/backend/channelmapping.cpp +++ b/src/animation/backend/channelmapping.cpp @@ -52,6 +52,8 @@ ChannelMapping::ChannelMapping() , m_property() , m_type(static_cast<int>(QVariant::Invalid)) , m_propertyName(nullptr) + , m_callback(nullptr) + , m_callbackFlags(0) { } @@ -64,6 +66,8 @@ void ChannelMapping::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePt m_property = data.property; m_type = data.type; m_propertyName = data.propertyName; + m_callback = data.callback; + m_callbackFlags = data.callbackFlags; } void ChannelMapping::cleanup() @@ -74,6 +78,8 @@ void ChannelMapping::cleanup() m_property.clear(); m_type = static_cast<int>(QVariant::Invalid); m_propertyName = nullptr; + m_callback = nullptr; + m_callbackFlags = 0; } void ChannelMapping::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) @@ -91,6 +97,10 @@ void ChannelMapping::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) m_type = change->value().toInt(); else if (change->propertyName() == QByteArrayLiteral("propertyName")) m_propertyName = static_cast<const char *>(const_cast<const void *>(change->value().value<void *>())); + else if (change->propertyName() == QByteArrayLiteral("callback")) + m_callback = static_cast<QAnimationCallback *>(change->value().value<void *>()); + else if (change->propertyName() == QByteArrayLiteral("callbackFlags")) + m_callbackFlags = QAnimationCallback::Flags(change->value().toInt()); break; } diff --git a/src/animation/backend/channelmapping_p.h b/src/animation/backend/channelmapping_p.h index 93cf5efed..f2dde0c74 100644 --- a/src/animation/backend/channelmapping_p.h +++ b/src/animation/backend/channelmapping_p.h @@ -50,6 +50,7 @@ #include <Qt3DAnimation/private/backendnode_p.h> #include <Qt3DAnimation/private/fcurve_p.h> +#include <Qt3DAnimation/qanimationcallback.h> #include <Qt3DCore/qnodeid.h> #include <QtCore/QMetaProperty> @@ -57,6 +58,7 @@ QT_BEGIN_NAMESPACE namespace Qt3DAnimation { + namespace Animation { class Handler; @@ -85,6 +87,12 @@ public: void setPropertyName(const char *propertyName) { m_propertyName = propertyName; } const char *propertyName() const { return m_propertyName; } + void setCallback(QAnimationCallback *callback) { m_callback = callback; } + QAnimationCallback *callback() const { return m_callback; } + + void setCallbackFlags(QAnimationCallback::Flags flags) { m_callbackFlags = flags; } + QAnimationCallback::Flags callbackFlags() const { return m_callbackFlags; } + private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; @@ -93,6 +101,8 @@ private: QString m_property; int m_type; const char *m_propertyName; + QAnimationCallback *m_callback; + QAnimationCallback::Flags m_callbackFlags; }; } // namespace Animation diff --git a/src/animation/backend/clipanimator.cpp b/src/animation/backend/clipanimator.cpp index 65fd0f57f..ea31698d0 100644 --- a/src/animation/backend/clipanimator.cpp +++ b/src/animation/backend/clipanimator.cpp @@ -40,6 +40,7 @@ #include <Qt3DAnimation/private/animationclip_p.h> #include <Qt3DAnimation/private/managers_p.h> #include <Qt3DAnimation/private/animationlogging_p.h> +#include <Qt3DAnimation/private/qanimationcallbacktrigger_p.h> #include <Qt3DCore/qpropertyupdatedchange.h> #include <Qt3DCore/private/qpropertyupdatedchangebase_p.h> @@ -129,6 +130,21 @@ void ClipAnimator::sendPropertyChanges(const QVector<Qt3DCore::QSceneChangePtr> notifyObservers(change); } +void ClipAnimator::sendCallbacks(const QVector<AnimationCallbackAndValue> &callbacks) +{ + for (const AnimationCallbackAndValue &callback : callbacks) { + if (callback.flags.testFlag(QAnimationCallback::OnThreadPool)) { + callback.callback->valueChanged(callback.value); + } else { + auto e = QAnimationCallbackTriggerPtr::create(peerId()); + e->setCallback(callback.callback); + e->setValue(callback.value); + e->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes); + notifyObservers(e); + } + } +} + } // namespace Animation } // namespace Qt3DAnimation diff --git a/src/animation/backend/clipanimator_p.h b/src/animation/backend/clipanimator_p.h index da9109cfb..f0f631a5c 100644 --- a/src/animation/backend/clipanimator_p.h +++ b/src/animation/backend/clipanimator_p.h @@ -91,6 +91,7 @@ public: void setCurrentLoop(int currentLoop) { m_currentLoop = currentLoop; } void sendPropertyChanges(const QVector<Qt3DCore::QSceneChangePtr> &changes); + void sendCallbacks(const QVector<AnimationCallbackAndValue> &callbacks); private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; diff --git a/src/animation/backend/evaluateblendclipanimatorjob.cpp b/src/animation/backend/evaluateblendclipanimatorjob.cpp index 76d24a4d3..e0f52765f 100644 --- a/src/animation/backend/evaluateblendclipanimatorjob.cpp +++ b/src/animation/backend/evaluateblendclipanimatorjob.cpp @@ -115,6 +115,10 @@ void EvaluateBlendClipAnimatorJob::run() finalFrame); // Send the property changes blendedClipAnimator->sendPropertyChanges(changes); + + // Trigger callbacks either on this thread or by notifying the gui thread. + const QVector<AnimationCallbackAndValue> callbacks = prepareCallbacks(mappingData, blendedResults); + blendedClipAnimator->sendCallbacks(callbacks); } } // Animation diff --git a/src/animation/backend/evaluateclipanimatorjob.cpp b/src/animation/backend/evaluateclipanimatorjob.cpp index e89405d63..e9f3d1da8 100644 --- a/src/animation/backend/evaluateclipanimatorjob.cpp +++ b/src/animation/backend/evaluateclipanimatorjob.cpp @@ -84,6 +84,9 @@ void EvaluateClipAnimatorJob::run() // Send the property changes clipAnimator->sendPropertyChanges(changes); + // Trigger callbacks either on this thread or by notifying the gui thread. + const QVector<AnimationCallbackAndValue> callbacks = prepareCallbacks(clipAnimator->mappingData(), channelResults); + clipAnimator->sendCallbacks(callbacks); } } // namespace Animation diff --git a/src/animation/frontend/frontend.pri b/src/animation/frontend/frontend.pri index 9ea438395..7f397462b 100644 --- a/src/animation/frontend/frontend.pri +++ b/src/animation/frontend/frontend.pri @@ -44,7 +44,9 @@ HEADERS += \ $$PWD/qchannelcomponent.h \ $$PWD/qkeyframe.h \ $$PWD/qanimationclip.h \ - $$PWD/qanimationclip_p.h + $$PWD/qanimationclip_p.h \ + $$PWD/qanimationcallback.h \ + $$PWD/qanimationcallbacktrigger_p.h SOURCES += \ $$PWD/qanimationaspect.cpp \ @@ -71,6 +73,7 @@ SOURCES += \ $$PWD/qchannel.cpp \ $$PWD/qchannelcomponent.cpp \ $$PWD/qkeyframe.cpp \ - $$PWD/qanimationclip.cpp + $$PWD/qanimationclip.cpp \ + $$PWD/qanimationcallbacktrigger.cpp INCLUDEPATH += $$PWD diff --git a/src/animation/frontend/qanimationcallback.h b/src/animation/frontend/qanimationcallback.h new file mode 100644 index 000000000..63e094918 --- /dev/null +++ b/src/animation/frontend/qanimationcallback.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QT3DANIMATION_QANIMATIONCALLBACK_H +#define QT3DANIMATION_QANIMATIONCALLBACK_H + +#include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DCore/qnode.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class QT3DANIMATIONSHARED_EXPORT QAnimationCallback +{ +public: + enum Flag { + OnThreadPool = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + virtual ~QAnimationCallback() { } + + virtual void valueChanged(const QVariant &value) = 0; +}; + +} // namespace Qt3DAnimation + +Q_DECLARE_OPERATORS_FOR_FLAGS(Qt3DAnimation::QAnimationCallback::Flags) + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QANIMATIONCALLBACK_H diff --git a/src/animation/frontend/qanimationcallbacktrigger.cpp b/src/animation/frontend/qanimationcallbacktrigger.cpp new file mode 100644 index 000000000..83fccb5a4 --- /dev/null +++ b/src/animation/frontend/qanimationcallbacktrigger.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 "qanimationcallbacktrigger_p.h" + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +QAnimationCallbackTrigger::QAnimationCallbackTrigger(Qt3DCore::QNodeId subjectId) + : Qt3DCore::QSceneChange(Qt3DCore::CallbackTriggered, subjectId), + m_callback(nullptr) +{ +} + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE diff --git a/src/animation/frontend/qanimationcallbacktrigger_p.h b/src/animation/frontend/qanimationcallbacktrigger_p.h new file mode 100644 index 000000000..9a05d421f --- /dev/null +++ b/src/animation/frontend/qanimationcallbacktrigger_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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_QANIMATIONCALLBACKTRIGGER_P_H +#define QT3DANIMATION_QANIMATIONCALLBACKTRIGGER_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/qt3danimation_global.h> +#include <Qt3DAnimation/qanimationcallback.h> +#include <Qt3DCore/qscenechange.h> +#include <QtCore/qsharedpointer.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DAnimation { + +class Q_AUTOTEST_EXPORT QAnimationCallbackTrigger : public Qt3DCore::QSceneChange +{ +public: + QAnimationCallbackTrigger(Qt3DCore::QNodeId subjectId); + + void setCallback(QAnimationCallback *callback) { m_callback = callback; } + QAnimationCallback *callback() const { return m_callback; } + + void setValue(const QVariant &value) { m_value = value; } + QVariant value() const { return m_value; } + +private: + QAnimationCallback *m_callback; + QVariant m_value; +}; + +typedef QSharedPointer<QAnimationCallbackTrigger> QAnimationCallbackTriggerPtr; + +} // namespace Qt3DAnimation + +QT_END_NAMESPACE + +#endif // QT3DANIMATION_QANIMATIONCALLBACKTRIGGER_P_H diff --git a/src/animation/frontend/qchannelmapping.cpp b/src/animation/frontend/qchannelmapping.cpp index faa77f5db..ec18332c7 100644 --- a/src/animation/frontend/qchannelmapping.cpp +++ b/src/animation/frontend/qchannelmapping.cpp @@ -53,6 +53,8 @@ QChannelMappingPrivate::QChannelMappingPrivate() , m_property() , m_propertyName(nullptr) , m_type(static_cast<int>(QVariant::Invalid)) + , m_callback(nullptr) + , m_callbackFlags(0) { } @@ -148,6 +150,12 @@ QString QChannelMapping::property() const return d->m_property; } +QAnimationCallback *QChannelMapping::callback() const +{ + Q_D(const QChannelMapping); + return d->m_callback; +} + void QChannelMapping::setChannelName(const QString &channelName) { Q_D(QChannelMapping); @@ -190,6 +198,56 @@ void QChannelMapping::setProperty(const QString &property) d->updatePropertyNameAndType(); } +/*! + Associates a \a callback object with this channel mapping. + + Such mappings do not have to have a target object and property name. When + the \a callback object is set, every change in the animated value will lead + to invoking the callback's + \l{QAnimationCallback::onValueChanged()}{onValueChanged()} function either + on the gui/main thread, or directly on one of the thread pool's worker + thread. This is controlled by \a flags. + + \a type specifies the type (for example, QVariant::Vector3D, + QVariant::Color, or QMetaType::Float) of the animated value. When animating + node properties this does not need to be provided separately, however it + becomes important to supply this when there is only a callback. + + \note A mapping can be associated both with a node property and a + callback. It is important however that \a type matches the type of the + property in this case. Note also that for properties of type QVariant (for + example, QParameter::value), the \a type is the type of the value stored in + the QVariant. + + \note The \a callback pointer is expected to stay valid while any + associated animators are running. + */ +void QChannelMapping::setCallback(int type, QAnimationCallback *callback, QAnimationCallback::Flags flags) +{ + Q_D(QChannelMapping); + if (d->m_type != type) { + d->m_type = type; + auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(id()); + e->setPropertyName("type"); + e->setValue(QVariant(d->m_type)); + notifyObservers(e); + } + if (d->m_callback != callback) { + d->m_callback = callback; + auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(id()); + e->setPropertyName("callback"); + e->setValue(QVariant::fromValue(static_cast<void *>(d->m_callback))); + notifyObservers(e); + } + if (d->m_callbackFlags != flags) { + d->m_callbackFlags = flags; + auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(id()); + e->setPropertyName("callbackFlags"); + e->setValue(QVariant::fromValue(int(d->m_callbackFlags))); + notifyObservers(e); + } +} + Qt3DCore::QNodeCreatedChangeBasePtr QChannelMapping::createNodeCreationChange() const { auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QChannelMappingData>::create(this); @@ -200,6 +258,8 @@ Qt3DCore::QNodeCreatedChangeBasePtr QChannelMapping::createNodeCreationChange() data.property = d->m_property; data.type = d->m_type; data.propertyName = d->m_propertyName; + data.callback = d->m_callback; + data.callbackFlags = d->m_callbackFlags; return creationChange; } diff --git a/src/animation/frontend/qchannelmapping.h b/src/animation/frontend/qchannelmapping.h index d768298fe..d14015b9f 100644 --- a/src/animation/frontend/qchannelmapping.h +++ b/src/animation/frontend/qchannelmapping.h @@ -38,6 +38,7 @@ #define QT3DANIMATION_QCHANNELMAPPING_H #include <Qt3DAnimation/qt3danimation_global.h> +#include <Qt3DAnimation/qanimationcallback.h> #include <Qt3DCore/qnode.h> QT_BEGIN_NAMESPACE @@ -60,6 +61,9 @@ public: QString channelName() const; Qt3DCore::QNode *target() const; QString property() const; + QAnimationCallback *callback() const; + + void setCallback(int type, QAnimationCallback *callback, QAnimationCallback::Flags flags); public Q_SLOTS: void setChannelName(const QString &channelName); diff --git a/src/animation/frontend/qchannelmapping_p.h b/src/animation/frontend/qchannelmapping_p.h index 2c48b0485..95b300888 100644 --- a/src/animation/frontend/qchannelmapping_p.h +++ b/src/animation/frontend/qchannelmapping_p.h @@ -49,6 +49,7 @@ // #include <Qt3DCore/private/qnode_p.h> +#include <Qt3DAnimation/qanimationcallback.h> QT_BEGIN_NAMESPACE @@ -68,6 +69,8 @@ public: QString m_property; const char *m_propertyName; int m_type; + QAnimationCallback *m_callback; + QAnimationCallback::Flags m_callbackFlags; }; struct QChannelMappingData @@ -77,6 +80,8 @@ struct QChannelMappingData QString property; int type; const char *propertyName; + QAnimationCallback *callback; + QAnimationCallback::Flags callbackFlags; }; } // namespace Qt3DAnimation diff --git a/src/animation/frontend/qclipanimator.cpp b/src/animation/frontend/qclipanimator.cpp index eb6c80aee..da6534d16 100644 --- a/src/animation/frontend/qclipanimator.cpp +++ b/src/animation/frontend/qclipanimator.cpp @@ -41,6 +41,7 @@ #include "qclipanimator_p.h" #include <Qt3DAnimation/qabstractanimationclip.h> #include <Qt3DAnimation/qchannelmapper.h> +#include <Qt3DAnimation/private/qanimationcallbacktrigger_p.h> QT_BEGIN_NAMESPACE @@ -165,6 +166,16 @@ Qt3DCore::QNodeCreatedChangeBasePtr QClipAnimator::createNodeCreationChange() co return creationChange; } +/*! \internal */ +void QClipAnimator::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) +{ + if (change->type() == Qt3DCore::CallbackTriggered) { + QAnimationCallbackTriggerPtr callbackTrigger = qSharedPointerCast<Qt3DAnimation::QAnimationCallbackTrigger>(change); + if (callbackTrigger->callback()) + callbackTrigger->callback()->valueChanged(callbackTrigger->value()); + } +} + } // namespace Qt3DAnimation QT_END_NAMESPACE diff --git a/src/animation/frontend/qclipanimator.h b/src/animation/frontend/qclipanimator.h index 311ac4ab0..7b06f9878 100644 --- a/src/animation/frontend/qclipanimator.h +++ b/src/animation/frontend/qclipanimator.h @@ -70,6 +70,7 @@ Q_SIGNALS: protected: QClipAnimator(QClipAnimatorPrivate &dd, Qt3DCore::QNode *parent = nullptr); + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override; private: Q_DECLARE_PRIVATE(QClipAnimator) diff --git a/src/core/changes/qscenechange.cpp b/src/core/changes/qscenechange.cpp index df15e239d..03fe7e5d7 100644 --- a/src/core/changes/qscenechange.cpp +++ b/src/core/changes/qscenechange.cpp @@ -58,6 +58,7 @@ namespace Qt3DCore { * \value PropertyValueRemoved A QNode has been removed from the scene. * \value ComponentAdded A QComponent has been added to a QEntity. * \value ComponentRemoved A QComponent has been removed from a QEntity. + * \value CallbackTriggered A QNode triggered a callback. * \value AllChanges Allows an observer to monitor for any of the above changes. */ diff --git a/src/core/changes/qscenechange.h b/src/core/changes/qscenechange.h index 854bab051..7679fb030 100644 --- a/src/core/changes/qscenechange.h +++ b/src/core/changes/qscenechange.h @@ -57,6 +57,7 @@ enum ChangeFlag { ComponentAdded = 1 << 5, ComponentRemoved = 1 << 6, CommandRequested = 1 << 7, + CallbackTriggered = 1 << 8, AllChanges = 0xFFFFFFFF }; Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag) diff --git a/tests/auto/animation/animationutils/tst_animationutils.cpp b/tests/auto/animation/animationutils/tst_animationutils.cpp index 8d697abf2..153e3d29d 100644 --- a/tests/auto/animation/animationutils/tst_animationutils.cpp +++ b/tests/auto/animation/animationutils/tst_animationutils.cpp @@ -61,6 +61,7 @@ Q_DECLARE_METATYPE(ClipEvaluationData) Q_DECLARE_METATYPE(ClipAnimator *) Q_DECLARE_METATYPE(BlendedClipAnimator *) Q_DECLARE_METATYPE(QVector<ChannelNameAndType>) +Q_DECLARE_METATYPE(QVector<AnimationCallbackAndValue>) namespace { @@ -122,6 +123,12 @@ bool fuzzyCompare(float x1, float x2) } } +class DummyCallback : public Qt3DAnimation::QAnimationCallback +{ +public: + void valueChanged(const QVariant &) override { } +}; + } // anonymous @@ -1105,6 +1112,117 @@ private Q_SLOTS: } } + void checkPrepareCallbacks_data() + { + QTest::addColumn<QVector<MappingData>>("mappingData"); + QTest::addColumn<QVector<float>>("channelResults"); + QTest::addColumn<QVector<AnimationCallbackAndValue> >("expectedValues"); + + QVector<MappingData> mappingData; + QVector<float> channelResults; + QVector<AnimationCallbackAndValue> expectedValues; + + // vec3 + { + DummyCallback callback; // safe since the object is never used, just the address + MappingData mapping; + mapping.targetId = Qt3DCore::QNodeId::createId(); + mapping.propertyName = "translation"; + mapping.type = static_cast<int>(QVariant::Vector3D); + mapping.channelIndices = QVector<int>() << 0 << 1 << 2; + mapping.callback = &callback; + mapping.callbackFlags = 0; + mappingData.push_back(mapping); + channelResults = QVector<float>() << 1.0f << 2.0f << 3.0f; + + AnimationCallbackAndValue cbv; + cbv.callback = mapping.callback; + cbv.flags = mapping.callbackFlags; + cbv.value = QVariant::fromValue<QVector3D>(QVector3D(1.0f, 2.0f, 3.0f)); + expectedValues.push_back(cbv); + + QTest::newRow("vec3 translation, no flags") << mappingData << channelResults << expectedValues; + + mappingData.clear(); + channelResults.clear(); + expectedValues.clear(); + } + + // double + { + DummyCallback callback; + MappingData mapping; + mapping.targetId = Qt3DCore::QNodeId::createId(); + mapping.propertyName = "something"; + mapping.type = static_cast<int>(QVariant::Double); + mapping.channelIndices = QVector<int>() << 0; + mapping.callback = &callback; + mapping.callbackFlags = 0; + mappingData.push_back(mapping); + channelResults = QVector<float>() << 1.0f; + + AnimationCallbackAndValue cbv; + cbv.callback = mapping.callback; + cbv.flags = mapping.callbackFlags; + cbv.value = QVariant(double(1.0)); + expectedValues.push_back(cbv); + + QTest::newRow("double, no flags") << mappingData << channelResults << expectedValues; + + mappingData.clear(); + channelResults.clear(); + expectedValues.clear(); + } + + // float, set a flag + { + DummyCallback callback; + MappingData mapping; + mapping.targetId = Qt3DCore::QNodeId::createId(); + mapping.propertyName = "opacity"; + mapping.type = static_cast<int>(QMetaType::Float); + mapping.channelIndices = QVector<int>() << 0; + mapping.callback = &callback; + mapping.callbackFlags = Qt3DAnimation::QAnimationCallback::OnThreadPool; + mappingData.push_back(mapping); + channelResults = QVector<float>() << 0.5f; + + AnimationCallbackAndValue cbv; + cbv.callback = mapping.callback; + cbv.flags = mapping.callbackFlags; + cbv.value = QVariant(float(0.5f)); + expectedValues.push_back(cbv); + + QTest::newRow("float, OnThreadPool") << mappingData << channelResults << expectedValues; + + mappingData.clear(); + channelResults.clear(); + expectedValues.clear(); + } + } + + void checkPrepareCallbacks() + { + // GIVEN + QFETCH(QVector<MappingData>, mappingData); + QFETCH(QVector<float>, channelResults); + QFETCH(QVector<AnimationCallbackAndValue>, expectedValues); + + // WHEN + QVector<AnimationCallbackAndValue> callbacks = prepareCallbacks(mappingData, channelResults); + + // THEN + QCOMPARE(callbacks.size(), expectedValues.size()); + for (int i = 0; i < callbacks.size(); ++i) { + auto expected = expectedValues[i]; + auto actual = callbacks[i]; + + QCOMPARE(actual.callback, expected.callback); + QCOMPARE(actual.flags, expected.flags); + QCOMPARE(actual.value, expected.value); + } + } + void checkEvaluateClipAtLocalTime_data() { QTest::addColumn<Handler *>("handler"); |