Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qabstractphysicsnode.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <QtQuick3D/private/qquick3dobject_p.h>
7#include <foundation/PxTransform.h>
8
10
11#include <QtGui/qquaternion.h>
12
14
15/*!
16 \qmltype PhysicsNode
17 \inherits Node
18 \inqmlmodule QtQuick3D.Physics
19 \since 6.4
20 \brief Base type for all objects in the physics scene.
21
22 PhysicsNode is the base type for all the objects that take part in the physics simulation. These
23 objects have a position in three-dimensional space and a geometrical shape.
24*/
25
26/*!
27 \qmlproperty list<CollisionShape> PhysicsNode::collisionShapes
28
29 This property contains the list of collision shapes. These shapes will be combined and act as a
30 single rigid body when interacting with other bodies.
31
32 \sa {Qt Quick 3D Physics Shapes, Bodies and Joints}
33*/
34
35/*!
36 \qmlproperty bool PhysicsNode::sendContactReports
37 This property determines whether this body will send contact reports when colliding with other
38 bodies.
39
40 Default value: \c{false}
41*/
42
43/*!
44 \qmlproperty bool PhysicsNode::receiveContactReports
45 This property determines whether this body will receive contact reports when colliding with
46 other bodies. If activated, this means that the bodyContact signal will be emitted on a
47 collision with a body that has sendContactReports set to true.
48
49 Default value: \c{false}
50*/
51
52/*!
53 \qmlproperty bool PhysicsNode::sendTriggerReports
54 This property determines whether this body will send reports when entering or leaving a trigger
55 body.
56
57 Read as this body enters a trigger body, and what it says then decides what is reported for
58 that stay.
59
60 Default value: \c{false}
61*/
62
63/*!
64 \qmlproperty bool PhysicsNode::receiveTriggerReports
65 This property determines whether this body will receive reports when entering or leaving a
66 trigger body.
67
68 Read as this body enters a trigger body, and what it says then decides what is reported for
69 that stay.
70
71 Default value: \c{false}
72*/
73
74/*!
75 \qmlproperty int PhysicsNode::filterGroup
76 This property determines what filter group this body is part of.
77
78 Default value is \c 0.
79
80 Range: \c{[0, 31]}
81
82 \sa PhysicsNode::filterIgnoreGroups
83*/
84
85/*!
86 \qmlproperty int PhysicsNode::filterIgnoreGroups
87 This property determines what groups this body should filter out collisions with.
88
89 \note This number is interpreted as a bitmask, meaning that if bit \c i is set then collisions
90 with \l filterGroup number \c i will be filtered. For instance, to filter groups \c{1}, \c{3}
91 and \c{4} then set the value to \c{0b11010}.
92
93 \sa PhysicsNode::filterGroup
94*/
95
96/*!
97 \qmlsignal PhysicsNode::bodyContact(PhysicsNode *body, list<vector3D> positions,
98 list<vector3D> impulses, list<vector3D> normals)
99
100 This signal is emitted when there is a collision between a dynamic body and any
101 other body. The \l {PhysicsNode::} {receiveContactReports} in this body and \l {PhysicsNode::}
102 {sendContactReports} in the colliding body need to be set to true. The parameters \a body, \a
103 positions, \a impulses and \a normals contain the other body, position, impulse force and normal
104 for each contact point at the same index.
105
106 \sa CharacterController::shapeHit
107 \sa PhysicsWorld::reportKinematicKinematicCollisions
108 \sa PhysicsWorld::reportStaticKinematicCollisions
109*/
110
111/*!
112 \qmlsignal PhysicsNode::enteredTriggerBody(TriggerBody *body)
113
114 This signal is emitted when this body enters the specified trigger \a body.
115
116 Emitted once per trigger body, however many collision shapes this body and that trigger body
117 have inside each other.
118
119 \note Only emitted when receiveTriggerReports was \c true as this body entered
120 \sa receiveTriggerReports exitedTriggerBody
121*/
122
123/*!
124 \qmlsignal PhysicsNode::exitedTriggerBody(TriggerBody *body)
125
126 This signal is emitted when this body exits the specified trigger \a body.
127
128 Emitted once the last of this body's collision shapes has left the trigger body, not when the
129 first one does.
130
131 \note Only emitted when receiveTriggerReports was \c true as this body entered, and not
132 emitted for a trigger body that is deleted while this body is still inside it.
133 \sa receiveTriggerReports enteredTriggerBody
134*/
135
136QAbstractPhysicsNode::QAbstractPhysicsNode()
137{
138 QPhysicsWorld::registerNode(this);
139}
140
141QAbstractPhysicsNode::~QAbstractPhysicsNode()
142{
143 for (auto shape : std::as_const(m_collisionShapes))
144 shape->disconnect(this);
145 QPhysicsWorld::deregisterNode(this);
146}
147
148QQmlListProperty<QAbstractCollisionShape> QAbstractPhysicsNode::collisionShapes()
149{
150 return QQmlListProperty<QAbstractCollisionShape>(
151 this, nullptr, QAbstractPhysicsNode::qmlAppendShape,
152 QAbstractPhysicsNode::qmlShapeCount, QAbstractPhysicsNode::qmlShapeAt,
153 QAbstractPhysicsNode::qmlClearShapes);
154}
155
156const QVector<QAbstractCollisionShape *> &QAbstractPhysicsNode::getCollisionShapesList() const
157{
158 return m_collisionShapes;
159}
160
161void QAbstractPhysicsNode::updateFromPhysicsTransform(const physx::PxTransform &transform)
162{
163 const auto pos = transform.p;
164 const auto rotation = transform.q;
165 const auto qtPosition = QVector3D(pos.x, pos.y, pos.z);
166 const auto qtRotation = QQuaternion(rotation.w, rotation.x, rotation.y, rotation.z);
167
168 // Get this nodes parent transform
169 const QQuick3DNode *parentNode = static_cast<QQuick3DNode *>(parentItem());
170
171 // setPosition() and setRotation() run this node's bindings, which can move
172 // the parent, so the parent is read before either write. A binding that
173 // deletes the node cannot be survived from here: setPosition() goes on
174 // writing to the node after running them. The backends ask whether the node
175 // is still there once a write has returned.
176 //
177 // No parent is the same space.
178 const QVector3D localPosition =
179 parentNode ? parentNode->mapPositionFromScene(qtPosition) : qtPosition;
180 const QQuaternion localRotation =
181 parentNode ? parentNode->sceneRotation().inverted() * qtRotation : qtRotation;
182
183 setPosition(localPosition);
184 setRotation(localRotation);
185}
186
187bool QAbstractPhysicsNode::sendContactReports() const
188{
189 return m_sendContactReports;
190}
191
192void QAbstractPhysicsNode::setSendContactReports(bool sendContactReports)
193{
194 if (m_sendContactReports == sendContactReports)
195 return;
196
197 m_sendContactReports = sendContactReports;
198 emit sendContactReportsChanged(m_sendContactReports);
199}
200
201bool QAbstractPhysicsNode::receiveContactReports() const
202{
203 return m_receiveContactReports;
204}
205
206void QAbstractPhysicsNode::setReceiveContactReports(bool receiveContactReports)
207{
208 if (m_receiveContactReports == receiveContactReports)
209 return;
210
211 m_receiveContactReports = receiveContactReports;
212 emit receiveContactReportsChanged(m_receiveContactReports);
213}
214
215bool QAbstractPhysicsNode::sendTriggerReports() const
216{
217 return m_sendTriggerReports;
218}
219
220void QAbstractPhysicsNode::setSendTriggerReports(bool sendTriggerReports)
221{
222 if (m_sendTriggerReports == sendTriggerReports)
223 return;
224
225 m_sendTriggerReports = sendTriggerReports;
226 emit sendTriggerReportsChanged(m_sendTriggerReports);
227}
228
229bool QAbstractPhysicsNode::receiveTriggerReports() const
230{
231 return m_receiveTriggerReports;
232}
233
234void QAbstractPhysicsNode::setReceiveTriggerReports(bool receiveTriggerReports)
235{
236 if (m_receiveTriggerReports == receiveTriggerReports)
237 return;
238
239 m_receiveTriggerReports = receiveTriggerReports;
240 emit receiveTriggerReportsChanged(m_receiveTriggerReports);
241}
242
243void QAbstractPhysicsNode::registerContact(QAbstractPhysicsNode *body,
244 const QVector<QVector3D> &positions,
245 const QVector<QVector3D> &impulses,
246 const QVector<QVector3D> &normals)
247{
248 emit bodyContact(body, positions, impulses, normals);
249}
250
251void QAbstractPhysicsNode::onShapeDestroyed(QObject *object)
252{
253 m_collisionShapes.removeAll(static_cast<QAbstractCollisionShape *>(object));
254}
255
256void QAbstractPhysicsNode::onShapeNeedsRebuild(QObject * /*object*/)
257{
258 m_shapesDirty = true;
259}
260
261void QAbstractPhysicsNode::qmlAppendShape(QQmlListProperty<QAbstractCollisionShape> *list,
262 QAbstractCollisionShape *shape)
263{
264 if (shape == nullptr)
265 return;
266 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
267 self->m_collisionShapes.push_back(shape);
268 self->m_hasStaticShapes = self->m_hasStaticShapes || shape->isStaticShape();
269
270 if (shape->parentItem() == nullptr) {
271 // If the material has no parent, check if it has a hierarchical parent that's a
272 // QQuick3DObject and re-parent it to that, e.g., inline materials
273 QQuick3DObject *parentItem = qobject_cast<QQuick3DObject *>(shape->parent());
274 if (parentItem) {
275 shape->setParentItem(parentItem);
276 } else { // If no valid parent was found, make sure the material refs our scene manager
277 const auto &scenManager = QQuick3DObjectPrivate::get(self)->sceneManager;
278 if (scenManager)
279 QQuick3DObjectPrivate::refSceneManager(shape, *scenManager);
280 // else: If there's no scene manager, defer until one is set, see itemChange()
281 }
282 }
283
284 // Make sure materials are removed when destroyed
285 connect(shape, &QAbstractCollisionShape::destroyed, self,
286 &QAbstractPhysicsNode::onShapeDestroyed);
287
288 // Connect to rebuild signal
289 connect(shape, &QAbstractCollisionShape::needsRebuild, self,
290 &QAbstractPhysicsNode::onShapeNeedsRebuild);
291}
292
293QAbstractCollisionShape *
294QAbstractPhysicsNode::qmlShapeAt(QQmlListProperty<QAbstractCollisionShape> *list, qsizetype index)
295{
296 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
297 return self->m_collisionShapes.at(index);
298}
299
300qsizetype QAbstractPhysicsNode::qmlShapeCount(QQmlListProperty<QAbstractCollisionShape> *list)
301{
302 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
303 return self->m_collisionShapes.count();
304}
305
306void QAbstractPhysicsNode::qmlClearShapes(QQmlListProperty<QAbstractCollisionShape> *list)
307{
308 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
309 for (const auto &shape : std::as_const(self->m_collisionShapes)) {
310 if (shape->parentItem() == nullptr)
311 QQuick3DObjectPrivate::get(shape)->derefSceneManager();
312 }
313 self->m_hasStaticShapes = false;
314 for (auto shape : std::as_const(self->m_collisionShapes))
315 shape->disconnect(self);
316 self->m_collisionShapes.clear();
317}
318
319int QAbstractPhysicsNode::filterGroup() const
320{
321 return m_filterGroup;
322}
323
324void QAbstractPhysicsNode::setfilterGroup(int newfilterGroup)
325{
326 if (m_filterGroup == newfilterGroup)
327 return;
328
329 if (newfilterGroup < 0 || newfilterGroup >= 32) {
330 qWarning() << "PhysicsNode: filterGroup" << newfilterGroup
331 << "is out of range [0, 31], ignoring. Values outside this range are"
332 " silently excluded from collision filtering by the underlying engine.";
333 return;
334 }
335
336 m_filterGroup = newfilterGroup;
337 m_filtersDirty = true;
338 emit filterGroupChanged();
339}
340
341int QAbstractPhysicsNode::filterIgnoreGroups() const
342{
343 return m_filterIgnoreGroups;
344}
345
346void QAbstractPhysicsNode::setFilterIgnoreGroups(int newFilterIgnoreGroups)
347{
348 if (m_filterIgnoreGroups == newFilterIgnoreGroups)
349 return;
350 m_filterIgnoreGroups = newFilterIgnoreGroups;
351 m_filtersDirty = true;
352 emit filterIgnoreGroupsChanged();
353}
354
355QT_END_NAMESPACE
Combined button and popup list for selecting options.