diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2019-08-09 10:22:25 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2019-08-13 14:32:29 +0200 |
commit | 3cdd4e12eb25757bb5711977ecf7ede419c44dd1 (patch) | |
tree | a41b7df4003f4ed79c2afb838d275a4703778e78 | |
parent | bb5caa526c242e68ae9ba5cd64933f7f3dea1aef (diff) |
QTransform: add worldMatrix property
Will make it more convenient to retrieve the world transform of a given
QEntity as well as monitor it for changes without having to traverse the
parent hierarchy of QEntity/QTransform
[ChangeLog] Add worldMatrix property on QTransform
Change-Id: Ie9ffb70c03b365850ed08693df2746701ca9a1fb
Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r-- | src/core/transforms/qtransform.cpp | 66 | ||||
-rw-r--r-- | src/core/transforms/qtransform.h | 5 | ||||
-rw-r--r-- | src/core/transforms/qtransform_p.h | 2 | ||||
-rw-r--r-- | src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp | 1 | ||||
-rw-r--r-- | src/render/backend/transform.cpp | 11 | ||||
-rw-r--r-- | src/render/backend/transform_p.h | 4 | ||||
-rw-r--r-- | src/render/jobs/updateworldtransformjob.cpp | 9 | ||||
-rw-r--r-- | tests/auto/core/qtransform/tst_qtransform.cpp | 49 | ||||
-rw-r--r-- | tests/auto/render/transform/tst_transform.cpp | 26 |
9 files changed, 167 insertions, 6 deletions
diff --git a/src/core/transforms/qtransform.cpp b/src/core/transforms/qtransform.cpp index d0e2628f7..bedf108f2 100644 --- a/src/core/transforms/qtransform.cpp +++ b/src/core/transforms/qtransform.cpp @@ -135,6 +135,18 @@ QTransformPrivate::~QTransformPrivate() */ /*! + \qmlproperty matrix4x4 QTransform::worldMatrix + + Holds the world transformation matrix for the transform. This assumes the + Transform component is being referenced by an Entity. This makes it more + convenient to identify when an Entity part of a subtree has been + transformed in the world even though its local transformation might not + have changed. + + \since 5.14 + */ + +/*! \qmlmethod quaternion Transform::fromAxisAndAngle(vector3d axis, real angle) Creates a quaternion from \a axis and \a angle. Returns the resulting quaternion. @@ -222,6 +234,35 @@ QTransform::QTransform(QTransformPrivate &dd, QNode *parent) { } +/*! + \internal + */ +void QTransform::sceneChangeEvent(const QSceneChangePtr &change) +{ + switch (change->type()) { + case PropertyUpdated: { + Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change); + if (propertyChange->propertyName() == QByteArrayLiteral("worldMatrix")) { + const bool blocked = blockNotifications(true); + setWorldMatrix(propertyChange->value().value<QMatrix4x4>()); + blockNotifications(blocked); + } + break; + } + default: + break; + } +} + +void QTransform::setWorldMatrix(const QMatrix4x4 &worldMatrix) +{ + Q_D(QTransform); + if (d->m_worldMatrix == worldMatrix) + return; + d->m_worldMatrix = worldMatrix; + emit worldMatrixChanged(worldMatrix); +} + void QTransform::setMatrix(const QMatrix4x4 &m) { Q_D(QTransform); @@ -240,7 +281,6 @@ void QTransform::setMatrix(const QMatrix4x4 &m) emit scale3DChanged(s); emit rotationChanged(r); emit translationChanged(t); - const bool wasBlocked = blockNotifications(true); emit matrixChanged(); emit scaleChanged(d->m_scale.x()); @@ -330,6 +370,30 @@ QMatrix4x4 QTransform::matrix() const } /*! + \property QTransform::worldMatrix + + Holds the world transformation matrix for the transform. This assumes the + QTransform component is being referenced by a QEntity. This makes it more + convenient to identify when a QEntity part of a subtree has been + transformed in the world even though its local transformation might not + have changed. + + \since 5.14 + */ + +/*! + Returns the world transformation matrix associated to the QTransform when + referenced by a QEntity which may be part of a QEntity hierarchy. + + \since 5.14 + */ +QMatrix4x4 QTransform::worldMatrix() const +{ + Q_D(const QTransform); + return d->m_worldMatrix; +} + +/*! \property Qt3DCore::QTransform::rotationX Holds the x rotation of the transform as Euler angle. diff --git a/src/core/transforms/qtransform.h b/src/core/transforms/qtransform.h index a80385a1b..527760df7 100644 --- a/src/core/transforms/qtransform.h +++ b/src/core/transforms/qtransform.h @@ -61,6 +61,7 @@ class Q_3DCORESHARED_EXPORT QTransform : public QComponent Q_PROPERTY(float rotationX READ rotationX WRITE setRotationX NOTIFY rotationXChanged) Q_PROPERTY(float rotationY READ rotationY WRITE setRotationY NOTIFY rotationYChanged) Q_PROPERTY(float rotationZ READ rotationZ WRITE setRotationZ NOTIFY rotationZChanged) + Q_PROPERTY(QMatrix4x4 worldMatrix READ worldMatrix NOTIFY worldMatrixChanged REVISION 14) public: explicit QTransform(QNode *parent = nullptr); @@ -88,6 +89,7 @@ public: Q_INVOKABLE static QMatrix4x4 rotateFromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis); QMatrix4x4 matrix() const; + QMatrix4x4 worldMatrix() const; float rotationX() const; float rotationY() const; @@ -113,9 +115,12 @@ Q_SIGNALS: void rotationXChanged(float rotationX); void rotationYChanged(float rotationY); void rotationZChanged(float rotationZ); + void worldMatrixChanged(const QMatrix4x4 &worldMatrix); protected: explicit QTransform(QTransformPrivate &dd, QNode *parent = nullptr); + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override; + void setWorldMatrix(const QMatrix4x4 &worldMatrix); private: Q_DECLARE_PRIVATE(QTransform) diff --git a/src/core/transforms/qtransform_p.h b/src/core/transforms/qtransform_p.h index 89bc55491..d44e5e157 100644 --- a/src/core/transforms/qtransform_p.h +++ b/src/core/transforms/qtransform_p.h @@ -75,6 +75,8 @@ public: mutable QMatrix4x4 m_matrix; mutable bool m_matrixDirty; + + QMatrix4x4 m_worldMatrix; }; struct QTransformData diff --git a/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp b/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp index 0f8f7d11e..59df221ab 100644 --- a/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp +++ b/src/quick3d/imports/core/qt3dquick3dcoreplugin.cpp @@ -69,6 +69,7 @@ void Qt3DQuick3DCorePlugin::registerTypes(const char *uri) qmlRegisterType<Qt3DCore::Quick::Quick3DEntityLoader, 12>(uri, 2, 12, "EntityLoader"); qmlRegisterType<Qt3DCore::Quick::Quick3DNodeInstantiator>(uri, 2, 0, "NodeInstantiator"); qmlRegisterType<Qt3DCore::QTransform>(uri, 2, 0, "Transform"); + qmlRegisterType<Qt3DCore::QTransform, 14>(uri, 2, 14, "Transform"); qmlRegisterType<Qt3DCore::QArmature>(uri, 2, 10, "Armature"); qmlRegisterUncreatableType<Qt3DCore::QAbstractSkeleton>(uri, 2, 10, "AbstractSkeleton", QStringLiteral("AbstractSkeleton is an abstract base class")); qmlRegisterType<Qt3DCore::QSkeletonLoader>(uri, 2, 10, "SkeletonLoader"); diff --git a/src/render/backend/transform.cpp b/src/render/backend/transform.cpp index 20574d6f7..6d011cf81 100644 --- a/src/render/backend/transform.cpp +++ b/src/render/backend/transform.cpp @@ -52,7 +52,7 @@ namespace Qt3DRender { namespace Render { Transform::Transform() - : BackendNode() + : BackendNode(ReadWrite) , m_rotation() , m_scale(1.0f, 1.0f, 1.0f) , m_translation() @@ -119,6 +119,15 @@ void Transform::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) BackendNode::sceneChangeEvent(e); } +void Transform::notifyWorldTransformChanged(const Matrix4x4 &worldMatrix) +{ + auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId()); + change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes); + change->setPropertyName("worldMatrix"); + change->setValue(convertToQMatrix4x4(worldMatrix)); + notifyObservers(change); +} + void Transform::updateMatrix() { QMatrix4x4 m; diff --git a/src/render/backend/transform_p.h b/src/render/backend/transform_p.h index 8e3e9d639..e3bcad317 100644 --- a/src/render/backend/transform_p.h +++ b/src/render/backend/transform_p.h @@ -77,10 +77,10 @@ public: QVector3D translation() const; void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override; - - void updateMatrix(); + void notifyWorldTransformChanged(const Matrix4x4 &worldMatrix); private: + void updateMatrix(); void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final; Matrix4x4 m_transformMatrix; diff --git a/src/render/jobs/updateworldtransformjob.cpp b/src/render/jobs/updateworldtransformjob.cpp index 1a9697843..6b8bccec2 100644 --- a/src/render/jobs/updateworldtransformjob.cpp +++ b/src/render/jobs/updateworldtransformjob.cpp @@ -61,10 +61,15 @@ void updateWorldTransformAndBounds(NodeManagers *manager, Entity *node, const Ma Matrix4x4 worldTransform(parentTransform); Transform *nodeTransform = node->renderComponent<Transform>(); - if (nodeTransform != nullptr && nodeTransform->isEnabled()) + const bool hasTransformComponent = nodeTransform != nullptr && nodeTransform->isEnabled(); + if (hasTransformComponent) worldTransform = worldTransform * nodeTransform->transformMatrix(); - *(node->worldTransform()) = worldTransform; + if (*(node->worldTransform()) != worldTransform) { + *(node->worldTransform()) = worldTransform; + if (hasTransformComponent) + nodeTransform->notifyWorldTransformChanged(worldTransform); + } const auto childrenHandles = node->childrenHandles(); for (const HEntity &handle : childrenHandles) { diff --git a/tests/auto/core/qtransform/tst_qtransform.cpp b/tests/auto/core/qtransform/tst_qtransform.cpp index f5527ebf5..dbdda1ce1 100644 --- a/tests/auto/core/qtransform/tst_qtransform.cpp +++ b/tests/auto/core/qtransform/tst_qtransform.cpp @@ -36,6 +36,15 @@ using namespace Qt3DCore; +class FakeTransform : public Qt3DCore::QTransform +{ +public: + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override + { + Qt3DCore::QTransform::sceneChangeEvent(change); + } +}; + class tst_QTransform : public QObject { Q_OBJECT @@ -49,6 +58,7 @@ private Q_SLOTS: // THEN QCOMPARE(transform.isShareable(), false); QCOMPARE(transform.matrix(), QMatrix4x4()); + QCOMPARE(transform.worldMatrix(), QMatrix4x4()); QCOMPARE(transform.scale(), 1.0f); QCOMPARE(transform.scale3D(), QVector3D(1.0f, 1.0f, 1.0f)); QCOMPARE(transform.rotation(), QQuaternion()); @@ -112,6 +122,7 @@ private Q_SLOTS: QCOMPARE(transform->translation(), cloneData.translation); QCOMPARE(transform->scale3D(), cloneData.scale); QCOMPARE(transform->rotation(), cloneData.rotation); + QCOMPARE(transform->worldMatrix(), QMatrix4x4()); } void checkPropertyUpdates() @@ -342,6 +353,44 @@ private Q_SLOTS: // Note: t.matrix() != t2.matrix() since different matrices // can result in the same scale, rotation, translation } + + void checkUpdateWorldTransform() + { + // GIVEN + TestArbiter arbiter; + FakeTransform t; + arbiter.setArbiterOnNode(&t); + + // WHEN + QSignalSpy spy(&t, SIGNAL(worldMatrixChanged(QMatrix4x4))); + + // THEN + QVERIFY(spy.isValid()); + + // WHEN + Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId())); + valueChange->setPropertyName("worldMatrix"); + const QMatrix4x4 newValue(1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f); + valueChange->setValue(newValue); + t.sceneChangeEvent(valueChange); + + // THEN + QCOMPARE(spy.count(), 1); + QCOMPARE(arbiter.events.size(), 0); + QCOMPARE(t.worldMatrix(), newValue); + + // WHEN + spy.clear(); + t.sceneChangeEvent(valueChange); + + // THEN + QCOMPARE(spy.count(), 0); + QCOMPARE(arbiter.events.size(), 0); + QCOMPARE(t.worldMatrix(), newValue); + } }; QTEST_MAIN(tst_QTransform) diff --git a/tests/auto/render/transform/tst_transform.cpp b/tests/auto/render/transform/tst_transform.cpp index 151baff76..5619f8ed3 100644 --- a/tests/auto/render/transform/tst_transform.cpp +++ b/tests/auto/render/transform/tst_transform.cpp @@ -32,8 +32,10 @@ #include <Qt3DCore/private/qtransform_p.h> #include <Qt3DRender/private/transform_p.h> #include <Qt3DCore/qpropertyupdatedchange.h> +#include <private/qbackendnode_p.h> #include "qbackendnodetester.h" #include "testrenderer.h" +#include "testpostmanarbiter.h" class tst_Transform : public Qt3DCore::QBackendNodeTester { @@ -171,6 +173,30 @@ private Q_SLOTS: } } + void checkWorldTransformUpdate() + { + // GIVEN + TestArbiter arbiter; + Qt3DRender::Render::Transform backend; + + Qt3DCore::QBackendNodePrivate::get(&backend)->setArbiter(&arbiter); + + // WHEN + const QMatrix4x4 expectedNewWorldMatrix(1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f); + Matrix4x4 newWorldMatrix(expectedNewWorldMatrix); + backend.notifyWorldTransformChanged(newWorldMatrix); + + // THEN + Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>(); + QCOMPARE(arbiter.events.count(), 1); + QCOMPARE(change->propertyName(), "worldMatrix"); + QCOMPARE(change->value().value<QMatrix4x4>(), expectedNewWorldMatrix); + + arbiter.events.clear(); + } }; QTEST_MAIN(tst_Transform) |