1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qphysxdynamicbody_p.h" |
5 | |
6 | #include "PxRigidDynamic.h" |
7 | |
8 | #include "qphysicscommands_p.h" |
9 | #include "qphysicsutils_p.h" |
10 | #include "qphysicsworld_p.h" |
11 | #include "qabstractphysicsbody_p.h" |
12 | #include "qdynamicrigidbody_p.h" |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | static void processCommandQueue(QQueue<QPhysicsCommand *> &commandQueue, |
17 | const QDynamicRigidBody &rigidBody, physx::PxRigidBody &body) |
18 | { |
19 | for (auto command : commandQueue) { |
20 | command->execute(rigidBody, body); |
21 | delete command; |
22 | } |
23 | |
24 | commandQueue.clear(); |
25 | } |
26 | |
27 | static QMatrix4x4 calculateKinematicNodeTransform(QQuick3DNode *node, |
28 | QHash<QQuick3DNode *, QMatrix4x4> &transformCache) |
29 | { |
30 | // already calculated transform |
31 | if (transformCache.contains(key: node)) |
32 | return transformCache[node]; |
33 | |
34 | QMatrix4x4 localTransform; |
35 | |
36 | // DynamicRigidBody vs StaticRigidBody use different values for calculating the local transform |
37 | if (auto drb = qobject_cast<const QDynamicRigidBody *>(object: node); drb != nullptr) { |
38 | if (!drb->isKinematic()) { |
39 | qWarning() << "Non-kinematic body as a parent of a kinematic body is unsupported" ; |
40 | } |
41 | localTransform = QSSGRenderNode::calculateTransformMatrix( |
42 | position: drb->kinematicPosition(), scale: drb->scale(), pivot: drb->kinematicPivot(), |
43 | rotation: drb->kinematicRotation()); |
44 | } else { |
45 | localTransform = QSSGRenderNode::calculateTransformMatrix(position: node->position(), scale: node->scale(), |
46 | pivot: node->pivot(), rotation: node->rotation()); |
47 | } |
48 | |
49 | QQuick3DNode *parent = node->parentNode(); |
50 | if (!parent) // no parent, local transform is scene transform |
51 | return localTransform; |
52 | |
53 | // calculate the parent scene transform and apply the nodes local transform |
54 | QMatrix4x4 parentTransform = calculateKinematicNodeTransform(node: parent, transformCache); |
55 | QMatrix4x4 sceneTransform = parentTransform * localTransform; |
56 | |
57 | transformCache[node] = sceneTransform; |
58 | return sceneTransform; |
59 | } |
60 | |
61 | static physx::PxRigidDynamicLockFlags getLockFlags(QDynamicRigidBody *body) |
62 | { |
63 | const auto lockAngular = body->angularAxisLock(); |
64 | const auto lockLinear = body->linearAxisLock(); |
65 | const int flags = (lockAngular & QDynamicRigidBody::AxisLock::LockX |
66 | ? physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_X |
67 | : 0) |
68 | | (lockAngular & QDynamicRigidBody::AxisLock::LockY |
69 | ? physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y |
70 | : 0) |
71 | | (lockAngular & QDynamicRigidBody::AxisLock::LockZ |
72 | ? physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z |
73 | : 0) |
74 | | (lockLinear & QDynamicRigidBody::AxisLock::LockX |
75 | ? physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_X |
76 | : 0) |
77 | | (lockLinear & QDynamicRigidBody::AxisLock::LockY |
78 | ? physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Y |
79 | : 0) |
80 | | (lockLinear & QDynamicRigidBody::AxisLock::LockZ |
81 | ? physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Z |
82 | : 0); |
83 | return static_cast<physx::PxRigidDynamicLockFlags>(flags); |
84 | } |
85 | |
86 | static physx::PxTransform getPhysXWorldTransform(const QMatrix4x4 transform) |
87 | { |
88 | auto rotationMatrix = transform; |
89 | QSSGUtils::mat44::normalize(m&: rotationMatrix); |
90 | auto rotation = |
91 | QQuaternion::fromRotationMatrix(rot3x3: QSSGUtils::mat44::getUpper3x3(m: rotationMatrix)).normalized(); |
92 | const QVector3D worldPosition = QSSGUtils::mat44::getPosition(m: transform); |
93 | return physx::PxTransform(QPhysicsUtils::toPhysXType(qvec: worldPosition), |
94 | QPhysicsUtils::toPhysXType(qquat: rotation)); |
95 | } |
96 | |
97 | QPhysXDynamicBody::QPhysXDynamicBody(QDynamicRigidBody *frontEnd) : QPhysXRigidBody(frontEnd) { } |
98 | |
99 | DebugDrawBodyType QPhysXDynamicBody::getDebugDrawBodyType() |
100 | { |
101 | auto dynamicActor = static_cast<physx::PxRigidDynamic *>(actor); |
102 | return dynamicActor->isSleeping() ? DebugDrawBodyType::DynamicSleeping |
103 | : DebugDrawBodyType::DynamicAwake; |
104 | } |
105 | |
106 | void QPhysXDynamicBody::sync(float deltaTime, QHash<QQuick3DNode *, QMatrix4x4> &transformCache) |
107 | { |
108 | auto *dynamicRigidBody = static_cast<QDynamicRigidBody *>(frontendNode); |
109 | // first update front end node from physx simulation |
110 | dynamicRigidBody->updateFromPhysicsTransform(transform: actor->getGlobalPose()); |
111 | |
112 | auto *dynamicActor = static_cast<physx::PxRigidDynamic *>(actor); |
113 | processCommandQueue(commandQueue&: dynamicRigidBody->commandQueue(), rigidBody: *dynamicRigidBody, body&: *dynamicActor); |
114 | if (dynamicRigidBody->isKinematic()) { |
115 | // Since this is a kinematic body we need to calculate the transform by hand and since |
116 | // bodies can occur in other bodies we need to calculate the tranform recursively for all |
117 | // parents. To save some computation we cache these transforms in 'transformCache'. |
118 | QMatrix4x4 transform = calculateKinematicNodeTransform(node: dynamicRigidBody, transformCache); |
119 | dynamicActor->setKinematicTarget(getPhysXWorldTransform(transform)); |
120 | } else { |
121 | dynamicActor->setRigidDynamicLockFlags(getLockFlags(body: dynamicRigidBody)); |
122 | } |
123 | |
124 | const bool disabledPrevious = actor->getActorFlags() & physx::PxActorFlag::eDISABLE_SIMULATION; |
125 | const bool disabled = !dynamicRigidBody->simulationEnabled(); |
126 | if (disabled != disabledPrevious) { |
127 | actor->setActorFlag(flag: physx::PxActorFlag::eDISABLE_SIMULATION, value: disabled); |
128 | if (!disabled && !dynamicRigidBody->isKinematic()) |
129 | dynamicActor->wakeUp(); |
130 | } |
131 | |
132 | QPhysXActorBody::sync(deltaTime, transformCache); |
133 | } |
134 | |
135 | void QPhysXDynamicBody::rebuildDirtyShapes(QPhysicsWorld *world, QPhysXWorld *physX) |
136 | { |
137 | if (!shapesDirty()) |
138 | return; |
139 | |
140 | buildShapes(physX); |
141 | |
142 | QDynamicRigidBody *drb = static_cast<QDynamicRigidBody *>(frontendNode); |
143 | |
144 | // Density must be set after shapes so the inertia tensor is set |
145 | if (!drb->hasStaticShapes()) { |
146 | // Body with only dynamic shapes, set/calculate mass |
147 | QPhysicsCommand *command = nullptr; |
148 | switch (drb->massMode()) { |
149 | case QDynamicRigidBody::MassMode::DefaultDensity: { |
150 | command = new QPhysicsCommandSetDensity(world->defaultDensity()); |
151 | break; |
152 | } |
153 | case QDynamicRigidBody::MassMode::CustomDensity: { |
154 | command = new QPhysicsCommandSetDensity(drb->density()); |
155 | break; |
156 | } |
157 | case QDynamicRigidBody::MassMode::Mass: { |
158 | const float mass = qMax(a: drb->mass(), b: 0.f); |
159 | command = new QPhysicsCommandSetMass(mass); |
160 | break; |
161 | } |
162 | case QDynamicRigidBody::MassMode::MassAndInertiaTensor: { |
163 | const float mass = qMax(a: drb->mass(), b: 0.f); |
164 | command = new QPhysicsCommandSetMassAndInertiaTensor(mass, drb->inertiaTensor()); |
165 | break; |
166 | } |
167 | case QDynamicRigidBody::MassMode::MassAndInertiaMatrix: { |
168 | const float mass = qMax(a: drb->mass(), b: 0.f); |
169 | command = new QPhysicsCommandSetMassAndInertiaMatrix(mass, drb->inertiaMatrix()); |
170 | break; |
171 | } |
172 | } |
173 | |
174 | drb->commandQueue().enqueue(t: command); |
175 | } else if (!drb->isKinematic()) { |
176 | // Body with static shapes that is not kinematic, this is disallowed |
177 | qWarning() << "Cannot make body containing trimesh/heightfield/plane non-kinematic, " |
178 | "forcing kinematic." ; |
179 | drb->setIsKinematic(true); |
180 | } |
181 | |
182 | auto *dynamicBody = static_cast<physx::PxRigidDynamic *>(actor); |
183 | dynamicBody->setRigidBodyFlag(flag: physx::PxRigidBodyFlag::eKINEMATIC, value: drb->isKinematic()); |
184 | |
185 | if (world->enableCCD() && !drb->isKinematic()) // CCD not supported for kinematic bodies |
186 | dynamicBody->setRigidBodyFlag(flag: physx::PxRigidBodyFlag::eENABLE_CCD, value: true); |
187 | |
188 | setShapesDirty(false); |
189 | } |
190 | |
191 | void QPhysXDynamicBody::updateDefaultDensity(float density) |
192 | { |
193 | QDynamicRigidBody *rigidBody = static_cast<QDynamicRigidBody *>(frontendNode); |
194 | rigidBody->updateDefaultDensity(defaultDensity: density); |
195 | } |
196 | |
197 | QT_END_NAMESPACE |
198 | |