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
qd6joint.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 "qd6joint_p.h"
5
6#include "physxnode/qphysxactorbody_p.h"
9
10#include <extensions/PxD6Joint.h>
11#include <PxPhysics.h>
12
14
15
16/*!
17 \qmltype D6Joint
18 \inqmlmodule QtQuick3D.Physics
19 \since 6.13
20 \brief A configurable joint with up to 6 degrees of freedom (3 linear, 3 angular).
21
22 The D6Joint allows custom configuration for each axis of movement and rotation.
23 Each degree of freedom can be set to \c Locked, \c Limited, or \c Free using the \l Motion enum.
24 Soft limits with spring stiffness and damping can be configured for linear and angular constraints.
25
26 \sa {DistanceJoint}
27 \sa {FixedJoint}
28 \sa {PrismaticJoint}
29 \sa {RevoluteJoint}
30 \sa {SphericalJoint}
31*/
32
33/*!
34 \qmlproperty enumeration D6Joint::xMotion
35 \since 6.13
36 \default D6Joint.Locked
37
38 Available options:
39
40 \value D6Joint.Locked
41 The degree of freedom is completely restricted and cannot move.
42 \value D6Joint.Limited
43 The degree of freedom is constrained within specified lower and upper limits.
44 \value D6Joint.Free
45 The degree of freedom is unconstrained and moves freely.
46
47 This property holds the motion constraint along the X axis.
48*/
49
50/*!
51 \qmlproperty enumeration D6Joint::yMotion
52 \since 6.13
53 \default D6Joint.Locked
54
55 Available options:
56
57 \value D6Joint.Locked
58 The degree of freedom is completely restricted and cannot move.
59 \value D6Joint.Limited
60 The degree of freedom is constrained within specified lower and upper limits.
61 \value D6Joint.Free
62 The degree of freedom is unconstrained and moves freely.
63
64 This property holds the motion constraint along the Y axis.
65*/
66
67/*!
68 \qmlproperty enumeration D6Joint::zMotion
69 \since 6.13
70 \default D6Joint.Locked
71
72 Available options:
73
74 \value D6Joint.Locked
75 The degree of freedom is completely restricted and cannot move.
76 \value D6Joint.Limited
77 The degree of freedom is constrained within specified lower and upper limits.
78 \value D6Joint.Free
79 The degree of freedom is unconstrained and moves freely.
80
81 This property holds the motion constraint along the Z axis.
82*/
83
84/*!
85 \qmlproperty enumeration D6Joint::twistMotion
86 \since 6.13
87 \default D6Joint.Locked
88
89 Available options:
90
91 \value D6Joint.Locked
92 The degree of freedom is completely restricted and cannot move.
93 \value D6Joint.Limited
94 The degree of freedom is constrained within specified lower and upper limits.
95 \value D6Joint.Free
96 The degree of freedom is unconstrained and moves freely.
97
98 This property holds the rotational constraint around the X axis (twist).
99*/
100
101/*!
102 \qmlproperty enumeration D6Joint::swingMotionY
103 \since 6.13
104 \default D6Joint.Locked
105
106 Available options:
107
108 \value D6Joint.Locked
109 The degree of freedom is completely restricted and cannot move.
110 \value D6Joint.Limited
111 The degree of freedom is constrained within specified lower and upper limits.
112 \value D6Joint.Free
113 The degree of freedom is unconstrained and moves freely.
114
115 This property holds the rotational constraint around the Y axis (swing Y).
116*/
117
118/*!
119 \qmlproperty enumeration D6Joint::swingMotionZ
120 \since 6.13
121 \default D6Joint.Locked
122
123 Available options:
124
125 \value D6Joint.Locked
126 The degree of freedom is completely restricted and cannot move.
127 \value D6Joint.Limited
128 The degree of freedom is constrained within specified lower and upper limits.
129 \value D6Joint.Free
130 The degree of freedom is unconstrained and moves freely.
131
132 This property holds the rotational constraint around the Z axis (swing Z).
133*/
134
135/*!
136 \qmlproperty real D6Joint::linearLimitXLower
137 \since 6.13
138 \default -1.0
139
140 This property holds the lower limit of movement along the X axis.
141 The value must be less than or equal to \l linearLimitXUpper.
142
143 Range: \c{(-\inf, \inf)}
144*/
145
146/*!
147 \qmlproperty real D6Joint::linearLimitXUpper
148 \since 6.13
149 \default 1.0
150
151 This property holds the upper limit of movement along the X axis.
152 The value must be greater than or equal to \l linearLimitXLower.
153
154 Range: \c{(-\inf, \inf)}
155*/
156
157/*!
158 \qmlproperty real D6Joint::linearLimitYLower
159 \since 6.13
160 \default -1.0
161
162 This property holds the lower limit of movement along the Y axis.
163 The value must be less than or equal to \l linearLimitYUpper.
164
165 Range: \c{(-\inf, \inf)}
166*/
167
168/*!
169 \qmlproperty real D6Joint::linearLimitYUpper
170 \since 6.13
171 \default 1.0
172
173 This property holds the upper limit of movement along the Y axis.
174 The value must be greater than or equal to \l linearLimitYLower.
175
176 Range: \c{(-\inf, \inf)}
177*/
178
179/*!
180 \qmlproperty real D6Joint::linearLimitZLower
181 \since 6.13
182 \default -1.0
183
184 This property holds the lower limit of movement along the Z axis.
185 The value must be less than or equal to \l linearLimitZUpper.
186
187 Range: \c{(-\inf, \inf)}
188*/
189
190/*!
191 \qmlproperty real D6Joint::linearLimitZUpper
192 \since 6.13
193 \default 1.0
194
195 This property holds the upper limit of movement along the Z axis.
196 The value must be greater than or equal to \l linearLimitZLower.
197
198 Range: \c{(-\inf, \inf)}
199*/
200
201/*!
202 \qmlproperty real D6Joint::twistLimitLower
203 \since 6.13
204 \default -pi/4
205
206 This property holds the lower angular limit (in radians) around the X axis.
207 The value must be less than or equal to \l twistLimitUpper.
208
209 Range: \c{(-2pi, 2pi)}
210*/
211
212/*!
213 \qmlproperty real D6Joint::twistLimitUpper
214 \since 6.13
215 \default pi/4
216
217 This property holds the upper angular limit (in radians) around the X axis.
218 The value must be greater than or equal to \l twistLimitLower.
219
220 Range: \c{(-2pi, 2pi)}
221*/
222
223/*!
224 \qmlproperty real D6Joint::swingLimitAngleY
225 \since 6.13
226 \default pi/4
227
228 This property holds the maximum swing angle limit (in radians) around the Y axis.
229
230 Range: \c{(0, pi)}
231*/
232
233/*!
234 \qmlproperty real D6Joint::swingLimitAngleZ
235 \since 6.13
236 \default pi/4
237
238 This property holds the maximum swing angle limit (in radians) around the Z axis.
239
240 Range: \c{(0, pi)}
241*/
242
243/*!
244 \qmlproperty real D6Joint::linearStiffness
245 \since 6.13
246 \default 0.0
247
248 This property holds the spring stiffness for linear motion constraints.
249 When set to a value greater than 0, linear limits act as soft constraints (springs).
250
251 Range: \c{[0, \inf)}
252*/
253
254/*!
255 \qmlproperty real D6Joint::linearDamping
256 \since 6.13
257 \default 0.0
258
259 This property holds the spring damping for linear motion constraints.
260
261 Range: \c{[0, \inf)}
262*/
263
264/*!
265 \qmlproperty real D6Joint::angularStiffness
266 \since 6.13
267 \default 0.0
268
269 This property holds the spring stiffness for angular motion constraints.
270 When set to a value greater than 0, angular limits act as soft constraints (springs).
271
272 Range: \c{[0, \inf)}
273*/
274
275/*!
276 \qmlproperty real D6Joint::angularDamping
277 \since 6.13
278 \default 0.0
279
280 This property holds the spring damping for angular motion constraints.
281
282 Range: \c{[0, \inf)}
283*/
284
285namespace {
286physx::PxD6Motion::Enum toPxEnum(QD6Joint::Motion val)
287{
288 switch (val) {
289 case QD6Joint::Motion::Locked: return physx::PxD6Motion::eLOCKED;
290 case QD6Joint::Motion::Limited: return physx::PxD6Motion::eLIMITED;
291 case QD6Joint::Motion::Free: return physx::PxD6Motion::eFREE;
292 }
293
294 Q_UNREACHABLE_RETURN(physx::PxD6Motion::eLOCKED);
295}
296}
297
298QD6Joint::QD6Joint() = default;
299
300QD6Joint::Motion QD6Joint::xMotion() const
301{
302 return m_xMotion;
303}
304
305void QD6Joint::setXMotion(Motion motion)
306{
307 if (m_xMotion == motion)
308 return;
309 m_xMotion = motion;
310 m_dirtyProperties = true;
311 emit xMotionChanged();
312}
313
314QD6Joint::Motion QD6Joint::yMotion() const
315{
316 return m_yMotion;
317}
318
319void QD6Joint::setYMotion(Motion motion)
320{
321 if (m_yMotion == motion)
322 return;
323 m_yMotion = motion;
324 m_dirtyProperties = true;
325 emit yMotionChanged();
326}
327
328QD6Joint::Motion QD6Joint::zMotion() const
329{
330 return m_zMotion;
331}
332
333void QD6Joint::setZMotion(Motion motion)
334{
335 if (m_zMotion == motion)
336 return;
337 m_zMotion = motion;
338 m_dirtyProperties = true;
339 emit zMotionChanged();
340}
341
342QD6Joint::Motion QD6Joint::twistMotion() const
343{
344 return m_twistMotion;
345}
346
347void QD6Joint::setTwistMotion(Motion motion)
348{
349 if (m_twistMotion == motion)
350 return;
351 m_twistMotion = motion;
352 m_dirtyProperties = true;
353 emit twistMotionChanged();
354}
355
356float QD6Joint::linearLimitXLower() const
357{
358 return m_linearLimitXLower;
359}
360
361void QD6Joint::setLinearLimitXLower(float limit)
362{
363 if (qFuzzyCompare(m_linearLimitXLower, limit))
364 return;
365 m_linearLimitXLower = limit;
366 m_dirtyProperties = true;
367 emit linearLimitXLowerChanged();
368}
369
370float QD6Joint::linearLimitXUpper() const
371{
372 return m_linearLimitXUpper;
373}
374
375void QD6Joint::setLinearLimitXUpper(float limit)
376{
377 if (qFuzzyCompare(m_linearLimitXUpper, limit))
378 return;
379 m_linearLimitXUpper = limit;
380 m_dirtyProperties = true;
381 emit linearLimitXUpperChanged();
382}
383
384float QD6Joint::linearLimitYLower() const
385{
386 return m_linearLimitYLower;
387}
388
389void QD6Joint::setLinearLimitYLower(float limit)
390{
391 if (qFuzzyCompare(m_linearLimitYLower, limit))
392 return;
393 m_linearLimitYLower = limit;
394 m_dirtyProperties = true;
395 emit linearLimitYLowerChanged();
396}
397
398float QD6Joint::linearLimitYUpper() const
399{
400 return m_linearLimitYUpper;
401}
402
403void QD6Joint::setLinearLimitYUpper(float limit)
404{
405 if (qFuzzyCompare(m_linearLimitYUpper, limit))
406 return;
407 m_linearLimitYUpper = limit;
408 m_dirtyProperties = true;
409 emit linearLimitYUpperChanged();
410}
411
412float QD6Joint::linearLimitZLower() const
413{
414 return m_linearLimitZLower;
415}
416
417void QD6Joint::setLinearLimitZLower(float limit)
418{
419 if (qFuzzyCompare(m_linearLimitZLower, limit))
420 return;
421 m_linearLimitZLower = limit;
422 m_dirtyProperties = true;
423 emit linearLimitZLowerChanged();
424}
425
426float QD6Joint::linearLimitZUpper() const
427{
428 return m_linearLimitZUpper;
429}
430
431void QD6Joint::setLinearLimitZUpper(float limit)
432{
433 if (qFuzzyCompare(m_linearLimitZUpper, limit))
434 return;
435 m_linearLimitZUpper = limit;
436 m_dirtyProperties = true;
437 emit linearLimitZUpperChanged();
438}
439
440float QD6Joint::twistLimitLower() const
441{
442 return m_twistLimitLower;
443}
444
445void QD6Joint::setTwistLimitLower(float limit)
446{
447 // PhysX requires the twist limit to be strictly within (-2*Pi, 2*Pi)
448 limit = qBound(-float(M_PI * 2) + 0.0001f, limit, float(M_PI * 2) - 0.0001f);
449
450 if (qFuzzyCompare(m_twistLimitLower, limit))
451 return;
452 m_twistLimitLower = limit;
453 m_dirtyProperties = true;
454 emit twistLimitLowerChanged();
455}
456
457float QD6Joint::twistLimitUpper() const
458{
459 return m_twistLimitUpper;
460}
461
462void QD6Joint::setTwistLimitUpper(float limit)
463{
464 // PhysX requires the twist limit to be strictly within (-2*Pi, 2*Pi)
465 limit = qBound(-float(M_PI * 2) + 0.0001f, limit, float(M_PI * 2) - 0.0001f);
466
467 if (qFuzzyCompare(m_twistLimitUpper, limit))
468 return;
469 m_twistLimitUpper = limit;
470 m_dirtyProperties = true;
471 emit twistLimitUpperChanged();
472}
473
474float QD6Joint::linearStiffness() const
475{
476 return m_linearStiffness;
477}
478
479void QD6Joint::setLinearStiffness(float stiffness)
480{
481 stiffness = qMax(0.f, stiffness);
482
483 if (qFuzzyCompare(m_linearStiffness, stiffness))
484 return;
485 m_linearStiffness = stiffness;
486 m_dirtyProperties = true;
487 emit linearStiffnessChanged();
488}
489
490float QD6Joint::linearDamping() const
491{
492 return m_linearDamping;
493}
494
495void QD6Joint::setLinearDamping(float damping)
496{
497 damping = qMax(0.f, damping);
498
499 if (qFuzzyCompare(m_linearDamping, damping))
500 return;
501 m_linearDamping = damping;
502 m_dirtyProperties = true;
503 emit linearDampingChanged();
504}
505
506float QD6Joint::angularStiffness() const
507{
508 return m_angularStiffness;
509}
510
511void QD6Joint::setAngularStiffness(float stiffness)
512{
513 stiffness = qMax(0.f, stiffness);
514 if (qFuzzyCompare(m_angularStiffness, stiffness))
515 return;
516 m_angularStiffness = stiffness;
517 m_dirtyProperties = true;
518 emit angularStiffnessChanged();
519}
520
521float QD6Joint::angularDamping() const
522{
523 return m_angularDamping;
524}
525
526void QD6Joint::setAngularDamping(float damping)
527{
528 damping = qMax(0.f, damping);
529 if (qFuzzyCompare(m_angularDamping, damping))
530 return;
531 m_angularDamping = damping;
532 m_dirtyProperties = true;
533 emit angularDampingChanged();
534}
535
536
537QD6Joint::Motion QD6Joint::swingMotionY() const
538{
539 return m_swingMotionY;
540}
541
542void QD6Joint::setSwingMotionY(Motion newSwingMotionY)
543{
544 if (m_swingMotionY == newSwingMotionY)
545 return;
546 m_swingMotionY = newSwingMotionY;
547 m_dirtyProperties = true;
548 emit swingMotionYChanged();
549}
550
551QD6Joint::Motion QD6Joint::swingMotionZ() const
552{
553 return m_swingMotionZ;
554}
555
556void QD6Joint::setSwingMotionZ(Motion newSwingMotionZ)
557{
558 if (m_swingMotionZ == newSwingMotionZ)
559 return;
560 m_swingMotionZ = newSwingMotionZ;
561 m_dirtyProperties = true;
562 emit swingMotionZChanged();
563}
564
565float QD6Joint::swingLimitAngleY() const
566{
567 return m_swingLimitAngleY;
568}
569
570void QD6Joint::setSwingLimitAngleY(float newSwingLimitAngleY)
571{
572 // Clamp swing angle between Math standard (0, Pi)
573 newSwingLimitAngleY = qBound(0.0001f, newSwingLimitAngleY, float(M_PI) - 0.0001f);
574
575 if (qFuzzyCompare(m_swingLimitAngleY, newSwingLimitAngleY))
576 return;
577 m_swingLimitAngleY = newSwingLimitAngleY;
578 m_dirtyProperties = true;
579 emit swingLimitAngleYChanged();
580}
581
582float QD6Joint::swingLimitAngleZ() const
583{
584 return m_swingLimitAngleZ;
585}
586
587void QD6Joint::setSwingLimitAngleZ(float newSwingLimitAngleZ)
588{
589 // Clamp swing angle between Math standard (0, Pi)
590 newSwingLimitAngleZ = qBound(0.0001f, newSwingLimitAngleZ, float(M_PI) - 0.0001f);
591
592 if (qFuzzyCompare(m_swingLimitAngleZ, newSwingLimitAngleZ))
593 return;
594 m_swingLimitAngleZ = newSwingLimitAngleZ;
595 m_dirtyProperties = true;
596 emit swingLimitAngleZChanged();
597}
598
599physx::PxJoint *QD6Joint::createPhysxJoint(physx::PxRigidActor *actorA,
600 physx::PxRigidActor *actorB,
601 const physx::PxTransform &trfA,
602 const physx::PxTransform &trfB)
603{
604 return physx::PxD6JointCreate(*StaticPhysXObjects::getReference().physics,
605 actorA, trfA, actorB, trfB);
606}
607
608void QD6Joint::setJointProperties()
609{
610 auto *d6Joint = static_cast<physx::PxD6Joint *>(m_joint);
611 if (!d6Joint)
612 return;
613
614 const physx::PxTolerancesScale scale =
615 StaticPhysXObjects::getReference().physics->getTolerancesScale();
616
617 d6Joint->setMotion(physx::PxD6Axis::eX, toPxEnum(m_xMotion));
618 d6Joint->setMotion(physx::PxD6Axis::eY, toPxEnum(m_yMotion));
619 d6Joint->setMotion(physx::PxD6Axis::eZ, toPxEnum(m_zMotion));
620 d6Joint->setMotion(physx::PxD6Axis::eTWIST, toPxEnum(m_twistMotion));
621 d6Joint->setMotion(physx::PxD6Axis::eSWING1, toPxEnum(m_swingMotionY));
622 d6Joint->setMotion(physx::PxD6Axis::eSWING2, toPxEnum(m_swingMotionZ));
623
624 if (m_xMotion == Motion::Limited) {
625 physx::PxJointLinearLimitPair limit(scale, qMin(m_linearLimitXLower, m_linearLimitXUpper),
626 qMax(m_linearLimitXLower, m_linearLimitXUpper));
627 limit.stiffness = m_linearStiffness; // Use linear stiffness for linear limits
628 limit.damping = m_linearDamping; // Use linear damping for linear limits
629 d6Joint->setLinearLimit(physx::PxD6Axis::eX, limit);
630 }
631
632 if (m_yMotion == Motion::Limited) {
633 physx::PxJointLinearLimitPair limit(scale, qMin(m_linearLimitYLower, m_linearLimitYUpper),
634 qMax(m_linearLimitYLower, m_linearLimitYUpper));
635 limit.stiffness = m_linearStiffness; // Use linear stiffness for linear limits
636 limit.damping = m_linearDamping; // Use linear damping for linear limits
637 d6Joint->setLinearLimit(physx::PxD6Axis::eY, limit);
638 }
639
640 if (m_zMotion == Motion::Limited) {
641 physx::PxJointLinearLimitPair limit(scale, qMin(m_linearLimitZLower, m_linearLimitZUpper),
642 qMax(m_linearLimitZLower, m_linearLimitZUpper));
643 limit.stiffness = m_linearStiffness; // Use linear stiffness for linear limits
644 limit.damping = m_linearDamping; // Use linear damping for linear limits
645 d6Joint->setLinearLimit(physx::PxD6Axis::eZ, limit);
646 }
647
648 if (m_twistMotion == Motion::Limited) {
649 physx::PxJointAngularLimitPair limit(qMin(m_twistLimitLower, m_twistLimitUpper),
650 qMax(m_twistLimitLower, m_twistLimitUpper));
651 limit.stiffness = m_angularStiffness; // Use angular stiffness for twist limit
652 limit.damping = m_angularDamping; // Use angular damping for twist limit
653 d6Joint->setTwistLimit(limit);
654 }
655
656 if (m_swingMotionY == Motion::Limited || m_swingMotionZ == Motion::Limited) {
657 // PhysX requires cone angles to be strictly within (0, Pi) range
658 const float angleY = qBound(0.0001f, m_swingLimitAngleY, float(M_PI) - 0.0001f);
659 const float angleZ = qBound(0.0001f, m_swingLimitAngleZ, float(M_PI) - 0.0001f);
660
661 physx::PxJointLimitCone limit(angleY, angleZ);
662 limit.stiffness = m_angularStiffness;
663 limit.damping = m_angularDamping;
664 d6Joint->setSwingLimit(limit);
665 }
666}
667
physx::PxD6Motion::Enum toPxEnum(QD6Joint::Motion val)
Definition qd6joint.cpp:286
#define QT_BEGIN_NAMESPACE
#define QT_END_NAMESPACE