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
qphysxactorbody.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include "PxMaterial.h"
8#include "PxPhysics.h"
9#include "PxRigidDynamic.h"
10#include "PxRigidActor.h"
11#include "PxScene.h"
12
13#include "physxnode/qphysxworld_p.h"
17#include "qplaneshape_p.h"
19
20#include <QtGui/qquaternion.h>
21
22#define PHYSX_RELEASE(x)
23 if (x != nullptr) {
24 x->release();
25 x = nullptr;
26 }
27
29
30static physx::PxTransform getPhysXLocalTransform(const QQuick3DNode *node)
31{
32 // Modify transforms to make the PhysX shapes match the QtQuick3D conventions
33 if (qobject_cast<const QPlaneShape *>(node) != nullptr) {
34 // Rotate the plane to make it match the built-in rectangle
35 const QQuaternion rotation = QPhysicsUtils::kMinus90YawRotation * node->rotation();
36 return physx::PxTransform(QPhysicsUtils::toPhysXType(node->position()),
37 QPhysicsUtils::toPhysXType(rotation));
38 } else if (auto *hf = qobject_cast<const QHeightFieldShape *>(node)) {
39 // Shift the height field so it's centered at the origin
40 return physx::PxTransform(QPhysicsUtils::toPhysXType(node->position() + hf->hfOffset()),
41 QPhysicsUtils::toPhysXType(node->rotation()));
42 }
43
44 const QQuaternion &rotation = node->rotation();
45 const QVector3D &localPosition = node->position();
46 const QVector3D &scale = node->sceneScale();
47 return physx::PxTransform(QPhysicsUtils::toPhysXType(localPosition * scale),
48 QPhysicsUtils::toPhysXType(rotation));
49}
50
51QPhysXActorBody::QPhysXActorBody(QAbstractPhysicsNode *frontEnd) : QAbstractPhysXNode(frontEnd) { }
52
54{
55 if (actor) {
56 physX->scene->removeActor(*actor);
58 }
60}
61
62void QPhysXActorBody::init(QPhysicsWorld * /*world*/, QPhysXWorld *physX)
63{
64 Q_ASSERT(!actor);
65
67 createActor(physX);
68
69 actor->userData = reinterpret_cast<void *>(frontendNode);
70 physX->scene->addActor(*actor);
71 setShapesDirty(true);
72}
73
74void QPhysXActorBody::sync(float /*deltaTime*/,
75 QHash<QQuick3DNode *, QMatrix4x4> & /*transformCache*/)
76{
77 // The physicsMaterial property, and the properties of the material itself, can change while
78 // the simulation is running. Materials are shared between all nodes that need the same
79 // properties, so rather than modifying the material in place, the node is moved to the
80 // material with the new properties, and its shapes are pointed at it.
81 if (updateMaterial() && material) {
82 for (auto *shape : std::as_const(shapes))
83 shape->setMaterials(&material, 1);
84 }
85}
86
88{
89 if (!frontendNode || !actor)
90 return;
91
92 // Go through the shapes and look for a change in pose (rotation, position)
93 // TODO: it is likely cheaper to connect a signal for changes on the position and rotation
94 // property and mark the node dirty then.
95 if (!shapesDirty()) {
96 const auto &collisionShapes = frontendNode->getCollisionShapesList();
97 const auto &physXShapes = shapes;
98
99 const int len = collisionShapes.size();
100 if (physXShapes.size() != len) {
101 // This should not really happen but check it anyway
102 setShapesDirty(true);
103 } else {
104 for (int i = 0; i < len; i++) {
105 auto poseNew = getPhysXLocalTransform(collisionShapes[i]);
106 auto poseOld = physXShapes[i]->getLocalPose();
107
108 if (!QPhysicsUtils::fuzzyEquals(poseNew, poseOld)) {
109 setShapesDirty(true);
110 break;
111 }
112 }
113 }
114 }
115}
116
117void QPhysXActorBody::rebuildDirtyShapes(QPhysicsWorld * /*world*/, QPhysXWorld *physX)
118{
119 if (!shapesDirty())
120 return;
121 buildShapes(physX);
122 setShapesDirty(false);
123}
124
126{
128 physx::PxTransform trf = QPhysicsUtils::toPhysXTransform(frontendNode->scenePosition(),
129 frontendNode->sceneRotation());
130 if (!trf.isSane()) {
131 qWarning() << "PhysicsNode: position/rotation is not finite, using identity instead.";
132 trf = physx::PxTransform(physx::PxIdentity);
133 }
134 actor = s_physx.physics->createRigidDynamic(trf);
135}
136
138{
139 return true;
140}
141
143{
144 return actor->getGlobalPose();
145}
146
148{
149 auto body = actor;
150 for (auto *shape : std::as_const(shapes)) {
151 body->detachShape(*shape);
152 PHYSX_RELEASE(shape);
153 }
154
155 // TODO: Only remove changed shapes?
156 shapes.clear();
157
158 for (const auto &collisionShape : frontendNode->getCollisionShapesList()) {
159 auto *geom = collisionShape->getPhysXGeometry();
160 if (!geom || !material)
161 continue;
162
163 auto &s_physx = StaticPhysXObjects::getReference();
164 // The shape is created exclusive to this actor: a shape that may be shared between
165 // actors cannot be modified once it is attached to one, and these shapes are modified
166 // while attached, for instance when their filter data changes.
167 auto physXShape = s_physx.physics->createShape(*geom, *material, true);
168 if (!physXShape) {
169 qWarning() << "QtQuick3DPhysics: could not create shape, invalid geometry.";
170 continue;
171 }
172
173 if (useTriggerFlag()) {
174 physXShape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, false);
175 physXShape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, true);
176 }
177
178 { // Setup filtering
179 physx::PxFilterData filterData;
180 filterData.word0 = frontendNode->filterGroup();
181 filterData.word1 = frontendNode->filterIgnoreGroups();
182 physXShape->setSimulationFilterData(filterData);
183 }
184
185 shapes.push_back(physXShape);
186 physx::PxTransform localPose = getPhysXLocalTransform(collisionShape);
187 if (!localPose.isSane()) {
188 qWarning() << "QtQuick3DPhysics: collision shape position/rotation is not finite, "
189 "using identity local pose instead.";
190 localPose = physx::PxTransform(physx::PxIdentity);
191 }
192 physXShape->setLocalPose(localPose);
193 body->attachShape(*physXShape);
194 }
195
196 // Filters are always clean after building shapes
197 setFiltersDirty(false);
198}
199
201{
202 if (!filtersDirty())
203 return;
204
205 // Go through all shapes and set the filter group and mask.
206 // TODO: What about shared shapes on several actors?
207 for (auto &physXShape : shapes) {
208 physx::PxFilterData filterData;
209 filterData.word0 = frontendNode->filterGroup();
210 filterData.word1 = frontendNode->filterIgnoreGroups();
211 physXShape->setSimulationFilterData(filterData);
212 }
213
214 setFiltersDirty(false);
215}
216
virtual void cleanup(QPhysXWorld *)
void setShapesDirty(bool dirty)
void setFiltersDirty(bool dirty)
void markDirtyShapes() override
void buildShapes(QPhysXWorld *physX)
void rebuildDirtyShapes(QPhysicsWorld *world, QPhysXWorld *physX) override
void init(QPhysicsWorld *world, QPhysXWorld *physX) override
bool debugGeometryCapability() override
void cleanup(QPhysXWorld *physX) override
physx::PxRigidActor * actor
virtual void createActor(QPhysXWorld *physX)
void sync(float deltaTime, QHash< QQuick3DNode *, QMatrix4x4 > &transformCache) override
void updateFilters() override
physx::PxTransform getGlobalPose() override
physx::PxScene * scene
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:178
#define PHYSX_RELEASE(x)
static QT_BEGIN_NAMESPACE physx::PxTransform getPhysXLocalTransform(const QQuick3DNode *node)
#define QT_BEGIN_NAMESPACE
#define QT_END_NAMESPACE
static StaticPhysXObjects & getReference()