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