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:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qobjectpicker.h"
41#include "qobjectpicker_p.h"
42#include <Qt3DCore/qentity.h>
43#include <Qt3DCore/private/qcomponent_p.h>
44#include <Qt3DCore/private/qscene_p.h>
45#include <Qt3DRender/qpickevent.h>
46#include <Qt3DRender/QViewport>
47#include <Qt3DRender/private/qpickevent_p.h>
48
49QT_BEGIN_NAMESPACE
50
51namespace Qt3DRender {
52
53/*!
54 \class Qt3DRender::QObjectPicker
55 \inmodule Qt3DRender
56
57 \brief The QObjectPicker class instantiates a component that can
58 be used to interact with a QEntity by a process known as picking.
59
60 For every combination of viewport and camera, picking casts a ray through the scene to
61 find entities who's bounding volume intersects the ray. The bounding volume is computed using
62 the values in the attribute buffer specified by the boundingVolumePositionAttribute of the
63 geometry.
64
65 The signals pressed(), released(), clicked(), moved(), entered(), and exited() are
66 emitted when the bounding volume defined by the pickAttribute property intersects
67 with a ray.
68
69 Most signals carry a QPickEvent instance. If QPickingSettings::pickMode() is set to
70 QPickingSettings::TrianglePicking, the actual type of the pick parameter will be
71 QPickTriangleEvent.
72
73 Pick queries are performed on mouse press and mouse release.
74 If drag is enabled, queries also happen on each mouse move while any button is pressed.
75 If hover is enabled, queries happen on every mouse move even if no button is pressed.
76
77 For generalised ray casting queries, see Qt3DRender::QRayCaster and Qt3DRender::QScreenRayCaster.
78
79 \sa Qt3DRender::QPickingSettings, Qt3DRender::QGeometry, Qt3DRender::QAttribute,
80 Qt3DRender::QPickEvent, Qt3DRender::QPickTriangleEvent, Qt3DRender::QNoPicking
81
82 \note Instances of this component shouldn't be shared, not respecting that
83 condition will most likely result in undefined behavior.
84
85 \note The camera far plane value affects picking and produces incorrect results due to
86 floating-point precision if it is greater than ~100 000.
87
88 \since 5.6
89*/
90
91/*!
92 \qmltype ObjectPicker
93 \instantiates Qt3DRender::QObjectPicker
94 \inqmlmodule Qt3D.Render
95 \brief The ObjectPicker class instantiates a component that can
96 be used to interact with an Entity by a process known as picking.
97
98 For every combination of viewport and camera, picking casts a ray through the scene to
99 find entities who's bounding volume intersects the ray. The bounding volume is computed using
100 the values in the attribute buffer specified by the boundingVolumePositionAttribute of the
101 geometry.
102
103 The signals pressed(), released(), clicked(), moved(), entered(), and exited() are
104 emitted when the bounding volume defined by the pickAttribute property intersects
105 with a ray.
106
107 Most signals carry a PickEvent instance. If PickingSettings.pickMode is set to
108 PickingSettings.TrianglePicking, the actual type of the pick parameter will be
109 PickTriangleEvent.
110
111 Pick queries are performed on mouse press and mouse release.
112 If drag is enabled, queries also happen on each mouse move while any button is pressed.
113 If hover is enabled, queries happen on every mouse move even if no button is pressed.
114
115 \sa PickingSettings, Geometry, Attribute, PickEvent, PickTriangleEvent, NoPicking
116
117 \note To receive hover events in QtQuick, the hoverEnabled property of Scene3D must also be set.
118
119 \note Instances of this component shouldn't be shared, not respecting that
120 condition will most likely result in undefined behavior.
121
122 \note The camera far plane value affects picking and produces incorrect results due to
123 floating-point precision if it is greater than ~100 000.
124 */
125
126/*!
127 \qmlsignal Qt3D.Render::ObjectPicker::clicked(PickEvent pick)
128
129 This signal is emitted when the bounding volume defined by the pickAttribute
130 property intersects with a ray on a mouse click the PickEvent \a pick contains
131 details of the event.
132*/
133
134/*!
135 \qmlsignal Qt3D.Render::ObjectPicker::pressed(PickEvent pick)
136
137 This signal is emitted when the bounding volume defined by the
138 pickAttribute property intersects with a ray on a mouse press. Intersection
139 information are accessible through the \a pick parameter.
140*/
141
142/*!
143 \qmlsignal Qt3D.Render::ObjectPicker::released(PickEvent pick)
144
145 This signal is emitted when the bounding volume defined by the
146 pickAttribute property intersects with a ray on a mouse release.
147 Intersection information are accessible through the \a pick parameter.
148*/
149
150/*!
151 \qmlsignal Qt3D.Render::ObjectPicker::clicked(PickEvent pick)
152
153 This signal is emitted when the bounding volume defined by the
154 pickAttribute property intersects with a ray on a mouse click. Intersection
155 information are accessible through the \a pick parameter.
156*/
157
158/*!
159 \qmlsignal Qt3D.Render::ObjectPicker::moved(PickEvent pick)
160
161 This signal is emitted when the bounding volume defined by the
162 pickAttribute property intersects with a ray on a mouse move with a button
163 pressed. Intersection information are accessible through the \a pick
164 parameter.
165*/
166
167/*!
168 \qmlsignal Qt3D.Render::ObjectPicker::entered()
169
170 This signal is emitted when the bounding volume defined by the pickAttribute
171 property intersects with a ray on the mouse entering the volume.
172*/
173
174/*!
175 \qmlsignal Qt3D.Render::ObjectPicker::exited()
176
177 This signal is emitted when the bounding volume defined by the pickAttribute
178 property intersects with a ray on the ray exiting the volume.
179*/
180
181/*!
182 \fn Qt3DRender::QObjectPicker::clicked(Qt3DRender::QPickEvent *pick)
183
184 This signal is emitted when the bounding volume defined by the pickAttribute
185 property intersects with a ray on a mouse click the QPickEvent \a pick contains
186 details of the event.
187*/
188
189/*!
190 \fn Qt3DRender::QObjectPicker::entered()
191
192 This signal is emitted when the bounding volume defined by the pickAttribute
193 property intersects with a ray on the mouse entering the volume.
194*/
195
196/*!
197 \fn Qt3DRender::QObjectPicker::exited()
198
199 This signal is emitted when the bounding volume defined by the pickAttribute
200 property intersects with a ray on the ray exiting the volume.
201*/
202
203/*!
204 \fn Qt3DRender::QObjectPicker::moved(Qt3DRender::QPickEvent *pick)
205
206 This signal is emitted when the bounding volume defined by the
207 pickAttribute property intersects with a ray on a mouse move with a button
208 pressed the QPickEvent \a pick contains details of the event.
209*/
210
211/*!
212 \fn Qt3DRender::QObjectPicker::pressed(Qt3DRender::QPickEvent *pick)
213
214 This signal is emitted when the bounding volume defined by the
215 pickAttribute property intersects with a ray on a mouse press the
216 QPickEvent \a pick contains details of the event.
217*/
218
219/*!
220 \fn Qt3DRender::QObjectPicker::released(Qt3DRender::QPickEvent *pick)
221
222 This signal is emitted when the bounding volume defined by the
223 pickAttribute property intersects with a ray on a mouse release the
224 QPickEvent \a pick contains details of the event.
225*/
226
227QObjectPicker::QObjectPicker(Qt3DCore::QNode *parent)
228 : Qt3DCore::QComponent(*new QObjectPickerPrivate(), parent)
229{
230}
231
232/*! \internal */
233QObjectPicker::~QObjectPicker()
234{
235}
236
237/*!
238 * Sets the hoverEnabled Property to \a hoverEnabled
239 */
240void QObjectPicker::setHoverEnabled(bool hoverEnabled)
241{
242 Q_D(QObjectPicker);
243 if (hoverEnabled != d->m_hoverEnabled) {
244 d->m_hoverEnabled = hoverEnabled;
245 emit hoverEnabledChanged(hoverEnabled);
246 }
247}
248
249/*!
250 \qmlproperty bool Qt3D.Render::ObjectPicker::hoverEnabled
251 Specifies if hover is enabled
252*/
253/*!
254 \property Qt3DRender::QObjectPicker::hoverEnabled
255 Specifies if hover is enabled
256 */
257/*!
258 * \return true if hover enabled
259 */
260bool QObjectPicker::isHoverEnabled() const
261{
262 Q_D(const QObjectPicker);
263 return d->m_hoverEnabled;
264}
265
266/*!
267 * Sets the dragEnabled Property to \a dragEnabled
268 */
269void QObjectPicker::setDragEnabled(bool dragEnabled)
270{
271 Q_D(QObjectPicker);
272 if (dragEnabled != d->m_dragEnabled) {
273 d->m_dragEnabled = dragEnabled;
274 emit dragEnabledChanged(dragEnabled);
275 }
276}
277
278/*!
279 * Sets the picker's priority to \a priority. This is used when the pick result
280 * mode on QPickingSettings is set to QPickingSettings::NearestPriorityPick.
281 * Picking results are sorted by highest priority and shortest picking
282 * distance.
283 *
284 * \since 5.13
285 */
286void QObjectPicker::setPriority(int priority)
287{
288 Q_D(QObjectPicker);
289 if (priority != d->m_priority) {
290 d->m_priority = priority;
291 emit priorityChanged(priority);
292 }
293}
294
295// TODO Unused remove in Qt6
296void QObjectPicker::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &)
297{
298}
299
300/*!
301 \qmlproperty bool Qt3D.Render::ObjectPicker::dragEnabled
302*/
303/*!
304 \property Qt3DRender::QObjectPicker::dragEnabled
305 Specifies if drag is enabled
306 */
307/*!
308 * \return true if dragging is enabled
309 */
310bool QObjectPicker::isDragEnabled() const
311{
312 Q_D(const QObjectPicker);
313 return d->m_dragEnabled;
314}
315
316/*!
317 \qmlproperty bool Qt3D.Render::ObjectPicker::containsMouse
318 Specifies if the object picker currently contains the mouse
319*/
320/*!
321 \property Qt3DRender::QObjectPicker::containsMouse
322 Specifies if the object picker currently contains the mouse
323 */
324/*!
325 * \return true if the object picker currently contains the mouse
326 */
327bool QObjectPicker::containsMouse() const
328{
329 Q_D(const QObjectPicker);
330 return d->m_containsMouse;
331}
332
333/*!
334 \qmlproperty bool Qt3D.Render::ObjectPicker::pressed
335 Specifies if the object picker is currently pressed
336*/
337/*!
338 \property Qt3DRender::QObjectPicker::pressed
339 Specifies if the object picker is currently pressed
340 */
341bool QObjectPicker::isPressed() const
342{
343 Q_D(const QObjectPicker);
344 return d->m_pressed;
345}
346
347/*!
348 \qmlproperty int Qt3D.Render::ObjectPicker::priority
349
350 The priority to be used when filtering pick results by priority when
351 PickingSettings.pickResultMode is set to PickingSettings.PriorityPick.
352*/
353/*!
354 \property Qt3DRender::QObjectPicker::priority
355
356 The priority to be used when filtering pick results by priority when
357 QPickingSettings::pickResultMode is set to
358 QPickingSettings::NearestPriorityPick.
359*/
360int QObjectPicker::priority() const
361{
362 Q_D(const QObjectPicker);
363 return d->m_priority;
364}
365
366/*!
367 \internal
368 */
369void QObjectPickerPrivate::setPressed(bool pressed)
370{
371 Q_Q(QObjectPicker);
372 if (m_pressed != pressed) {
373 const bool blocked = q->blockNotifications(block: true);
374 m_pressed = pressed;
375 emit q->pressedChanged(pressed);
376 q->blockNotifications(block: blocked);
377 }
378}
379
380/*!
381 \internal
382*/
383void QObjectPickerPrivate::setContainsMouse(bool containsMouse)
384{
385 Q_Q(QObjectPicker);
386 if (m_containsMouse != containsMouse) {
387 const bool blocked = q->blockNotifications(block: true);
388 m_containsMouse = containsMouse;
389 emit q->containsMouseChanged(containsMouse);
390 q->blockNotifications(block: blocked);
391 }
392}
393
394void QObjectPickerPrivate::propagateEvent(QPickEvent *event, EventType type)
395{
396 if (!m_entities.isEmpty()) {
397 Qt3DCore::QEntity *entity = m_entities.first();
398 Qt3DCore::QEntity *parentEntity = nullptr;
399 while (entity != nullptr && entity->parentEntity() != nullptr && !event->isAccepted()) {
400 parentEntity = entity->parentEntity();
401 const auto components = parentEntity->components();
402 for (Qt3DCore::QComponent *c : components) {
403 if (auto objectPicker = qobject_cast<Qt3DRender::QObjectPicker *>(object: c)) {
404 QObjectPickerPrivate *objectPickerPrivate = static_cast<QObjectPickerPrivate *>(QObjectPickerPrivate::get(q: objectPicker));
405 switch (type) {
406 case Pressed:
407 objectPickerPrivate->pressedEvent(event);
408 break;
409 case Released:
410 objectPickerPrivate->releasedEvent(event);
411 break;
412 case Clicked:
413 objectPickerPrivate->clickedEvent(event);
414 break;
415 case EventType::Moved:
416 objectPickerPrivate->movedEvent(event);
417 break;
418 }
419 break;
420 }
421 }
422 entity = parentEntity;
423 }
424 }
425}
426
427/*!
428 \internal
429 */
430void QObjectPickerPrivate::pressedEvent(QPickEvent *event)
431{
432 Q_Q(QObjectPicker);
433 emit q->pressed(pick: event);
434
435 m_acceptedLastPressedEvent = event->isAccepted();
436 if (!m_acceptedLastPressedEvent) {
437 // Travel parents to transmit the event
438 propagateEvent(event, type: Pressed);
439 } else {
440 setPressed(true);
441 }
442}
443
444/*!
445 \internal
446 */
447void QObjectPickerPrivate::clickedEvent(QPickEvent *event)
448{
449 Q_Q(QObjectPicker);
450 emit q->clicked(pick: event);
451 if (!event->isAccepted())
452 propagateEvent(event, type: Clicked);
453}
454
455/*!
456 \internal
457 */
458void QObjectPickerPrivate::movedEvent(QPickEvent *event)
459{
460 Q_Q(QObjectPicker);
461 emit q->moved(pick: event);
462 if (!event->isAccepted())
463 propagateEvent(event, type: EventType::Moved);
464}
465
466/*!
467 \internal
468 */
469void QObjectPickerPrivate::releasedEvent(QPickEvent *event)
470{
471 Q_Q(QObjectPicker);
472 if (m_acceptedLastPressedEvent) {
473 emit q->released(pick: event);
474 setPressed(false);
475 } else {
476 event->setAccepted(false);
477 propagateEvent(event, type: Released);
478 }
479}
480
481Qt3DCore::QNodeCreatedChangeBasePtr QObjectPicker::createNodeCreationChange() const
482{
483 auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QObjectPickerData>::create(arguments: this);
484 auto &data = creationChange->data;
485 Q_D(const QObjectPicker);
486 data.hoverEnabled = d->m_hoverEnabled;
487 data.dragEnabled = d->m_dragEnabled;
488 data.priority = d->m_priority;
489 return creationChange;
490}
491
492} // Qt3DRender
493
494QT_END_NAMESPACE
495

source code of qt3d/src/render/picking/qobjectpicker.cpp