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 and Bodies}{Shapes and Bodies overview documentation}
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 Default value: \c{false}
58*/
59
60/*!
61 \qmlproperty bool PhysicsNode::receiveTriggerReports
62 This property determines whether this body will receive reports when entering or leaving a
63 trigger body.
64
65 Default value: \c{false}
66*/
67
68/*!
69 \qmlproperty int PhysicsNode::filterGroup
70 This property determines what filter group this body is part of.
71
72 Default value is \c 0.
73
74 Range: \c{[0, 32]}
75
76 \sa PhysicsNode::filterIgnoreGroups
77*/
78
79/*!
80 \qmlproperty int PhysicsNode::filterIgnoreGroups
81 This property determines what groups this body should filter out collisions with.
82
83 \note This number is interpreted as a bitmask, meaning that if bit \c i is set then collisions
84 with \l filterGroup number \c i will be filtered. For instance, to filter groups \c{1}, \c{3}
85 and \c{4} then set the value to \c{0b11010}.
86
87 \sa PhysicsNode::filterGroup
88*/
89
90/*!
91 \qmlsignal PhysicsNode::bodyContact(PhysicsNode *body, list<vector3D> positions,
92 list<vector3D> impulses, list<vector3D> normals)
93
94 This signal is emitted when there is a collision between a dynamic body and any
95 other body. The \l {PhysicsNode::} {receiveContactReports} in this body and \l {PhysicsNode::}
96 {sendContactReports} in the colliding body need to be set to true. The parameters \a body, \a
97 positions, \a impulses and \a normals contain the other body, position, impulse force and normal
98 for each contact point at the same index.
99
100 \sa CharacterController::shapeHit
101 \sa PhysicsWorld::reportKinematicKinematicCollisions
102 \sa PhysicsWorld::reportStaticKinematicCollisions
103*/
104
105/*!
106 \qmlsignal PhysicsNode::enteredTriggerBody(TriggerBody *body)
107
108 This signal is emitted when this body enters the specified trigger \a body.
109
110 \note Only emitted when receiveTriggerReports is \c true
111 \sa receiveTriggerReports exitedTriggerBody
112*/
113
114/*!
115 \qmlsignal PhysicsNode::exitedTriggerBody(TriggerBody *body)
116
117 This signal is emitted when this body exits the specified trigger \a body.
118
119 \note Only emitted when receiveTriggerReports is \c true
120 \sa receiveTriggerReports enteredTriggerBody
121*/
122
123QAbstractPhysicsNode::QAbstractPhysicsNode()
124{
125 QPhysicsWorld::registerNode(this);
126}
127
128QAbstractPhysicsNode::~QAbstractPhysicsNode()
129{
130 for (auto shape : std::as_const(m_collisionShapes))
131 shape->disconnect(this);
132 QPhysicsWorld::deregisterNode(this);
133}
134
135QQmlListProperty<QAbstractCollisionShape> QAbstractPhysicsNode::collisionShapes()
136{
137 return QQmlListProperty<QAbstractCollisionShape>(
138 this, nullptr, QAbstractPhysicsNode::qmlAppendShape,
139 QAbstractPhysicsNode::qmlShapeCount, QAbstractPhysicsNode::qmlShapeAt,
140 QAbstractPhysicsNode::qmlClearShapes);
141}
142
143const QVector<QAbstractCollisionShape *> &QAbstractPhysicsNode::getCollisionShapesList() const
144{
145 return m_collisionShapes;
146}
147
148void QAbstractPhysicsNode::updateFromPhysicsTransform(const physx::PxTransform &transform)
149{
150 const auto pos = transform.p;
151 const auto rotation = transform.q;
152 const auto qtPosition = QVector3D(pos.x, pos.y, pos.z);
153 const auto qtRotation = QQuaternion(rotation.w, rotation.x, rotation.y, rotation.z);
154
155 // Get this nodes parent transform
156 const QQuick3DNode *parentNode = static_cast<QQuick3DNode *>(parentItem());
157
158 if (!parentNode) {
159 // then it is the same space
160 setRotation(qtRotation);
161 setPosition(qtPosition);
162 } else {
163 setPosition(parentNode->mapPositionFromScene(qtPosition));
164 const auto relativeRotation = parentNode->sceneRotation().inverted() * qtRotation;
165 setRotation(relativeRotation);
166 }
167}
168
169bool QAbstractPhysicsNode::sendContactReports() const
170{
171 return m_sendContactReports;
172}
173
174void QAbstractPhysicsNode::setSendContactReports(bool sendContactReports)
175{
176 if (m_sendContactReports == sendContactReports)
177 return;
178
179 m_sendContactReports = sendContactReports;
180 emit sendContactReportsChanged(m_sendContactReports);
181}
182
183bool QAbstractPhysicsNode::receiveContactReports() const
184{
185 return m_receiveContactReports;
186}
187
188void QAbstractPhysicsNode::setReceiveContactReports(bool receiveContactReports)
189{
190 if (m_receiveContactReports == receiveContactReports)
191 return;
192
193 m_receiveContactReports = receiveContactReports;
194 emit receiveContactReportsChanged(m_receiveContactReports);
195}
196
197bool QAbstractPhysicsNode::sendTriggerReports() const
198{
199 return m_sendTriggerReports;
200}
201
202void QAbstractPhysicsNode::setSendTriggerReports(bool sendTriggerReports)
203{
204 if (m_sendTriggerReports == sendTriggerReports)
205 return;
206
207 m_sendTriggerReports = sendTriggerReports;
208 emit sendTriggerReportsChanged(m_sendTriggerReports);
209}
210
211bool QAbstractPhysicsNode::receiveTriggerReports() const
212{
213 return m_receiveTriggerReports;
214}
215
216void QAbstractPhysicsNode::setReceiveTriggerReports(bool receiveTriggerReports)
217{
218 if (m_receiveTriggerReports == receiveTriggerReports)
219 return;
220
221 m_receiveTriggerReports = receiveTriggerReports;
222 emit receiveTriggerReportsChanged(m_receiveTriggerReports);
223}
224
225void QAbstractPhysicsNode::registerContact(QAbstractPhysicsNode *body,
226 const QVector<QVector3D> &positions,
227 const QVector<QVector3D> &impulses,
228 const QVector<QVector3D> &normals)
229{
230 emit bodyContact(body, positions, impulses, normals);
231}
232
233void QAbstractPhysicsNode::onShapeDestroyed(QObject *object)
234{
235 m_collisionShapes.removeAll(static_cast<QAbstractCollisionShape *>(object));
236}
237
238void QAbstractPhysicsNode::onShapeNeedsRebuild(QObject * /*object*/)
239{
240 m_shapesDirty = true;
241}
242
243void QAbstractPhysicsNode::qmlAppendShape(QQmlListProperty<QAbstractCollisionShape> *list,
244 QAbstractCollisionShape *shape)
245{
246 if (shape == nullptr)
247 return;
248 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
249 self->m_collisionShapes.push_back(shape);
250 self->m_hasStaticShapes = self->m_hasStaticShapes || shape->isStaticShape();
251
252 if (shape->parentItem() == nullptr) {
253 // If the material has no parent, check if it has a hierarchical parent that's a
254 // QQuick3DObject and re-parent it to that, e.g., inline materials
255 QQuick3DObject *parentItem = qobject_cast<QQuick3DObject *>(shape->parent());
256 if (parentItem) {
257 shape->setParentItem(parentItem);
258 } else { // If no valid parent was found, make sure the material refs our scene manager
259 const auto &scenManager = QQuick3DObjectPrivate::get(self)->sceneManager;
260 if (scenManager)
261 QQuick3DObjectPrivate::refSceneManager(shape, *scenManager);
262 // else: If there's no scene manager, defer until one is set, see itemChange()
263 }
264 }
265
266 // Make sure materials are removed when destroyed
267 connect(shape, &QAbstractCollisionShape::destroyed, self,
268 &QAbstractPhysicsNode::onShapeDestroyed);
269
270 // Connect to rebuild signal
271 connect(shape, &QAbstractCollisionShape::needsRebuild, self,
272 &QAbstractPhysicsNode::onShapeNeedsRebuild);
273}
274
275QAbstractCollisionShape *
276QAbstractPhysicsNode::qmlShapeAt(QQmlListProperty<QAbstractCollisionShape> *list, qsizetype index)
277{
278 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
279 return self->m_collisionShapes.at(index);
280}
281
282qsizetype QAbstractPhysicsNode::qmlShapeCount(QQmlListProperty<QAbstractCollisionShape> *list)
283{
284 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
285 return self->m_collisionShapes.count();
286}
287
288void QAbstractPhysicsNode::qmlClearShapes(QQmlListProperty<QAbstractCollisionShape> *list)
289{
290 QAbstractPhysicsNode *self = static_cast<QAbstractPhysicsNode *>(list->object);
291 for (const auto &shape : std::as_const(self->m_collisionShapes)) {
292 if (shape->parentItem() == nullptr)
293 QQuick3DObjectPrivate::get(shape)->derefSceneManager();
294 }
295 self->m_hasStaticShapes = false;
296 for (auto shape : std::as_const(self->m_collisionShapes))
297 shape->disconnect(self);
298 self->m_collisionShapes.clear();
299}
300
301int QAbstractPhysicsNode::filterGroup() const
302{
303 return m_filterGroup;
304}
305
306void QAbstractPhysicsNode::setfilterGroup(int newfilterGroup)
307{
308 if (m_filterGroup == newfilterGroup)
309 return;
310 m_filterGroup = newfilterGroup;
311 m_filtersDirty = true;
312 emit filterGroupChanged();
313}
314
315int QAbstractPhysicsNode::filterIgnoreGroups() const
316{
317 return m_filterIgnoreGroups;
318}
319
320void QAbstractPhysicsNode::setFilterIgnoreGroups(int newFilterIgnoreGroups)
321{
322 if (m_filterIgnoreGroups == newFilterIgnoreGroups)
323 return;
324 m_filterIgnoreGroups = newFilterIgnoreGroups;
325 m_filtersDirty = true;
326 emit filterIgnoreGroupsChanged();
327}
328
329QT_END_NAMESPACE
Combined button and popup list for selecting options.