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> |
31 | QT_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 | |
42 | using namespace Qt3DCore; |
43 | |
44 | class FakeTransform : public Qt3DCore::QTransform |
45 | { |
46 | public: |
47 | void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override |
48 | { |
49 | Qt3DCore::QTransform::sceneChangeEvent(change); |
50 | } |
51 | }; |
52 | |
53 | class tst_QTransform : public QObject |
54 | { |
55 | Q_OBJECT |
56 | |
57 | private 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 | |
381 | QTEST_MAIN(tst_QTransform) |
382 | |
383 | #include "tst_qtransform.moc" |
384 | |