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

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