7#include "physxnode/qabstractphysxnode_p.h"
8#include "physxnode/qphysxworld_p.h"
12#include "joints/qjoint_p.h"
25#include "PxPhysicsAPI.h"
26#include "cooking/PxCooking.h"
27#include <foundation/PxSimpleTypes.h>
29#include <QtQuick/private/qquickframeanimation_p.h>
30#include <QtQuick3D/private/qquick3dobject_p.h>
31#include <QtQuick3D/private/qquick3dnode_p.h>
32#include <QtQuick3D/private/qquick3dmodel_p.h>
33#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
34#include <QtQuick3DUtils/private/qssgutils_p.h>
36#include <QtCore/qvarlengtharray.h>
37#include <QtGui/qquaternion.h>
39#include <QtEnvironmentVariables>
44
45
46
47
48
49
50
51
52
55
56
57
58
59
62
63
64
67
68
69
70
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
98
99
100
101
102
103
104
107
108
109
110
111
112
113
114
115
116
119
120
121
122
123
124
125
126
127
128
131
132
133
134
135
136
139
140
141
142
143
144
145
146
147
148
149
152
153
154
155
156
157
158
159
160
161
162
165
166
167
168
169
170
171
172
175
176
177
178
179
180
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
212
215
216
217
218
219
220
221
222
223
224
225
226
227
230
231
232
233
234
235
236
237
238
239
240
241
242
243
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
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
298
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
331
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
365
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
403
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
436
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
473
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
512
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
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
569
592 qWarning() <<
"QtQuick3DPhysics: cannot run a scene query before the physics world is "
597 if (!(includeStatic || includeDynamic)) {
598 qWarning() <<
"QtQuick3DPhysics: a scene query needs includeStatic or includeDynamic to "
612 physx::PxRaycastCallback &hitCallback,
613 physx::PxHitFlags hitFlags,
614 physx::PxQueryFlags extraQueryFlags = {},
615 physx::PxQueryFilterCallback *filterCall =
nullptr)
620 const physx::PxVec3 pxOrigin = QPhysicsUtils::toPhysXType(origin);
621 if (!pxOrigin.isFinite()) {
622 qWarning() <<
"QtQuick3DPhysics: the query origin must be finite.";
626 const physx::PxVec3 pxDirection = QPhysicsUtils::toPhysXType(direction.normalized());
627 if (!pxDirection.isNormalized()) {
628 qWarning() <<
"QtQuick3DPhysics: the query direction must be a non-null, finite vector.";
632 if (!qIsFinite(maxDistance) || maxDistance <= 0) {
633 qWarning() <<
"QtQuick3DPhysics: the query distance must be finite and greater than 0.";
638 physx::PxQueryFlags filter = extraQueryFlags;
640 filter |= physx::PxQueryFlag::eSTATIC;
642 filter |= physx::PxQueryFlag::eDYNAMIC;
645 return scene->raycast(pxOrigin, pxDirection, maxDistance, hitCallback, hitFlags,
646 physx::PxQueryFilterData(filter), filterCall);
650 QAbstractCollisionShape *shape,
658 const physx::PxGeometry *geometry = shape ? shape->getPhysXGeometry() :
nullptr;
660 qWarning() <<
"QtQuick3DPhysics: the query shape has no geometry yet.";
666 switch (geometry->getType()) {
667 case physx::PxGeometryType::eSPHERE:
668 case physx::PxGeometryType::eCAPSULE:
669 case physx::PxGeometryType::eBOX:
670 case physx::PxGeometryType::eCONVEXMESH:
673 qWarning() <<
"QtQuick3DPhysics: sweep and overlap queries only support SphereShape, "
674 "CapsuleShape, BoxShape and ConvexMeshShape as the query shape.";
682 QAbstractCollisionShape *shape,
687 physx::PxSweepCallback &hitCallback,
688 physx::PxHitFlags hitFlags,
689 physx::PxQueryFlags extraQueryFlags = {})
691 const physx::PxGeometry *geometry = validateQueryShape(scene, shape, includeStatic, includeDynamic);
696 const physx::PxVec3 pxDirection = QPhysicsUtils::toPhysXType(direction.normalized());
697 if (!pxDirection.isNormalized()) {
698 qWarning() <<
"QtQuick3DPhysics: the query direction must be a non-null, finite vector.";
702 if (!qIsFinite(maxDistance) || maxDistance <= 0) {
703 qWarning() <<
"QtQuick3DPhysics: the query distance must be finite and greater than 0.";
708 physx::PxQueryFlags filter = extraQueryFlags;
710 filter |= physx::PxQueryFlag::eSTATIC;
712 filter |= physx::PxQueryFlag::eDYNAMIC;
714 const physx::
PxTransform pose(QPhysicsUtils::toPhysXType(shape->scenePosition()),
715 QPhysicsUtils::toPhysXType(shape->sceneRotation()));
716 if (!pose.isSane()) {
717 qWarning() <<
"QtQuick3DPhysics: the query position/rotation is not finite.";
722 return scene->sweep(*geometry, pose, pxDirection, maxDistance, hitCallback, hitFlags,
723 physx::PxQueryFilterData(filter));
727 QAbstractCollisionShape *shape,
730 physx::PxOverlapCallback &hitCallback,
731 physx::PxQueryFlags extraQueryFlags = {})
734 const physx::PxGeometry *geometry = validateQueryShape(scene, shape, includeStatic, includeDynamic);
740 physx::PxQueryFlags filter = extraQueryFlags;
742 filter |= physx::PxQueryFlag::eSTATIC;
744 filter |= physx::PxQueryFlag::eDYNAMIC;
746 const physx::
PxTransform pose(QPhysicsUtils::toPhysXType(shape->scenePosition()),
747 QPhysicsUtils::toPhysXType(shape->sceneRotation()));
748 if (!pose.isSane()) {
749 qWarning() <<
"QtQuick3DPhysics: the query position/rotation is not finite.";
754 return scene->overlap(*geometry, pose, hitCallback,
755 physx::PxQueryFilterData(filter));
776class ExcludedBodiesFilter :
public physx::PxQueryFilterCallback
779 explicit ExcludedBodiesFilter(QSpan<
const QAbstractPhysicsNode *
const> excludedBodies)
780 : m_excludedBodies(excludedBodies)
784 physx::PxQueryHitType::Enum preFilter(
const physx::PxFilterData & ,
785 const physx::PxShape * ,
786 const physx::PxRigidActor *actor,
787 physx::PxHitFlags & ) override
789 const auto *body =
static_cast<
const QAbstractPhysicsNode *>(actor->userData);
791 return physx::PxQueryHitType::eNONE;
792 const bool excluded = std::find(m_excludedBodies.begin(), m_excludedBodies.end(), body)
793 != m_excludedBodies.end();
794 return excluded ? physx::PxQueryHitType::eNONE : physx::PxQueryHitType::eBLOCK;
797 physx::PxQueryHitType::Enum postFilter(
const physx::PxFilterData & ,
798 const physx::PxQueryHit & ,
799 const physx::PxShape * ,
800 const physx::PxRigidActor * ) override
802 return physx::PxQueryHitType::eBLOCK;
806 QSpan<
const QAbstractPhysicsNode *
const> m_excludedBodies;
813template<
class PxCbT,
class HitT,
class PxHitT, physx::PxU32 BufferSize = 16>
814struct DynamicQueryCallback :
public PxCbT
816 DynamicQueryCallback() : PxCbT(localBuffer, BufferSize) {}
818 physx::PxAgain processTouches(
const PxHitT *buffer, physx::PxU32 nbHits)
override
820 hits.reserve(hits.size() + nbHits);
821 for (physx::PxU32 i = 0; i < nbHits; ++i)
822 hits.emplaceBack(buffer[i], buffer[i]);
826 PxHitT localBuffer[BufferSize];
834void QPhysicsWorld::DebugModelHolder::releaseMeshPointer()
836 if (
auto base =
static_cast<physx::PxBase *>(ptr); base)
841const QVector3D &QPhysicsWorld::DebugModelHolder::halfExtents()
const
845void QPhysicsWorld::DebugModelHolder::setHalfExtents(
const QVector3D &halfExtents)
849float QPhysicsWorld::DebugModelHolder::radius()
const
853void QPhysicsWorld::DebugModelHolder::setRadius(
float radius)
857float QPhysicsWorld::DebugModelHolder::heightScale()
const
861void QPhysicsWorld::DebugModelHolder::setHeightScale(
float heightScale)
863 data.setX(heightScale);
865float QPhysicsWorld::DebugModelHolder::halfHeight()
const
869void QPhysicsWorld::DebugModelHolder::setHalfHeight(
float halfHeight)
871 data.setY(halfHeight);
873float QPhysicsWorld::DebugModelHolder::rowScale()
const
877void QPhysicsWorld::DebugModelHolder::setRowScale(
float rowScale)
881float QPhysicsWorld::DebugModelHolder::columnScale()
const
885void QPhysicsWorld::DebugModelHolder::setColumnScale(
float columnScale)
887 data.setZ(columnScale);
889physx::PxConvexMesh *QPhysicsWorld::DebugModelHolder::getConvexMesh()
891 return static_cast<physx::PxConvexMesh *>(ptr);
893void QPhysicsWorld::DebugModelHolder::setConvexMesh(physx::PxConvexMesh *mesh)
895 ptr =
static_cast<
void *>(mesh);
897physx::PxTriangleMesh *QPhysicsWorld::DebugModelHolder::getTriangleMesh()
899 return static_cast<physx::PxTriangleMesh *>(ptr);
901void QPhysicsWorld::DebugModelHolder::setTriangleMesh(physx::PxTriangleMesh *mesh)
903 ptr =
static_cast<
void *>(mesh);
905physx::PxHeightField *QPhysicsWorld::DebugModelHolder::getHeightField()
907 return static_cast<physx::PxHeightField *>(ptr);
909void QPhysicsWorld::DebugModelHolder::setHeightField(physx::PxHeightField *hf)
911 ptr =
static_cast<physx::PxHeightField *>(hf);
926void QPhysicsWorld::registerNode(QAbstractPhysicsNode *physicsNode)
928 auto world = getWorld(physicsNode);
930 world->m_newPhysicsNodes.push_back(physicsNode);
932 worldManager.orphanNodes.push_back(physicsNode);
936void QPhysicsWorld::deregisterNode(QAbstractPhysicsNode *physicsNode)
938 for (
auto world : std::as_const(worldManager.worlds)) {
939 world->m_newPhysicsNodes.removeAll(physicsNode);
940 if (physicsNode->m_backendObject) {
941 Q_ASSERT(physicsNode->m_backendObject->frontendNode == physicsNode);
942 physicsNode->m_backendObject->detachFrontend();
943 physicsNode->m_backendObject->frontendNode =
nullptr;
944 physicsNode->m_backendObject->isRemoved =
true;
945 physicsNode->m_backendObject =
nullptr;
947 world->m_removedPhysicsNodes.insert(physicsNode);
949 worldManager.orphanNodes.removeAll(physicsNode);
952void QPhysicsWorld::registerJoint(QPhysicsJoint *joint)
954 auto world = getWorld(joint);
956 world->m_joints.push_back(joint);
958 worldManager.orphanJoints.push_back(joint);
962void QPhysicsWorld::deregisterJoint(QPhysicsJoint *joint)
964 for (
auto world : worldManager.worlds) {
965 world->m_removedJoints.insert(joint->getPhysXBackend());
968 qsizetype idx = world->m_joints.indexOf(joint);
970 world->m_joints.swapItemsAt(idx, world->m_joints.size() - 1);
971 world->m_joints.pop_back();
974 worldManager.orphanJoints.removeAll(joint);
977void QPhysicsWorld::registerRaycaster(QRaycaster *raycaster)
979 if (
auto *world = getWorld(raycaster))
980 world->m_raycasters.push_back(raycaster);
982 worldManager.orphanRaycasters.push_back(raycaster);
985void QPhysicsWorld::deregisterRaycaster(QRaycaster *raycaster)
987 for (
auto *world : std::as_const(worldManager.worlds))
988 world->m_raycasters.removeAll(raycaster);
989 worldManager.orphanRaycasters.removeAll(raycaster);
992void QPhysicsWorld::registerContact(QAbstractPhysicsNode *sender, QAbstractPhysicsNode *receiver,
993 const QVector<QVector3D> &positions,
994 const QVector<QVector3D> &impulses,
995 const QVector<QVector3D> &normals)
1002 BodyContact contact;
1003 contact.sender = sender;
1004 contact.receiver = receiver;
1005 contact.positions = positions;
1006 contact.impulses = impulses;
1007 contact.normals = normals;
1009 m_registeredContacts.push_back(contact);
1012QPhysicsWorld::QPhysicsWorld(QObject *parent) : QObject(parent)
1014 m_inDesignStudio = !qEnvironmentVariableIsEmpty(
"QML_PUPPET_MODE");
1015 m_physx =
new QPhysXWorld;
1016 m_physx->createWorld();
1018 worldManager.worlds.push_back(
this);
1020 matchOrphanJoints();
1021 matchOrphanRaycasters();
1023 m_frameAnimator =
new FrameAnimator;
1024 connect(m_frameAnimator, &QQuickFrameAnimation::triggered,
this,
1025 &QPhysicsWorld::simulateFrame);
1028QPhysicsWorld::~QPhysicsWorld()
1030 if (m_frameAnimator) {
1031 m_frameAnimator->stop();
1032 delete m_frameAnimator;
1035 if (m_physx->scene && m_physx->isRunning && !m_frameFetched)
1036 m_physx->scene->fetchResults(
true);
1038 for (
auto body : std::as_const(m_physXBodies)) {
1039 body->cleanup(m_physx);
1042 m_physXBodies.clear();
1043 m_physx->deleteWorld();
1045 worldManager.worlds.removeAll(
this);
1050 worldManager.orphanRaycasters += m_raycasters;
1051 m_raycasters.clear();
1053 if (!qtPhysicsTimingsFile.isEmpty()) {
1054 if (m_frameTimings.isEmpty()) {
1055 qWarning() <<
"No frame timings saved.";
1056 }
else if (
auto csvFile = QFile(qtPhysicsTimingsFile); csvFile.open(QIODevice::WriteOnly)) {
1057 QTextStream out(&csvFile);
1058 for (
int i = 1; i < m_frameTimings.size(); i++) {
1059 out << i <<
"," << m_frameTimings[i] <<
'\n';
1063 qWarning() <<
"Could not open timings file " << qtPhysicsTimingsFile;
1068void QPhysicsWorld::classBegin() {}
1070void QPhysicsWorld::componentComplete()
1072 if ((!m_running && !m_inDesignStudio) || m_physicsInitialized)
1077QVector3D QPhysicsWorld::gravity()
const
1082bool QPhysicsWorld::running()
const
1087bool QPhysicsWorld::forceDebugDraw()
const
1089 return m_forceDebugDraw;
1092bool QPhysicsWorld::enableCCD()
const
1097float QPhysicsWorld::typicalLength()
const
1099 return m_typicalLength;
1102float QPhysicsWorld::typicalSpeed()
const
1104 return m_typicalSpeed;
1107bool QPhysicsWorld::isNodeRemoved(QAbstractPhysicsNode *object)
1109 return m_removedPhysicsNodes.contains(object);
1112void QPhysicsWorld::setGravity(QVector3D gravity)
1114 if (m_gravity == gravity)
1117 if (!QPhysicsUtils::isFinite(gravity)) {
1118 qWarning() <<
"Warning: 'gravity' must be finite, ignored";
1122 m_gravity = gravity;
1123 if (m_physx->scene) {
1124 m_physx->scene->setGravity(QPhysicsUtils::toPhysXType(m_gravity));
1126 emit gravityChanged(m_gravity);
1129void QPhysicsWorld::setRunning(
bool running)
1131 if (m_running == running)
1134 m_running = running;
1135 if (!m_inDesignStudio && m_running && !m_physicsInitialized)
1139 m_frameAnimator->start();
1141 m_frameAnimator->stop();
1143 emit runningChanged(m_running);
1146void QPhysicsWorld::setForceDebugDraw(
bool forceDebugDraw)
1148 if (m_forceDebugDraw == forceDebugDraw)
1151 m_forceDebugDraw = forceDebugDraw;
1152 if (!m_forceDebugDraw)
1156 emit forceDebugDrawChanged(m_forceDebugDraw);
1159QQuick3DNode *QPhysicsWorld::viewport()
const
1164void QPhysicsWorld::setHasIndividualDebugDraw()
1166 m_hasIndividualDebugDraw =
true;
1169void QPhysicsWorld::setViewport(QQuick3DNode *viewport)
1171 if (m_viewport == viewport)
1174 m_viewport = viewport;
1177 for (
auto material : std::as_const(m_debugMaterials))
1179 m_debugMaterials.clear();
1181 for (
auto &holder : m_collisionShapeDebugModels) {
1182 holder.releaseMeshPointer();
1183 delete holder.model;
1185 m_collisionShapeDebugModels.clear();
1187 emit viewportChanged(m_viewport);
1190void QPhysicsWorld::setMinimumTimestep(
float minTimestep)
1192 if (qFuzzyCompare(m_minTimestep, minTimestep))
1195 if (!qIsFinite(minTimestep)) {
1196 qWarning(
"Minimum timestep must be finite, ignoring");
1200 if (minTimestep > m_maxTimestep) {
1201 qWarning(
"Minimum timestep greater than maximum timestep, value clamped");
1202 minTimestep = qMin(minTimestep, m_maxTimestep);
1205 if (minTimestep < 0.f) {
1206 qWarning(
"Minimum timestep less than zero, value clamped");
1207 minTimestep = qMax(minTimestep, 0.f);
1210 if (qFuzzyCompare(m_minTimestep, minTimestep))
1213 m_minTimestep = minTimestep;
1214 emit minimumTimestepChanged(m_minTimestep);
1217void QPhysicsWorld::setMaximumTimestep(
float maxTimestep)
1219 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
1222 if (!qIsFinite(maxTimestep)) {
1223 qWarning(
"Maximum timestep must be finite, ignoring");
1227 if (maxTimestep < 0.f) {
1228 qWarning(
"Maximum timestep less than zero, value clamped");
1229 maxTimestep = qMax(maxTimestep, 0.f);
1232 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
1235 m_maxTimestep = maxTimestep;
1236 emit maximumTimestepChanged(maxTimestep);
1239void QPhysicsWorld::setupDebugMaterials(QQuick3DNode *sceneNode)
1241 if (!m_debugMaterials.isEmpty())
1244 const int lineWidth = m_inDesignStudio ? 1 : 3;
1247 for (
auto color : { QColorConstants::Svg::chartreuse, QColorConstants::Svg::cyan,
1248 QColorConstants::Svg::lightsalmon, QColorConstants::Svg::red,
1249 QColorConstants::Svg::blueviolet, QColorConstants::Svg::black }) {
1250 auto debugMaterial =
new QQuick3DPrincipledMaterial();
1251 debugMaterial->setLineWidth(lineWidth);
1252 debugMaterial->setParentItem(sceneNode);
1253 debugMaterial->setParent(sceneNode);
1254 debugMaterial->setBaseColor(color);
1255 debugMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
1256 debugMaterial->setCullMode(QQuick3DMaterial::NoCulling);
1257 m_debugMaterials.push_back(debugMaterial);
1261void QPhysicsWorld::updateDebugDraw()
1263 if (!(m_forceDebugDraw || m_hasIndividualDebugDraw)) {
1265 for (
auto &holder : m_collisionShapeDebugModels) {
1266 holder.releaseMeshPointer();
1267 delete holder.model;
1269 m_collisionShapeDebugModels.clear();
1274 auto sceneNode = m_viewport ? m_viewport : m_scene;
1276 if (sceneNode ==
nullptr)
1279 setupDebugMaterials(sceneNode);
1280 m_hasIndividualDebugDraw =
false;
1283 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>> currentCollisionShapes;
1284 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
1286 for (QAbstractPhysXNode *node : std::as_const(m_physXBodies)) {
1289 if (!node->frontendNode || !node->debugGeometryCapability())
1292 const auto &collisionShapes = node->frontendNode->getCollisionShapesList();
1293 const int materialIdx =
static_cast<
int>(node->getDebugDrawBodyType());
1294 const int length = collisionShapes.length();
1295 for (
int idx = 0; idx < length; idx++) {
1296 const auto collisionShape = collisionShapes[idx];
1298 if (!m_forceDebugDraw && !collisionShape->enableDebugDraw())
1301 DebugModelHolder &holder =
1302 m_collisionShapeDebugModels[std::make_pair(collisionShape, node)];
1303 auto &model = holder.model;
1305 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
1307 m_hasIndividualDebugDraw =
1308 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
1312 model =
new QQuick3DModel();
1313 model->setParentItem(sceneNode);
1314 model->setParent(sceneNode);
1315 model->setCastsShadows(
false);
1316 model->setReceivesShadows(
false);
1317 model->setCastsReflections(
false);
1320 model->setVisible(
true);
1323 auto material = m_debugMaterials[materialIdx];
1324 QQmlListReference materialsRef(model,
"materials");
1325 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
1326 materialsRef.clear();
1327 materialsRef.append(material);
1333 if (qobject_cast<QCharacterController *>(node->frontendNode)) {
1334 QCapsuleShape *capsuleShape = qobject_cast<QCapsuleShape *>(collisionShape);
1338 const float radius = capsuleShape->diameter() * 0.5;
1339 const float halfHeight = capsuleShape->height() * 0.5;
1341 if (!qFuzzyCompare(radius, holder.radius())
1342 || !qFuzzyCompare(halfHeight, holder.halfHeight())) {
1343 auto geom = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
1344 geom->setParent(model);
1345 model->setGeometry(geom);
1346 holder.setRadius(radius);
1347 holder.setHalfHeight(halfHeight);
1350 model->setPosition(node->frontendNode->scenePosition());
1351 model->setRotation(node->frontendNode->sceneRotation()
1352 * QQuaternion::fromEulerAngles(0, 0, 90));
1356 if (node->shapes.length() < length)
1359 const auto physXShape = node->shapes[idx];
1360 auto localPose = physXShape->getLocalPose();
1362 const physx::PxGeometry &geometry = physXShape->getGeometry();
1364 switch (geometry.getType()) {
1365 case physx::PxGeometryType::eBOX: {
1366 const auto &boxGeometry =
static_cast<
const physx::PxBoxGeometry &>(geometry);
1367 const auto &halfExtentsOld = holder.halfExtents();
1368 const auto halfExtents = QPhysicsUtils::toQtType(boxGeometry.halfExtents);
1369 if (!qFuzzyCompare(halfExtentsOld, halfExtents)) {
1370 auto geom = QDebugDrawHelper::generateBoxGeometry(halfExtents);
1371 geom->setParent(model);
1372 model->setGeometry(geom);
1373 holder.setHalfExtents(halfExtents);
1379 case physx::PxGeometryType::eSPHERE: {
1380 const auto &sphereGeometry =
static_cast<
const physx::PxSphereGeometry &>(geometry);
1381 const float radius = holder.radius();
1382 if (!qFuzzyCompare(sphereGeometry.radius, radius)) {
1383 auto geom = QDebugDrawHelper::generateSphereGeometry(sphereGeometry.radius);
1384 geom->setParent(model);
1385 model->setGeometry(geom);
1386 holder.setRadius(sphereGeometry.radius);
1391 case physx::PxGeometryType::eCAPSULE: {
1392 const auto &capsuleGeometry =
1393 static_cast<
const physx::PxCapsuleGeometry &>(geometry);
1394 const float radius = holder.radius();
1395 const float halfHeight = holder.halfHeight();
1397 if (!qFuzzyCompare(capsuleGeometry.radius, radius)
1398 || !qFuzzyCompare(capsuleGeometry.halfHeight, halfHeight)) {
1399 auto geom = QDebugDrawHelper::generateCapsuleGeometry(
1400 capsuleGeometry.radius, capsuleGeometry.halfHeight);
1401 geom->setParent(model);
1402 model->setGeometry(geom);
1403 holder.setRadius(capsuleGeometry.radius);
1404 holder.setHalfHeight(capsuleGeometry.halfHeight);
1409 case physx::PxGeometryType::ePLANE:{
1411 const QQuaternion rotation =
1412 QPhysicsUtils::kMinus90YawRotation * QPhysicsUtils::toQtType(localPose.q);
1413 localPose = physx::PxTransform(localPose.p, QPhysicsUtils::toPhysXType(rotation));
1415 if (model->geometry() ==
nullptr) {
1416 auto geom = QDebugDrawHelper::generatePlaneGeometry();
1417 geom->setParent(model);
1418 model->setGeometry(geom);
1426 case physx::PxGeometryType::eHEIGHTFIELD: {
1427 const auto &heightFieldGeometry =
1428 static_cast<
const physx::PxHeightFieldGeometry &>(geometry);
1429 const float heightScale = holder.heightScale();
1430 const float rowScale = holder.rowScale();
1431 const float columnScale = holder.columnScale();
1433 if (
auto heightField = holder.getHeightField();
1434 heightField && heightField != heightFieldGeometry.heightField) {
1435 heightField->release();
1436 holder.setHeightField(
nullptr);
1439 if (!qFuzzyCompare(heightFieldGeometry.heightScale, heightScale)
1440 || !qFuzzyCompare(heightFieldGeometry.rowScale, rowScale)
1441 || !qFuzzyCompare(heightFieldGeometry.columnScale, columnScale)
1442 || !holder.getHeightField()) {
1443 if (!holder.getHeightField()) {
1444 heightFieldGeometry.heightField->acquireReference();
1445 holder.setHeightField(heightFieldGeometry.heightField);
1447 auto geom = QDebugDrawHelper::generateHeightFieldGeometry(
1448 heightFieldGeometry.heightField, heightFieldGeometry.heightScale,
1449 heightFieldGeometry.rowScale, heightFieldGeometry.columnScale);
1450 geom->setParent(model);
1451 model->setGeometry(geom);
1452 holder.setHeightScale(heightFieldGeometry.heightScale);
1453 holder.setRowScale(heightFieldGeometry.rowScale);
1454 holder.setColumnScale(heightFieldGeometry.columnScale);
1459 case physx::PxGeometryType::eCONVEXMESH: {
1460 const auto &convexMeshGeometry =
1461 static_cast<
const physx::PxConvexMeshGeometry &>(geometry);
1462 const auto rotation = convexMeshGeometry.scale.rotation * localPose.q;
1463 localPose = physx::PxTransform(localPose.p, rotation);
1464 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry.scale.scale));
1466 if (
auto convexMesh = holder.getConvexMesh();
1467 convexMesh && convexMesh != convexMeshGeometry.convexMesh) {
1468 convexMesh->release();
1469 holder.setConvexMesh(
nullptr);
1472 if (!model->geometry() || !holder.getConvexMesh()) {
1473 if (!holder.getConvexMesh()) {
1474 convexMeshGeometry.convexMesh->acquireReference();
1475 holder.setConvexMesh(convexMeshGeometry.convexMesh);
1477 auto geom = QDebugDrawHelper::generateConvexMeshGeometry(
1478 convexMeshGeometry.convexMesh);
1479 geom->setParent(model);
1480 model->setGeometry(geom);
1485 case physx::PxGeometryType::eTRIANGLEMESH: {
1486 const auto &triangleMeshGeometry =
1487 static_cast<
const physx::PxTriangleMeshGeometry &>(geometry);
1488 const auto rotation = triangleMeshGeometry.scale.rotation * localPose.q;
1489 localPose = physx::PxTransform(localPose.p, rotation);
1490 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry.scale.scale));
1492 if (
auto triangleMesh = holder.getTriangleMesh();
1493 triangleMesh && triangleMesh != triangleMeshGeometry.triangleMesh) {
1494 triangleMesh->release();
1495 holder.setTriangleMesh(
nullptr);
1498 if (!model->geometry() || !holder.getTriangleMesh()) {
1499 if (!holder.getTriangleMesh()) {
1500 triangleMeshGeometry.triangleMesh->acquireReference();
1501 holder.setTriangleMesh(triangleMeshGeometry.triangleMesh);
1503 auto geom = QDebugDrawHelper::generateTriangleMeshGeometry(
1504 triangleMeshGeometry.triangleMesh);
1505 geom->setParent(model);
1506 model->setGeometry(geom);
1512 case physx::PxGeometryType::eCONVEXCORE:
1513 case physx::PxGeometryType::ePARTICLESYSTEM:
1514 case physx::PxGeometryType::eTETRAHEDRONMESH:
1515 case physx::PxGeometryType::eCUSTOM:
1516 case physx::PxGeometryType::eINVALID:
1517 case physx::PxGeometryType::eGEOMETRY_COUNT:
1522 auto globalPose = node->getGlobalPose();
1523 auto finalPose = globalPose.transform(localPose);
1525 model->setRotation(QPhysicsUtils::toQtType(finalPose.q));
1526 model->setPosition(QPhysicsUtils::toQtType(finalPose.p));
1531 m_collisionShapeDebugModels.removeIf(
1532 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>,
1533 DebugModelHolder>::iterator it) {
1534 if (!currentCollisionShapes.contains(it.key())) {
1535 auto holder = it.value();
1536 holder.releaseMeshPointer();
1538 delete holder.model;
1547 if (
auto shape = qobject_cast<QAbstractPhysicsNode *>(node)) {
1548 nodes.push_back(shape);
1552 auto childItems = node->childItems();
1553 for (QQuick3DObject *child : std::as_const(childItems))
1554 collectPhysicsNodes(child, nodes);
1557void QPhysicsWorld::updateDebugDrawDesignStudio()
1560 auto sceneNode = m_viewport ? m_viewport : m_scene;
1562 if (sceneNode ==
nullptr)
1565 setupDebugMaterials(sceneNode);
1568 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>> currentCollisionShapes;
1569 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
1571 QList<QAbstractPhysicsNode *> activePhysicsNodes;
1572 activePhysicsNodes.reserve(m_collisionShapeDebugModels.size());
1573 collectPhysicsNodes(m_scene, activePhysicsNodes);
1575 for (QAbstractPhysicsNode *node : std::as_const(activePhysicsNodes)) {
1577 const auto &collisionShapes = node->getCollisionShapesList();
1578 const int materialIdx = 0;
1579 const int length = collisionShapes.length();
1581 const bool isCharacterController = qobject_cast<QCharacterController *>(node) !=
nullptr;
1583 for (
int idx = 0; idx < length; idx++) {
1584 QAbstractCollisionShape *collisionShape = collisionShapes[idx];
1585 DebugModelHolder &holder =
1586 m_DesignStudioDebugModels[std::make_pair(collisionShape, node)];
1587 auto &model = holder.model;
1589 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
1591 m_hasIndividualDebugDraw =
1592 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
1599 model =
new QQuick3DModel();
1600 model->setParentItem(sceneNode);
1601 model->setParent(sceneNode);
1602 model->setCastsShadows(
false);
1603 model->setReceivesShadows(
false);
1604 model->setCastsReflections(
false);
1607 const bool hasGeometry = holder.geometry !=
nullptr;
1608 QVector3D scenePosition = collisionShape->scenePosition();
1609 QQuaternion sceneRotation = collisionShape->sceneRotation();
1610 QQuick3DGeometry *newGeometry =
nullptr;
1612 if (isCharacterController)
1613 sceneRotation = sceneRotation * QQuaternion::fromEulerAngles(QVector3D(0, 0, 90));
1616 auto material = m_debugMaterials[materialIdx];
1617 QQmlListReference materialsRef(model,
"materials");
1618 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
1619 materialsRef.clear();
1620 materialsRef.append(material);
1624 if (
auto shape = qobject_cast<QBoxShape *>(collisionShape)) {
1625 const auto &halfExtentsOld = holder.halfExtents();
1626 const auto halfExtents = shape->sceneScale() * shape->extents() * 0.5f;
1627 if (!qFuzzyCompare(halfExtentsOld, halfExtents) || !hasGeometry) {
1628 newGeometry = QDebugDrawHelper::generateBoxGeometry(halfExtents);
1629 holder.setHalfExtents(halfExtents);
1631 }
else if (
auto shape = qobject_cast<QSphereShape *>(collisionShape)) {
1632 const float radiusOld = holder.radius();
1633 const float radius = shape->sceneScale().x() * shape->diameter() * 0.5f;
1634 if (!qFuzzyCompare(radiusOld, radius) || !hasGeometry) {
1635 newGeometry = QDebugDrawHelper::generateSphereGeometry(radius);
1636 holder.setRadius(radius);
1638 }
else if (
auto shape = qobject_cast<QCapsuleShape *>(collisionShape)) {
1639 const float radiusOld = holder.radius();
1640 const float halfHeightOld = holder.halfHeight();
1641 const float radius = shape->sceneScale().y() * shape->diameter() * 0.5f;
1642 const float halfHeight = shape->sceneScale().x() * shape->height() * 0.5f;
1644 if ((!qFuzzyCompare(radiusOld, radius) || !qFuzzyCompare(halfHeightOld, halfHeight))
1646 newGeometry = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
1647 holder.setRadius(radius);
1648 holder.setHalfHeight(halfHeight);
1650 }
else if (qobject_cast<QPlaneShape *>(collisionShape)) {
1652 newGeometry = QDebugDrawHelper::generatePlaneGeometry();
1653 }
else if (
auto shape = qobject_cast<QHeightFieldShape *>(collisionShape)) {
1654 physx::PxHeightFieldGeometry *heightFieldGeometry =
1655 static_cast<physx::PxHeightFieldGeometry *>(shape->getPhysXGeometry());
1656 const float heightScale = holder.heightScale();
1657 const float rowScale = holder.rowScale();
1658 const float columnScale = holder.columnScale();
1659 scenePosition += shape->hfOffset();
1660 if (!heightFieldGeometry) {
1661 qWarning() <<
"Could not get height field";
1662 }
else if (!qFuzzyCompare(heightFieldGeometry->heightScale, heightScale)
1663 || !qFuzzyCompare(heightFieldGeometry->rowScale, rowScale)
1664 || !qFuzzyCompare(heightFieldGeometry->columnScale, columnScale)
1666 newGeometry = QDebugDrawHelper::generateHeightFieldGeometry(
1667 heightFieldGeometry->heightField, heightFieldGeometry->heightScale,
1668 heightFieldGeometry->rowScale, heightFieldGeometry->columnScale);
1669 holder.setHeightScale(heightFieldGeometry->heightScale);
1670 holder.setRowScale(heightFieldGeometry->rowScale);
1671 holder.setColumnScale(heightFieldGeometry->columnScale);
1673 }
else if (
auto shape = qobject_cast<QConvexMeshShape *>(collisionShape)) {
1674 auto convexMeshGeometry =
1675 static_cast<physx::PxConvexMeshGeometry *>(shape->getPhysXGeometry());
1676 if (!convexMeshGeometry) {
1677 qWarning() <<
"Could not get convex mesh";
1679 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry->scale.scale));
1682 newGeometry = QDebugDrawHelper::generateConvexMeshGeometry(
1683 convexMeshGeometry->convexMesh);
1686 }
else if (
auto shape = qobject_cast<QTriangleMeshShape *>(collisionShape)) {
1687 physx::PxTriangleMeshGeometry *triangleMeshGeometry =
1688 static_cast<physx::PxTriangleMeshGeometry *>(shape->getPhysXGeometry());
1689 if (!triangleMeshGeometry) {
1690 qWarning() <<
"Could not get triangle mesh";
1692 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry->scale.scale));
1695 newGeometry = QDebugDrawHelper::generateTriangleMeshGeometry(
1696 triangleMeshGeometry->triangleMesh);
1702 delete holder.geometry;
1703 holder.geometry = newGeometry;
1706 model->setGeometry(holder.geometry);
1707 model->setVisible(
true);
1709 model->setRotation(sceneRotation);
1710 model->setPosition(scenePosition);
1715 m_DesignStudioDebugModels.removeIf(
1716 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>,
1717 DebugModelHolder>::iterator it) {
1718 if (!currentCollisionShapes.contains(it.key())) {
1719 auto holder = it.value();
1720 holder.releaseMeshPointer();
1722 delete holder.geometry;
1723 delete holder.model;
1731void QPhysicsWorld::disableDebugDraw()
1733 m_hasIndividualDebugDraw =
false;
1735 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1736 if (!body->frontendNode)
1739 const auto &collisionShapes = body->frontendNode->getCollisionShapesList();
1740 const int length = collisionShapes.length();
1741 for (
int idx = 0; idx < length; idx++) {
1742 const auto collisionShape = collisionShapes[idx];
1743 if (collisionShape->enableDebugDraw()) {
1744 m_hasIndividualDebugDraw =
true;
1751void QPhysicsWorld::setEnableCCD(
bool enableCCD)
1753 if (m_enableCCD == enableCCD)
1756 if (m_physicsInitialized) {
1758 <<
"Warning: Changing 'enableCCD' after physics is initialized will have no effect";
1762 m_enableCCD = enableCCD;
1763 emit enableCCDChanged(m_enableCCD);
1766void QPhysicsWorld::setTypicalLength(
float typicalLength)
1768 if (qFuzzyCompare(typicalLength, m_typicalLength))
1771 if (!qIsFinite(typicalLength) || typicalLength <= 0.f) {
1772 qWarning() <<
"Warning: 'typicalLength' value less than zero, ignored";
1776 if (m_physicsInitialized) {
1777 qWarning() <<
"Warning: Changing 'typicalLength' after physics is initialized will have "
1782 m_typicalLength = typicalLength;
1784 emit typicalLengthChanged(typicalLength);
1787void QPhysicsWorld::setTypicalSpeed(
float typicalSpeed)
1789 if (qFuzzyCompare(typicalSpeed, m_typicalSpeed))
1792 if (!qIsFinite(typicalSpeed) || typicalSpeed <= 0.f) {
1793 qWarning() <<
"Warning: 'typicalSpeed' must be finite and greater than zero, ignored";
1797 if (m_physicsInitialized) {
1798 qWarning() <<
"Warning: Changing 'typicalSpeed' after physics is initialized will have "
1803 m_typicalSpeed = typicalSpeed;
1805 emit typicalSpeedChanged(typicalSpeed);
1808float QPhysicsWorld::defaultDensity()
const
1810 return m_defaultDensity;
1813float QPhysicsWorld::minimumTimestep()
const
1815 return m_minTimestep;
1818float QPhysicsWorld::maximumTimestep()
const
1820 return m_maxTimestep;
1823void QPhysicsWorld::setDefaultDensity(
float defaultDensity)
1825 defaultDensity = qBound(0.0000001f, defaultDensity, PX_MAX_F32);
1827 if (qFuzzyCompare(m_defaultDensity, defaultDensity))
1829 m_defaultDensity = defaultDensity;
1832 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1833 if (body->frontendNode)
1834 body->updateDefaultDensity(m_defaultDensity);
1837 emit defaultDensityChanged(defaultDensity);
1842void QPhysicsWorld::cleanupRemovedNodes()
1844 m_physXBodies.removeIf([
this](QAbstractPhysXNode *body) {
1845 return body->cleanupIfRemoved(m_physx);
1847 m_removedPhysicsNodes.clear();
1853void QPhysicsWorld::invalidateTriggerOverlaps(QSpan<QAbstractPhysXNode *
const> rebuiltBodies)
1855 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1856 auto *trigger = qobject_cast<QTriggerBody *>(body->frontendNode);
1859 for (QAbstractPhysXNode *rebuilt : rebuiltBodies) {
1864 if (rebuilt->frontendNode)
1865 trigger->invalidateOverlaps(rebuilt->frontendNode);
1869 m_triggerOverlapsInvalidated =
true;
1872void QPhysicsWorld::dropUnreportedTriggerOverlaps()
1876 if (!m_triggerOverlapsInvalidated)
1878 m_triggerOverlapsInvalidated =
false;
1884 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1885 if (
auto *trigger = qobject_cast<QTriggerBody *>(body->frontendNode))
1886 trigger->dropUnreportedOverlaps();
1890void QPhysicsWorld::cleanupRemovedJoints()
1892 for (physx::PxJoint *joint : m_removedJoints) {
1896 m_removedJoints.clear();
1899void QPhysicsWorld::initPhysics()
1901 Q_ASSERT(!m_physicsInitialized);
1903 const unsigned int numThreads = m_numThreads >= 0 ? m_numThreads : qMax(0, QThread::idealThreadCount());
1904 m_physx->createScene(m_typicalLength, m_typicalSpeed, m_gravity,
this, numThreads);
1905 m_frameAnimator->start();
1906 m_physicsInitialized =
true;
1909void QPhysicsWorld::simulateFrame()
1911 constexpr double MILLIONTH = 0.000001;
1912 constexpr double THOUSANDTH = 0.001;
1914 if (m_inDesignStudio) {
1915 frameFinishedDesignStudio();
1919 if (!m_physx->isRunning) {
1921 m_physx->isRunning =
true;
1922 const double minTimestepSecs = m_minTimestep * 0.001;
1923 m_physx->scene->simulate(minTimestepSecs);
1924 m_currTimeStep = minTimestepSecs;
1929 if (!m_frameFetched && !m_physx->scene->checkResults()) {
1934 if (!m_frameFetched) {
1935 m_physx->scene->fetchResults(
true);
1936 frameFinished(m_currTimeStep);
1937 m_frameFetched =
true;
1938 if (Q_UNLIKELY(!qtPhysicsTimingsFile.isEmpty())) {
1939 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1940 m_frameTimings.append(deltaMS);
1945 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1946 if (deltaMS < m_minTimestep)
1948 const double deltaSecs = qMin<
double>(deltaMS, m_maxTimestep) * THOUSANDTH;
1950 m_physx->scene->simulate(deltaSecs);
1951 m_frameFetched =
false;
1952 m_currTimeStep = deltaSecs;
1955void QPhysicsWorld::frameFinished(
float deltaTime)
1958 matchOrphanJoints();
1959 matchOrphanRaycasters();
1965 dropUnreportedTriggerOverlaps();
1967 emitContactCallbacks();
1968 cleanupRemovedNodes();
1969 cleanupRemovedJoints();
1971 for (
auto *node : std::as_const(m_newPhysicsNodes)) {
1972 auto *body = node->createPhysXBackend();
1973 body->init(
this, m_physx);
1974 m_physXBodies.push_back(body);
1976 m_newPhysicsNodes.clear();
1978 QHash<QQuick3DNode *, QMatrix4x4> transformCache;
1979 QVarLengthArray<QAbstractPhysXNode *, 8> rebuiltBodies;
1982 for (
auto *physXBody : std::as_const(m_physXBodies)) {
1985 if (!physXBody->frontendNode)
1988 physXBody->markDirtyShapes();
1989 const bool wasDirty = physXBody->shapesDirty();
1990 physXBody->rebuildDirtyShapes(
this, m_physx);
1993 if (!physXBody->frontendNode)
1998 if (wasDirty && !physXBody->shapesDirty())
1999 rebuiltBodies.append(physXBody);
2000 physXBody->updateFilters();
2003 physXBody->sync(deltaTime, transformCache);
2006 if (!rebuiltBodies.isEmpty())
2007 invalidateTriggerOverlaps(rebuiltBodies);
2009 for (QPhysicsJoint *joint : std::as_const(m_joints)) {
2010 joint->updatePhysXBackend();
2013 const QList<QRaycaster *> raycasters = m_raycasters;
2014 for (QRaycaster *raycaster : raycasters) {
2015 if (m_raycasters.contains(raycaster))
2016 raycaster->updateRaycast(
this);
2020 emit frameDone(deltaTime * 1000);
2023void QPhysicsWorld::frameFinishedDesignStudio()
2027 matchOrphanJoints();
2030 matchOrphanRaycasters();
2031 emitContactCallbacks();
2032 cleanupRemovedNodes();
2034 m_newPhysicsNodes.clear();
2036 updateDebugDrawDesignStudio();
2039QPhysicsWorld *QPhysicsWorld::getWorld(QQuick3DNode *node)
2041 for (QPhysicsWorld *world : std::as_const(worldManager.worlds)) {
2042 if (!world->m_scene) {
2046 QQuick3DNode *nodeCurr = node;
2049 if (nodeCurr == world->m_scene)
2052 while (nodeCurr->parentNode()) {
2053 nodeCurr = nodeCurr->parentNode();
2054 if (nodeCurr == world->m_scene)
2062QPhysicsWorld *QPhysicsWorld::getWorld(QPhysicsJoint *joint)
2064 for (QPhysicsWorld *world : worldManager.worlds) {
2065 if (!world->m_scene) {
2069 QObject *nodeCurr = joint;
2071 while (nodeCurr->parent()) {
2072 nodeCurr = nodeCurr->parent();
2073 if (nodeCurr == world->m_scene) {
2075 }
else if (
auto view3d = qobject_cast<QQuick3DViewport *>(nodeCurr);
2076 view3d && view3d->scene() == world->m_scene) {
2086void QPhysicsWorld::matchOrphanNodes()
2089 if (worldManager.orphanNodes.isEmpty())
2092 qsizetype numNodes = worldManager.orphanNodes.length();
2095 while (idx < numNodes) {
2096 auto node = worldManager.orphanNodes[idx];
2097 auto world = getWorld(node);
2098 if (world ==
this) {
2099 world->m_newPhysicsNodes.push_back(node);
2101 worldManager.orphanNodes.swapItemsAt(idx, numNodes - 1);
2102 worldManager.orphanNodes.pop_back();
2110void QPhysicsWorld::matchOrphanJoints()
2112 if (worldManager.orphanJoints.isEmpty())
2115 qsizetype numNodes = worldManager.orphanJoints.length();
2118 while (idx < numNodes) {
2119 auto node = worldManager.orphanJoints[idx];
2120 auto world = getWorld(node);
2121 if (world ==
this) {
2122 world->m_joints.push_back(node);
2124 worldManager.orphanJoints.swapItemsAt(idx, numNodes - 1);
2125 worldManager.orphanJoints.pop_back();
2133void QPhysicsWorld::matchOrphanRaycasters()
2136 while (idx < worldManager.orphanRaycasters.size()) {
2137 QRaycaster *raycaster = worldManager.orphanRaycasters.at(idx);
2138 if (getWorld(raycaster) ==
this) {
2139 m_raycasters.push_back(raycaster);
2140 worldManager.orphanRaycasters.removeAt(idx);
2147void QPhysicsWorld::findPhysicsNodes()
2152 if (m_scene ==
nullptr)
2156 QList<QQuick3DObject *> children = m_scene->childItems();
2157 while (!children.empty()) {
2158 auto child = children.takeFirst();
2159 if (
auto converted = qobject_cast<QAbstractPhysicsNode *>(child); converted !=
nullptr) {
2161 if (converted->m_backendObject !=
nullptr) {
2162 qWarning() <<
"Warning: physics node already associated with a backend node.";
2166 m_newPhysicsNodes.push_back(converted);
2167 worldManager.orphanNodes.removeAll(converted);
2169 children.append(child->childItems());
2173void QPhysicsWorld::emitContactCallbacks()
2175 for (
const QPhysicsWorld::BodyContact &contact : std::as_const(m_registeredContacts)) {
2176 if (m_removedPhysicsNodes.contains(contact.sender)
2177 || m_removedPhysicsNodes.contains(contact.receiver))
2179 contact.receiver->registerContact(contact.sender, contact.positions, contact.impulses,
2183 m_registeredContacts.clear();
2186physx::PxPhysics *QPhysicsWorld::getPhysics()
2188 return StaticPhysXObjects::getReference().physics;
2191const physx::PxCookingParams *QPhysicsWorld::getCookingParams()
2196 if (!StaticPhysXObjects::getReference().foundationCreated)
2200 static const physx::PxCookingParams params { physx::PxTolerancesScale() };
2204physx::PxControllerManager *QPhysicsWorld::controllerManager()
2206 if (m_physx->scene && !m_physx->controllerManager) {
2207 m_physx->controllerManager = PxCreateControllerManager(*m_physx->scene);
2208 qCDebug(lcQuick3dPhysics) <<
"Created controller manager" << m_physx->controllerManager;
2210 return m_physx->controllerManager;
2213QQuick3DNode *QPhysicsWorld::scene()
const
2218void QPhysicsWorld::setScene(QQuick3DNode *newScene)
2220 if (m_scene == newScene)
2228 for (
auto body : std::as_const(m_physXBodies)) {
2229 if (body->frontendNode)
2230 deregisterNode(body->frontendNode);
2236 const QList<QRaycaster *> raycasters = std::exchange(m_raycasters, {});
2237 worldManager.orphanRaycasters += raycasters;
2238 for (QRaycaster *raycaster : raycasters) {
2239 if (worldManager.orphanRaycasters.contains(raycaster))
2240 raycaster->clearHit();
2244 bool sceneOK =
true;
2245 for (QPhysicsWorld *world : std::as_const(worldManager.worlds)) {
2246 if (world !=
this && world->scene() == newScene) {
2248 qWarning() <<
"Warning: scene already associated with physics world";
2254 matchOrphanRaycasters();
2256 emit sceneChanged();
2259int QPhysicsWorld::numThreads()
const
2261 return m_numThreads;
2264void QPhysicsWorld::setNumThreads(
int newNumThreads)
2266 newNumThreads = qMin(newNumThreads, 256);
2268 if (m_numThreads == newNumThreads)
2271 m_numThreads = newNumThreads;
2272 emit numThreadsChanged();
2275bool QPhysicsWorld::reportKinematicKinematicCollisions()
const
2277 return m_reportKinematicKinematicCollisions;
2280void QPhysicsWorld::setReportKinematicKinematicCollisions(
2281 bool newReportKinematicKinematicCollisions)
2283 if (m_reportKinematicKinematicCollisions == newReportKinematicKinematicCollisions)
2285 m_reportKinematicKinematicCollisions = newReportKinematicKinematicCollisions;
2286 emit reportKinematicKinematicCollisionsChanged();
2289bool QPhysicsWorld::reportStaticKinematicCollisions()
const
2291 return m_reportStaticKinematicCollisions;
2294void QPhysicsWorld::setReportStaticKinematicCollisions(
bool newReportStaticKinematicCollisions)
2296 if (m_reportStaticKinematicCollisions == newReportStaticKinematicCollisions)
2298 m_reportStaticKinematicCollisions = newReportStaticKinematicCollisions;
2299 emit reportStaticKinematicCollisionsChanged();
2302QPhysicsWorld::QueryStructure QPhysicsWorld::staticQueryStructure()
const
2304 return m_staticQueryStructure;
2307void QPhysicsWorld::setStaticQueryStructure(QueryStructure newStaticQueryStructure)
2309 if (m_staticQueryStructure == newStaticQueryStructure)
2312 if (newStaticQueryStructure == QueryStructure::NoStructure) {
2314 <<
"Warning: The QueryStructure::NoStructure is not supported for staticQueryStructure. Available options: [StaticTree, DynamicTree]";
2318 if (m_physicsInitialized) {
2320 <<
"Warning: Changing 'staticQueryStructure' after physics is initialized will have no effect";
2324 m_staticQueryStructure = newStaticQueryStructure;
2325 emit staticQueryStructureChanged();
2328QPhysicsWorld::QueryStructure QPhysicsWorld::dynamicQueryStructure()
const
2330 return m_dynamicQueryStructure;
2333void QPhysicsWorld::setDynamicQueryStructure(QueryStructure newDynamicQueryStructure)
2335 if (m_dynamicQueryStructure == newDynamicQueryStructure)
2338 if (m_physicsInitialized) {
2340 <<
"Warning: Changing 'dynamicQueryStructure' after physics is initialized will have no effect";
2344 m_dynamicQueryStructure = newDynamicQueryStructure;
2345 emit dynamicQueryStructureChanged();
2348bool QPhysicsWorld::testRaycastQuery(
const QVector3D &origin,
2349 const QVector3D &direction,
2352 bool includeDynamic)
const
2354 physx::PxRaycastBuffer hitBuffer;
2355 return raycastImpl(m_physx->scene, origin, direction,
2356 maxDistance, includeStatic, includeDynamic,
2358 physx::PxHitFlags(),
2359 physx::PxQueryFlag::eANY_HIT);
2362QQuick3DPhysicsLocationHit QPhysicsWorld::singleRaycastQuery(
const QVector3D &origin,
2363 const QVector3D &direction,
2366 bool includeDynamic)
const
2368 physx::PxRaycastBuffer hitBuffer;
2369 const bool status = raycastImpl(m_physx->scene, origin, direction,
2370 maxDistance, includeStatic, includeDynamic,
2372 physx::PxHitFlag::eDEFAULT);
2374 if (status && hitBuffer.hasBlock) {
2375 return QQuick3DPhysicsLocationHit(hitBuffer.block,
2382QList<QQuick3DPhysicsLocationHit> QPhysicsWorld::multiRaycastQuery(
const QVector3D &origin,
2383 const QVector3D &direction,
2386 bool includeDynamic)
const
2388 DynamicQueryCallback<physx::PxRaycastCallback,
2389 QQuick3DPhysicsLocationHit,
2390 physx::PxRaycastHit> hitBuffer;
2392 if (raycastImpl(m_physx->scene, origin, direction,
2393 maxDistance, includeStatic, includeDynamic,
2395 physx::PxHitFlag::eDEFAULT,
2396 physx::PxQueryFlag::eNO_BLOCK)) {
2398 return hitBuffer.hits;
2404QQuick3DPhysicsLocationHit
2405QPhysicsWorld::closestRaycastHit(
const QVector3D &origin,
const QVector3D &direction,
2406 float maxDistance,
bool includeStatic,
bool includeDynamic,
2407 QSpan<
const QAbstractPhysicsNode *
const> excludedBodies)
const
2409 ExcludedBodiesFilter filter(excludedBodies);
2410 physx::PxRaycastBuffer hitBuffer;
2411 const bool status = raycastImpl(m_physx->scene, origin, direction, maxDistance, includeStatic,
2412 includeDynamic, hitBuffer, physx::PxHitFlag::eDEFAULT,
2413 physx::PxQueryFlag::ePREFILTER, &filter);
2415 if (status && hitBuffer.hasBlock)
2416 return QQuick3DPhysicsLocationHit(hitBuffer.block, hitBuffer.block);
2421bool QPhysicsWorld::testSweepQuery(QAbstractCollisionShape *shape,
2422 const QVector3D &direction,
2425 bool includeDynamic)
const
2427 physx::PxSweepBuffer hitBuffer;
2428 return sweepImpl(m_physx->scene, shape, direction,
2429 maxDistance, includeStatic, includeDynamic,
2431 physx::PxHitFlags(),
2432 physx::PxQueryFlag::eANY_HIT);
2435QQuick3DPhysicsLocationHit QPhysicsWorld::singleSweepQuery(QAbstractCollisionShape *shape,
2436 const QVector3D &direction,
2439 bool includeDynamic)
const
2441 physx::PxSweepBuffer hitBuffer;
2442 const bool status = sweepImpl(m_physx->scene, shape,
2443 direction, maxDistance, includeStatic, includeDynamic,
2445 physx::PxHitFlag::eDEFAULT | physx::PxHitFlag::eMTD);
2447 if (status && hitBuffer.hasBlock) {
2448 return QQuick3DPhysicsLocationHit(hitBuffer.block, hitBuffer.block);
2454QList<QQuick3DPhysicsLocationHit> QPhysicsWorld::multiSweepQuery(QAbstractCollisionShape *shape,
2455 const QVector3D &direction,
2458 bool includeDynamic)
const
2460 DynamicQueryCallback<physx::PxSweepCallback,
2461 QQuick3DPhysicsLocationHit,
2462 physx::PxSweepHit> hitBuffer;
2464 if (sweepImpl(m_physx->scene, shape,
2465 direction, maxDistance, includeStatic, includeDynamic,
2467 physx::PxHitFlag::eDEFAULT | physx::PxHitFlag::eMTD,
2468 physx::PxQueryFlag::eNO_BLOCK)) {
2469 return hitBuffer.hits;
2475bool QPhysicsWorld::testOverlapQuery(QAbstractCollisionShape *shape,
2477 bool includeDynamic)
const
2479 physx::PxOverlapBuffer hitBuffer;
2480 return overlapImpl(m_physx->scene,
2481 shape, includeStatic, includeDynamic,
2483 physx::PxQueryFlag::eANY_HIT);
2486QList<QQuick3DPhysicsQueryHit> QPhysicsWorld::multiOverlapQuery(QAbstractCollisionShape *shape,
2488 bool includeDynamic)
const
2490 DynamicQueryCallback<physx::PxOverlapCallback,
2491 QQuick3DPhysicsQueryHit,
2492 physx::PxOverlapHit> hitBuffer;
2494 if (overlapImpl(m_physx->scene,
2495 shape, includeStatic, includeDynamic,
2497 physx::PxQueryFlag::eNO_BLOCK)) {
2498 return hitBuffer.hits;
2506#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 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={}, physx::PxQueryFilterCallback *filterCall=nullptr)
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 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< QRaycaster * > orphanRaycasters
QVector< QPhysicsJoint * > orphanJoints