Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qphysxworld.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
4#include "qphysxworld_p.h"
5
6#include "characterkinematic/PxControllerManager.h"
7#include "cooking/PxCooking.h"
8#include "extensions/PxDefaultCpuDispatcher.h"
9#include "pvd/PxPvdTransport.h"
10#include "PxFoundation.h"
11#include "PxPhysics.h"
12#include "PxPhysicsVersion.h"
13#include "PxRigidActor.h"
14#include "PxScene.h"
15#include "PxSimulationEventCallback.h"
16
18#include "qphysicsutils_p.h"
19#include "qphysicsworld_p.h"
21#include "qtriggerbody_p.h"
22
24
25class SimulationEventCallback : public physx::PxSimulationEventCallback
26{
27public:
28 SimulationEventCallback(QPhysicsWorld *worldIn) : world(worldIn) {};
29 virtual ~SimulationEventCallback() = default;
30
31 void onTrigger(physx::PxTriggerPair *pairs, physx::PxU32 count) override
32 {
33 QMutexLocker locker(&world->m_removedPhysicsNodesMutex);
34
35 for (physx::PxU32 i = 0; i < count; i++) {
36 // ignore pairs when shapes have been deleted
37 if (pairs[i].flags
38 & (physx::PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER
39 | physx::PxTriggerPairFlag::eREMOVED_SHAPE_OTHER))
40 continue;
41
42 QTriggerBody *triggerNode =
43 static_cast<QTriggerBody *>(pairs[i].triggerActor->userData);
44
45 QAbstractPhysicsNode *otherNode =
46 static_cast<QAbstractPhysicsNode *>(pairs[i].otherActor->userData);
47
48 if (!triggerNode || !otherNode) {
49 qWarning() << "QtQuick3DPhysics internal error: null pointer in trigger collision.";
50 continue;
51 }
52
53 if (world->isNodeRemoved(triggerNode) || world->isNodeRemoved(otherNode))
54 continue;
55
56 if (pairs->status == physx::PxPairFlag::eNOTIFY_TOUCH_FOUND) {
57 if (otherNode->sendTriggerReports()) {
58 triggerNode->registerCollision(otherNode);
59 }
60 if (otherNode->receiveTriggerReports()) {
61 emit otherNode->enteredTriggerBody(triggerNode);
62 }
63 } else if (pairs->status == physx::PxPairFlag::eNOTIFY_TOUCH_LOST) {
64 if (otherNode->sendTriggerReports()) {
65 triggerNode->deregisterCollision(otherNode);
66 }
67 if (otherNode->receiveTriggerReports()) {
68 emit otherNode->exitedTriggerBody(triggerNode);
69 }
70 }
71 }
72 }
73
74 void onConstraintBreak(physx::PxConstraintInfo * /*constraints*/,
75 physx::PxU32 /*count*/) override {};
76 void onWake(physx::PxActor ** /*actors*/, physx::PxU32 /*count*/) override {};
77 void onSleep(physx::PxActor ** /*actors*/, physx::PxU32 /*count*/) override {};
78 void onContact(const physx::PxContactPairHeader &pairHeader, const physx::PxContactPair *pairs,
79 physx::PxU32 nbPairs) override
80 {
81 QMutexLocker locker(&world->m_removedPhysicsNodesMutex);
82 constexpr physx::PxU32 bufferSize = 64;
83 physx::PxContactPairPoint contacts[bufferSize];
84
85 for (physx::PxU32 i = 0; i < nbPairs; i++) {
86 const physx::PxContactPair &contactPair = pairs[i];
87
88 if (contactPair.events & physx::PxPairFlag::eNOTIFY_TOUCH_FOUND) {
89 QAbstractPhysicsNode *trigger =
90 static_cast<QAbstractPhysicsNode *>(pairHeader.actors[0]->userData);
92 static_cast<QAbstractPhysicsNode *>(pairHeader.actors[1]->userData);
93
94 if (!trigger || !other || world->isNodeRemoved(trigger)
95 || world->isNodeRemoved(other) || !trigger->m_backendObject
96 || !other->m_backendObject)
97 continue;
98
99 const bool triggerReceive =
100 trigger->receiveContactReports() && other->sendContactReports();
101 const bool otherReceive =
102 other->receiveContactReports() && trigger->sendContactReports();
103
104 if (!triggerReceive && !otherReceive)
105 continue;
106
107 physx::PxU32 nbContacts = pairs[i].extractContacts(contacts, bufferSize);
108
109 QList<QVector3D> positions;
110 QList<QVector3D> impulses;
111 QList<QVector3D> normals;
112
113 positions.reserve(nbContacts);
114 impulses.reserve(nbContacts);
115 normals.reserve(nbContacts);
116
117 for (physx::PxU32 j = 0; j < nbContacts; j++) {
118 physx::PxVec3 position = contacts[j].position;
119 physx::PxVec3 impulse = contacts[j].impulse;
120 physx::PxVec3 normal = contacts[j].normal;
121
123 impulses.push_back(QPhysicsUtils::toQtType(impulse));
124 normals.push_back(QPhysicsUtils::toQtType(normal));
125 }
126
127 QList<QVector3D> normalsInverted;
128 normalsInverted.reserve(normals.size());
129 for (const QVector3D &v : normals) {
130 normalsInverted.push_back(QVector3D(-v.x(), -v.y(), -v.z()));
131 }
132
133 if (triggerReceive)
134 world->registerContact(other, trigger, positions, impulses, normals);
135 if (otherReceive)
136 world->registerContact(trigger, other, positions, impulses, normalsInverted);
137 }
138 }
139 };
140 void onAdvance(const physx::PxRigidBody *const * /*bodyBuffer*/,
141 const physx::PxTransform * /*poseBuffer*/,
142 const physx::PxU32 /*count*/) override {};
143
144private:
145 QPhysicsWorld *world = nullptr;
146};
147
148static constexpr bool isBitSet(quint32 value, quint32 position)
149{
150 Q_ASSERT(position <= 32);
151 return value & (1 << (position));
152}
153
154static physx::PxFilterFlags
155contactReportFilterShader(physx::PxFilterObjectAttributes /*attributes0*/,
156 physx::PxFilterData filterData0,
157 physx::PxFilterObjectAttributes /*attributes1*/,
158 physx::PxFilterData filterData1, physx::PxPairFlags &pairFlags,
159 const void * /*constantBlock*/, physx::PxU32 /*constantBlockSize*/)
160{
161 // First word is id, second is collision mask
162 const quint32 id0 = filterData0.word0;
163 const quint32 id1 = filterData1.word0;
164 const quint32 mask0 = filterData0.word1;
165 const quint32 mask1 = filterData1.word1;
166
167 // If any 'id' bit is set in the other mask it means collisions should be ignored
168 if (id0 < 32 && id1 < 32 && (isBitSet(mask0, id1) || isBitSet(mask1, id0))) {
169 // We return a 'suppress' since that will still re-evaluate when filter data is changed.
170 return physx::PxFilterFlag::eSUPPRESS;
171 }
172
173 // Makes objects collide
174 const auto defaultCollisonFlags =
175 physx::PxPairFlag::eSOLVE_CONTACT | physx::PxPairFlag::eDETECT_DISCRETE_CONTACT;
176
177 // For trigger body detection
178 const auto notifyTouchFlags =
179 physx::PxPairFlag::eNOTIFY_TOUCH_FOUND | physx::PxPairFlag::eNOTIFY_TOUCH_LOST;
180
181 // For contact detection
182 const auto notifyContactFlags = physx::PxPairFlag::eNOTIFY_CONTACT_POINTS;
183
184 pairFlags = defaultCollisonFlags | notifyTouchFlags | notifyContactFlags;
185 return physx::PxFilterFlag::eDEFAULT;
186}
187
188static physx::PxFilterFlags
189contactReportFilterShaderCCD(physx::PxFilterObjectAttributes /*attributes0*/,
190 physx::PxFilterData /*filterData0*/,
191 physx::PxFilterObjectAttributes /*attributes1*/,
192 physx::PxFilterData /*filterData1*/, physx::PxPairFlags &pairFlags,
193 const void * /*constantBlock*/, physx::PxU32 /*constantBlockSize*/)
194{
195 // Makes objects collide
196 const auto defaultCollisonFlags = physx::PxPairFlag::eSOLVE_CONTACT
197 | physx::PxPairFlag::eDETECT_DISCRETE_CONTACT | physx::PxPairFlag::eDETECT_CCD_CONTACT;
198
199 // For trigger body detection
200 const auto notifyTouchFlags =
201 physx::PxPairFlag::eNOTIFY_TOUCH_FOUND | physx::PxPairFlag::eNOTIFY_TOUCH_LOST;
202
203 // For contact detection
204 const auto notifyContactFlags = physx::PxPairFlag::eNOTIFY_CONTACT_POINTS;
205
206 pairFlags = defaultCollisonFlags | notifyTouchFlags | notifyContactFlags;
207 return physx::PxFilterFlag::eDEFAULT;
208}
209
210#define PHYSX_RELEASE(x) \
211 if (x != nullptr) { \
212 x->release(); \
213 x = nullptr; \
214 }
215
217{
218 auto &s_physx = StaticPhysXObjects::getReference();
219 s_physx.foundationRefCount++;
220
221 if (s_physx.foundationCreated)
222 return;
223
224 s_physx.foundation = PxCreateFoundation(
225 PX_PHYSICS_VERSION, s_physx.defaultAllocatorCallback, s_physx.defaultErrorCallback);
226 if (!s_physx.foundation)
227 qFatal("PxCreateFoundation failed!");
228
229 s_physx.foundationCreated = true;
230
231#if PHYSX_ENABLE_PVD
232 s_physx.pvd = PxCreatePvd(*m_physx->foundation);
233 s_physx.transport = physx::PxDefaultPvdSocketTransportCreate("qt", 5425, 10);
234 s_physx.pvd->connect(*m_physx->transport, physx::PxPvdInstrumentationFlag::eALL);
235#endif
236
237 // FIXME: does the tolerance matter?
238 s_physx.cooking = PxCreateCooking(PX_PHYSICS_VERSION, *s_physx.foundation,
239 physx::PxCookingParams(physx::PxTolerancesScale()));
240
241}
242
244{
245 auto &s_physx = StaticPhysXObjects::getReference();
246 s_physx.foundationRefCount--;
247 if (s_physx.foundationRefCount == 0) {
250 PHYSX_RELEASE(s_physx.dispatcher);
251 PHYSX_RELEASE(s_physx.cooking);
252 PHYSX_RELEASE(s_physx.transport);
253 PHYSX_RELEASE(s_physx.pvd);
254 PHYSX_RELEASE(s_physx.physics);
255 PHYSX_RELEASE(s_physx.foundation);
256
257 delete callback;
258 callback = nullptr;
259 s_physx.foundationCreated = false;
260 s_physx.physicsCreated = false;
261 } else {
262 delete callback;
263 callback = nullptr;
266 }
267}
268
269void QPhysXWorld::createScene(float typicalLength, float typicalSpeed, const QVector3D &gravity,
270 bool enableCCD, QPhysicsWorld *physicsWorld, unsigned int numThreads)
271{
272 if (scene) {
273 qWarning() << "Scene already created";
274 return;
275 }
276
277 physx::PxTolerancesScale scale;
278 scale.length = typicalLength;
279 scale.speed = typicalSpeed;
280
281 auto &s_physx = StaticPhysXObjects::getReference();
282
283 if (!s_physx.physicsCreated) {
284 constexpr bool recordMemoryAllocations = true;
285 s_physx.physics = PxCreatePhysics(PX_PHYSICS_VERSION, *s_physx.foundation, scale,
286 recordMemoryAllocations, s_physx.pvd);
287 if (!s_physx.physics)
288 qFatal("PxCreatePhysics failed!");
289
290 s_physx.dispatcher = physx::PxDefaultCpuDispatcherCreate(numThreads);
291 s_physx.physicsCreated = true;
292 }
293
294 callback = new SimulationEventCallback(physicsWorld);
295
296 physx::PxSceneDesc sceneDesc(scale);
297 sceneDesc.gravity = QPhysicsUtils::toPhysXType(gravity);
298 sceneDesc.cpuDispatcher = s_physx.dispatcher;
299
300 if (enableCCD) {
301 sceneDesc.filterShader = contactReportFilterShaderCCD;
302 sceneDesc.flags |= physx::PxSceneFlag::eENABLE_CCD;
303 } else {
304 sceneDesc.filterShader = contactReportFilterShader;
305 }
306 sceneDesc.solverType = physx::PxSolverType::eTGS;
307 sceneDesc.simulationEventCallback = callback;
308
309 if (physicsWorld->reportKinematicKinematicCollisions())
310 sceneDesc.kineKineFilteringMode = physx::PxPairFilteringMode::eKEEP;
311 if (physicsWorld->reportStaticKinematicCollisions())
312 sceneDesc.staticKineFilteringMode = physx::PxPairFilteringMode::eKEEP;
313
314 scene = s_physx.physics->createScene(sceneDesc);
315}
316
\inmodule QtCore
Definition qmutex.h:313
void createWorld()
SimulationEventCallback * callback
void deleteWorld()
physx::PxScene * scene
void createScene(float typicalLength, float typicalSpeed, const QVector3D &gravity, bool enableCCD, QPhysicsWorld *physicsWorld, unsigned int numThreads)
physx::PxControllerManager * controllerManager
bool isNodeRemoved(QAbstractPhysicsNode *object)
void registerContact(QAbstractPhysicsNode *sender, QAbstractPhysicsNode *receiver, const QVector< QVector3D > &positions, const QVector< QVector3D > &impulses, const QVector< QVector3D > &normals)
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
SimulationEventCallback(QPhysicsWorld *worldIn)
void onConstraintBreak(physx::PxConstraintInfo *, physx::PxU32) override
virtual ~SimulationEventCallback()=default
void onContact(const physx::PxContactPairHeader &pairHeader, const physx::PxContactPair *pairs, physx::PxU32 nbPairs) override
void onAdvance(const physx::PxRigidBody *const *, const physx::PxTransform *, const physx::PxU32) override
void onWake(physx::PxActor **, physx::PxU32) override
void onTrigger(physx::PxTriggerPair *pairs, physx::PxU32 count) override
void onSleep(physx::PxActor **, physx::PxU32) override
Q_ALWAYS_INLINE physx::PxVec3 toPhysXType(const QVector3D &qvec)
Q_ALWAYS_INLINE QVector3D toQtType(const physx::PxVec3 &vec)
Combined button and popup list for selecting options.
#define PHYSX_RELEASE(x)
static const QCssKnownValue positions[NumKnownPositionModes - 1]
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
GLsizei const GLfloat * v
[13]
GLenum GLenum GLsizei count
GLbitfield flags
GLenum GLenum GLenum GLenum GLenum scale
static physx::PxFilterFlags contactReportFilterShaderCCD(physx::PxFilterObjectAttributes, physx::PxFilterData, physx::PxFilterObjectAttributes, physx::PxFilterData, physx::PxPairFlags &pairFlags, const void *, physx::PxU32)
static constexpr bool isBitSet(quint32 value, quint32 position)
static physx::PxFilterFlags contactReportFilterShader(physx::PxFilterObjectAttributes, physx::PxFilterData filterData0, physx::PxFilterObjectAttributes, physx::PxFilterData filterData1, physx::PxPairFlags &pairFlags, const void *, physx::PxU32)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
unsigned int quint32
Definition qtypes.h:50
QSharedPointer< T > other(t)
[5]
static StaticPhysXObjects & getReference()