14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
48
49
50
51
52
53
54
57
58
59
60
61
62
63
66
67
68
69
70
71
72
75
76
77
78
79
80
81
82
83
84
85
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
105
106
107
108
109
110
113
114
115
116
117
118
119
120
123
124
125
126
127
130
131
132
133
134
135
136
139
140
141
142
143
146
147
148
149
151QRaycaster::QRaycaster(QQuick3DNode *parent) : QQuick3DNode(parent)
153 QPhysicsWorld::registerRaycaster(
this);
156QRaycaster::~QRaycaster()
158 QPhysicsWorld::deregisterRaycaster(
this);
161bool QRaycaster::enabled()
const
166void QRaycaster::setEnabled(
bool enabled)
168 if (m_enabled == enabled)
173 emit enabledChanged();
176QVector3D QRaycaster::direction()
const
181void QRaycaster::setDirection(
const QVector3D &direction)
183 if (m_direction == direction)
185 if (!QPhysicsUtils::isFinite(direction)) {
186 qWarning() <<
"Raycaster: direction must be finite, ignoring.";
189 m_direction = direction;
190 if (m_direction.isNull())
192 emit directionChanged();
195float QRaycaster::maxDistance()
const
197 return m_maxDistance;
200void QRaycaster::setMaxDistance(
float maxDistance)
202 if (qFuzzyCompare(m_maxDistance, maxDistance))
204 if (!qIsFinite(maxDistance) || maxDistance < 0.0f) {
205 qWarning() <<
"Raycaster: maxDistance must be finite and non-negative, ignoring.";
208 m_maxDistance = maxDistance;
209 if (m_maxDistance == 0.0f)
211 emit maxDistanceChanged();
214bool QRaycaster::excludeParentBody()
const
216 return m_excludeParentBody;
219void QRaycaster::setExcludeParentBody(
bool excludeParentBody)
221 if (m_excludeParentBody == excludeParentBody)
223 m_excludeParentBody = excludeParentBody;
224 emit excludeParentBodyChanged();
227bool QRaycaster::includeStatic()
const
229 return m_includeStatic;
232void QRaycaster::setIncludeStatic(
bool includeStatic)
234 if (m_includeStatic == includeStatic)
236 m_includeStatic = includeStatic;
237 if (!m_includeStatic && !m_includeDynamic)
239 emit includeStaticChanged();
242bool QRaycaster::includeDynamic()
const
244 return m_includeDynamic;
247void QRaycaster::setIncludeDynamic(
bool includeDynamic)
249 if (m_includeDynamic == includeDynamic)
251 m_includeDynamic = includeDynamic;
252 if (!m_includeStatic && !m_includeDynamic)
254 emit includeDynamicChanged();
257bool QRaycaster::isColliding()
const
259 return m_isColliding;
262QQuick3DPhysicsLocationHit QRaycaster::hit()
const
267QQmlListProperty<QAbstractPhysicsNode> QRaycaster::excludedBodies()
269 return QQmlListProperty<QAbstractPhysicsNode>(
270 this,
nullptr, QRaycaster::qmlAppendExcludedBody, QRaycaster::qmlExcludedBodyCount,
271 QRaycaster::qmlExcludedBodyAt, QRaycaster::qmlClearExcludedBodies);
274void QRaycaster::qmlAppendExcludedBody(QQmlListProperty<QAbstractPhysicsNode> *list,
275 QAbstractPhysicsNode *body)
278 static_cast<QRaycaster *>(list->object)->m_excludedBodies.push_back(body);
281QAbstractPhysicsNode *QRaycaster::qmlExcludedBodyAt(QQmlListProperty<QAbstractPhysicsNode> *list,
284 auto *self =
static_cast<QRaycaster *>(list->object);
285 if (index < 0 || index >= self->m_excludedBodies.size())
287 return self->m_excludedBodies.at(index).data();
290qsizetype QRaycaster::qmlExcludedBodyCount(QQmlListProperty<QAbstractPhysicsNode> *list)
292 return static_cast<QRaycaster *>(list->object)->m_excludedBodies.size();
295void QRaycaster::qmlClearExcludedBodies(QQmlListProperty<QAbstractPhysicsNode> *list)
297 static_cast<QRaycaster *>(list->object)->m_excludedBodies.clear();
301void QRaycaster::gatherExcludedBodies(
302 QVarLengthArray<
const QAbstractPhysicsNode *, 4> &excluded)
const
304 if (m_excludeParentBody) {
307 for (QQuick3DNode *node = parentNode(); node; node = node->parentNode()) {
308 if (
auto *body = qobject_cast<QAbstractPhysicsNode *>(node))
309 excluded.append(body);
313 if (m_excludedBodies.isEmpty())
316 excluded.reserve(excluded.size() + m_excludedBodies.size());
317 for (
const QPointer<QAbstractPhysicsNode> &body : m_excludedBodies) {
319 excluded.append(body.data());
323void QRaycaster::updateRaycast(QPhysicsWorld *world)
325 if (!m_enabled || m_maxDistance == 0.0f || (!m_includeStatic && !m_includeDynamic)) {
331 const QVector3D direction = mapDirectionToScene(m_direction);
332 if (direction.isNull()) {
337 QVarLengthArray<
const QAbstractPhysicsNode *, 4> excluded;
338 gatherExcludedBodies(excluded);
339 updateHit(world->closestRaycastHit(scenePosition(), direction, m_maxDistance, m_includeStatic,
340 m_includeDynamic, excluded));
346void QRaycaster::updateHit(
const QQuick3DPhysicsLocationHit &hit)
348 const QPointer<QRaycaster> self(
this);
349 const QPointer<QAbstractPhysicsNode> previousBody(m_hit.body());
350 const QPointer<QAbstractPhysicsNode> body(hit.body());
351 const bool isColliding = !body.isNull();
352 const bool hitHasChanged = m_hit.body() != body || m_hit.shape() != hit.shape()
353 || m_hit.position() != hit.position() || m_hit.normal() != hit.normal()
354 || !qFuzzyCompare(m_hit.distance(), hit.distance());
355 const bool collisionChanged = m_isColliding != isColliding;
358 m_isColliding = isColliding;
362 if (collisionChanged && !self.isNull())
363 emit isCollidingChanged();
365 if (!self.isNull() && previousBody != body) {
366 if (!previousBody.isNull())
367 emit bodyExited(previousBody);
368 if (!self.isNull() && !body.isNull())
369 emit bodyEntered(body);
373void QRaycaster::clearHit()
Combined button and popup list for selecting options.