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

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