1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 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/QTest> |
34 | #include <Qt3DCore/qpropertyupdatedchange.h> |
35 | #include <Qt3DCore/private/qnode_p.h> |
36 | #include <Qt3DCore/private/qscene_p.h> |
37 | #include <Qt3DCore/private/qnodecreatedchangegenerator_p.h> |
38 | |
39 | #include <Qt3DRender/qgeometry.h> |
40 | #include <Qt3DRender/private/qgeometry_p.h> |
41 | #include <Qt3DRender/qattribute.h> |
42 | #include <Qt3DRender/qbuffer.h> |
43 | |
44 | #include <QSignalSpy> |
45 | |
46 | #include "testpostmanarbiter.h" |
47 | |
48 | class FakeGeometry : public Qt3DRender::QGeometry |
49 | { |
50 | Q_OBJECT |
51 | |
52 | public: |
53 | void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override |
54 | { |
55 | Qt3DRender::QGeometry::sceneChangeEvent(change); |
56 | } |
57 | }; |
58 | |
59 | class tst_QGeometry: public QObject |
60 | { |
61 | Q_OBJECT |
62 | |
63 | private Q_SLOTS: |
64 | |
65 | void checkCloning_data() |
66 | { |
67 | QTest::addColumn<Qt3DRender::QGeometry *>(name: "geometry" ); |
68 | QTest::addColumn<int>(name: "attributeCount" ); |
69 | |
70 | Qt3DRender::QGeometry *defaultConstructed = new Qt3DRender::QGeometry(); |
71 | QTest::newRow(dataTag: "defaultConstructed" ) << defaultConstructed << 0; |
72 | |
73 | Qt3DRender::QGeometry *geometry1 = new Qt3DRender::QGeometry(); |
74 | Qt3DRender::QAttribute *attribute = new Qt3DRender::QAttribute(nullptr, QStringLiteral("Attr1" ), Qt3DRender::QAttribute::Float, 4, 454); |
75 | geometry1->addAttribute(attribute); |
76 | geometry1->addAttribute(attribute: new Qt3DRender::QAttribute(nullptr, QStringLiteral("Attr2" ), Qt3DRender::QAttribute::Float, 4, 555)); |
77 | geometry1->setBoundingVolumePositionAttribute(attribute); |
78 | QTest::newRow(dataTag: "2 attributes" ) << geometry1 << 2; |
79 | |
80 | |
81 | Qt3DRender::QGeometry *geometry2 = new Qt3DRender::QGeometry(); |
82 | attribute = new Qt3DRender::QAttribute(nullptr, QStringLiteral("Attr2" ), Qt3DRender::QAttribute::Float, 4, 383); |
83 | geometry2->addAttribute(attribute: new Qt3DRender::QAttribute(nullptr, QStringLiteral("Attr1" ), Qt3DRender::QAttribute::Float, 3, 427)); |
84 | geometry2->addAttribute(attribute); |
85 | geometry2->addAttribute(attribute: new Qt3DRender::QAttribute(nullptr, QStringLiteral("Attr3" ), Qt3DRender::QAttribute::Float, 2, 327)); |
86 | geometry2->removeAttribute(attribute); |
87 | QTest::newRow(dataTag: "3 - 1 attributes" ) << geometry2 << 2; |
88 | } |
89 | |
90 | void checkCloning() |
91 | { |
92 | // GIVEN |
93 | QFETCH(Qt3DRender::QGeometry *, geometry); |
94 | QFETCH(int, attributeCount); |
95 | |
96 | // WHEN |
97 | Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(geometry); |
98 | QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges = creationChangeGenerator.creationChanges(); |
99 | |
100 | // THEN |
101 | QCOMPARE(creationChanges.size(), 1 + geometry->childNodes().size()); |
102 | |
103 | const Qt3DCore::QNodeCreatedChangePtr<Qt3DRender::QGeometryData> creationChangeData = |
104 | qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QGeometryData>>(src: creationChanges.first()); |
105 | const Qt3DRender::QGeometryData &cloneData = creationChangeData->data; |
106 | |
107 | QCOMPARE(geometry->id(), creationChangeData->subjectId()); |
108 | QCOMPARE(geometry->isEnabled(), creationChangeData->isNodeEnabled()); |
109 | QCOMPARE(geometry->metaObject(), creationChangeData->metaObject()); |
110 | |
111 | QCOMPARE(attributeCount, geometry->attributes().count()); |
112 | QCOMPARE(attributeCount, cloneData.attributeIds.count()); |
113 | if (geometry->boundingVolumePositionAttribute()) |
114 | QCOMPARE(geometry->boundingVolumePositionAttribute()->id(), cloneData.boundingVolumePositionAttributeId); |
115 | |
116 | for (int i = 0; i < attributeCount; ++i) { |
117 | Qt3DRender::QAttribute *originalAttribute = static_cast<Qt3DRender::QAttribute *>(geometry->attributes()[i]); |
118 | QCOMPARE(originalAttribute->id(), cloneData.attributeIds.at(i)); |
119 | } |
120 | } |
121 | |
122 | void checkPropertyUpdates() |
123 | { |
124 | // GIVEN |
125 | TestArbiter arbiter; |
126 | QScopedPointer<Qt3DRender::QGeometry> geometry(new Qt3DRender::QGeometry()); |
127 | arbiter.setArbiterOnNode(geometry.data()); |
128 | |
129 | // WHEN |
130 | Qt3DRender::QAttribute attr; |
131 | geometry->addAttribute(attribute: &attr); |
132 | QCoreApplication::processEvents(); |
133 | |
134 | // THEN |
135 | QCOMPARE(arbiter.events.size(), 0); |
136 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
137 | QCOMPARE(arbiter.dirtyNodes.front(), geometry.data()); |
138 | |
139 | arbiter.dirtyNodes.clear(); |
140 | |
141 | // WHEN |
142 | geometry->addAttribute(attribute: &attr); |
143 | QCoreApplication::processEvents(); |
144 | |
145 | // THEN |
146 | QCOMPARE(arbiter.events.size(), 0); |
147 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
148 | |
149 | // WHEN |
150 | geometry->removeAttribute(attribute: &attr); |
151 | QCoreApplication::processEvents(); |
152 | |
153 | // THEN |
154 | QCOMPARE(arbiter.events.size(), 0); |
155 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
156 | QCOMPARE(arbiter.dirtyNodes.front(), geometry.data()); |
157 | |
158 | arbiter.events.clear(); |
159 | } |
160 | |
161 | void checkAttributeBookkeeping() |
162 | { |
163 | // GIVEN |
164 | QScopedPointer<Qt3DRender::QGeometry> geometry(new Qt3DRender::QGeometry); |
165 | { |
166 | // WHEN |
167 | Qt3DRender::QAttribute attribute; |
168 | geometry->addAttribute(attribute: &attribute); |
169 | |
170 | // THEN |
171 | QCOMPARE(attribute.parent(), geometry.data()); |
172 | QCOMPARE(geometry->attributes().size(), 1); |
173 | } |
174 | // THEN (Should not crash and parameter be unset) |
175 | QVERIFY(geometry->attributes().isEmpty()); |
176 | |
177 | { |
178 | // WHEN |
179 | Qt3DRender::QGeometry someOtherGeometry; |
180 | QScopedPointer<Qt3DRender::QAttribute> attribute(new Qt3DRender::QAttribute(&someOtherGeometry)); |
181 | geometry->addAttribute(attribute: attribute.data()); |
182 | |
183 | // THEN |
184 | QCOMPARE(attribute->parent(), &someOtherGeometry); |
185 | QCOMPARE(geometry->attributes().size(), 1); |
186 | |
187 | // WHEN |
188 | geometry.reset(); |
189 | attribute.reset(); |
190 | |
191 | // THEN Should not crash when the attribute is destroyed (tests for failed removal of destruction helper) |
192 | } |
193 | } |
194 | |
195 | void checkExtentUpdates() |
196 | { |
197 | // GIVEN |
198 | TestArbiter arbiter; |
199 | QScopedPointer<FakeGeometry> geometry(new FakeGeometry()); |
200 | arbiter.setArbiterOnNode(geometry.data()); |
201 | QSignalSpy spyMinExtent(geometry.data(), SIGNAL(minExtentChanged(QVector3D))); |
202 | QSignalSpy spyMaxExtent(geometry.data(), SIGNAL(maxExtentChanged(QVector3D))); |
203 | |
204 | // THEN |
205 | QVERIFY(spyMinExtent.isValid()); |
206 | QVERIFY(spyMaxExtent.isValid()); |
207 | QCOMPARE(geometry->minExtent(), QVector3D()); |
208 | QCOMPARE(geometry->maxExtent(), QVector3D()); |
209 | |
210 | // WHEN |
211 | Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId())); |
212 | valueChange->setPropertyName("extent" ); |
213 | valueChange->setValue(QVariant::fromValue(value: QPair<QVector3D, QVector3D>(QVector3D(10.0f, 10.f, 10.0f), |
214 | QVector3D()))); |
215 | geometry->sceneChangeEvent(change: valueChange); |
216 | |
217 | // THEN |
218 | QCOMPARE(spyMinExtent.count(), 1); |
219 | QCOMPARE(spyMaxExtent.count(), 0); |
220 | QCOMPARE(geometry->minExtent(), QVector3D(10.0f, 10.0f, 10.0f)); |
221 | |
222 | spyMinExtent.clear(); |
223 | |
224 | // WHEN |
225 | valueChange->setPropertyName("extent" ); |
226 | valueChange->setValue(QVariant::fromValue(value: QPair<QVector3D, QVector3D>(QVector3D(10.0f, 10.f, 10.0f), |
227 | QVector3D(11.0f, 11.f, 11.0f)))); |
228 | geometry->sceneChangeEvent(change: valueChange); |
229 | |
230 | // THEN |
231 | QCOMPARE(spyMinExtent.count(), 0); |
232 | QCOMPARE(spyMaxExtent.count(), 1); |
233 | QCOMPARE(geometry->maxExtent(), QVector3D(11.0f, 11.0f, 11.0f)); |
234 | |
235 | spyMaxExtent.clear(); |
236 | } |
237 | }; |
238 | |
239 | QTEST_MAIN(tst_QGeometry) |
240 | |
241 | #include "tst_qgeometry.moc" |
242 | |