1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qdynamicrigidbody_p.h"
5#include "qphysicscommands_p.h"
6#include "qphysicsworld_p.h"
7#include "physxnode/qphysxdynamicbody_p.h"
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 \qmltype DynamicRigidBody
13 \inqmlmodule QtQuick3D.Physics
14 \inherits PhysicsBody
15 \since 6.4
16 \brief A physical body that can move or be moved.
17
18 This type defines a dynamic rigid body: an object that is part of the physics
19 scene and behaves like a physical object with mass and velocity.
20
21 \note \l{TriangleMeshShape}{triangle mesh}, \l{HeightFieldShape}{height field} and
22 \l{PlaneShape}{plane} geometry shapes are not allowed as collision shapes when
23 \l isKinematic is \c false.
24*/
25
26/*!
27 \qmlproperty float DynamicRigidBody::mass
28
29 This property defines the mass of the body. Note that this is only used when massMode is not
30 \c {DynamicRigidBody.CustomDensity} or \c {DynamicRigidBody.DefaultDensity}. Also note that
31 a value of 0 is interpreted as infinite mass and that negative numbers are not allowed.
32
33 Default value: \c 1
34
35 Range: \c{[0, inf]}
36
37 \sa massMode
38*/
39
40/*!
41 \qmlproperty float DynamicRigidBody::density
42
43 This property defines the density of the body. This is only used when massMode is set to \c
44 {DynamicRigidBody.CustomDensity}.
45
46 Default value: \c{0.001}
47
48 Range: \c{(0, inf]}
49 \sa massMode
50*/
51
52/*!
53 \qmlproperty AxisLock DynamicRigidBody::linearAxisLock
54
55 This property locks the linear velocity of the body along the axes defined by the
56 DynamicRigidBody.AxisLock enum. To lock several axes just bitwise-or their enum values.
57
58 Available options:
59
60 \value DynamicRigidBody.None
61 No axis lock.
62
63 \value DynamicRigidBody.LockX
64 Lock X axis.
65
66 \value DynamicRigidBody.LockY
67 Lock Y axis.
68
69 \value DynamicRigidBody.LockZ
70 Lock Z axis.
71
72 Default value: \c{DynamicRigidBody.None}
73*/
74
75/*!
76 \qmlproperty AxisLock DynamicRigidBody::angularAxisLock
77
78 This property locks the angular velocity of the body along the axes defined by the
79 DynamicRigidBody.AxisLock enum. To lock several axes just bitwise-or their enum values.
80
81 Available options:
82
83 \value DynamicRigidBody.None
84 No axis lock.
85
86 \value DynamicRigidBody.LockX
87 Lock X axis.
88
89 \value DynamicRigidBody.LockY
90 Lock Y axis.
91
92 \value DynamicRigidBody.LockZ
93 Lock Z axis.
94
95 Default value: \c{DynamicRigidBody.None}
96*/
97
98/*!
99 \qmlproperty bool DynamicRigidBody::isKinematic
100 This property defines whether the object is kinematic or not. A kinematic object does not get
101 influenced by external forces and can be seen as an object of infinite mass. If this property is
102 set then in every simulation frame the physical object will be moved to its target position
103 regardless of external forces. Note that to move and rotate the kinematic object you need to use
104 the kinematicPosition, kinematicRotation, kinematicEulerRotation and kinematicPivot properties.
105
106 Default value: \c{false}
107
108 \sa kinematicPosition, kinematicRotation, kinematicEulerRotation, kinematicPivot
109*/
110
111/*!
112 \qmlproperty bool DynamicRigidBody::gravityEnabled
113 This property defines whether the object is going to be affected by gravity or not.
114
115 Default value: \c{true}
116*/
117
118/*!
119 \qmlproperty MassMode DynamicRigidBody::massMode
120
121 This property holds the enum which describes how mass and inertia are calculated for this body.
122
123 Available options:
124
125 \value DynamicRigidBody.DefaultDensity
126 Use the density specified in the \l {PhysicsWorld::}{defaultDensity} property in
127 PhysicsWorld to calculate mass and inertia assuming a uniform density.
128
129 \value DynamicRigidBody.CustomDensity
130 Use specified density in the specified in the \l {DynamicRigidBody::}{density} to
131 calculate mass and inertia assuming a uniform density.
132
133 \value DynamicRigidBody.Mass
134 Use the specified mass to calculate inertia assuming a uniform density.
135
136 \value DynamicRigidBody.MassAndInertiaTensor
137 Use the specified mass value and inertia tensor.
138
139 \value DynamicRigidBody.MassAndInertiaMatrix
140 Use the specified mass value and calculate inertia from the specified inertia
141 matrix.
142
143 Default value: \c{DynamicRigidBody.DefaultDensity}
144*/
145
146/*!
147 \qmlproperty vector3d DynamicRigidBody::inertiaTensor
148
149 Defines the inertia tensor vector, using a parameter specified in mass space coordinates.
150
151 This is the diagonal vector of a 3x3 diagonal matrix, if you have a non diagonal world/actor
152 space inertia tensor then you should use \l{DynamicRigidBody::inertiaMatrix}{inertiaMatrix}
153 instead.
154
155 The inertia tensor components must be positive and a value of 0 in any component is
156 interpreted as infinite inertia along that axis. Note that this is only used when
157 massMode is set to \c DynamicRigidBody.MassAndInertiaTensor.
158
159 Default value: \c{(1, 1, 1)}
160
161 \sa massMode, inertiaMatrix
162*/
163
164/*!
165 \qmlproperty vector3d DynamicRigidBody::centerOfMassPosition
166
167 Defines the position of the center of mass relative to the body. Note that this is only used
168 when massMode is set to \c DynamicRigidBody.MassAndInertiaTensor.
169
170 Default value: \c{(0, 0, 0)}
171
172 \sa massMode, inertiaTensor
173*/
174
175/*!
176 \qmlproperty quaternion DynamicRigidBody::centerOfMassRotation
177
178 Defines the rotation of the center of mass pose, i.e. it specifies the orientation of the body's
179 principal inertia axes relative to the body. Note that this is only used when massMode is set to
180 \c DynamicRigidBody.MassAndInertiaTensor.
181
182 Default value: \c{(1, 0, 0, 0)}
183
184 \sa massMode, inertiaTensor
185*/
186
187/*!
188 \qmlproperty list<float> DynamicRigidBody::inertiaMatrix
189
190 Defines the inertia tensor matrix. This is a 3x3 matrix in column-major order. Note that this
191 matrix is expected to be diagonalizable. Note that this is only used when massMode is set to
192 \c DynamicRigidBody.MassAndInertiaMatrix.
193
194 Default value: A 3x3 identity matrix
195
196 \sa massMode, inertiaTensor
197*/
198
199/*!
200 \qmlproperty vector3d DynamicRigidBody::kinematicPosition
201 \since 6.5
202
203 Defines the position of the object when it is kinematic, i.e. when \l isKinematic is set to \c
204 true. On each iteration of the simulation the physical object will be updated according to this
205 value.
206
207 Default value: \c{(0, 0, 0)}
208
209 \sa isKinematic, kinematicRotation, kinematicEulerRotation, kinematicPivot
210*/
211
212/*!
213 \qmlproperty vector3d DynamicRigidBody::kinematicRotation
214 \since 6.5
215
216 Defines the rotation of the object when it is kinematic, i.e. when \l isKinematic is set to \c
217 true. On each iteration of the simulation the physical object will be updated according to this
218 value.
219
220 Default value: \c{(0, 0, 0)}
221
222 \sa isKinematic, kinematicPosition, kinematicEulerRotation, kinematicPivot
223*/
224
225/*!
226 \qmlproperty vector4d DynamicRigidBody::kinematicEulerRotation
227 \since 6.5
228
229 Defines the euler rotation of the object when it is kinematic, i.e. when \l isKinematic is set to \c
230 true. On each iteration of the simulation the physical object will be updated according to this
231 value.
232
233 Default value: \c{(1, 0, 0, 0)}
234
235 \sa isKinematic, kinematicPosition, kinematicEulerRotation, kinematicPivot
236*/
237
238/*!
239 \qmlproperty vector3d DynamicRigidBody::kinematicPivot
240 \since 6.5
241
242 Defines the pivot of the object when it is kinematic, i.e. when \l isKinematic is set to \c
243 true. On each iteration of the simulation the physical object will be updated according to this
244 value.
245
246 Default value: \c{(0, 0, 0)}
247
248 \sa isKinematic, kinematicPosition, kinematicEulerRotation, kinematicRotation
249*/
250
251/*!
252 \qmlmethod DynamicRigidBody::applyCentralForce(vector3d force)
253
254 Applies a \a force on the center of the body.
255*/
256
257/*!
258 \qmlmethod DynamicRigidBody::applyForce(vector3d force, vector3d position)
259
260 Applies a \a force at a \a position on the body.
261*/
262
263/*!
264 \qmlmethod DynamicRigidBody::applyTorque(vector3d torque)
265
266 Applies a \a torque on the body.
267*/
268
269/*!
270 \qmlmethod DynamicRigidBody::applyCentralImpulse(vector3d impulse)
271
272 Applies an \a impulse on the center of the body.
273*/
274
275/*!
276 \qmlmethod DynamicRigidBody::applyImpulse(vector3d impulse, vector3d position)
277
278 Applies an \a impulse at a \a position on the body.
279*/
280
281/*!
282 \qmlmethod DynamicRigidBody::applyTorqueImpulse(vector3d impulse)
283
284 Applies a torque \a impulse on the body.
285*/
286
287/*!
288 \qmlmethod DynamicRigidBody::setAngularVelocity(vector3d angularVelocity)
289
290 Sets the \a angularVelocity of the body.
291*/
292
293/*!
294 \qmlmethod DynamicRigidBody::setLinearVelocity(vector3d linearVelocity)
295
296 Sets the \a linearVelocity of the body.
297*/
298
299/*!
300 \qmlmethod DynamicRigidBody::reset(vector3d position, vector3d eulerRotation)
301
302 Resets the body's \a position and \a eulerRotation.
303*/
304
305QDynamicRigidBody::QDynamicRigidBody() = default;
306
307QDynamicRigidBody::~QDynamicRigidBody()
308{
309 qDeleteAll(c: m_commandQueue);
310 m_commandQueue.clear();
311}
312
313const QQuaternion &QDynamicRigidBody::centerOfMassRotation() const
314{
315 return m_centerOfMassRotation;
316}
317
318void QDynamicRigidBody::setCenterOfMassRotation(const QQuaternion &newCenterOfMassRotation)
319{
320 if (qFuzzyCompare(q1: m_centerOfMassRotation, q2: newCenterOfMassRotation))
321 return;
322 m_centerOfMassRotation = newCenterOfMassRotation;
323
324 // Only inertia tensor is using rotation
325 if (m_massMode == MassMode::MassAndInertiaTensor)
326 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaTensor(m_mass, m_inertiaTensor));
327
328 emit centerOfMassRotationChanged();
329}
330
331const QVector3D &QDynamicRigidBody::centerOfMassPosition() const
332{
333 return m_centerOfMassPosition;
334}
335
336void QDynamicRigidBody::setCenterOfMassPosition(const QVector3D &newCenterOfMassPosition)
337{
338 if (qFuzzyCompare(v1: m_centerOfMassPosition, v2: newCenterOfMassPosition))
339 return;
340
341 switch (m_massMode) {
342 case MassMode::MassAndInertiaTensor: {
343 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaTensor(m_mass, m_inertiaTensor));
344 break;
345 }
346 case MassMode::MassAndInertiaMatrix: {
347 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaMatrix(m_mass, m_inertiaMatrix));
348 break;
349 }
350 case MassMode::DefaultDensity:
351 case MassMode::CustomDensity:
352 case MassMode::Mass:
353 break;
354 }
355
356 m_centerOfMassPosition = newCenterOfMassPosition;
357 emit centerOfMassPositionChanged();
358}
359
360QDynamicRigidBody::MassMode QDynamicRigidBody::massMode() const
361{
362 return m_massMode;
363}
364
365void QDynamicRigidBody::setMassMode(const MassMode newMassMode)
366{
367 if (m_massMode == newMassMode)
368 return;
369
370 switch (newMassMode) {
371 case MassMode::DefaultDensity: {
372 auto world = QPhysicsWorld::getWorld(node: this);
373 if (world) {
374 m_commandQueue.enqueue(t: new QPhysicsCommandSetDensity(world->defaultDensity()));
375 } else {
376 qWarning() << "No physics world found, cannot set default density.";
377 }
378 break;
379 }
380 case MassMode::CustomDensity: {
381 m_commandQueue.enqueue(t: new QPhysicsCommandSetDensity(m_density));
382 break;
383 }
384 case MassMode::Mass: {
385 m_commandQueue.enqueue(t: new QPhysicsCommandSetMass(m_mass));
386 break;
387 }
388 case MassMode::MassAndInertiaTensor: {
389 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaTensor(m_mass, m_inertiaTensor));
390 break;
391 }
392 case MassMode::MassAndInertiaMatrix: {
393 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaMatrix(m_mass, m_inertiaMatrix));
394 break;
395 }
396 }
397
398 m_massMode = newMassMode;
399 emit massModeChanged();
400}
401
402const QVector3D &QDynamicRigidBody::inertiaTensor() const
403{
404 return m_inertiaTensor;
405}
406
407void QDynamicRigidBody::setInertiaTensor(const QVector3D &newInertiaTensor)
408{
409 if (qFuzzyCompare(v1: m_inertiaTensor, v2: newInertiaTensor))
410 return;
411 m_inertiaTensor = newInertiaTensor;
412
413 if (m_massMode == MassMode::MassAndInertiaTensor)
414 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaTensor(m_mass, m_inertiaTensor));
415
416 emit inertiaTensorChanged();
417}
418
419const QList<float> &QDynamicRigidBody::readInertiaMatrix() const
420{
421 return m_inertiaMatrixList;
422}
423
424static bool fuzzyEquals(const QList<float> &a, const QList<float> &b)
425{
426 if (a.length() != b.length())
427 return false;
428
429 const int length = a.length();
430 for (int i = 0; i < length; i++)
431 if (!qFuzzyCompare(p1: a[i], p2: b[i]))
432 return false;
433
434 return true;
435}
436
437void QDynamicRigidBody::setInertiaMatrix(const QList<float> &newInertiaMatrix)
438{
439 if (fuzzyEquals(a: m_inertiaMatrixList, b: newInertiaMatrix))
440 return;
441
442 m_inertiaMatrixList = newInertiaMatrix;
443 const int elemsToCopy = qMin(a: m_inertiaMatrixList.length(), b: 9);
444 memcpy(dest: m_inertiaMatrix.data(), src: m_inertiaMatrixList.data(), n: elemsToCopy * sizeof(float));
445 memset(s: m_inertiaMatrix.data() + elemsToCopy, c: 0, n: (9 - elemsToCopy) * sizeof(float));
446
447 if (m_massMode == MassMode::MassAndInertiaMatrix)
448 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaMatrix(m_mass, m_inertiaMatrix));
449
450 emit inertiaMatrixChanged();
451}
452
453const QMatrix3x3 &QDynamicRigidBody::inertiaMatrix() const
454{
455 return m_inertiaMatrix;
456}
457
458float QDynamicRigidBody::mass() const
459{
460 return m_mass;
461}
462
463bool QDynamicRigidBody::isKinematic() const
464{
465 return m_isKinematic;
466}
467
468bool QDynamicRigidBody::gravityEnabled() const
469{
470 return m_gravityEnabled;
471}
472
473void QDynamicRigidBody::setMass(float mass)
474{
475 if (mass < 0.f || qFuzzyCompare(p1: m_mass, p2: mass))
476 return;
477
478 switch (m_massMode) {
479 case QDynamicRigidBody::MassMode::Mass:
480 m_commandQueue.enqueue(t: new QPhysicsCommandSetMass(mass));
481 break;
482 case QDynamicRigidBody::MassMode::MassAndInertiaTensor:
483 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaTensor(mass, m_inertiaTensor));
484 break;
485 case QDynamicRigidBody::MassMode::MassAndInertiaMatrix:
486 m_commandQueue.enqueue(t: new QPhysicsCommandSetMassAndInertiaMatrix(mass, m_inertiaMatrix));
487 break;
488 case QDynamicRigidBody::MassMode::DefaultDensity:
489 case QDynamicRigidBody::MassMode::CustomDensity:
490 break;
491 }
492
493 m_mass = mass;
494 emit massChanged(mass: m_mass);
495}
496
497float QDynamicRigidBody::density() const
498{
499 return m_density;
500}
501
502void QDynamicRigidBody::setDensity(float density)
503{
504 if (qFuzzyCompare(p1: m_density, p2: density))
505 return;
506
507 if (m_massMode == MassMode::CustomDensity)
508 m_commandQueue.enqueue(t: new QPhysicsCommandSetDensity(density));
509
510 m_density = density;
511 emit densityChanged(density: m_density);
512}
513
514void QDynamicRigidBody::setIsKinematic(bool isKinematic)
515{
516 if (m_isKinematic == isKinematic)
517 return;
518
519 if (hasStaticShapes() && !isKinematic) {
520 qWarning()
521 << "Cannot make body containing trimesh/heightfield/plane non-kinematic, ignoring.";
522 return;
523 }
524
525 m_isKinematic = isKinematic;
526 m_commandQueue.enqueue(t: new QPhysicsCommandSetIsKinematic(m_isKinematic));
527 emit isKinematicChanged(isKinematic: m_isKinematic);
528}
529
530void QDynamicRigidBody::setGravityEnabled(bool gravityEnabled)
531{
532 if (m_gravityEnabled == gravityEnabled)
533 return;
534
535 m_gravityEnabled = gravityEnabled;
536 m_commandQueue.enqueue(t: new QPhysicsCommandSetGravityEnabled(m_gravityEnabled));
537 emit gravityEnabledChanged();
538}
539
540void QDynamicRigidBody::setAngularVelocity(const QVector3D &angularVelocity)
541{
542 m_commandQueue.enqueue(t: new QPhysicsCommandSetAngularVelocity(angularVelocity));
543}
544
545QDynamicRigidBody::AxisLock QDynamicRigidBody::linearAxisLock() const
546{
547 return m_linearAxisLock;
548}
549
550void QDynamicRigidBody::setLinearAxisLock(AxisLock newAxisLockLinear)
551{
552 if (m_linearAxisLock == newAxisLockLinear)
553 return;
554 m_linearAxisLock = newAxisLockLinear;
555 emit linearAxisLockChanged();
556}
557
558QDynamicRigidBody::AxisLock QDynamicRigidBody::angularAxisLock() const
559{
560 return m_angularAxisLock;
561}
562
563void QDynamicRigidBody::setAngularAxisLock(AxisLock newAxisLockAngular)
564{
565 if (m_angularAxisLock == newAxisLockAngular)
566 return;
567 m_angularAxisLock = newAxisLockAngular;
568 emit angularAxisLockChanged();
569}
570
571QQueue<QPhysicsCommand *> &QDynamicRigidBody::commandQueue()
572{
573 return m_commandQueue;
574}
575
576void QDynamicRigidBody::updateDefaultDensity(float defaultDensity)
577{
578 if (m_massMode == MassMode::DefaultDensity)
579 m_commandQueue.enqueue(t: new QPhysicsCommandSetDensity(defaultDensity));
580}
581
582void QDynamicRigidBody::applyCentralForce(const QVector3D &force)
583{
584 m_commandQueue.enqueue(t: new QPhysicsCommandApplyCentralForce(force));
585}
586
587void QDynamicRigidBody::applyForce(const QVector3D &force, const QVector3D &position)
588{
589 m_commandQueue.enqueue(t: new QPhysicsCommandApplyForce(force, position));
590}
591
592void QDynamicRigidBody::applyTorque(const QVector3D &torque)
593{
594 m_commandQueue.enqueue(t: new QPhysicsCommandApplyTorque(torque));
595}
596
597void QDynamicRigidBody::applyCentralImpulse(const QVector3D &impulse)
598{
599 m_commandQueue.enqueue(t: new QPhysicsCommandApplyCentralImpulse(impulse));
600}
601
602void QDynamicRigidBody::applyImpulse(const QVector3D &impulse, const QVector3D &position)
603{
604 m_commandQueue.enqueue(t: new QPhysicsCommandApplyImpulse(impulse, position));
605}
606
607void QDynamicRigidBody::applyTorqueImpulse(const QVector3D &impulse)
608{
609 m_commandQueue.enqueue(t: new QPhysicsCommandApplyTorqueImpulse(impulse));
610}
611
612void QDynamicRigidBody::setLinearVelocity(const QVector3D &linearVelocity)
613{
614 m_commandQueue.enqueue(t: new QPhysicsCommandSetLinearVelocity(linearVelocity));
615}
616
617void QDynamicRigidBody::reset(const QVector3D &position, const QVector3D &eulerRotation)
618{
619 m_commandQueue.enqueue(t: new QPhysicsCommandReset(position, eulerRotation));
620}
621
622void QDynamicRigidBody::setKinematicRotation(const QQuaternion &rotation)
623{
624 if (m_kinematicRotation == rotation)
625 return;
626
627 m_kinematicRotation = rotation;
628 emit kinematicRotationChanged(kinematicRotation: m_kinematicRotation);
629 emit kinematicEulerRotationChanged(kinematicEulerRotation: m_kinematicRotation.getEulerRotation());
630}
631
632QQuaternion QDynamicRigidBody::kinematicRotation() const
633{
634 return m_kinematicRotation.getQuaternionRotation();
635}
636
637void QDynamicRigidBody::setKinematicEulerRotation(const QVector3D &rotation)
638{
639 if (m_kinematicRotation == rotation)
640 return;
641
642 m_kinematicRotation = rotation;
643 emit kinematicEulerRotationChanged(kinematicEulerRotation: m_kinematicRotation);
644 emit kinematicRotationChanged(kinematicRotation: m_kinematicRotation.getQuaternionRotation());
645}
646
647QVector3D QDynamicRigidBody::kinematicEulerRotation() const
648{
649 return m_kinematicRotation.getEulerRotation();
650}
651
652void QDynamicRigidBody::setKinematicPivot(const QVector3D &pivot)
653{
654 m_kinematicPivot = pivot;
655 emit kinematicPivotChanged(kinematicPivot: m_kinematicPivot);
656}
657
658QVector3D QDynamicRigidBody::kinematicPivot() const
659{
660 return m_kinematicPivot;
661}
662
663QAbstractPhysXNode *QDynamicRigidBody::createPhysXBackend()
664{
665 return new QPhysXDynamicBody(this);
666}
667
668void QDynamicRigidBody::setKinematicPosition(const QVector3D &position)
669{
670 m_kinematicPosition = position;
671 emit kinematicPositionChanged(kinematicPosition: m_kinematicPosition);
672}
673
674QVector3D QDynamicRigidBody::kinematicPosition() const
675{
676 return m_kinematicPosition;
677}
678
679QT_END_NAMESPACE
680

source code of qtquick3dphysics/src/quick3dphysics/qdynamicrigidbody.cpp