Revision control
++ // Copyright (C) 2019 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QTest>
++ #include <QSignalSpy>
++
++ #include <QtQuick3D/private/qquick3dnode_p.h>
++ #include <QtQuick3DUtils/private/qssgutils_p.h>
++
++ #include <QtQuick3DRuntimeRender/private/qssgrendernode_p.h>
++
++ #include <QtGui/qquaternion.h>
++
++ class tst_QQuick3DNode : public QObject
++ {
++ Q_OBJECT
++
++ // Work-around to get access to updateSpatialNode
++ class NodeItem : public QQuick3DNode
++ {
++ public:
++ using QQuick3DNode::updateSpatialNode;
++ };
++
++ private slots:
++ void testProperties();
++ void testSceneRotation();
++ void testEnums();
++ void testPositionMapping();
++ void testDirectionMapping();
++ };
++
++ void tst_QQuick3DNode::testProperties()
++ {
++ NodeItem nodeItem;
++ auto node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(nullptr));
++ const auto originalNode = node; // for comparisons later...
++ QVERIFY(node);
++
++ // Positions
++ const float x = 50.0f;
++ nodeItem.setX(x);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QCOMPARE(x, nodeItem.x());
++ QVERIFY(qFuzzyCompare(x, QSSGUtils::mat44::getPosition(node->localTransform).x()));
++
++ const float y = 60.0f;
++ nodeItem.setY(y);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QCOMPARE(y, nodeItem.y());
++ QVERIFY(qFuzzyCompare(y, QSSGUtils::mat44::getPosition(node->localTransform).y()));
++
++ const float z = 70.0f;
++ nodeItem.setZ(z);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QCOMPARE(z, nodeItem.z());
++ QVERIFY(qFuzzyCompare(z, QSSGUtils::mat44::getPosition(node->localTransform).z()));
++
++ QVector3D pos1(x, y, z);
++ QCOMPARE(pos1, nodeItem.position());
++ QVERIFY(qFuzzyCompare(pos1, QSSGUtils::mat44::getPosition(node->localTransform)));
++
++ pos1 *= 2.0;
++ nodeItem.setPosition(pos1);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QCOMPARE(pos1, nodeItem.position());
++ QVERIFY(qFuzzyCompare(pos1, QSSGUtils::mat44::getPosition(node->localTransform)));
++
++ // Rotation, Scale, Pivot
++ QCOMPARE(nodeItem.rotation(), QQuaternion());
++ QCOMPARE(nodeItem.scale(), QVector3D(1, 1, 1));
++ QCOMPARE(nodeItem.pivot(), QVector3D());
++
++ QQuaternion rot = QQuaternion::fromEulerAngles(100, 200, 300);
++ nodeItem.setRotation(rot);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QVERIFY(qFuzzyCompare(rot, nodeItem.rotation()));
++ QVERIFY(qFuzzyCompare(rot, QQuaternion::fromRotationMatrix(QSSGUtils::mat44::getUpper3x3(node->localTransform))));
++
++ QVector3D scale(0.5, 1.0, 2.0);
++ nodeItem.setScale(scale);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QCOMPARE(scale, nodeItem.scale());
++ QVERIFY(qFuzzyCompare(scale, QSSGUtils::mat44::getScale(node->localTransform)));
++
++ QVector3D pivot(10, 20, 30);
++ nodeItem.setPivot(pivot);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QCOMPARE(pivot, nodeItem.pivot());
++ QCOMPARE(pivot, node->pivot);
++
++ // Opacity
++ const float opacity = 0.2f;
++ nodeItem.setLocalOpacity(opacity);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QCOMPARE(opacity, nodeItem.localOpacity());
++ QCOMPARE(opacity, node->localOpacity);
++
++ // Visibility
++ nodeItem.setVisible(false);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QVERIFY(!node->getLocalState(QSSGRenderNode::LocalState::Active));
++ QSignalSpy spy(&nodeItem, SIGNAL(visibleChanged()));
++ nodeItem.setVisible(true);
++ node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(node));
++ QVERIFY(node->getLocalState(QSSGRenderNode::LocalState::Active));
++ QCOMPARE(spy.size(), 1);
++
++ QCOMPARE(originalNode, node);
++ }
++
++ void tst_QQuick3DNode::testSceneRotation()
++ {
++ QQuick3DNode parentNode;
++ QQuick3DNode selfNode(&parentNode);
++
++ const auto rotX45 = QQuaternion::fromAxisAndAngle(QVector3D(1.0f, 0.0f, 0.0f), 45.0f);
++ parentNode.setRotation(rotX45);
++ QVERIFY(qFuzzyCompare(selfNode.sceneRotation(), rotX45));
++
++ const auto rotZ30 = QQuaternion::fromAxisAndAngle(QVector3D(0.0f, 0.0f, 1.0f), 30.0f);
++ selfNode.setRotation(rotZ30);
++ QVERIFY(qFuzzyCompare(selfNode.sceneRotation(), rotX45 * rotZ30));
++
++ // !m_hasInheritedUniformScale path, but the XY plane is scaled uniformly.
++ parentNode.setScale(QVector3D(5.0f, 5.0f, 1.0f));
++ QVERIFY(qFuzzyCompare(selfNode.sceneRotation(), rotX45 * rotZ30));
++ }
++
++ void tst_QQuick3DNode::testEnums()
++ {
++ NodeItem nodeItem;
++ auto node = static_cast<QSSGRenderNode *>(nodeItem.updateSpatialNode(nullptr));
++ QVERIFY(node);
++ }
++
++ void tst_QQuick3DNode::testPositionMapping()
++ {
++ QQuick3DNode nodeA, nodeB;
++ nodeB.setPosition(QVector3D{2.0f, 2.0f, 2.0f});
++
++ { // map pos TO scene A
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapPositionToScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map pos TO scene B
++ const QVector3D expected { 3.0f, 3.0f, 3.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeB.mapPositionToScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map pos FROM scene A
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapPositionFromScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map pos FROM scene B
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 3.0f, 3.0f, 3.0f };
++ const auto res = nodeB.mapPositionFromScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map node A's postion TO node B
++ const QVector3D expected { -1.0f, -1.0f, -1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapPositionToNode(&nodeB, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map node A's postion to node 'null'
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapPositionToNode(nullptr, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map pos FROM node B's pos
++ const QVector3D expected { 3.0f, 3.0f, 3.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapPositionFromNode(&nodeB, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map pos FROM null node
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapPositionFromNode(nullptr, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++ }
++
++ void tst_QQuick3DNode::testDirectionMapping()
++ {
++ QQuick3DNode nodeA, nodeB;
++ nodeB.setEulerRotation(QVector3D{0.0f, 0.0f, 90.0f});
++
++ { // map dir TO scene A
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapDirectionToScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map dir TO scene B
++ const QVector3D expected { -1.0f, 1.0f, 0.0f };
++ const QVector3D input { 1.0f, 1.0f, 0.0f };
++ const auto res = nodeB.mapDirectionToScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map dir FROM scene A
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapDirectionFromScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map dir FROM scene B
++ const QVector3D expected { 1.0f, -1.0f, 0.0f };
++ const QVector3D input { 1.0f, 1.0f, 0.0f };
++ const auto res = nodeB.mapDirectionFromScene(input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map direction FROM node A's TO node B's space
++ const QVector3D expected { 1.0f, -1.0f, 0.0f };
++ const QVector3D input { 1.0f, 1.0f, 0.0f };
++ const auto res = nodeA.mapDirectionToNode(&nodeB, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map direction FROM node A's TO null node
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapDirectionToNode(nullptr, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map direction FROM node B's pos
++ const QVector3D expected { -1.0f, 1.0f, 0.0f };
++ const QVector3D input { 1.0f, 1.0f, 0.0f };
++ const auto res = nodeA.mapDirectionFromNode(&nodeB, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++
++ { // map pos FROM null node
++ const QVector3D expected { 1.0f, 1.0f, 1.0f };
++ const QVector3D input { 1.0f, 1.0f, 1.0f };
++ const auto res = nodeA.mapDirectionFromNode(nullptr, input);
++ QVERIFY(qFuzzyCompare(res, expected));
++ }
++ }
++
++ QTEST_APPLESS_MAIN(tst_QQuick3DNode)
++ #include "tst_qquick3dnode.moc"