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
5
6#include "PxMaterial.h"
7#include "PxPhysics.h"
8#include "PxRigidDynamic.h"
9#include "PxRigidActor.h"
10#include "PxScene.h"
11
12#include "physxnode/qphysxworld_p.h"
16#include "qplaneshape_p.h"
18
19#include <QtGui/qquaternion.h>
20
21#define PHYSX_RELEASE(x)
22 if (x != nullptr) {
23 x->release();
24 x = nullptr;
25 }
26
28
29static physx::PxTransform getPhysXLocalTransform(const QQuick3DNode *node)
30{
31 // Modify transforms to make the PhysX shapes match the QtQuick3D conventions
32 if (qobject_cast<const QPlaneShape *>(node) != nullptr) {
33 // Rotate the plane to make it match the built-in rectangle
34 const QQuaternion rotation = QPhysicsUtils::kMinus90YawRotation * node->rotation();
35 return physx::PxTransform(QPhysicsUtils::toPhysXType(node->position()),
36 QPhysicsUtils::toPhysXType(rotation));
37 } else if (auto *hf = qobject_cast<const QHeightFieldShape *>(node)) {
38 // Shift the height field so it's centered at the origin
39 return physx::PxTransform(QPhysicsUtils::toPhysXType(node->position() + hf->hfOffset()),
40 QPhysicsUtils::toPhysXType(node->rotation()));
41 }
42
43 const QQuaternion &rotation = node->rotation();
44 const QVector3D &localPosition = node->position();
45 const QVector3D &scale = node->sceneScale();
46 return physx::PxTransform(QPhysicsUtils::toPhysXType(localPosition * scale),
47 QPhysicsUtils::toPhysXType(rotation));
48}
49
50QPhysXActorBody::QPhysXActorBody(QAbstractPhysicsNode *frontEnd) : QAbstractPhysXNode(frontEnd) { }
51
53{
54 if (actor) {
55 physX->scene->removeActor(*actor);
57 }
59}
60
61void QPhysXActorBody::init(QPhysicsWorld * /*world*/, QPhysXWorld *physX)
62{
63 Q_ASSERT(!actor);
64
65 createMaterial(physX);
66 createActor(physX);
67
68 actor->userData = reinterpret_cast<void *>(frontendNode);
69 physX->scene->addActor(*actor);
70 setShapesDirty(true);
71}
72
73void QPhysXActorBody::sync(float /*deltaTime*/,
74 QHash<QQuick3DNode *, QMatrix4x4> & /*transformCache*/)
75{
76 auto *body = static_cast<QAbstractPhysicsBody *>(frontendNode);
77 if (QPhysicsMaterial *qtMaterial = body->physicsMaterial()) {
78 const float staticFriction = qtMaterial->staticFriction();
79 const float dynamicFriction = qtMaterial->dynamicFriction();
80 const float restitution = qtMaterial->restitution();
81 if (material->getStaticFriction() != staticFriction)
82 material->setStaticFriction(staticFriction);
83 if (material->getDynamicFriction() != dynamicFriction)
84 material->setDynamicFriction(dynamicFriction);
85 if (material->getRestitution() != restitution)
86 material->setRestitution(restitution);
87 }
88}
89
91{
92 if (!frontendNode || !actor)
93 return;
94
95 // Go through the shapes and look for a change in pose (rotation, position)
96 // TODO: it is likely cheaper to connect a signal for changes on the position and rotation
97 // property and mark the node dirty then.
98 if (!shapesDirty()) {
99 const auto &collisionShapes = frontendNode->getCollisionShapesList();
100 const auto &physXShapes = shapes;
101
102 const int len = collisionShapes.size();
103 if (physXShapes.size() != len) {
104 // This should not really happen but check it anyway
105 setShapesDirty(true);
106 } else {
107 for (int i = 0; i < len; i++) {
108 auto poseNew = getPhysXLocalTransform(collisionShapes[i]);
109 auto poseOld = physXShapes[i]->getLocalPose();
110
111 if (!QPhysicsUtils::fuzzyEquals(poseNew, poseOld)) {
112 setShapesDirty(true);
113 break;
114 }
115 }
116 }
117 }
118}
119
120void QPhysXActorBody::rebuildDirtyShapes(QPhysicsWorld * /*world*/, QPhysXWorld *physX)
121{
122 if (!shapesDirty())
123 return;
124 buildShapes(physX);
125 setShapesDirty(false);
126}
127
129{
131 const physx::PxTransform trf = QPhysicsUtils::toPhysXTransform(frontendNode->scenePosition(),
132 frontendNode->sceneRotation());
133 actor = s_physx.physics->createRigidDynamic(trf);
134}
135
137{
138 return true;
139}
140
142{
143 return actor->getGlobalPose();
144}
145
147{
148 auto body = actor;
149 for (auto *shape : shapes) {
150 body->detachShape(*shape);
151 PHYSX_RELEASE(shape);
152 }
153
154 // TODO: Only remove changed shapes?
155 shapes.clear();
156
157 for (const auto &collisionShape : frontendNode->getCollisionShapesList()) {
158 // TODO: shapes can be shared between multiple actors.
159 // Do we need to create new ones for every body?
160 auto *geom = collisionShape->getPhysXGeometry();
161 if (!geom || !material)
162 continue;
163
164 auto &s_physx = StaticPhysXObjects::getReference();
165 auto physXShape = s_physx.physics->createShape(*geom, *material);
166
167 if (useTriggerFlag()) {
168 physXShape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, false);
169 physXShape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, true);
170 }
171
172 { // Setup filtering
173 physx::PxFilterData filterData;
174 filterData.word0 = frontendNode->filterGroup();
175 filterData.word1 = frontendNode->filterIgnoreGroups();
176 physXShape->setSimulationFilterData(filterData);
177 }
178
179 shapes.push_back(physXShape);
180 physXShape->setLocalPose(getPhysXLocalTransform(collisionShape));
181 body->attachShape(*physXShape);
182 }
183
184 // Filters are always clean after building shapes
185 setFiltersDirty(false);
186}
187
189{
190 if (!filtersDirty())
191 return;
192
193 // Go through all shapes and set the filter group and mask.
194 // TODO: What about shared shapes on several actors?
195 for (auto &physXShape : shapes) {
196 physx::PxFilterData filterData;
197 filterData.word0 = frontendNode->filterGroup();
198 filterData.word1 = frontendNode->filterIgnoreGroups();
199 physXShape->setSimulationFilterData(filterData);
200 }
201
202 setFiltersDirty(false);
203}
204
QAbstractPhysicsNode * frontendNode
virtual void cleanup(QPhysXWorld *)
physx::PxMaterial * material
QAbstractPhysXNode(QAbstractPhysicsNode *node)
void setShapesDirty(bool dirty)
void setFiltersDirty(bool dirty)
virtual void createMaterial(QPhysXWorld *physX)
void markDirtyShapes() override
void buildShapes(QPhysXWorld *physX)
void rebuildDirtyShapes(QPhysicsWorld *world, QPhysXWorld *physX) override
void init(QPhysicsWorld *world, QPhysXWorld *physX) override
bool debugGeometryCapability() override
QPhysXActorBody(QAbstractPhysicsNode *frontEnd)
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:171
#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()