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
qjoint.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qjoint_p.h"
5
6#include "physxnode/qabstractphysxnode_p.h"
7#include "physxnode/qphysxactorbody_p.h"
12
13#include <extensions/PxJoint.h>
14
16
17/*!
18 \qmltype PhysicsJoint
19 \inqmlmodule QtQuick3D.Physics
20 \since 6.12
21 \brief Base type for joints.
22
23 This is the abstract base type for all joints.
24
25 All joints have two bodies, and for each body a position and an orientation of the joint
26 relative to its local frame. If one of the bodies is not set then the joint will be
27 connected to the world.
28
29 \note At least one of the bodies needs to be dynamic.
30
31 \sa {DistanceJoint}
32 \sa {FixedJoint}
33 \sa {PrismaticJoint}
34 \sa {RevoluteJoint}
35 \sa {SphericalJoint}
36 \sa {FlexibleJoint}
37 \sa {D6Joint}
38*/
39
40/*!
41 \qmlproperty PhysicsBody* PhysicsJoint::bodyA
42 \since 6.12
43 \default null
44
45 This property defines the first of the bodies this joint connects to.
46*/
47
48/*!
49 \qmlproperty PhysicsBody* PhysicsJoint::bodyB
50 \since 6.12
51 \default null
52
53 This property defines the second of the bodies this joint connects to.
54*/
55
56/*!
57 \qmlproperty vector3d PhysicsJoint::positionA
58 \since 6.12
59 \default (0, 0, 0)
60
61 The position of the joint relative to PhysicsJoint::bodyA.
62*/
63
64/*!
65 \qmlproperty vector3d PhysicsJoint::positionB
66 \since 6.12
67 \default (0, 0, 0)
68
69 The position of the joint relative to PhysicsJoint::bodyB.
70*/
71
72/*!
73 \qmlproperty quaternion PhysicsJoint::orientationA
74 \since 6.12
75 \default (1, 0, 0, 0)
76
77 The orientation of the joint relative to PhysicsJoint::bodyA.
78*/
79
80/*!
81 \qmlproperty quaternion PhysicsJoint::orientationB
82 \since 6.12
83 \default (1, 0, 0, 0)
84
85 The orientation of the joint relative to PhysicsJoint::bodyB.
86*/
87
88QPhysicsJoint::QPhysicsJoint()
89{
90 QPhysicsWorld::registerJoint(this);
91};
92
93QPhysicsJoint::~QPhysicsJoint()
94{
95 QPhysicsWorld::deregisterJoint(this);
96};
97
98QAbstractPhysicsBody *QPhysicsJoint::bodyA() const
99{
100 return m_bodyA;
101}
102
103void QPhysicsJoint::setBodyA(QAbstractPhysicsBody *newBodyA)
104{
105 if (m_bodyA == newBodyA)
106 return;
107
108 if (m_bodyA)
109 m_bodyA->disconnect(this);
110 if (newBodyA)
111 connect(newBodyA, &QAbstractPhysicsBody::destroyed, this, &QPhysicsJoint::onBodyDestroyed);
112
113 m_bodyA = newBodyA;
114 m_needsRebuild = true;
115 emit bodyAChanged();
116}
117
118QAbstractPhysicsBody *QPhysicsJoint::bodyB() const
119{
120 return m_bodyB;
121}
122
123void QPhysicsJoint::setBodyB(QAbstractPhysicsBody *newBodyB)
124{
125 if (m_bodyB == newBodyB)
126 return;
127
128 if (m_bodyB)
129 m_bodyB->disconnect(this);
130 if (newBodyB)
131 connect(newBodyB, &QAbstractPhysicsBody::destroyed, this, &QPhysicsJoint::onBodyDestroyed);
132
133 m_bodyB = newBodyB;
134 m_needsRebuild = true;
135 emit bodyBChanged();
136}
137
138QVector3D QPhysicsJoint::positionA() const
139{
140 return m_positionA;
141}
142
143void QPhysicsJoint::setPositionA(const QVector3D &newPositionA)
144{
145 if (m_positionA == newPositionA)
146 return;
147 m_positionA = newPositionA;
148 m_needsRebuild = true;
149 emit positionAChanged();
150}
151
152QVector3D QPhysicsJoint::positionB() const
153{
154 return m_positionB;
155}
156
157void QPhysicsJoint::setPositionB(const QVector3D &newPositionB)
158{
159 if (m_positionB == newPositionB)
160 return;
161 m_positionB = newPositionB;
162 m_needsRebuild = true;
163 emit positionBChanged();
164}
165
166QQuaternion QPhysicsJoint::orientationA() const
167{
168 return m_orientationA;
169}
170
171void QPhysicsJoint::setOrientationA(const QQuaternion &newOrientationA)
172{
173 if (m_orientationA == newOrientationA)
174 return;
175 m_orientationA = newOrientationA;
176 m_needsRebuild = true;
177 emit orientationAChanged();
178}
179
180QQuaternion QPhysicsJoint::orientationB() const
181{
182 return m_orientationB;
183}
184
185void QPhysicsJoint::setOrientationB(const QQuaternion &newOrientationB)
186{
187 if (m_orientationB == newOrientationB)
188 return;
189 m_orientationB = newOrientationB;
190 m_needsRebuild = true;
191 emit orientationBChanged();
192}
193
194physx::PxJoint *QPhysicsJoint::getPhysXBackend() const
195{
196 return m_joint;
197}
198
199void QPhysicsJoint::onBodyDestroyed(QObject *body)
200{
201 if (!body)
202 return;
203 body->disconnect(this);
204 if (m_bodyA == body)
205 setBodyA(nullptr);
206 if (m_bodyB == body)
207 setBodyB(nullptr);
208}
209
210void QPhysicsJoint::updatePhysXBackend()
211{
212 if (!m_dirtyProperties && !m_needsRebuild)
213 return;
214
215 if (m_joint && m_needsRebuild) {
216 m_joint->release();
217 m_joint = nullptr;
218 }
219
220 if (!m_bodyA && !m_bodyB)
221 return;
222
223 QPhysXActorBody *actorBodyA = m_bodyA && m_bodyA->m_backendObject
224 ? qobject_cast<QPhysXActorBody *>(m_bodyA->m_backendObject)
225 : nullptr;
226 QPhysXActorBody *actorBodyB = m_bodyB && m_bodyB->m_backendObject
227 ? qobject_cast<QPhysXActorBody *>(m_bodyB->m_backendObject)
228 : nullptr;
229 physx::PxRigidActor *actorA = actorBodyA ? actorBodyA->actor : nullptr;
230 physx::PxRigidActor *actorB = actorBodyB ? actorBodyB->actor : nullptr;
231
232 if (!actorA && !actorB)
233 return;
234
235 Q_ASSERT(StaticPhysXObjects::getReference().physicsCreated);
236 Q_ASSERT(StaticPhysXObjects::getReference().physics);
237
238 // At least one body needs to be dynamic
239 const bool compatibleTypes =
240 (actorA && actorA->getType() == physx::PxActorType::Enum::eRIGID_DYNAMIC)
241 || (actorB && actorB->getType() == physx::PxActorType::Enum::eRIGID_DYNAMIC);
242
243 if (!compatibleTypes) {
244 qWarning() << "QPhysicsJoint: Incompatible body types used for joint.";
245 }
246
247 if (m_needsRebuild && compatibleTypes) {
248 auto trfA = physx::PxTransform(QPhysicsUtils::toPhysXType(m_positionA),
249 QPhysicsUtils::toPhysXType(m_orientationA));
250 auto trfB = physx::PxTransform(QPhysicsUtils::toPhysXType(m_positionB),
251 QPhysicsUtils::toPhysXType(m_orientationB));
252 if (!trfA.isSane()) {
253 qWarning() << "PhysicsJoint: positionA/orientationA is not finite, using identity "
254 "instead.";
255 trfA = physx::PxTransform(physx::PxIdentity);
256 }
257 if (!trfB.isSane()) {
258 qWarning() << "PhysicsJoint: positionB/orientationB is not finite, using identity "
259 "instead.";
260 trfB = physx::PxTransform(physx::PxIdentity);
261 }
262 m_joint = createPhysxJoint(actorA, actorB, trfA, trfB);
263 }
264
265 if (m_joint && (m_dirtyProperties || m_needsRebuild)) {
266 setJointProperties();
267 }
268
269 m_dirtyProperties = false;
270 m_needsRebuild = false;
271}
272
#define QT_BEGIN_NAMESPACE
#define QT_END_NAMESPACE
#define emit