7#include "physxnode/qabstractphysxnode_p.h"
8#include "physxnode/qphysxworld_p.h"
12#include "joints/qjoint_p.h"
24#include "PxPhysicsAPI.h"
25#include "cooking/PxCooking.h"
26#include <foundation/PxSimpleTypes.h>
28#include <QtQuick/private/qquickframeanimation_p.h>
29#include <QtQuick3D/private/qquick3dobject_p.h>
30#include <QtQuick3D/private/qquick3dnode_p.h>
31#include <QtQuick3D/private/qquick3dmodel_p.h>
32#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
33#include <QtQuick3DUtils/private/qssgutils_p.h>
35#include <QtCore/qvarlengtharray.h>
36#include <QtGui/qquaternion.h>
38#include <QtEnvironmentVariables>
43
44
45
46
47
48
49
50
51
54
55
56
57
58
61
62
63
66
67
68
69
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
97
98
99
100
101
102
103
106
107
108
109
110
111
112
113
114
115
118
119
120
121
122
123
124
125
126
127
130
131
132
133
134
135
138
139
140
141
142
143
144
145
146
147
148
151
152
153
154
155
156
157
158
159
160
161
164
165
166
167
168
169
170
171
174
175
176
177
178
179
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
214
215
216
217
218
219
220
221
222
223
224
225
226
229
230
231
232
233
234
235
236
237
238
239
240
241
242
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
591 qWarning() <<
"QtQuick3DPhysics: cannot run a scene query before the physics world is "
596 if (!(includeStatic || includeDynamic)) {
597 qWarning() <<
"QtQuick3DPhysics: a scene query needs includeStatic or includeDynamic to "
611 physx::PxRaycastCallback &hitCallback,
612 physx::PxHitFlags hitFlags,
613 physx::PxQueryFlags extraQueryFlags = {})
618 const physx::PxVec3 pxOrigin = QPhysicsUtils::toPhysXType(origin);
619 if (!pxOrigin.isFinite()) {
620 qWarning() <<
"QtQuick3DPhysics: the query origin must be finite.";
624 const physx::PxVec3 pxDirection = QPhysicsUtils::toPhysXType(direction.normalized());
625 if (!pxDirection.isNormalized()) {
626 qWarning() <<
"QtQuick3DPhysics: the query direction must be a non-null, finite vector.";
630 if (!qIsFinite(maxDistance) || maxDistance <= 0) {
631 qWarning() <<
"QtQuick3DPhysics: the query distance must be finite and greater than 0.";
636 physx::PxQueryFlags filter = extraQueryFlags;
638 filter |= physx::PxQueryFlag::eSTATIC;
640 filter |= physx::PxQueryFlag::eDYNAMIC;
643 return scene->raycast(pxOrigin, pxDirection, maxDistance, hitCallback, hitFlags,
644 physx::PxQueryFilterData(filter));
648 QAbstractCollisionShape *shape,
656 const physx::PxGeometry *geometry = shape ? shape->getPhysXGeometry() :
nullptr;
658 qWarning() <<
"QtQuick3DPhysics: the query shape has no geometry yet.";
664 switch (geometry->getType()) {
665 case physx::PxGeometryType::eSPHERE:
666 case physx::PxGeometryType::eCAPSULE:
667 case physx::PxGeometryType::eBOX:
668 case physx::PxGeometryType::eCONVEXMESH:
671 qWarning() <<
"QtQuick3DPhysics: sweep and overlap queries only support SphereShape, "
672 "CapsuleShape, BoxShape and ConvexMeshShape as the query shape.";
680 QAbstractCollisionShape *shape,
685 physx::PxSweepCallback &hitCallback,
686 physx::PxHitFlags hitFlags,
687 physx::PxQueryFlags extraQueryFlags = {})
689 const physx::PxGeometry *geometry = validateQueryShape(scene, shape, includeStatic, includeDynamic);
694 const physx::PxVec3 pxDirection = QPhysicsUtils::toPhysXType(direction.normalized());
695 if (!pxDirection.isNormalized()) {
696 qWarning() <<
"QtQuick3DPhysics: the query direction must be a non-null, finite vector.";
700 if (!qIsFinite(maxDistance) || maxDistance <= 0) {
701 qWarning() <<
"QtQuick3DPhysics: the query distance must be finite and greater than 0.";
706 physx::PxQueryFlags filter = extraQueryFlags;
708 filter |= physx::PxQueryFlag::eSTATIC;
710 filter |= physx::PxQueryFlag::eDYNAMIC;
712 const physx::
PxTransform pose(QPhysicsUtils::toPhysXType(shape->scenePosition()),
713 QPhysicsUtils::toPhysXType(shape->sceneRotation()));
714 if (!pose.isSane()) {
715 qWarning() <<
"QtQuick3DPhysics: the query position/rotation is not finite.";
720 return scene->sweep(*geometry, pose, pxDirection, maxDistance, hitCallback, hitFlags,
721 physx::PxQueryFilterData(filter));
725 QAbstractCollisionShape *shape,
728 physx::PxOverlapCallback &hitCallback,
729 physx::PxQueryFlags extraQueryFlags = {})
732 const physx::PxGeometry *geometry = validateQueryShape(scene, shape, includeStatic, includeDynamic);
738 physx::PxQueryFlags filter = extraQueryFlags;
740 filter |= physx::PxQueryFlag::eSTATIC;
742 filter |= physx::PxQueryFlag::eDYNAMIC;
744 const physx::
PxTransform pose(QPhysicsUtils::toPhysXType(shape->scenePosition()),
745 QPhysicsUtils::toPhysXType(shape->sceneRotation()));
746 if (!pose.isSane()) {
747 qWarning() <<
"QtQuick3DPhysics: the query position/rotation is not finite.";
752 return scene->overlap(*geometry, pose, hitCallback,
753 physx::PxQueryFilterData(filter));
774template<
class PxCbT,
class HitT,
class PxHitT, physx::PxU32 BufferSize = 16>
775struct DynamicQueryCallback :
public PxCbT
777 DynamicQueryCallback() : PxCbT(localBuffer, BufferSize) {}
779 physx::PxAgain processTouches(
const PxHitT *buffer, physx::PxU32 nbHits)
override
781 hits.reserve(hits.size() + nbHits);
782 for (physx::PxU32 i = 0; i < nbHits; ++i)
783 hits.emplaceBack(buffer[i], buffer[i]);
787 PxHitT localBuffer[BufferSize];
795void QPhysicsWorld::DebugModelHolder::releaseMeshPointer()
797 if (
auto base =
static_cast<physx::PxBase *>(ptr); base)
802const QVector3D &QPhysicsWorld::DebugModelHolder::halfExtents()
const
806void QPhysicsWorld::DebugModelHolder::setHalfExtents(
const QVector3D &halfExtents)
810float QPhysicsWorld::DebugModelHolder::radius()
const
814void QPhysicsWorld::DebugModelHolder::setRadius(
float radius)
818float QPhysicsWorld::DebugModelHolder::heightScale()
const
822void QPhysicsWorld::DebugModelHolder::setHeightScale(
float heightScale)
824 data.setX(heightScale);
826float QPhysicsWorld::DebugModelHolder::halfHeight()
const
830void QPhysicsWorld::DebugModelHolder::setHalfHeight(
float halfHeight)
832 data.setY(halfHeight);
834float QPhysicsWorld::DebugModelHolder::rowScale()
const
838void QPhysicsWorld::DebugModelHolder::setRowScale(
float rowScale)
842float QPhysicsWorld::DebugModelHolder::columnScale()
const
846void QPhysicsWorld::DebugModelHolder::setColumnScale(
float columnScale)
848 data.setZ(columnScale);
850physx::PxConvexMesh *QPhysicsWorld::DebugModelHolder::getConvexMesh()
852 return static_cast<physx::PxConvexMesh *>(ptr);
854void QPhysicsWorld::DebugModelHolder::setConvexMesh(physx::PxConvexMesh *mesh)
856 ptr =
static_cast<
void *>(mesh);
858physx::PxTriangleMesh *QPhysicsWorld::DebugModelHolder::getTriangleMesh()
860 return static_cast<physx::PxTriangleMesh *>(ptr);
862void QPhysicsWorld::DebugModelHolder::setTriangleMesh(physx::PxTriangleMesh *mesh)
864 ptr =
static_cast<
void *>(mesh);
866physx::PxHeightField *QPhysicsWorld::DebugModelHolder::getHeightField()
868 return static_cast<physx::PxHeightField *>(ptr);
870void QPhysicsWorld::DebugModelHolder::setHeightField(physx::PxHeightField *hf)
872 ptr =
static_cast<physx::PxHeightField *>(hf);
886void QPhysicsWorld::registerNode(QAbstractPhysicsNode *physicsNode)
888 auto world = getWorld(physicsNode);
890 world->m_newPhysicsNodes.push_back(physicsNode);
892 worldManager.orphanNodes.push_back(physicsNode);
896void QPhysicsWorld::deregisterNode(QAbstractPhysicsNode *physicsNode)
898 for (
auto world : std::as_const(worldManager.worlds)) {
899 world->m_newPhysicsNodes.removeAll(physicsNode);
900 if (physicsNode->m_backendObject) {
901 Q_ASSERT(physicsNode->m_backendObject->frontendNode == physicsNode);
902 physicsNode->m_backendObject->detachFrontend();
903 physicsNode->m_backendObject->frontendNode =
nullptr;
904 physicsNode->m_backendObject->isRemoved =
true;
905 physicsNode->m_backendObject =
nullptr;
907 world->m_removedPhysicsNodes.insert(physicsNode);
909 worldManager.orphanNodes.removeAll(physicsNode);
912void QPhysicsWorld::registerJoint(QPhysicsJoint *joint)
914 auto world = getWorld(joint);
916 world->m_joints.push_back(joint);
918 worldManager.orphanJoints.push_back(joint);
922void QPhysicsWorld::deregisterJoint(QPhysicsJoint *joint)
924 for (
auto world : worldManager.worlds) {
925 world->m_removedJoints.insert(joint->getPhysXBackend());
928 qsizetype idx = world->m_joints.indexOf(joint);
930 world->m_joints.swapItemsAt(idx, world->m_joints.size() - 1);
931 world->m_joints.pop_back();
934 worldManager.orphanJoints.removeAll(joint);
937void QPhysicsWorld::registerContact(QAbstractPhysicsNode *sender, QAbstractPhysicsNode *receiver,
938 const QVector<QVector3D> &positions,
939 const QVector<QVector3D> &impulses,
940 const QVector<QVector3D> &normals)
948 contact.sender = sender;
949 contact.receiver = receiver;
950 contact.positions = positions;
951 contact.impulses = impulses;
952 contact.normals = normals;
954 m_registeredContacts.push_back(contact);
957QPhysicsWorld::QPhysicsWorld(QObject *parent) : QObject(parent)
959 m_inDesignStudio = !qEnvironmentVariableIsEmpty(
"QML_PUPPET_MODE");
960 m_physx =
new QPhysXWorld;
961 m_physx->createWorld();
963 worldManager.worlds.push_back(
this);
967 m_frameAnimator =
new FrameAnimator;
968 connect(m_frameAnimator, &QQuickFrameAnimation::triggered,
this,
969 &QPhysicsWorld::simulateFrame);
972QPhysicsWorld::~QPhysicsWorld()
974 if (m_frameAnimator) {
975 m_frameAnimator->stop();
976 delete m_frameAnimator;
979 if (m_physx->scene && m_physx->isRunning && !m_frameFetched)
980 m_physx->scene->fetchResults(
true);
982 for (
auto body : std::as_const(m_physXBodies)) {
983 body->cleanup(m_physx);
986 m_physXBodies.clear();
987 m_physx->deleteWorld();
989 worldManager.worlds.removeAll(
this);
991 if (!qtPhysicsTimingsFile.isEmpty()) {
992 if (m_frameTimings.isEmpty()) {
993 qWarning() <<
"No frame timings saved.";
994 }
else if (
auto csvFile = QFile(qtPhysicsTimingsFile); csvFile.open(QIODevice::WriteOnly)) {
995 QTextStream out(&csvFile);
996 for (
int i = 1; i < m_frameTimings.size(); i++) {
997 out << i <<
"," << m_frameTimings[i] <<
'\n';
1001 qWarning() <<
"Could not open timings file " << qtPhysicsTimingsFile;
1006void QPhysicsWorld::classBegin() {}
1008void QPhysicsWorld::componentComplete()
1010 if ((!m_running && !m_inDesignStudio) || m_physicsInitialized)
1015QVector3D QPhysicsWorld::gravity()
const
1020bool QPhysicsWorld::running()
const
1025bool QPhysicsWorld::forceDebugDraw()
const
1027 return m_forceDebugDraw;
1030bool QPhysicsWorld::enableCCD()
const
1035float QPhysicsWorld::typicalLength()
const
1037 return m_typicalLength;
1040float QPhysicsWorld::typicalSpeed()
const
1042 return m_typicalSpeed;
1045bool QPhysicsWorld::isNodeRemoved(QAbstractPhysicsNode *object)
1047 return m_removedPhysicsNodes.contains(object);
1050void QPhysicsWorld::setGravity(QVector3D gravity)
1052 if (m_gravity == gravity)
1055 if (!QPhysicsUtils::isFinite(gravity)) {
1056 qWarning() <<
"Warning: 'gravity' must be finite, ignored";
1060 m_gravity = gravity;
1061 if (m_physx->scene) {
1062 m_physx->scene->setGravity(QPhysicsUtils::toPhysXType(m_gravity));
1064 emit gravityChanged(m_gravity);
1067void QPhysicsWorld::setRunning(
bool running)
1069 if (m_running == running)
1072 m_running = running;
1073 if (!m_inDesignStudio && m_running && !m_physicsInitialized)
1077 m_frameAnimator->start();
1079 m_frameAnimator->stop();
1081 emit runningChanged(m_running);
1084void QPhysicsWorld::setForceDebugDraw(
bool forceDebugDraw)
1086 if (m_forceDebugDraw == forceDebugDraw)
1089 m_forceDebugDraw = forceDebugDraw;
1090 if (!m_forceDebugDraw)
1094 emit forceDebugDrawChanged(m_forceDebugDraw);
1097QQuick3DNode *QPhysicsWorld::viewport()
const
1102void QPhysicsWorld::setHasIndividualDebugDraw()
1104 m_hasIndividualDebugDraw =
true;
1107void QPhysicsWorld::setViewport(QQuick3DNode *viewport)
1109 if (m_viewport == viewport)
1112 m_viewport = viewport;
1115 for (
auto material : std::as_const(m_debugMaterials))
1117 m_debugMaterials.clear();
1119 for (
auto &holder : m_collisionShapeDebugModels) {
1120 holder.releaseMeshPointer();
1121 delete holder.model;
1123 m_collisionShapeDebugModels.clear();
1125 emit viewportChanged(m_viewport);
1128void QPhysicsWorld::setMinimumTimestep(
float minTimestep)
1130 if (qFuzzyCompare(m_minTimestep, minTimestep))
1133 if (!qIsFinite(minTimestep)) {
1134 qWarning(
"Minimum timestep must be finite, ignoring");
1138 if (minTimestep > m_maxTimestep) {
1139 qWarning(
"Minimum timestep greater than maximum timestep, value clamped");
1140 minTimestep = qMin(minTimestep, m_maxTimestep);
1143 if (minTimestep < 0.f) {
1144 qWarning(
"Minimum timestep less than zero, value clamped");
1145 minTimestep = qMax(minTimestep, 0.f);
1148 if (qFuzzyCompare(m_minTimestep, minTimestep))
1151 m_minTimestep = minTimestep;
1152 emit minimumTimestepChanged(m_minTimestep);
1155void QPhysicsWorld::setMaximumTimestep(
float maxTimestep)
1157 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
1160 if (!qIsFinite(maxTimestep)) {
1161 qWarning(
"Maximum timestep must be finite, ignoring");
1165 if (maxTimestep < 0.f) {
1166 qWarning(
"Maximum timestep less than zero, value clamped");
1167 maxTimestep = qMax(maxTimestep, 0.f);
1170 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
1173 m_maxTimestep = maxTimestep;
1174 emit maximumTimestepChanged(maxTimestep);
1177void QPhysicsWorld::setupDebugMaterials(QQuick3DNode *sceneNode)
1179 if (!m_debugMaterials.isEmpty())
1182 const int lineWidth = m_inDesignStudio ? 1 : 3;
1185 for (
auto color : { QColorConstants::Svg::chartreuse, QColorConstants::Svg::cyan,
1186 QColorConstants::Svg::lightsalmon, QColorConstants::Svg::red,
1187 QColorConstants::Svg::blueviolet, QColorConstants::Svg::black }) {
1188 auto debugMaterial =
new QQuick3DPrincipledMaterial();
1189 debugMaterial->setLineWidth(lineWidth);
1190 debugMaterial->setParentItem(sceneNode);
1191 debugMaterial->setParent(sceneNode);
1192 debugMaterial->setBaseColor(color);
1193 debugMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
1194 debugMaterial->setCullMode(QQuick3DMaterial::NoCulling);
1195 m_debugMaterials.push_back(debugMaterial);
1199void QPhysicsWorld::updateDebugDraw()
1201 if (!(m_forceDebugDraw || m_hasIndividualDebugDraw)) {
1203 for (
auto &holder : m_collisionShapeDebugModels) {
1204 holder.releaseMeshPointer();
1205 delete holder.model;
1207 m_collisionShapeDebugModels.clear();
1212 auto sceneNode = m_viewport ? m_viewport : m_scene;
1214 if (sceneNode ==
nullptr)
1217 setupDebugMaterials(sceneNode);
1218 m_hasIndividualDebugDraw =
false;
1221 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>> currentCollisionShapes;
1222 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
1224 for (QAbstractPhysXNode *node : std::as_const(m_physXBodies)) {
1227 if (!node->frontendNode || !node->debugGeometryCapability())
1230 const auto &collisionShapes = node->frontendNode->getCollisionShapesList();
1231 const int materialIdx =
static_cast<
int>(node->getDebugDrawBodyType());
1232 const int length = collisionShapes.length();
1233 for (
int idx = 0; idx < length; idx++) {
1234 const auto collisionShape = collisionShapes[idx];
1236 if (!m_forceDebugDraw && !collisionShape->enableDebugDraw())
1239 DebugModelHolder &holder =
1240 m_collisionShapeDebugModels[std::make_pair(collisionShape, node)];
1241 auto &model = holder.model;
1243 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
1245 m_hasIndividualDebugDraw =
1246 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
1250 model =
new QQuick3DModel();
1251 model->setParentItem(sceneNode);
1252 model->setParent(sceneNode);
1253 model->setCastsShadows(
false);
1254 model->setReceivesShadows(
false);
1255 model->setCastsReflections(
false);
1258 model->setVisible(
true);
1261 auto material = m_debugMaterials[materialIdx];
1262 QQmlListReference materialsRef(model,
"materials");
1263 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
1264 materialsRef.clear();
1265 materialsRef.append(material);
1271 if (qobject_cast<QCharacterController *>(node->frontendNode)) {
1272 QCapsuleShape *capsuleShape = qobject_cast<QCapsuleShape *>(collisionShape);
1276 const float radius = capsuleShape->diameter() * 0.5;
1277 const float halfHeight = capsuleShape->height() * 0.5;
1279 if (!qFuzzyCompare(radius, holder.radius())
1280 || !qFuzzyCompare(halfHeight, holder.halfHeight())) {
1281 auto geom = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
1282 geom->setParent(model);
1283 model->setGeometry(geom);
1284 holder.setRadius(radius);
1285 holder.setHalfHeight(halfHeight);
1288 model->setPosition(node->frontendNode->scenePosition());
1289 model->setRotation(node->frontendNode->sceneRotation()
1290 * QQuaternion::fromEulerAngles(0, 0, 90));
1294 if (node->shapes.length() < length)
1297 const auto physXShape = node->shapes[idx];
1298 auto localPose = physXShape->getLocalPose();
1300 const physx::PxGeometry &geometry = physXShape->getGeometry();
1302 switch (geometry.getType()) {
1303 case physx::PxGeometryType::eBOX: {
1304 const auto &boxGeometry =
static_cast<
const physx::PxBoxGeometry &>(geometry);
1305 const auto &halfExtentsOld = holder.halfExtents();
1306 const auto halfExtents = QPhysicsUtils::toQtType(boxGeometry.halfExtents);
1307 if (!qFuzzyCompare(halfExtentsOld, halfExtents)) {
1308 auto geom = QDebugDrawHelper::generateBoxGeometry(halfExtents);
1309 geom->setParent(model);
1310 model->setGeometry(geom);
1311 holder.setHalfExtents(halfExtents);
1317 case physx::PxGeometryType::eSPHERE: {
1318 const auto &sphereGeometry =
static_cast<
const physx::PxSphereGeometry &>(geometry);
1319 const float radius = holder.radius();
1320 if (!qFuzzyCompare(sphereGeometry.radius, radius)) {
1321 auto geom = QDebugDrawHelper::generateSphereGeometry(sphereGeometry.radius);
1322 geom->setParent(model);
1323 model->setGeometry(geom);
1324 holder.setRadius(sphereGeometry.radius);
1329 case physx::PxGeometryType::eCAPSULE: {
1330 const auto &capsuleGeometry =
1331 static_cast<
const physx::PxCapsuleGeometry &>(geometry);
1332 const float radius = holder.radius();
1333 const float halfHeight = holder.halfHeight();
1335 if (!qFuzzyCompare(capsuleGeometry.radius, radius)
1336 || !qFuzzyCompare(capsuleGeometry.halfHeight, halfHeight)) {
1337 auto geom = QDebugDrawHelper::generateCapsuleGeometry(
1338 capsuleGeometry.radius, capsuleGeometry.halfHeight);
1339 geom->setParent(model);
1340 model->setGeometry(geom);
1341 holder.setRadius(capsuleGeometry.radius);
1342 holder.setHalfHeight(capsuleGeometry.halfHeight);
1347 case physx::PxGeometryType::ePLANE:{
1349 const QQuaternion rotation =
1350 QPhysicsUtils::kMinus90YawRotation * QPhysicsUtils::toQtType(localPose.q);
1351 localPose = physx::PxTransform(localPose.p, QPhysicsUtils::toPhysXType(rotation));
1353 if (model->geometry() ==
nullptr) {
1354 auto geom = QDebugDrawHelper::generatePlaneGeometry();
1355 geom->setParent(model);
1356 model->setGeometry(geom);
1364 case physx::PxGeometryType::eHEIGHTFIELD: {
1365 const auto &heightFieldGeometry =
1366 static_cast<
const physx::PxHeightFieldGeometry &>(geometry);
1367 const float heightScale = holder.heightScale();
1368 const float rowScale = holder.rowScale();
1369 const float columnScale = holder.columnScale();
1371 if (
auto heightField = holder.getHeightField();
1372 heightField && heightField != heightFieldGeometry.heightField) {
1373 heightField->release();
1374 holder.setHeightField(
nullptr);
1377 if (!qFuzzyCompare(heightFieldGeometry.heightScale, heightScale)
1378 || !qFuzzyCompare(heightFieldGeometry.rowScale, rowScale)
1379 || !qFuzzyCompare(heightFieldGeometry.columnScale, columnScale)
1380 || !holder.getHeightField()) {
1381 if (!holder.getHeightField()) {
1382 heightFieldGeometry.heightField->acquireReference();
1383 holder.setHeightField(heightFieldGeometry.heightField);
1385 auto geom = QDebugDrawHelper::generateHeightFieldGeometry(
1386 heightFieldGeometry.heightField, heightFieldGeometry.heightScale,
1387 heightFieldGeometry.rowScale, heightFieldGeometry.columnScale);
1388 geom->setParent(model);
1389 model->setGeometry(geom);
1390 holder.setHeightScale(heightFieldGeometry.heightScale);
1391 holder.setRowScale(heightFieldGeometry.rowScale);
1392 holder.setColumnScale(heightFieldGeometry.columnScale);
1397 case physx::PxGeometryType::eCONVEXMESH: {
1398 const auto &convexMeshGeometry =
1399 static_cast<
const physx::PxConvexMeshGeometry &>(geometry);
1400 const auto rotation = convexMeshGeometry.scale.rotation * localPose.q;
1401 localPose = physx::PxTransform(localPose.p, rotation);
1402 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry.scale.scale));
1404 if (
auto convexMesh = holder.getConvexMesh();
1405 convexMesh && convexMesh != convexMeshGeometry.convexMesh) {
1406 convexMesh->release();
1407 holder.setConvexMesh(
nullptr);
1410 if (!model->geometry() || !holder.getConvexMesh()) {
1411 if (!holder.getConvexMesh()) {
1412 convexMeshGeometry.convexMesh->acquireReference();
1413 holder.setConvexMesh(convexMeshGeometry.convexMesh);
1415 auto geom = QDebugDrawHelper::generateConvexMeshGeometry(
1416 convexMeshGeometry.convexMesh);
1417 geom->setParent(model);
1418 model->setGeometry(geom);
1423 case physx::PxGeometryType::eTRIANGLEMESH: {
1424 const auto &triangleMeshGeometry =
1425 static_cast<
const physx::PxTriangleMeshGeometry &>(geometry);
1426 const auto rotation = triangleMeshGeometry.scale.rotation * localPose.q;
1427 localPose = physx::PxTransform(localPose.p, rotation);
1428 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry.scale.scale));
1430 if (
auto triangleMesh = holder.getTriangleMesh();
1431 triangleMesh && triangleMesh != triangleMeshGeometry.triangleMesh) {
1432 triangleMesh->release();
1433 holder.setTriangleMesh(
nullptr);
1436 if (!model->geometry() || !holder.getTriangleMesh()) {
1437 if (!holder.getTriangleMesh()) {
1438 triangleMeshGeometry.triangleMesh->acquireReference();
1439 holder.setTriangleMesh(triangleMeshGeometry.triangleMesh);
1441 auto geom = QDebugDrawHelper::generateTriangleMeshGeometry(
1442 triangleMeshGeometry.triangleMesh);
1443 geom->setParent(model);
1444 model->setGeometry(geom);
1450 case physx::PxGeometryType::eCONVEXCORE:
1451 case physx::PxGeometryType::ePARTICLESYSTEM:
1452 case physx::PxGeometryType::eTETRAHEDRONMESH:
1453 case physx::PxGeometryType::eCUSTOM:
1454 case physx::PxGeometryType::eINVALID:
1455 case physx::PxGeometryType::eGEOMETRY_COUNT:
1460 auto globalPose = node->getGlobalPose();
1461 auto finalPose = globalPose.transform(localPose);
1463 model->setRotation(QPhysicsUtils::toQtType(finalPose.q));
1464 model->setPosition(QPhysicsUtils::toQtType(finalPose.p));
1469 m_collisionShapeDebugModels.removeIf(
1470 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>,
1471 DebugModelHolder>::iterator it) {
1472 if (!currentCollisionShapes.contains(it.key())) {
1473 auto holder = it.value();
1474 holder.releaseMeshPointer();
1476 delete holder.model;
1485 if (
auto shape = qobject_cast<QAbstractPhysicsNode *>(node)) {
1486 nodes.push_back(shape);
1490 auto childItems = node->childItems();
1491 for (QQuick3DObject *child : std::as_const(childItems))
1492 collectPhysicsNodes(child, nodes);
1495void QPhysicsWorld::updateDebugDrawDesignStudio()
1498 auto sceneNode = m_viewport ? m_viewport : m_scene;
1500 if (sceneNode ==
nullptr)
1503 setupDebugMaterials(sceneNode);
1506 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>> currentCollisionShapes;
1507 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
1509 QList<QAbstractPhysicsNode *> activePhysicsNodes;
1510 activePhysicsNodes.reserve(m_collisionShapeDebugModels.size());
1511 collectPhysicsNodes(m_scene, activePhysicsNodes);
1513 for (QAbstractPhysicsNode *node : std::as_const(activePhysicsNodes)) {
1515 const auto &collisionShapes = node->getCollisionShapesList();
1516 const int materialIdx = 0;
1517 const int length = collisionShapes.length();
1519 const bool isCharacterController = qobject_cast<QCharacterController *>(node) !=
nullptr;
1521 for (
int idx = 0; idx < length; idx++) {
1522 QAbstractCollisionShape *collisionShape = collisionShapes[idx];
1523 DebugModelHolder &holder =
1524 m_DesignStudioDebugModels[std::make_pair(collisionShape, node)];
1525 auto &model = holder.model;
1527 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
1529 m_hasIndividualDebugDraw =
1530 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
1537 model =
new QQuick3DModel();
1538 model->setParentItem(sceneNode);
1539 model->setParent(sceneNode);
1540 model->setCastsShadows(
false);
1541 model->setReceivesShadows(
false);
1542 model->setCastsReflections(
false);
1545 const bool hasGeometry = holder.geometry !=
nullptr;
1546 QVector3D scenePosition = collisionShape->scenePosition();
1547 QQuaternion sceneRotation = collisionShape->sceneRotation();
1548 QQuick3DGeometry *newGeometry =
nullptr;
1550 if (isCharacterController)
1551 sceneRotation = sceneRotation * QQuaternion::fromEulerAngles(QVector3D(0, 0, 90));
1554 auto material = m_debugMaterials[materialIdx];
1555 QQmlListReference materialsRef(model,
"materials");
1556 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
1557 materialsRef.clear();
1558 materialsRef.append(material);
1562 if (
auto shape = qobject_cast<QBoxShape *>(collisionShape)) {
1563 const auto &halfExtentsOld = holder.halfExtents();
1564 const auto halfExtents = shape->sceneScale() * shape->extents() * 0.5f;
1565 if (!qFuzzyCompare(halfExtentsOld, halfExtents) || !hasGeometry) {
1566 newGeometry = QDebugDrawHelper::generateBoxGeometry(halfExtents);
1567 holder.setHalfExtents(halfExtents);
1569 }
else if (
auto shape = qobject_cast<QSphereShape *>(collisionShape)) {
1570 const float radiusOld = holder.radius();
1571 const float radius = shape->sceneScale().x() * shape->diameter() * 0.5f;
1572 if (!qFuzzyCompare(radiusOld, radius) || !hasGeometry) {
1573 newGeometry = QDebugDrawHelper::generateSphereGeometry(radius);
1574 holder.setRadius(radius);
1576 }
else if (
auto shape = qobject_cast<QCapsuleShape *>(collisionShape)) {
1577 const float radiusOld = holder.radius();
1578 const float halfHeightOld = holder.halfHeight();
1579 const float radius = shape->sceneScale().y() * shape->diameter() * 0.5f;
1580 const float halfHeight = shape->sceneScale().x() * shape->height() * 0.5f;
1582 if ((!qFuzzyCompare(radiusOld, radius) || !qFuzzyCompare(halfHeightOld, halfHeight))
1584 newGeometry = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
1585 holder.setRadius(radius);
1586 holder.setHalfHeight(halfHeight);
1588 }
else if (qobject_cast<QPlaneShape *>(collisionShape)) {
1590 newGeometry = QDebugDrawHelper::generatePlaneGeometry();
1591 }
else if (
auto shape = qobject_cast<QHeightFieldShape *>(collisionShape)) {
1592 physx::PxHeightFieldGeometry *heightFieldGeometry =
1593 static_cast<physx::PxHeightFieldGeometry *>(shape->getPhysXGeometry());
1594 const float heightScale = holder.heightScale();
1595 const float rowScale = holder.rowScale();
1596 const float columnScale = holder.columnScale();
1597 scenePosition += shape->hfOffset();
1598 if (!heightFieldGeometry) {
1599 qWarning() <<
"Could not get height field";
1600 }
else if (!qFuzzyCompare(heightFieldGeometry->heightScale, heightScale)
1601 || !qFuzzyCompare(heightFieldGeometry->rowScale, rowScale)
1602 || !qFuzzyCompare(heightFieldGeometry->columnScale, columnScale)
1604 newGeometry = QDebugDrawHelper::generateHeightFieldGeometry(
1605 heightFieldGeometry->heightField, heightFieldGeometry->heightScale,
1606 heightFieldGeometry->rowScale, heightFieldGeometry->columnScale);
1607 holder.setHeightScale(heightFieldGeometry->heightScale);
1608 holder.setRowScale(heightFieldGeometry->rowScale);
1609 holder.setColumnScale(heightFieldGeometry->columnScale);
1611 }
else if (
auto shape = qobject_cast<QConvexMeshShape *>(collisionShape)) {
1612 auto convexMeshGeometry =
1613 static_cast<physx::PxConvexMeshGeometry *>(shape->getPhysXGeometry());
1614 if (!convexMeshGeometry) {
1615 qWarning() <<
"Could not get convex mesh";
1617 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry->scale.scale));
1620 newGeometry = QDebugDrawHelper::generateConvexMeshGeometry(
1621 convexMeshGeometry->convexMesh);
1624 }
else if (
auto shape = qobject_cast<QTriangleMeshShape *>(collisionShape)) {
1625 physx::PxTriangleMeshGeometry *triangleMeshGeometry =
1626 static_cast<physx::PxTriangleMeshGeometry *>(shape->getPhysXGeometry());
1627 if (!triangleMeshGeometry) {
1628 qWarning() <<
"Could not get triangle mesh";
1630 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry->scale.scale));
1633 newGeometry = QDebugDrawHelper::generateTriangleMeshGeometry(
1634 triangleMeshGeometry->triangleMesh);
1640 delete holder.geometry;
1641 holder.geometry = newGeometry;
1644 model->setGeometry(holder.geometry);
1645 model->setVisible(
true);
1647 model->setRotation(sceneRotation);
1648 model->setPosition(scenePosition);
1653 m_DesignStudioDebugModels.removeIf(
1654 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>,
1655 DebugModelHolder>::iterator it) {
1656 if (!currentCollisionShapes.contains(it.key())) {
1657 auto holder = it.value();
1658 holder.releaseMeshPointer();
1660 delete holder.geometry;
1661 delete holder.model;
1669void QPhysicsWorld::disableDebugDraw()
1671 m_hasIndividualDebugDraw =
false;
1673 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1674 if (!body->frontendNode)
1677 const auto &collisionShapes = body->frontendNode->getCollisionShapesList();
1678 const int length = collisionShapes.length();
1679 for (
int idx = 0; idx < length; idx++) {
1680 const auto collisionShape = collisionShapes[idx];
1681 if (collisionShape->enableDebugDraw()) {
1682 m_hasIndividualDebugDraw =
true;
1689void QPhysicsWorld::setEnableCCD(
bool enableCCD)
1691 if (m_enableCCD == enableCCD)
1694 if (m_physicsInitialized) {
1696 <<
"Warning: Changing 'enableCCD' after physics is initialized will have no effect";
1700 m_enableCCD = enableCCD;
1701 emit enableCCDChanged(m_enableCCD);
1704void QPhysicsWorld::setTypicalLength(
float typicalLength)
1706 if (qFuzzyCompare(typicalLength, m_typicalLength))
1709 if (!qIsFinite(typicalLength) || typicalLength <= 0.f) {
1710 qWarning() <<
"Warning: 'typicalLength' value less than zero, ignored";
1714 if (m_physicsInitialized) {
1715 qWarning() <<
"Warning: Changing 'typicalLength' after physics is initialized will have "
1720 m_typicalLength = typicalLength;
1722 emit typicalLengthChanged(typicalLength);
1725void QPhysicsWorld::setTypicalSpeed(
float typicalSpeed)
1727 if (qFuzzyCompare(typicalSpeed, m_typicalSpeed))
1730 if (!qIsFinite(typicalSpeed) || typicalSpeed <= 0.f) {
1731 qWarning() <<
"Warning: 'typicalSpeed' must be finite and greater than zero, ignored";
1735 if (m_physicsInitialized) {
1736 qWarning() <<
"Warning: Changing 'typicalSpeed' after physics is initialized will have "
1741 m_typicalSpeed = typicalSpeed;
1743 emit typicalSpeedChanged(typicalSpeed);
1746float QPhysicsWorld::defaultDensity()
const
1748 return m_defaultDensity;
1751float QPhysicsWorld::minimumTimestep()
const
1753 return m_minTimestep;
1756float QPhysicsWorld::maximumTimestep()
const
1758 return m_maxTimestep;
1761void QPhysicsWorld::setDefaultDensity(
float defaultDensity)
1763 defaultDensity = qBound(0.0000001f, defaultDensity, PX_MAX_F32);
1765 if (qFuzzyCompare(m_defaultDensity, defaultDensity))
1767 m_defaultDensity = defaultDensity;
1770 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1771 if (body->frontendNode)
1772 body->updateDefaultDensity(m_defaultDensity);
1775 emit defaultDensityChanged(defaultDensity);
1780void QPhysicsWorld::cleanupRemovedNodes()
1782 m_physXBodies.removeIf([
this](QAbstractPhysXNode *body) {
1783 return body->cleanupIfRemoved(m_physx);
1785 m_removedPhysicsNodes.clear();
1791void QPhysicsWorld::invalidateTriggerOverlaps(QSpan<QAbstractPhysXNode *
const> rebuiltBodies)
1793 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1794 auto *trigger = qobject_cast<QTriggerBody *>(body->frontendNode);
1797 for (QAbstractPhysXNode *rebuilt : rebuiltBodies) {
1802 if (rebuilt->frontendNode)
1803 trigger->invalidateOverlaps(rebuilt->frontendNode);
1807 m_triggerOverlapsInvalidated =
true;
1810void QPhysicsWorld::dropUnreportedTriggerOverlaps()
1814 if (!m_triggerOverlapsInvalidated)
1816 m_triggerOverlapsInvalidated =
false;
1822 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1823 if (
auto *trigger = qobject_cast<QTriggerBody *>(body->frontendNode))
1824 trigger->dropUnreportedOverlaps();
1828void QPhysicsWorld::cleanupRemovedJoints()
1830 for (physx::PxJoint *joint : m_removedJoints) {
1834 m_removedJoints.clear();
1837void QPhysicsWorld::initPhysics()
1839 Q_ASSERT(!m_physicsInitialized);
1841 const unsigned int numThreads = m_numThreads >= 0 ? m_numThreads : qMax(0, QThread::idealThreadCount());
1842 m_physx->createScene(m_typicalLength, m_typicalSpeed, m_gravity,
this, numThreads);
1843 m_frameAnimator->start();
1844 m_physicsInitialized =
true;
1847void QPhysicsWorld::simulateFrame()
1849 constexpr double MILLIONTH = 0.000001;
1850 constexpr double THOUSANDTH = 0.001;
1852 if (m_inDesignStudio) {
1853 frameFinishedDesignStudio();
1857 if (!m_physx->isRunning) {
1859 m_physx->isRunning =
true;
1860 const double minTimestepSecs = m_minTimestep * 0.001;
1861 m_physx->scene->simulate(minTimestepSecs);
1862 m_currTimeStep = minTimestepSecs;
1867 if (!m_frameFetched && !m_physx->scene->checkResults()) {
1872 if (!m_frameFetched) {
1873 m_physx->scene->fetchResults(
true);
1874 frameFinished(m_currTimeStep);
1875 m_frameFetched =
true;
1876 if (Q_UNLIKELY(!qtPhysicsTimingsFile.isEmpty())) {
1877 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1878 m_frameTimings.append(deltaMS);
1883 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1884 if (deltaMS < m_minTimestep)
1886 const double deltaSecs = qMin<
double>(deltaMS, m_maxTimestep) * THOUSANDTH;
1888 m_physx->scene->simulate(deltaSecs);
1889 m_frameFetched =
false;
1890 m_currTimeStep = deltaSecs;
1893void QPhysicsWorld::frameFinished(
float deltaTime)
1896 matchOrphanJoints();
1902 dropUnreportedTriggerOverlaps();
1904 emitContactCallbacks();
1905 cleanupRemovedNodes();
1906 cleanupRemovedJoints();
1908 for (
auto *node : std::as_const(m_newPhysicsNodes)) {
1909 auto *body = node->createPhysXBackend();
1910 body->init(
this, m_physx);
1911 m_physXBodies.push_back(body);
1913 m_newPhysicsNodes.clear();
1915 QHash<QQuick3DNode *, QMatrix4x4> transformCache;
1916 QVarLengthArray<QAbstractPhysXNode *, 8> rebuiltBodies;
1919 for (
auto *physXBody : std::as_const(m_physXBodies)) {
1922 if (!physXBody->frontendNode)
1925 physXBody->markDirtyShapes();
1926 const bool wasDirty = physXBody->shapesDirty();
1927 physXBody->rebuildDirtyShapes(
this, m_physx);
1930 if (!physXBody->frontendNode)
1935 if (wasDirty && !physXBody->shapesDirty())
1936 rebuiltBodies.append(physXBody);
1937 physXBody->updateFilters();
1940 physXBody->sync(deltaTime, transformCache);
1943 if (!rebuiltBodies.isEmpty())
1944 invalidateTriggerOverlaps(rebuiltBodies);
1946 for (QPhysicsJoint *joint : std::as_const(m_joints)) {
1947 joint->updatePhysXBackend();
1951 emit frameDone(deltaTime * 1000);
1954void QPhysicsWorld::frameFinishedDesignStudio()
1958 matchOrphanJoints();
1959 emitContactCallbacks();
1960 cleanupRemovedNodes();
1962 m_newPhysicsNodes.clear();
1964 updateDebugDrawDesignStudio();
1967QPhysicsWorld *QPhysicsWorld::getWorld(QQuick3DNode *node)
1969 for (QPhysicsWorld *world : std::as_const(worldManager.worlds)) {
1970 if (!world->m_scene) {
1974 QQuick3DNode *nodeCurr = node;
1977 if (nodeCurr == world->m_scene)
1980 while (nodeCurr->parentNode()) {
1981 nodeCurr = nodeCurr->parentNode();
1982 if (nodeCurr == world->m_scene)
1990QPhysicsWorld *QPhysicsWorld::getWorld(QPhysicsJoint *joint)
1992 for (QPhysicsWorld *world : worldManager.worlds) {
1993 if (!world->m_scene) {
1997 QObject *nodeCurr = joint;
1999 while (nodeCurr->parent()) {
2000 nodeCurr = nodeCurr->parent();
2001 if (nodeCurr == world->m_scene) {
2003 }
else if (
auto view3d = qobject_cast<QQuick3DViewport *>(nodeCurr);
2004 view3d && view3d->scene() == world->m_scene) {
2014void QPhysicsWorld::matchOrphanNodes()
2017 if (worldManager.orphanNodes.isEmpty())
2020 qsizetype numNodes = worldManager.orphanNodes.length();
2023 while (idx < numNodes) {
2024 auto node = worldManager.orphanNodes[idx];
2025 auto world = getWorld(node);
2026 if (world ==
this) {
2027 world->m_newPhysicsNodes.push_back(node);
2029 worldManager.orphanNodes.swapItemsAt(idx, numNodes - 1);
2030 worldManager.orphanNodes.pop_back();
2038void QPhysicsWorld::matchOrphanJoints()
2040 if (worldManager.orphanJoints.isEmpty())
2043 qsizetype numNodes = worldManager.orphanJoints.length();
2046 while (idx < numNodes) {
2047 auto node = worldManager.orphanJoints[idx];
2048 auto world = getWorld(node);
2049 if (world ==
this) {
2050 world->m_joints.push_back(node);
2052 worldManager.orphanJoints.swapItemsAt(idx, numNodes - 1);
2053 worldManager.orphanJoints.pop_back();
2061void QPhysicsWorld::findPhysicsNodes()
2066 if (m_scene ==
nullptr)
2070 QList<QQuick3DObject *> children = m_scene->childItems();
2071 while (!children.empty()) {
2072 auto child = children.takeFirst();
2073 if (
auto converted = qobject_cast<QAbstractPhysicsNode *>(child); converted !=
nullptr) {
2075 if (converted->m_backendObject !=
nullptr) {
2076 qWarning() <<
"Warning: physics node already associated with a backend node.";
2080 m_newPhysicsNodes.push_back(converted);
2081 worldManager.orphanNodes.removeAll(converted);
2083 children.append(child->childItems());
2087void QPhysicsWorld::emitContactCallbacks()
2089 for (
const QPhysicsWorld::BodyContact &contact : std::as_const(m_registeredContacts)) {
2090 if (m_removedPhysicsNodes.contains(contact.sender)
2091 || m_removedPhysicsNodes.contains(contact.receiver))
2093 contact.receiver->registerContact(contact.sender, contact.positions, contact.impulses,
2097 m_registeredContacts.clear();
2100physx::PxPhysics *QPhysicsWorld::getPhysics()
2102 return StaticPhysXObjects::getReference().physics;
2105const physx::PxCookingParams *QPhysicsWorld::getCookingParams()
2110 if (!StaticPhysXObjects::getReference().foundationCreated)
2114 static const physx::PxCookingParams params { physx::PxTolerancesScale() };
2118physx::PxControllerManager *QPhysicsWorld::controllerManager()
2120 if (m_physx->scene && !m_physx->controllerManager) {
2121 m_physx->controllerManager = PxCreateControllerManager(*m_physx->scene);
2122 qCDebug(lcQuick3dPhysics) <<
"Created controller manager" << m_physx->controllerManager;
2124 return m_physx->controllerManager;
2127QQuick3DNode *QPhysicsWorld::scene()
const
2132void QPhysicsWorld::setScene(QQuick3DNode *newScene)
2134 if (m_scene == newScene)
2142 for (
auto body : std::as_const(m_physXBodies)) {
2143 if (body->frontendNode)
2144 deregisterNode(body->frontendNode);
2148 bool sceneOK =
true;
2149 for (QPhysicsWorld *world : std::as_const(worldManager.worlds)) {
2150 if (world !=
this && world->scene() == newScene) {
2152 qWarning() <<
"Warning: scene already associated with physics world";
2158 emit sceneChanged();
2161int QPhysicsWorld::numThreads()
const
2163 return m_numThreads;
2166void QPhysicsWorld::setNumThreads(
int newNumThreads)
2168 newNumThreads = qMin(newNumThreads, 256);
2170 if (m_numThreads == newNumThreads)
2173 m_numThreads = newNumThreads;
2174 emit numThreadsChanged();
2177bool QPhysicsWorld::reportKinematicKinematicCollisions()
const
2179 return m_reportKinematicKinematicCollisions;
2182void QPhysicsWorld::setReportKinematicKinematicCollisions(
2183 bool newReportKinematicKinematicCollisions)
2185 if (m_reportKinematicKinematicCollisions == newReportKinematicKinematicCollisions)
2187 m_reportKinematicKinematicCollisions = newReportKinematicKinematicCollisions;
2188 emit reportKinematicKinematicCollisionsChanged();
2191bool QPhysicsWorld::reportStaticKinematicCollisions()
const
2193 return m_reportStaticKinematicCollisions;
2196void QPhysicsWorld::setReportStaticKinematicCollisions(
bool newReportStaticKinematicCollisions)
2198 if (m_reportStaticKinematicCollisions == newReportStaticKinematicCollisions)
2200 m_reportStaticKinematicCollisions = newReportStaticKinematicCollisions;
2201 emit reportStaticKinematicCollisionsChanged();
2204QPhysicsWorld::QueryStructure QPhysicsWorld::staticQueryStructure()
const
2206 return m_staticQueryStructure;
2209void QPhysicsWorld::setStaticQueryStructure(QueryStructure newStaticQueryStructure)
2211 if (m_staticQueryStructure == newStaticQueryStructure)
2214 if (newStaticQueryStructure == QueryStructure::NoStructure) {
2216 <<
"Warning: The QueryStructure::NoStructure is not supported for staticQueryStructure. Available options: [StaticTree, DynamicTree]";
2220 if (m_physicsInitialized) {
2222 <<
"Warning: Changing 'staticQueryStructure' after physics is initialized will have no effect";
2226 m_staticQueryStructure = newStaticQueryStructure;
2227 emit staticQueryStructureChanged();
2230QPhysicsWorld::QueryStructure QPhysicsWorld::dynamicQueryStructure()
const
2232 return m_dynamicQueryStructure;
2235void QPhysicsWorld::setDynamicQueryStructure(QueryStructure newDynamicQueryStructure)
2237 if (m_dynamicQueryStructure == newDynamicQueryStructure)
2240 if (m_physicsInitialized) {
2242 <<
"Warning: Changing 'dynamicQueryStructure' after physics is initialized will have no effect";
2246 m_dynamicQueryStructure = newDynamicQueryStructure;
2247 emit dynamicQueryStructureChanged();
2250bool QPhysicsWorld::testRaycastQuery(
const QVector3D &origin,
2251 const QVector3D &direction,
2254 bool includeDynamic)
const
2256 physx::PxRaycastBuffer hitBuffer;
2257 return raycastImpl(m_physx->scene, origin, direction,
2258 maxDistance, includeStatic, includeDynamic,
2260 physx::PxHitFlags(),
2261 physx::PxQueryFlag::eANY_HIT);
2264QQuick3DPhysicsLocationHit QPhysicsWorld::singleRaycastQuery(
const QVector3D &origin,
2265 const QVector3D &direction,
2268 bool includeDynamic)
const
2270 physx::PxRaycastBuffer hitBuffer;
2271 const bool status = raycastImpl(m_physx->scene, origin, direction,
2272 maxDistance, includeStatic, includeDynamic,
2274 physx::PxHitFlag::eDEFAULT);
2276 if (status && hitBuffer.hasBlock) {
2277 return QQuick3DPhysicsLocationHit(hitBuffer.block,
2284QList<QQuick3DPhysicsLocationHit> QPhysicsWorld::multiRaycastQuery(
const QVector3D &origin,
2285 const QVector3D &direction,
2288 bool includeDynamic)
const
2290 DynamicQueryCallback<physx::PxRaycastCallback,
2291 QQuick3DPhysicsLocationHit,
2292 physx::PxRaycastHit> hitBuffer;
2294 if (raycastImpl(m_physx->scene, origin, direction,
2295 maxDistance, includeStatic, includeDynamic,
2297 physx::PxHitFlag::eDEFAULT,
2298 physx::PxQueryFlag::eNO_BLOCK)) {
2300 return hitBuffer.hits;
2306bool QPhysicsWorld::testSweepQuery(QAbstractCollisionShape *shape,
2307 const QVector3D &direction,
2310 bool includeDynamic)
const
2312 physx::PxSweepBuffer hitBuffer;
2313 return sweepImpl(m_physx->scene, shape, direction,
2314 maxDistance, includeStatic, includeDynamic,
2316 physx::PxHitFlags(),
2317 physx::PxQueryFlag::eANY_HIT);
2320QQuick3DPhysicsLocationHit QPhysicsWorld::singleSweepQuery(QAbstractCollisionShape *shape,
2321 const QVector3D &direction,
2324 bool includeDynamic)
const
2326 physx::PxSweepBuffer hitBuffer;
2327 const bool status = sweepImpl(m_physx->scene, shape,
2328 direction, maxDistance, includeStatic, includeDynamic,
2330 physx::PxHitFlag::eDEFAULT | physx::PxHitFlag::eMTD);
2332 if (status && hitBuffer.hasBlock) {
2333 return QQuick3DPhysicsLocationHit(hitBuffer.block, hitBuffer.block);
2339QList<QQuick3DPhysicsLocationHit> QPhysicsWorld::multiSweepQuery(QAbstractCollisionShape *shape,
2340 const QVector3D &direction,
2343 bool includeDynamic)
const
2345 DynamicQueryCallback<physx::PxSweepCallback,
2346 QQuick3DPhysicsLocationHit,
2347 physx::PxSweepHit> hitBuffer;
2349 if (sweepImpl(m_physx->scene, shape,
2350 direction, maxDistance, includeStatic, includeDynamic,
2352 physx::PxHitFlag::eDEFAULT | physx::PxHitFlag::eMTD,
2353 physx::PxQueryFlag::eNO_BLOCK)) {
2354 return hitBuffer.hits;
2360bool QPhysicsWorld::testOverlapQuery(QAbstractCollisionShape *shape,
2362 bool includeDynamic)
const
2364 physx::PxOverlapBuffer hitBuffer;
2365 return overlapImpl(m_physx->scene,
2366 shape, includeStatic, includeDynamic,
2368 physx::PxQueryFlag::eANY_HIT);
2371QList<QQuick3DPhysicsQueryHit> QPhysicsWorld::multiOverlapQuery(QAbstractCollisionShape *shape,
2373 bool includeDynamic)
const
2375 DynamicQueryCallback<physx::PxOverlapCallback,
2376 QQuick3DPhysicsQueryHit,
2377 physx::PxOverlapHit> hitBuffer;
2379 if (overlapImpl(m_physx->scene,
2380 shape, includeStatic, includeDynamic,
2382 physx::PxQueryFlag::eNO_BLOCK)) {
2383 return hitBuffer.hits;
2391#include "qphysicsworld.moc"
The QVector3D class represents a vector or vertex in 3D space.
PxTransformT< float > PxTransform
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
static QWorldManager worldManager
static bool validateQuery(const physx::PxScene *scene, bool includeStatic, bool includeDynamic)
static const physx::PxGeometry * validateQueryShape(const physx::PxScene *scene, QAbstractCollisionShape *shape, bool includeStatic, bool includeDynamic)
static void collectPhysicsNodes(QQuick3DObject *node, QList< QAbstractPhysicsNode * > &nodes)
static const QString qtPhysicsTimingsFile
static bool raycastImpl(const physx::PxScene *scene, const QVector3D &origin, const QVector3D &direction, float maxDistance, bool includeStatic, bool includeDynamic, physx::PxRaycastCallback &hitCallback, physx::PxHitFlags hitFlags, physx::PxQueryFlags extraQueryFlags={})
static bool sweepImpl(const physx::PxScene *scene, QAbstractCollisionShape *shape, const QVector3D &direction, float maxDistance, bool includeStatic, bool includeDynamic, physx::PxSweepCallback &hitCallback, physx::PxHitFlags hitFlags, physx::PxQueryFlags extraQueryFlags={})
static bool overlapImpl(const physx::PxScene *scene, QAbstractCollisionShape *shape, bool includeStatic, bool includeDynamic, physx::PxOverlapCallback &hitCallback, physx::PxQueryFlags extraQueryFlags={})
#define QT_BEGIN_NAMESPACE
QVector< QAbstractPhysicsNode * > orphanNodes
QVector< QPhysicsWorld * > worlds
QVector< QPhysicsJoint * > orphanJoints