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 | #include <QtTest/QtTest> |
30 | #include <Qt3DCore/qentity.h> |
31 | #include <Qt3DCore/qtransform.h> |
32 | #include <Qt3DCore/QPropertyUpdatedChange> |
33 | #include <Qt3DCore/private/qscene_p.h> |
34 | #include <Qt3DRender/private/qboundingvolumeprovider_p.h> |
35 | #include <Qt3DRender/private/pickboundingvolumejob_p.h> |
36 | #include <Qt3DRender/private/objectpicker_p.h> |
37 | #include <Qt3DRender/qobjectpicker.h> |
38 | #include <Qt3DRender/private/qobjectpicker_p.h> |
39 | #include <Qt3DExtras/qspheremesh.h> |
40 | #include <Qt3DRender/qattribute.h> |
41 | #include <Qt3DRender/qbuffer.h> |
42 | #include <Qt3DRender/qbufferdatagenerator.h> |
43 | #include <Qt3DExtras/qspheregeometry.h> |
44 | #include <Qt3DRender/qpickevent.h> |
45 | |
46 | using namespace Qt3DCore; |
47 | using namespace Qt3DRender; |
48 | using namespace Qt3DExtras; |
49 | using namespace Qt3DRender::Render; |
50 | |
51 | class MyObjectPicker : public Qt3DRender::QObjectPicker |
52 | { |
53 | Q_OBJECT |
54 | public: |
55 | MyObjectPicker(Qt3DCore::QNode *parent = nullptr) |
56 | : Qt3DRender::QObjectPicker(parent) |
57 | {} |
58 | |
59 | void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) final |
60 | { |
61 | Qt3DRender::QObjectPicker::sceneChangeEvent(change); |
62 | } |
63 | }; |
64 | |
65 | class PickableEntity : public QEntity |
66 | { |
67 | Q_OBJECT |
68 | public: |
69 | explicit PickableEntity(const QVector3D &position, float radius, QEntity *parent = nullptr) |
70 | : QEntity(parent) |
71 | , picker(new MyObjectPicker(this)) |
72 | , mesh(new QSphereMesh(this)) |
73 | , transform(new Qt3DCore::QTransform(this)) |
74 | , acceptsEvents(true) |
75 | , pressedCalled(0) |
76 | , releasedCalled(0) |
77 | , clickedCalled(0) |
78 | { |
79 | mesh->setRadius(radius); |
80 | QSphereGeometry *g = static_cast<QSphereGeometry *>(mesh->geometry()); |
81 | QAttribute *positionAttr = static_cast<QAttribute *>(g->attributes().first()); |
82 | Qt3DRender::QBuffer *vertexBuffer = static_cast<Qt3DRender::QBuffer *>(positionAttr->buffer()); |
83 | |
84 | // Load the geometry |
85 | QT_WARNING_PUSH |
86 | QT_WARNING_DISABLE_DEPRECATED |
87 | const QByteArray data = (*vertexBuffer->dataGenerator())(); |
88 | vertexBuffer->setData(data); |
89 | QT_WARNING_POP |
90 | |
91 | transform->setTranslation(position); |
92 | |
93 | addComponent(comp: picker); |
94 | addComponent(comp: mesh); |
95 | addComponent(comp: transform); |
96 | |
97 | QObject::connect(sender: picker, signal: &QObjectPicker::pressed, receiver: this, slot: &PickableEntity::onPressed); |
98 | QObject::connect(sender: picker, signal: &QObjectPicker::released, receiver: this, slot: &PickableEntity::onReleased); |
99 | QObject::connect(sender: picker, signal: &QObjectPicker::clicked, receiver: this, slot: &PickableEntity::onClicked); |
100 | } |
101 | |
102 | MyObjectPicker *picker; |
103 | QSphereMesh *mesh; |
104 | Qt3DCore::QTransform *transform; |
105 | |
106 | bool acceptsEvents; |
107 | int pressedCalled; |
108 | int releasedCalled; |
109 | int clickedCalled; |
110 | |
111 | private Q_SLOTS: |
112 | void onPressed(QPickEvent *e) |
113 | { |
114 | e->setAccepted(acceptsEvents); |
115 | ++pressedCalled; |
116 | } |
117 | |
118 | void onReleased(QPickEvent *e) |
119 | { |
120 | e->setAccepted(acceptsEvents); |
121 | if (acceptsEvents) |
122 | ++releasedCalled; |
123 | } |
124 | |
125 | void onClicked(QPickEvent *e) |
126 | { |
127 | e->setAccepted(acceptsEvents); |
128 | ++clickedCalled; |
129 | } |
130 | }; |
131 | |
132 | class tst_Picking : public QObject |
133 | { |
134 | Q_OBJECT |
135 | public: |
136 | tst_Picking() |
137 | { |
138 | qRegisterMetaType<Qt3DCore::QNode*>(); |
139 | } |
140 | |
141 | ~tst_Picking() {} |
142 | |
143 | private Q_SLOTS: |
144 | void testEventPressedAcceptPropagation() |
145 | { |
146 | // GIVEN |
147 | Qt3DCore::QScene scene; |
148 | PickableEntity root(QVector3D(), 5.0f); |
149 | PickableEntity child1(QVector3D(), 5.0f, &root); |
150 | PickableEntity child2(QVector3D(), 5.0f, &root); |
151 | PickableEntity child11(QVector3D(), 5.0f, &child1); |
152 | Qt3DCore::QNodePrivate::get(q: root.picker)->setScene(&scene); |
153 | Qt3DCore::QNodePrivate::get(q: child1.picker)->setScene(&scene); |
154 | Qt3DCore::QNodePrivate::get(q: child2.picker)->setScene(&scene); |
155 | Qt3DCore::QNodePrivate::get(q: child11.picker)->setScene(&scene); |
156 | |
157 | QCoreApplication::processEvents(); |
158 | |
159 | auto dpicker = [](QObjectPicker *node) { |
160 | return static_cast<QObjectPickerPrivate *>(QObjectPickerPrivate::get(q: node)); |
161 | }; |
162 | |
163 | // WHEN |
164 | Qt3DRender::QPickEventPtr event(new Qt3DRender::QPickEvent()); |
165 | dpicker(child11.picker)->pressedEvent(event: event.data()); |
166 | |
167 | // THEN |
168 | QCOMPARE(root.pressedCalled, 0); |
169 | QCOMPARE(child1.pressedCalled, 0); |
170 | QCOMPARE(child2.pressedCalled, 0); |
171 | QCOMPARE(child11.pressedCalled, 1); |
172 | |
173 | // WHEN |
174 | child11.pressedCalled = 0; |
175 | child11.acceptsEvents = false; |
176 | dpicker(child11.picker)->pressedEvent(event: event.data()); |
177 | |
178 | // THEN |
179 | QCOMPARE(root.pressedCalled, 0); |
180 | QCOMPARE(child1.pressedCalled, 1); |
181 | QCOMPARE(child2.pressedCalled, 0); |
182 | QCOMPARE(child11.pressedCalled, 1); |
183 | |
184 | // WHEN |
185 | child1.acceptsEvents = false; |
186 | child1.pressedCalled = 0; |
187 | child11.acceptsEvents = false; |
188 | child11.pressedCalled = 0; |
189 | dpicker(child11.picker)->pressedEvent(event: event.data()); |
190 | |
191 | // THEN |
192 | QCOMPARE(root.pressedCalled, 1); |
193 | QCOMPARE(child1.pressedCalled, 1); |
194 | QCOMPARE(child2.pressedCalled, 0); |
195 | QCOMPARE(child11.pressedCalled, 1); |
196 | } |
197 | |
198 | void testEventReleasedAcceptPropagation() |
199 | { |
200 | // GIVEN |
201 | Qt3DCore::QScene scene; |
202 | PickableEntity root(QVector3D(), 5.0f); |
203 | PickableEntity child1(QVector3D(), 5.0f, &root); |
204 | PickableEntity child2(QVector3D(), 5.0f, &root); |
205 | PickableEntity child11(QVector3D(), 5.0f, &child1); |
206 | Qt3DCore::QNodePrivate::get(q: root.picker)->setScene(&scene); |
207 | Qt3DCore::QNodePrivate::get(q: child1.picker)->setScene(&scene); |
208 | Qt3DCore::QNodePrivate::get(q: child2.picker)->setScene(&scene); |
209 | Qt3DCore::QNodePrivate::get(q: child11.picker)->setScene(&scene); |
210 | |
211 | QCoreApplication::processEvents(); |
212 | |
213 | auto dpicker = [](QObjectPicker *node) { |
214 | return static_cast<QObjectPickerPrivate *>(QObjectPickerPrivate::get(q: node)); |
215 | }; |
216 | |
217 | // WHEN |
218 | Qt3DRender::QPickEventPtr event(new Qt3DRender::QPickEvent()); |
219 | dpicker(child11.picker)->pressedEvent(event: event.data()); |
220 | dpicker(child11.picker)->releasedEvent(event: event.data()); |
221 | |
222 | // THEN |
223 | QCOMPARE(root.releasedCalled, 0); |
224 | QCOMPARE(child1.releasedCalled, 0); |
225 | QCOMPARE(child2.releasedCalled, 0); |
226 | QCOMPARE(child11.releasedCalled, 1); |
227 | |
228 | // WHEN |
229 | child11.releasedCalled = 0; |
230 | child11.pressedCalled = 0; |
231 | child11.acceptsEvents = false; |
232 | dpicker(child11.picker)->pressedEvent(event: event.data()); |
233 | dpicker(child11.picker)->releasedEvent(event: event.data()); |
234 | |
235 | // THEN |
236 | QCOMPARE(child1.pressedCalled, 1); |
237 | QCOMPARE(child11.pressedCalled, 1); |
238 | |
239 | QCOMPARE(root.releasedCalled, 0); |
240 | QCOMPARE(child1.releasedCalled, 1); |
241 | QCOMPARE(child2.releasedCalled, 0); |
242 | QCOMPARE(child11.releasedCalled, 0); |
243 | } |
244 | |
245 | void testEventClickedAcceptPropagation() |
246 | { |
247 | // GIVEN |
248 | Qt3DCore::QScene scene; |
249 | PickableEntity root(QVector3D(), 5.0f); |
250 | PickableEntity child1(QVector3D(), 5.0f, &root); |
251 | PickableEntity child2(QVector3D(), 5.0f, &root); |
252 | PickableEntity child11(QVector3D(), 5.0f, &child1); |
253 | Qt3DCore::QNodePrivate::get(q: root.picker)->setScene(&scene); |
254 | Qt3DCore::QNodePrivate::get(q: child1.picker)->setScene(&scene); |
255 | Qt3DCore::QNodePrivate::get(q: child2.picker)->setScene(&scene); |
256 | Qt3DCore::QNodePrivate::get(q: child11.picker)->setScene(&scene); |
257 | |
258 | QCoreApplication::processEvents(); |
259 | |
260 | auto dpicker = [](QObjectPicker *node) { |
261 | return static_cast<QObjectPickerPrivate *>(QObjectPickerPrivate::get(q: node)); |
262 | }; |
263 | |
264 | // WHEN |
265 | Qt3DRender::QPickEventPtr event(new Qt3DRender::QPickEvent()); |
266 | dpicker(child11.picker)->clickedEvent(event: event.data()); |
267 | |
268 | // THEN |
269 | QCOMPARE(root.clickedCalled, 0); |
270 | QCOMPARE(child1.clickedCalled, 0); |
271 | QCOMPARE(child2.clickedCalled, 0); |
272 | QCOMPARE(child11.clickedCalled, 1); |
273 | |
274 | // WHEN |
275 | child11.clickedCalled = 0; |
276 | child11.acceptsEvents = false; |
277 | dpicker(child11.picker)->clickedEvent(event: event.data()); |
278 | |
279 | // THEN |
280 | QCOMPARE(root.clickedCalled, 0); |
281 | QCOMPARE(child1.clickedCalled, 1); |
282 | QCOMPARE(child2.clickedCalled, 0); |
283 | QCOMPARE(child11.clickedCalled, 1); |
284 | |
285 | // WHEN |
286 | child1.acceptsEvents = false; |
287 | child1.clickedCalled = 0; |
288 | child11.acceptsEvents = false; |
289 | child11.clickedCalled = 0; |
290 | dpicker(child11.picker)->clickedEvent(event: event.data()); |
291 | |
292 | // THEN |
293 | QCOMPARE(root.clickedCalled, 1); |
294 | QCOMPARE(child1.clickedCalled, 1); |
295 | QCOMPARE(child2.clickedCalled, 0); |
296 | QCOMPARE(child11.clickedCalled, 1); |
297 | } |
298 | }; |
299 | |
300 | QTEST_MAIN(tst_Picking) |
301 | |
302 | #include "tst_picking.moc" |
303 | |