1/****************************************************************************
2**
3** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29// TODO Remove in Qt6
30#include <QtCore/qcompilerdetection.h>
31QT_WARNING_DISABLE_DEPRECATED
32
33#include <QtTest/QtTest>
34#include <Qt3DCore/qpropertyupdatedchange.h>
35#include <Qt3DCore/qtransform.h>
36#include <Qt3DCore/qcomponent.h>
37#include <Qt3DCore/private/qtransform_p.h>
38#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
39#include <QtCore/qscopedpointer.h>
40#include "testpostmanarbiter.h"
41
42using namespace Qt3DCore;
43
44class FakeTransform : public Qt3DCore::QTransform
45{
46public:
47 void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override
48 {
49 Qt3DCore::QTransform::sceneChangeEvent(change);
50 }
51};
52
53class tst_QTransform : public QObject
54{
55 Q_OBJECT
56
57private Q_SLOTS:
58 void defaultConstruction()
59 {
60 // GIVEN
61 Qt3DCore::QTransform transform;
62
63 // THEN
64 QCOMPARE(transform.isShareable(), false);
65 QCOMPARE(transform.matrix(), QMatrix4x4());
66 QCOMPARE(transform.worldMatrix(), QMatrix4x4());
67 QCOMPARE(transform.scale(), 1.0f);
68 QCOMPARE(transform.scale3D(), QVector3D(1.0f, 1.0f, 1.0f));
69 QCOMPARE(transform.rotation(), QQuaternion());
70 QCOMPARE(transform.rotationX(), 0.0f);
71 QCOMPARE(transform.rotationY(), 0.0f);
72 QCOMPARE(transform.rotationZ(), 0.0f);
73 QCOMPARE(transform.translation(), QVector3D(0.0f, 0.0f, 0.0f));
74 }
75
76 void checkCloning_data()
77 {
78 QTest::addColumn<Qt3DCore::QTransform *>(name: "transform");
79
80 Qt3DCore::QTransform *defaultConstructed = new Qt3DCore::QTransform();
81 QTest::newRow(dataTag: "defaultConstructed") << defaultConstructed;
82
83 Qt3DCore::QTransform *matrixPropertySet = new Qt3DCore::QTransform();
84 matrixPropertySet->setMatrix(Qt3DCore::QTransform::rotateAround(point: QVector3D(0.1877f, 0.6868f, 0.3884f), angle: 45.0f, axis: QVector3D(0.0f, 0.0f, 1.0f)));
85 QTest::newRow(dataTag: "matrixPropertySet") << matrixPropertySet;
86
87 Qt3DCore::QTransform *translationSet = new Qt3DCore::QTransform();
88 translationSet->setTranslation(QVector3D(0.1877f, 0.6868f, 0.3884f));
89 QTest::newRow(dataTag: "translationSet") << translationSet;
90
91 Qt3DCore::QTransform *scaleSet = new Qt3DCore::QTransform();
92 scaleSet->setScale3D(QVector3D(0.1f, 0.6f, 0.3f));
93 QTest::newRow(dataTag: "scaleSet") << scaleSet;
94
95 Qt3DCore::QTransform *rotationSet = new Qt3DCore::QTransform();
96 scaleSet->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: 30.0f));
97 QTest::newRow(dataTag: "rotationSet") << rotationSet;
98
99 Qt3DCore::QTransform *eulerRotationSet = new Qt3DCore::QTransform();
100 eulerRotationSet->setRotationX(90.0f);
101 eulerRotationSet->setRotationY(10.0f);
102 eulerRotationSet->setRotationZ(1.0f);
103 QTest::newRow(dataTag: "eulerRotationSet") << eulerRotationSet;
104 }
105
106 void checkCloning()
107 {
108 // GIVEN
109 QFETCH(Qt3DCore::QTransform *, transform);
110
111 // WHEN
112 Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(transform);
113 QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges = creationChangeGenerator.creationChanges();
114
115 // THEN
116 QCOMPARE(creationChanges.size(), 1);
117
118 const Qt3DCore::QNodeCreatedChangePtr<Qt3DCore::QTransformData> creationChangeData =
119 qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DCore::QTransformData>>(src: creationChanges.first());
120 const Qt3DCore::QTransformData &cloneData = creationChangeData->data;
121
122 // THEN
123 QCOMPARE(creationChangeData->subjectId(), transform->id());
124 QCOMPARE(creationChangeData->isNodeEnabled(), transform->isEnabled());
125 QCOMPARE(creationChangeData->metaObject(), transform->metaObject());
126 QCOMPARE(creationChangeData->parentId(), transform->parentNode() ? transform->parentNode()->id() : Qt3DCore::QNodeId());
127 QCOMPARE(transform->translation(), cloneData.translation);
128 QCOMPARE(transform->scale3D(), cloneData.scale);
129 QCOMPARE(transform->rotation(), cloneData.rotation);
130 QCOMPARE(transform->worldMatrix(), QMatrix4x4());
131 }
132
133 void checkPropertyUpdates()
134 {
135 // GIVEN
136 TestArbiter arbiter;
137 QScopedPointer<Qt3DCore::QTransform> transform(new Qt3DCore::QTransform());
138 arbiter.setArbiterOnNode(transform.data());
139
140 // WHEN
141 transform->setTranslation(QVector3D(454.0f, 427.0f, 383.0f));
142
143 // THEN
144 QCOMPARE(arbiter.dirtyNodes.size(), 1);
145 QCOMPARE(arbiter.dirtyNodes.front(), transform.data());
146
147 arbiter.dirtyNodes.clear();
148
149 // WHEN
150 QQuaternion q = Qt3DCore::QTransform::fromAxisAndAngle(axis: QVector3D(0.0f, 1.0f, 0.0f), angle: 90.0f);
151 transform->setRotation(q);
152
153 // THEN
154 QCOMPARE(arbiter.dirtyNodes.size(), 1);
155 QCOMPARE(arbiter.dirtyNodes.front(), transform.data());
156
157 arbiter.dirtyNodes.clear();
158
159 // WHEN
160 transform->setScale3D(QVector3D(883.0f, 1200.0f, 1340.0f));
161 QCoreApplication::processEvents();
162
163 // THEN
164 QCOMPARE(arbiter.dirtyNodes.size(), 1);
165 QCOMPARE(arbiter.dirtyNodes.front(), transform.data());
166
167 arbiter.dirtyNodes.clear();
168
169 // WHEN
170 // Force the transform to update its matrix
171 (void)transform->matrix();
172
173 transform->setMatrix(QMatrix4x4());
174
175 // THEN
176 QCOMPARE(arbiter.dirtyNodes.size(), 1);
177 QCOMPARE(arbiter.dirtyNodes.front(), transform.data());
178
179 arbiter.dirtyNodes.clear();
180
181 // WHEN
182 transform->setRotationX(20.0f);
183 QCoreApplication::processEvents();
184
185 // THEN
186 QCOMPARE(arbiter.dirtyNodes.size(), 1);
187 QCOMPARE(arbiter.dirtyNodes.front(), transform.data());
188
189 arbiter.dirtyNodes.clear();
190 }
191
192 void checkSignalEmittion()
193 {
194 // GIVEN
195 QScopedPointer<Qt3DCore::QTransform> transform(new Qt3DCore::QTransform());
196
197 int rotationXChangedCount = 0;
198 int rotationYChangedCount = 0;
199 int rotationZChangedCount = 0;
200 int rotationChangedCount = 0;
201 int matrixChangedCount = 0;
202 int scaleChangedCount = 0;
203 int scale3DChangedCount = 0;
204 int translationChangedCount = 0;
205
206 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::rotationChanged, slot: [&] { ++rotationChangedCount; });
207 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::rotationXChanged, slot: [&] { ++rotationXChangedCount; });
208 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::rotationYChanged, slot: [&] { ++rotationYChangedCount; });
209 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::rotationZChanged, slot: [&] { ++rotationZChangedCount; });
210 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::matrixChanged, slot: [&] { ++matrixChangedCount; });
211 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::scale3DChanged, slot: [&] { ++scale3DChangedCount; });
212 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::scaleChanged, slot: [&] { ++scaleChangedCount; });
213 QObject::connect(sender: transform.data(), signal: &Qt3DCore::QTransform::translationChanged, slot: [&] { ++translationChangedCount; });
214
215 // WHEN
216 transform->setRotationX(180.0f);
217
218 // THEN
219 QCOMPARE(rotationXChangedCount, 1);
220 QCOMPARE(rotationYChangedCount, 0);
221 QCOMPARE(rotationZChangedCount, 0);
222 QCOMPARE(rotationChangedCount, 1);
223 QCOMPARE(matrixChangedCount, 1);
224 QCOMPARE(scaleChangedCount, 0);
225 QCOMPARE(scale3DChangedCount, 0);
226 QCOMPARE(translationChangedCount, 0);
227
228 // WHEN
229 transform->setRotationY(180.0f);
230
231 // THEN
232 QCOMPARE(rotationXChangedCount, 1);
233 QCOMPARE(rotationYChangedCount, 1);
234 QCOMPARE(rotationZChangedCount, 0);
235 QCOMPARE(rotationChangedCount, 2);
236 QCOMPARE(matrixChangedCount, 2);
237 QCOMPARE(scaleChangedCount, 0);
238 QCOMPARE(scale3DChangedCount, 0);
239 QCOMPARE(translationChangedCount, 0);
240
241 // WHEN
242 transform->setRotationZ(180.0f);
243
244 // THEN
245 QCOMPARE(rotationXChangedCount, 1);
246 QCOMPARE(rotationYChangedCount, 1);
247 QCOMPARE(rotationZChangedCount, 1);
248 QCOMPARE(rotationChangedCount, 3);
249 QCOMPARE(matrixChangedCount, 3);
250 QCOMPARE(scaleChangedCount, 0);
251 QCOMPARE(scale3DChangedCount, 0);
252 QCOMPARE(translationChangedCount, 0);
253
254 // WHEN
255 transform->setRotation(Qt3DCore::QTransform::fromEulerAngles(pitch: 15.0f, yaw: 25.0f, roll: 84.0f));
256
257 // THEN
258 QCOMPARE(rotationXChangedCount, 2);
259 QCOMPARE(rotationYChangedCount, 2);
260 QCOMPARE(rotationZChangedCount, 2);
261 QCOMPARE(rotationChangedCount, 4);
262 QCOMPARE(matrixChangedCount, 4);
263 QCOMPARE(scaleChangedCount, 0);
264 QCOMPARE(scale3DChangedCount, 0);
265 QCOMPARE(translationChangedCount, 0);
266
267 // WHEN
268 transform->setMatrix(QMatrix4x4());
269
270 // THEN
271 QCOMPARE(rotationXChangedCount, 3);
272 QCOMPARE(rotationYChangedCount, 3);
273 QCOMPARE(rotationZChangedCount, 3);
274 QCOMPARE(rotationChangedCount, 5);
275 QCOMPARE(matrixChangedCount, 5);
276 QCOMPARE(scaleChangedCount, 1);
277 QCOMPARE(scale3DChangedCount, 1);
278 QCOMPARE(translationChangedCount, 1);
279
280 // WHEN
281 transform->setScale(18.0f);
282
283 // THEN
284 QCOMPARE(rotationXChangedCount, 3);
285 QCOMPARE(rotationYChangedCount, 3);
286 QCOMPARE(rotationZChangedCount, 3);
287 QCOMPARE(rotationChangedCount, 5);
288 QCOMPARE(matrixChangedCount, 6);
289 QCOMPARE(scaleChangedCount, 2);
290 QCOMPARE(scale3DChangedCount, 2);
291 QCOMPARE(translationChangedCount, 1);
292
293 // WHEN
294 transform->setScale3D(QVector3D(15.0f, 18.0f, 15.0f));
295
296 // THEN
297 QCOMPARE(rotationXChangedCount, 3);
298 QCOMPARE(rotationYChangedCount, 3);
299 QCOMPARE(rotationZChangedCount, 3);
300 QCOMPARE(rotationChangedCount, 5);
301 QCOMPARE(matrixChangedCount, 7);
302 QCOMPARE(scaleChangedCount, 2);
303 QCOMPARE(scale3DChangedCount, 3);
304 QCOMPARE(translationChangedCount, 1);
305
306 // WHEN
307 transform->setTranslation(QVector3D(350.0f, 383.0f, 454.0f));
308
309 // THEN
310 QCOMPARE(rotationXChangedCount, 3);
311 QCOMPARE(rotationYChangedCount, 3);
312 QCOMPARE(rotationZChangedCount, 3);
313 QCOMPARE(rotationChangedCount, 5);
314 QCOMPARE(matrixChangedCount, 8);
315 QCOMPARE(scaleChangedCount, 2);
316 QCOMPARE(scale3DChangedCount, 3);
317 QCOMPARE(translationChangedCount, 2);
318 }
319
320 void checkCompositionDecomposition()
321 {
322 // GIVEN
323 Qt3DCore::QTransform t;
324 Qt3DCore::QTransform t2;
325 QMatrix4x4 m = Qt3DCore::QTransform::rotateAround(point: QVector3D(0.1877f, 0.6868f, 0.3884f), angle: 45.0f, axis: QVector3D(0.0f, 0.0f, 1.0f));
326
327 // WHEN
328 t.setMatrix(m);
329 t2.setScale3D(t.scale3D());
330 t2.setRotation(t.rotation());
331 t2.setTranslation(t.translation());
332
333 // THEN
334 QCOMPARE(t.scale3D(), t2.scale3D());
335 QCOMPARE(t.rotation(), t2.rotation());
336 QCOMPARE(t.translation(), t2.translation());
337
338 // Note: t.matrix() != t2.matrix() since different matrices
339 // can result in the same scale, rotation, translation
340 }
341
342 void checkUpdateWorldTransform()
343 {
344 // GIVEN
345 TestArbiter arbiter;
346 FakeTransform t;
347 arbiter.setArbiterOnNode(&t);
348
349 // WHEN
350 QSignalSpy spy(&t, SIGNAL(worldMatrixChanged(QMatrix4x4)));
351
352 // THEN
353 QVERIFY(spy.isValid());
354
355 // WHEN
356 Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
357 valueChange->setPropertyName("worldMatrix");
358 const QMatrix4x4 newValue(1.0f, 2.0f, 3.0f, 4.0f,
359 5.0f, 6.0f, 7.0f, 8.0f,
360 9.0f, 10.0f, 11.0f, 12.0f,
361 13.0f, 14.0f, 15.0f, 16.0f);
362 valueChange->setValue(newValue);
363 t.sceneChangeEvent(change: valueChange);
364
365 // THEN
366 QCOMPARE(spy.count(), 1);
367 QCOMPARE(arbiter.events.size(), 0);
368 QCOMPARE(t.worldMatrix(), newValue);
369
370 // WHEN
371 spy.clear();
372 t.sceneChangeEvent(change: valueChange);
373
374 // THEN
375 QCOMPARE(spy.count(), 0);
376 QCOMPARE(arbiter.events.size(), 0);
377 QCOMPARE(t.worldMatrix(), newValue);
378 }
379};
380
381QTEST_MAIN(tst_QTransform)
382
383#include "tst_qtransform.moc"
384

source code of qt3d/tests/auto/core/qtransform/tst_qtransform.cpp