7#include "physxnode/qabstractphysxnode_p.h"
8#include "physxnode/qphysxworld_p.h"
22#include "PxPhysicsAPI.h"
23#include "cooking/PxCooking.h"
25#include <QtQuick/private/qquickframeanimation_p.h>
26#include <QtQuick3D/private/qquick3dobject_p.h>
27#include <QtQuick3D/private/qquick3dnode_p.h>
28#include <QtQuick3D/private/qquick3dmodel_p.h>
29#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
30#include <QtQuick3DUtils/private/qssgutils_p.h>
32#include <QtGui/qquaternion.h>
34#include <QtEnvironmentVariables>
36#define PHYSX_ENABLE_PVD 0
41
42
43
44
45
46
47
48
49
52
53
54
55
56
59
60
61
64
65
66
67
70
71
72
73
74
75
76
77
80
81
82
83
84
85
86
89
90
91
92
93
94
95
96
97
98
101
102
103
104
105
106
107
108
109
110
113
114
115
116
117
118
121
122
123
124
125
126
127
128
129
130
131
134
135
136
137
138
139
140
141
142
143
144
147
148
149
150
151
152
153
154
157
158
159
160
161
162
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
197
198
199
200
201
202
203
204
205
206
207
208
209
212
213
214
215
216
217
218
219
220
221
222
223
224
225
257void QPhysicsWorld::DebugModelHolder::releaseMeshPointer()
259 if (
auto base =
static_cast<physx::PxBase *>(ptr); base)
264const QVector3D &QPhysicsWorld::DebugModelHolder::halfExtents()
const
268void QPhysicsWorld::DebugModelHolder::setHalfExtents(
const QVector3D &halfExtents)
272float QPhysicsWorld::DebugModelHolder::radius()
const
276void QPhysicsWorld::DebugModelHolder::setRadius(
float radius)
280float QPhysicsWorld::DebugModelHolder::heightScale()
const
284void QPhysicsWorld::DebugModelHolder::setHeightScale(
float heightScale)
286 data.setX(heightScale);
288float QPhysicsWorld::DebugModelHolder::halfHeight()
const
292void QPhysicsWorld::DebugModelHolder::setHalfHeight(
float halfHeight)
294 data.setY(halfHeight);
296float QPhysicsWorld::DebugModelHolder::rowScale()
const
300void QPhysicsWorld::DebugModelHolder::setRowScale(
float rowScale)
304float QPhysicsWorld::DebugModelHolder::columnScale()
const
308void QPhysicsWorld::DebugModelHolder::setColumnScale(
float columnScale)
310 data.setZ(columnScale);
312physx::PxConvexMesh *QPhysicsWorld::DebugModelHolder::getConvexMesh()
314 return static_cast<physx::PxConvexMesh *>(ptr);
316void QPhysicsWorld::DebugModelHolder::setConvexMesh(physx::PxConvexMesh *mesh)
318 ptr =
static_cast<
void *>(mesh);
320physx::PxTriangleMesh *QPhysicsWorld::DebugModelHolder::getTriangleMesh()
322 return static_cast<physx::PxTriangleMesh *>(ptr);
324void QPhysicsWorld::DebugModelHolder::setTriangleMesh(physx::PxTriangleMesh *mesh)
326 ptr =
static_cast<
void *>(mesh);
328physx::PxHeightField *QPhysicsWorld::DebugModelHolder::getHeightField()
330 return static_cast<physx::PxHeightField *>(ptr);
332void QPhysicsWorld::DebugModelHolder::setHeightField(physx::PxHeightField *hf)
334 ptr =
static_cast<physx::PxHeightField *>(hf);
347void QPhysicsWorld::registerNode(QAbstractPhysicsNode *physicsNode)
349 auto world = getWorld(physicsNode);
351 world->m_newPhysicsNodes.push_back(physicsNode);
353 worldManager.orphanNodes.push_back(physicsNode);
357void QPhysicsWorld::deregisterNode(QAbstractPhysicsNode *physicsNode)
359 for (
auto world : std::as_const(worldManager.worlds)) {
360 world->m_newPhysicsNodes.removeAll(physicsNode);
361 QMutexLocker locker(&world->m_removedPhysicsNodesMutex);
362 if (physicsNode->m_backendObject) {
363 Q_ASSERT(physicsNode->m_backendObject->frontendNode == physicsNode);
364 physicsNode->m_backendObject->frontendNode =
nullptr;
365 physicsNode->m_backendObject->isRemoved =
true;
366 physicsNode->m_backendObject =
nullptr;
368 world->m_removedPhysicsNodes.insert(physicsNode);
370 worldManager.orphanNodes.removeAll(physicsNode);
373void QPhysicsWorld::registerContact(QAbstractPhysicsNode *sender, QAbstractPhysicsNode *receiver,
374 const QVector<QVector3D> &positions,
375 const QVector<QVector3D> &impulses,
376 const QVector<QVector3D> &normals)
385 contact.sender = sender;
386 contact.receiver = receiver;
387 contact.positions = positions;
388 contact.impulses = impulses;
389 contact.normals = normals;
391 m_registeredContacts.push_back(contact);
394QPhysicsWorld::QPhysicsWorld(QObject *parent) : QObject(parent)
396 m_inDesignStudio = !qEnvironmentVariableIsEmpty(
"QML_PUPPET_MODE");
397 m_physx =
new QPhysXWorld;
398 m_physx->createWorld();
400 worldManager.worlds.push_back(
this);
403 m_frameAnimator =
new FrameAnimator;
404 connect(m_frameAnimator, &QQuickFrameAnimation::triggered,
this,
405 &QPhysicsWorld::simulateFrame);
408QPhysicsWorld::~QPhysicsWorld()
410 if (m_frameAnimator) {
411 m_frameAnimator->stop();
412 delete m_frameAnimator;
416 m_physx->scene->fetchResults(
true);
418 for (
auto body : std::as_const(m_physXBodies)) {
419 body->cleanup(m_physx);
422 m_physXBodies.clear();
423 m_physx->deleteWorld();
425 worldManager.worlds.removeAll(
this);
427 if (!qtPhysicsTimingsFile.isEmpty()) {
428 if (m_frameTimings.isEmpty()) {
429 qWarning() <<
"No frame timings saved.";
430 }
else if (
auto csvFile = QFile(qtPhysicsTimingsFile); csvFile.open(QIODevice::WriteOnly)) {
431 QTextStream out(&csvFile);
432 for (
int i = 1; i < m_frameTimings.size(); i++) {
433 out << i <<
"," << m_frameTimings[i] <<
'\n';
437 qWarning() <<
"Could not open timings file " << qtPhysicsTimingsFile;
442void QPhysicsWorld::classBegin() {}
444void QPhysicsWorld::componentComplete()
446 if ((!m_running && !m_inDesignStudio) || m_physicsInitialized)
451QVector3D QPhysicsWorld::gravity()
const
456bool QPhysicsWorld::running()
const
461bool QPhysicsWorld::forceDebugDraw()
const
463 return m_forceDebugDraw;
466bool QPhysicsWorld::enableCCD()
const
471float QPhysicsWorld::typicalLength()
const
473 return m_typicalLength;
476float QPhysicsWorld::typicalSpeed()
const
478 return m_typicalSpeed;
481bool QPhysicsWorld::isNodeRemoved(QAbstractPhysicsNode *object)
483 return m_removedPhysicsNodes.contains(object);
486void QPhysicsWorld::setGravity(QVector3D gravity)
488 if (m_gravity == gravity)
492 if (m_physx->scene) {
493 m_physx->scene->setGravity(QPhysicsUtils::toPhysXType(m_gravity));
495 emit gravityChanged(m_gravity);
498void QPhysicsWorld::setRunning(
bool running)
500 if (m_running == running)
504 if (!m_inDesignStudio && m_running && !m_physicsInitialized)
508 m_frameAnimator->start();
510 m_frameAnimator->stop();
512 emit runningChanged(m_running);
515void QPhysicsWorld::setForceDebugDraw(
bool forceDebugDraw)
517 if (m_forceDebugDraw == forceDebugDraw)
520 m_forceDebugDraw = forceDebugDraw;
521 if (!m_forceDebugDraw)
525 emit forceDebugDrawChanged(m_forceDebugDraw);
528QQuick3DNode *QPhysicsWorld::viewport()
const
533void QPhysicsWorld::setHasIndividualDebugDraw()
535 m_hasIndividualDebugDraw =
true;
538void QPhysicsWorld::setViewport(QQuick3DNode *viewport)
540 if (m_viewport == viewport)
543 m_viewport = viewport;
546 for (
auto material : std::as_const(m_debugMaterials))
548 m_debugMaterials.clear();
550 for (
auto &holder : m_collisionShapeDebugModels) {
551 holder.releaseMeshPointer();
554 m_collisionShapeDebugModels.clear();
556 emit viewportChanged(m_viewport);
559void QPhysicsWorld::setMinimumTimestep(
float minTimestep)
561 if (qFuzzyCompare(m_minTimestep, minTimestep))
564 if (minTimestep > m_maxTimestep) {
565 qWarning(
"Minimum timestep greater than maximum timestep, value clamped");
566 minTimestep = qMin(minTimestep, m_maxTimestep);
569 if (minTimestep < 0.f) {
570 qWarning(
"Minimum timestep less than zero, value clamped");
571 minTimestep = qMax(minTimestep, 0.f);
574 if (qFuzzyCompare(m_minTimestep, minTimestep))
577 m_minTimestep = minTimestep;
578 emit minimumTimestepChanged(m_minTimestep);
581void QPhysicsWorld::setMaximumTimestep(
float maxTimestep)
583 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
586 if (maxTimestep < 0.f) {
587 qWarning(
"Maximum timestep less than zero, value clamped");
588 maxTimestep = qMax(maxTimestep, 0.f);
591 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
594 m_maxTimestep = maxTimestep;
595 emit maximumTimestepChanged(maxTimestep);
598void QPhysicsWorld::setupDebugMaterials(QQuick3DNode *sceneNode)
600 if (!m_debugMaterials.isEmpty())
603 const int lineWidth = m_inDesignStudio ? 1 : 3;
606 for (
auto color : { QColorConstants::Svg::chartreuse, QColorConstants::Svg::cyan,
607 QColorConstants::Svg::lightsalmon, QColorConstants::Svg::red,
608 QColorConstants::Svg::blueviolet, QColorConstants::Svg::black }) {
609 auto debugMaterial =
new QQuick3DPrincipledMaterial();
610 debugMaterial->setLineWidth(lineWidth);
611 debugMaterial->setParentItem(sceneNode);
612 debugMaterial->setParent(sceneNode);
613 debugMaterial->setBaseColor(color);
614 debugMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
615 debugMaterial->setCullMode(QQuick3DMaterial::NoCulling);
616 m_debugMaterials.push_back(debugMaterial);
620void QPhysicsWorld::updateDebugDraw()
622 if (!(m_forceDebugDraw || m_hasIndividualDebugDraw)) {
624 for (
auto &holder : m_collisionShapeDebugModels) {
625 holder.releaseMeshPointer();
628 m_collisionShapeDebugModels.clear();
633 auto sceneNode = m_viewport ? m_viewport : m_scene;
635 if (sceneNode ==
nullptr)
638 setupDebugMaterials(sceneNode);
639 m_hasIndividualDebugDraw =
false;
642 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>> currentCollisionShapes;
643 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
645 for (QAbstractPhysXNode *node : std::as_const(m_physXBodies)) {
646 if (!node->debugGeometryCapability())
649 const auto &collisionShapes = node->frontendNode->getCollisionShapesList();
650 const int materialIdx =
static_cast<
int>(node->getDebugDrawBodyType());
651 const int length = collisionShapes.length();
652 for (
int idx = 0; idx < length; idx++) {
653 const auto collisionShape = collisionShapes[idx];
655 if (!m_forceDebugDraw && !collisionShape->enableDebugDraw())
658 DebugModelHolder &holder =
659 m_collisionShapeDebugModels[std::make_pair(collisionShape, node)];
660 auto &model = holder.model;
662 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
664 m_hasIndividualDebugDraw =
665 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
669 model =
new QQuick3DModel();
670 model->setParentItem(sceneNode);
671 model->setParent(sceneNode);
672 model->setCastsShadows(
false);
673 model->setReceivesShadows(
false);
674 model->setCastsReflections(
false);
677 model->setVisible(
true);
680 auto material = m_debugMaterials[materialIdx];
681 QQmlListReference materialsRef(model,
"materials");
682 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
683 materialsRef.clear();
684 materialsRef.append(material);
690 if (qobject_cast<QCharacterController *>(node->frontendNode)) {
691 QCapsuleShape *capsuleShape = qobject_cast<QCapsuleShape *>(collisionShape);
695 const float radius = capsuleShape->diameter() * 0.5;
696 const float halfHeight = capsuleShape->height() * 0.5;
698 if (!qFuzzyCompare(radius, holder.radius())
699 || !qFuzzyCompare(halfHeight, holder.halfHeight())) {
700 auto geom = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
701 geom->setParent(model);
702 model->setGeometry(geom);
703 holder.setRadius(radius);
704 holder.setHalfHeight(halfHeight);
707 model->setPosition(node->frontendNode->scenePosition());
708 model->setRotation(node->frontendNode->sceneRotation()
709 * QQuaternion::fromEulerAngles(0, 0, 90));
713 if (node->shapes.length() < length)
716 const auto physXShape = node->shapes[idx];
717 auto localPose = physXShape->getLocalPose();
719 switch (physXShape->getGeometryType()) {
720 case physx::PxGeometryType::eBOX: {
721 physx::PxBoxGeometry boxGeometry;
722 physXShape->getBoxGeometry(boxGeometry);
723 const auto &halfExtentsOld = holder.halfExtents();
724 const auto halfExtents = QPhysicsUtils::toQtType(boxGeometry.halfExtents);
725 if (!qFuzzyCompare(halfExtentsOld, halfExtents)) {
726 auto geom = QDebugDrawHelper::generateBoxGeometry(halfExtents);
727 geom->setParent(model);
728 model->setGeometry(geom);
729 holder.setHalfExtents(halfExtents);
735 case physx::PxGeometryType::eSPHERE: {
736 physx::PxSphereGeometry sphereGeometry;
737 physXShape->getSphereGeometry(sphereGeometry);
738 const float radius = holder.radius();
739 if (!qFuzzyCompare(sphereGeometry.radius, radius)) {
740 auto geom = QDebugDrawHelper::generateSphereGeometry(sphereGeometry.radius);
741 geom->setParent(model);
742 model->setGeometry(geom);
743 holder.setRadius(sphereGeometry.radius);
748 case physx::PxGeometryType::eCAPSULE: {
749 physx::PxCapsuleGeometry capsuleGeometry;
750 physXShape->getCapsuleGeometry(capsuleGeometry);
751 const float radius = holder.radius();
752 const float halfHeight = holder.halfHeight();
754 if (!qFuzzyCompare(capsuleGeometry.radius, radius)
755 || !qFuzzyCompare(capsuleGeometry.halfHeight, halfHeight)) {
756 auto geom = QDebugDrawHelper::generateCapsuleGeometry(
757 capsuleGeometry.radius, capsuleGeometry.halfHeight);
758 geom->setParent(model);
759 model->setGeometry(geom);
760 holder.setRadius(capsuleGeometry.radius);
761 holder.setHalfHeight(capsuleGeometry.halfHeight);
766 case physx::PxGeometryType::ePLANE:{
767 physx::PxPlaneGeometry planeGeometry;
768 physXShape->getPlaneGeometry(planeGeometry);
770 const QQuaternion rotation =
771 QPhysicsUtils::kMinus90YawRotation * QPhysicsUtils::toQtType(localPose.q);
772 localPose = physx::PxTransform(localPose.p, QPhysicsUtils::toPhysXType(rotation));
774 if (model->geometry() ==
nullptr) {
775 auto geom = QDebugDrawHelper::generatePlaneGeometry();
776 geom->setParent(model);
777 model->setGeometry(geom);
785 case physx::PxGeometryType::eHEIGHTFIELD: {
786 physx::PxHeightFieldGeometry heightFieldGeometry;
787 bool success = physXShape->getHeightFieldGeometry(heightFieldGeometry);
789 const float heightScale = holder.heightScale();
790 const float rowScale = holder.rowScale();
791 const float columnScale = holder.columnScale();
793 if (
auto heightField = holder.getHeightField();
794 heightField && heightField != heightFieldGeometry.heightField) {
795 heightField->release();
796 holder.setHeightField(
nullptr);
799 if (!qFuzzyCompare(heightFieldGeometry.heightScale, heightScale)
800 || !qFuzzyCompare(heightFieldGeometry.rowScale, rowScale)
801 || !qFuzzyCompare(heightFieldGeometry.columnScale, columnScale)
802 || !holder.getHeightField()) {
803 if (!holder.getHeightField()) {
804 heightFieldGeometry.heightField->acquireReference();
805 holder.setHeightField(heightFieldGeometry.heightField);
807 auto geom = QDebugDrawHelper::generateHeightFieldGeometry(
808 heightFieldGeometry.heightField, heightFieldGeometry.heightScale,
809 heightFieldGeometry.rowScale, heightFieldGeometry.columnScale);
810 geom->setParent(model);
811 model->setGeometry(geom);
812 holder.setHeightScale(heightFieldGeometry.heightScale);
813 holder.setRowScale(heightFieldGeometry.rowScale);
814 holder.setColumnScale(heightFieldGeometry.columnScale);
819 case physx::PxGeometryType::eCONVEXMESH: {
820 physx::PxConvexMeshGeometry convexMeshGeometry;
821 const bool success = physXShape->getConvexMeshGeometry(convexMeshGeometry);
823 const auto rotation = convexMeshGeometry.scale.rotation * localPose.q;
824 localPose = physx::PxTransform(localPose.p, rotation);
825 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry.scale.scale));
827 if (
auto convexMesh = holder.getConvexMesh();
828 convexMesh && convexMesh != convexMeshGeometry.convexMesh) {
829 convexMesh->release();
830 holder.setConvexMesh(
nullptr);
833 if (!model->geometry() || !holder.getConvexMesh()) {
834 if (!holder.getConvexMesh()) {
835 convexMeshGeometry.convexMesh->acquireReference();
836 holder.setConvexMesh(convexMeshGeometry.convexMesh);
838 auto geom = QDebugDrawHelper::generateConvexMeshGeometry(
839 convexMeshGeometry.convexMesh);
840 geom->setParent(model);
841 model->setGeometry(geom);
846 case physx::PxGeometryType::eTRIANGLEMESH: {
847 physx::PxTriangleMeshGeometry triangleMeshGeometry;
848 const bool success = physXShape->getTriangleMeshGeometry(triangleMeshGeometry);
850 const auto rotation = triangleMeshGeometry.scale.rotation * localPose.q;
851 localPose = physx::PxTransform(localPose.p, rotation);
852 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry.scale.scale));
854 if (
auto triangleMesh = holder.getTriangleMesh();
855 triangleMesh && triangleMesh != triangleMeshGeometry.triangleMesh) {
856 triangleMesh->release();
857 holder.setTriangleMesh(
nullptr);
860 if (!model->geometry() || !holder.getTriangleMesh()) {
861 if (!holder.getTriangleMesh()) {
862 triangleMeshGeometry.triangleMesh->acquireReference();
863 holder.setTriangleMesh(triangleMeshGeometry.triangleMesh);
865 auto geom = QDebugDrawHelper::generateTriangleMeshGeometry(
866 triangleMeshGeometry.triangleMesh);
867 geom->setParent(model);
868 model->setGeometry(geom);
873 case physx::PxGeometryType::eINVALID:
874 case physx::PxGeometryType::eGEOMETRY_COUNT:
879 auto globalPose = node->getGlobalPose();
880 auto finalPose = globalPose.transform(localPose);
882 model->setRotation(QPhysicsUtils::toQtType(finalPose.q));
883 model->setPosition(QPhysicsUtils::toQtType(finalPose.p));
888 m_collisionShapeDebugModels.removeIf(
889 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>,
890 DebugModelHolder>::iterator it) {
891 if (!currentCollisionShapes.contains(it.key())) {
892 auto holder = it.value();
893 holder.releaseMeshPointer();
904 if (
auto shape = qobject_cast<QAbstractPhysicsNode *>(node)) {
905 nodes.push_back(shape);
909 auto childItems = node->childItems();
910 for (QQuick3DObject *child : std::as_const(childItems))
911 collectPhysicsNodes(child, nodes);
914void QPhysicsWorld::updateDebugDrawDesignStudio()
917 auto sceneNode = m_viewport ? m_viewport : m_scene;
919 if (sceneNode ==
nullptr)
922 setupDebugMaterials(sceneNode);
925 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>> currentCollisionShapes;
926 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
928 QList<QAbstractPhysicsNode *> activePhysicsNodes;
929 activePhysicsNodes.reserve(m_collisionShapeDebugModels.size());
930 collectPhysicsNodes(m_scene, activePhysicsNodes);
932 for (QAbstractPhysicsNode *node : std::as_const(activePhysicsNodes)) {
934 const auto &collisionShapes = node->getCollisionShapesList();
935 const int materialIdx = 0;
936 const int length = collisionShapes.length();
938 const bool isCharacterController = qobject_cast<QCharacterController *>(node) !=
nullptr;
940 for (
int idx = 0; idx < length; idx++) {
941 QAbstractCollisionShape *collisionShape = collisionShapes[idx];
942 DebugModelHolder &holder =
943 m_DesignStudioDebugModels[std::make_pair(collisionShape, node)];
944 auto &model = holder.model;
946 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
948 m_hasIndividualDebugDraw =
949 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
956 model =
new QQuick3DModel();
957 model->setParentItem(sceneNode);
958 model->setParent(sceneNode);
959 model->setCastsShadows(
false);
960 model->setReceivesShadows(
false);
961 model->setCastsReflections(
false);
964 const bool hasGeometry = holder.geometry !=
nullptr;
965 QVector3D scenePosition = collisionShape->scenePosition();
966 QQuaternion sceneRotation = collisionShape->sceneRotation();
967 QQuick3DGeometry *newGeometry =
nullptr;
969 if (isCharacterController)
970 sceneRotation = sceneRotation * QQuaternion::fromEulerAngles(QVector3D(0, 0, 90));
973 auto material = m_debugMaterials[materialIdx];
974 QQmlListReference materialsRef(model,
"materials");
975 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
976 materialsRef.clear();
977 materialsRef.append(material);
981 if (
auto shape = qobject_cast<QBoxShape *>(collisionShape)) {
982 const auto &halfExtentsOld = holder.halfExtents();
983 const auto halfExtents = shape->sceneScale() * shape->extents() * 0.5f;
984 if (!qFuzzyCompare(halfExtentsOld, halfExtents) || !hasGeometry) {
985 newGeometry = QDebugDrawHelper::generateBoxGeometry(halfExtents);
986 holder.setHalfExtents(halfExtents);
988 }
else if (
auto shape = qobject_cast<QSphereShape *>(collisionShape)) {
989 const float radiusOld = holder.radius();
990 const float radius = shape->sceneScale().x() * shape->diameter() * 0.5f;
991 if (!qFuzzyCompare(radiusOld, radius) || !hasGeometry) {
992 newGeometry = QDebugDrawHelper::generateSphereGeometry(radius);
993 holder.setRadius(radius);
995 }
else if (
auto shape = qobject_cast<QCapsuleShape *>(collisionShape)) {
996 const float radiusOld = holder.radius();
997 const float halfHeightOld = holder.halfHeight();
998 const float radius = shape->sceneScale().y() * shape->diameter() * 0.5f;
999 const float halfHeight = shape->sceneScale().x() * shape->height() * 0.5f;
1001 if ((!qFuzzyCompare(radiusOld, radius) || !qFuzzyCompare(halfHeightOld, halfHeight))
1003 newGeometry = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
1004 holder.setRadius(radius);
1005 holder.setHalfHeight(halfHeight);
1007 }
else if (qobject_cast<QPlaneShape *>(collisionShape)) {
1009 newGeometry = QDebugDrawHelper::generatePlaneGeometry();
1010 }
else if (
auto shape = qobject_cast<QHeightFieldShape *>(collisionShape)) {
1011 physx::PxHeightFieldGeometry *heightFieldGeometry =
1012 static_cast<physx::PxHeightFieldGeometry *>(shape->getPhysXGeometry());
1013 const float heightScale = holder.heightScale();
1014 const float rowScale = holder.rowScale();
1015 const float columnScale = holder.columnScale();
1016 scenePosition += shape->hfOffset();
1017 if (!heightFieldGeometry) {
1018 qWarning() <<
"Could not get height field";
1019 }
else if (!qFuzzyCompare(heightFieldGeometry->heightScale, heightScale)
1020 || !qFuzzyCompare(heightFieldGeometry->rowScale, rowScale)
1021 || !qFuzzyCompare(heightFieldGeometry->columnScale, columnScale)
1023 newGeometry = QDebugDrawHelper::generateHeightFieldGeometry(
1024 heightFieldGeometry->heightField, heightFieldGeometry->heightScale,
1025 heightFieldGeometry->rowScale, heightFieldGeometry->columnScale);
1026 holder.setHeightScale(heightFieldGeometry->heightScale);
1027 holder.setRowScale(heightFieldGeometry->rowScale);
1028 holder.setColumnScale(heightFieldGeometry->columnScale);
1030 }
else if (
auto shape = qobject_cast<QConvexMeshShape *>(collisionShape)) {
1031 auto convexMeshGeometry =
1032 static_cast<physx::PxConvexMeshGeometry *>(shape->getPhysXGeometry());
1033 if (!convexMeshGeometry) {
1034 qWarning() <<
"Could not get convex mesh";
1036 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry->scale.scale));
1039 newGeometry = QDebugDrawHelper::generateConvexMeshGeometry(
1040 convexMeshGeometry->convexMesh);
1043 }
else if (
auto shape = qobject_cast<QTriangleMeshShape *>(collisionShape)) {
1044 physx::PxTriangleMeshGeometry *triangleMeshGeometry =
1045 static_cast<physx::PxTriangleMeshGeometry *>(shape->getPhysXGeometry());
1046 if (!triangleMeshGeometry) {
1047 qWarning() <<
"Could not get triangle mesh";
1049 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry->scale.scale));
1052 newGeometry = QDebugDrawHelper::generateTriangleMeshGeometry(
1053 triangleMeshGeometry->triangleMesh);
1059 delete holder.geometry;
1060 holder.geometry = newGeometry;
1063 model->setGeometry(holder.geometry);
1064 model->setVisible(
true);
1066 model->setRotation(sceneRotation);
1067 model->setPosition(scenePosition);
1072 m_DesignStudioDebugModels.removeIf(
1073 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>,
1074 DebugModelHolder>::iterator it) {
1075 if (!currentCollisionShapes.contains(it.key())) {
1076 auto holder = it.value();
1077 holder.releaseMeshPointer();
1079 delete holder.geometry;
1080 delete holder.model;
1088void QPhysicsWorld::disableDebugDraw()
1090 m_hasIndividualDebugDraw =
false;
1092 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies)) {
1093 const auto &collisionShapes = body->frontendNode->getCollisionShapesList();
1094 const int length = collisionShapes.length();
1095 for (
int idx = 0; idx < length; idx++) {
1096 const auto collisionShape = collisionShapes[idx];
1097 if (collisionShape->enableDebugDraw()) {
1098 m_hasIndividualDebugDraw =
true;
1105void QPhysicsWorld::setEnableCCD(
bool enableCCD)
1107 if (m_enableCCD == enableCCD)
1110 if (m_physicsInitialized) {
1112 <<
"Warning: Changing 'enableCCD' after physics is initialized will have no effect";
1116 m_enableCCD = enableCCD;
1117 emit enableCCDChanged(m_enableCCD);
1120void QPhysicsWorld::setTypicalLength(
float typicalLength)
1122 if (qFuzzyCompare(typicalLength, m_typicalLength))
1125 if (typicalLength <= 0.f) {
1126 qWarning() <<
"Warning: 'typicalLength' value less than zero, ignored";
1130 if (m_physicsInitialized) {
1131 qWarning() <<
"Warning: Changing 'typicalLength' after physics is initialized will have "
1136 m_typicalLength = typicalLength;
1138 emit typicalLengthChanged(typicalLength);
1141void QPhysicsWorld::setTypicalSpeed(
float typicalSpeed)
1143 if (qFuzzyCompare(typicalSpeed, m_typicalSpeed))
1146 if (m_physicsInitialized) {
1147 qWarning() <<
"Warning: Changing 'typicalSpeed' after physics is initialized will have "
1152 m_typicalSpeed = typicalSpeed;
1154 emit typicalSpeedChanged(typicalSpeed);
1157float QPhysicsWorld::defaultDensity()
const
1159 return m_defaultDensity;
1162float QPhysicsWorld::minimumTimestep()
const
1164 return m_minTimestep;
1167float QPhysicsWorld::maximumTimestep()
const
1169 return m_maxTimestep;
1172void QPhysicsWorld::setDefaultDensity(
float defaultDensity)
1174 if (qFuzzyCompare(m_defaultDensity, defaultDensity))
1176 m_defaultDensity = defaultDensity;
1179 for (QAbstractPhysXNode *body : std::as_const(m_physXBodies))
1180 body->updateDefaultDensity(m_defaultDensity);
1182 emit defaultDensityChanged(defaultDensity);
1187void QPhysicsWorld::cleanupRemovedNodes()
1189 m_physXBodies.removeIf([
this](QAbstractPhysXNode *body) {
1190 return body->cleanupIfRemoved(m_physx);
1192 m_removedPhysicsNodes.clear();
1195void QPhysicsWorld::initPhysics()
1197 Q_ASSERT(!m_physicsInitialized);
1199 const unsigned int numThreads = m_numThreads >= 0 ? m_numThreads : qMax(0, QThread::idealThreadCount());
1200 m_physx->createScene(m_typicalLength, m_typicalSpeed, m_gravity, m_enableCCD,
this, numThreads);
1201 m_frameAnimator->start();
1202 m_physicsInitialized =
true;
1205void QPhysicsWorld::simulateFrame()
1207 constexpr double MILLIONTH = 0.000001;
1208 constexpr double THOUSANDTH = 0.001;
1210 if (m_inDesignStudio) {
1211 frameFinishedDesignStudio();
1215 if (!m_physx->isRunning) {
1217 m_physx->isRunning =
true;
1218 const double minTimestepSecs = m_minTimestep * 0.001;
1219 m_physx->scene->simulate(minTimestepSecs);
1220 m_currTimeStep = minTimestepSecs;
1225 if (!m_frameFetched && !m_physx->scene->checkResults()) {
1230 if (!m_frameFetched) {
1231 m_physx->scene->fetchResults(
true);
1232 frameFinished(m_currTimeStep);
1233 m_frameFetched =
true;
1234 if (Q_UNLIKELY(!qtPhysicsTimingsFile.isEmpty())) {
1235 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1236 m_frameTimings.append(deltaMS);
1241 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1242 if (deltaMS < m_minTimestep)
1244 const double deltaSecs = qMin<
double>(deltaMS, m_maxTimestep) * THOUSANDTH;
1246 m_physx->scene->simulate(deltaSecs);
1247 m_frameFetched =
false;
1248 m_currTimeStep = deltaSecs;
1251void QPhysicsWorld::frameFinished(
float deltaTime)
1254 emitContactCallbacks();
1255 cleanupRemovedNodes();
1256 for (
auto *node : std::as_const(m_newPhysicsNodes)) {
1257 auto *body = node->createPhysXBackend();
1258 body->init(
this, m_physx);
1259 m_physXBodies.push_back(body);
1261 m_newPhysicsNodes.clear();
1263 QHash<QQuick3DNode *, QMatrix4x4> transformCache;
1266 for (
auto *physXBody : std::as_const(m_physXBodies)) {
1267 physXBody->markDirtyShapes();
1268 physXBody->rebuildDirtyShapes(
this, m_physx);
1269 physXBody->updateFilters();
1272 physXBody->sync(deltaTime, transformCache);
1276 emit frameDone(deltaTime * 1000);
1279void QPhysicsWorld::frameFinishedDesignStudio()
1283 emitContactCallbacks();
1284 cleanupRemovedNodes();
1286 m_newPhysicsNodes.clear();
1288 updateDebugDrawDesignStudio();
1291QPhysicsWorld *QPhysicsWorld::getWorld(QQuick3DNode *node)
1293 for (QPhysicsWorld *world : std::as_const(worldManager.worlds)) {
1294 if (!world->m_scene) {
1298 QQuick3DNode *nodeCurr = node;
1301 if (nodeCurr == world->m_scene)
1304 while (nodeCurr->parentNode()) {
1305 nodeCurr = nodeCurr->parentNode();
1306 if (nodeCurr == world->m_scene)
1314void QPhysicsWorld::matchOrphanNodes()
1317 if (worldManager.orphanNodes.isEmpty())
1320 qsizetype numNodes = worldManager.orphanNodes.length();
1323 while (idx < numNodes) {
1324 auto node = worldManager.orphanNodes[idx];
1325 auto world = getWorld(node);
1326 if (world ==
this) {
1327 world->m_newPhysicsNodes.push_back(node);
1329 worldManager.orphanNodes.swapItemsAt(idx, numNodes - 1);
1330 worldManager.orphanNodes.pop_back();
1338void QPhysicsWorld::findPhysicsNodes()
1343 if (m_scene ==
nullptr)
1347 QList<QQuick3DObject *> children = m_scene->childItems();
1348 while (!children.empty()) {
1349 auto child = children.takeFirst();
1350 if (
auto converted = qobject_cast<QAbstractPhysicsNode *>(child); converted !=
nullptr) {
1352 if (converted->m_backendObject !=
nullptr) {
1353 qWarning() <<
"Warning: physics node already associated with a backend node.";
1357 m_newPhysicsNodes.push_back(converted);
1358 worldManager.orphanNodes.removeAll(converted);
1360 children.append(child->childItems());
1364void QPhysicsWorld::emitContactCallbacks()
1366 for (
const QPhysicsWorld::BodyContact &contact : std::as_const(m_registeredContacts)) {
1367 if (m_removedPhysicsNodes.contains(contact.sender)
1368 || m_removedPhysicsNodes.contains(contact.receiver))
1370 contact.receiver->registerContact(contact.sender, contact.positions, contact.impulses,
1374 m_registeredContacts.clear();
1377physx::PxPhysics *QPhysicsWorld::getPhysics()
1379 return StaticPhysXObjects::getReference().physics;
1382physx::PxCooking *QPhysicsWorld::getCooking()
1384 return StaticPhysXObjects::getReference().cooking;
1387physx::PxControllerManager *QPhysicsWorld::controllerManager()
1389 if (m_physx->scene && !m_physx->controllerManager) {
1390 m_physx->controllerManager = PxCreateControllerManager(*m_physx->scene);
1391 qCDebug(lcQuick3dPhysics) <<
"Created controller manager" << m_physx->controllerManager;
1393 return m_physx->controllerManager;
1396QQuick3DNode *QPhysicsWorld::scene()
const
1401void QPhysicsWorld::setScene(QQuick3DNode *newScene)
1403 if (m_scene == newScene)
1409 for (
auto body : std::as_const(m_physXBodies)) {
1410 deregisterNode(body->frontendNode);
1414 bool sceneOK =
true;
1415 for (QPhysicsWorld *world : std::as_const(worldManager.worlds)) {
1416 if (world !=
this && world->scene() == newScene) {
1418 qWarning() <<
"Warning: scene already associated with physics world";
1424 emit sceneChanged();
1427int QPhysicsWorld::numThreads()
const
1429 return m_numThreads;
1432void QPhysicsWorld::setNumThreads(
int newNumThreads)
1434 if (m_numThreads == newNumThreads)
1436 m_numThreads = newNumThreads;
1437 emit numThreadsChanged();
1440bool QPhysicsWorld::reportKinematicKinematicCollisions()
const
1442 return m_reportKinematicKinematicCollisions;
1445void QPhysicsWorld::setReportKinematicKinematicCollisions(
1446 bool newReportKinematicKinematicCollisions)
1448 if (m_reportKinematicKinematicCollisions == newReportKinematicKinematicCollisions)
1450 m_reportKinematicKinematicCollisions = newReportKinematicKinematicCollisions;
1451 emit reportKinematicKinematicCollisionsChanged();
1454bool QPhysicsWorld::reportStaticKinematicCollisions()
const
1456 return m_reportStaticKinematicCollisions;
1459void QPhysicsWorld::setReportStaticKinematicCollisions(
bool newReportStaticKinematicCollisions)
1461 if (m_reportStaticKinematicCollisions == newReportStaticKinematicCollisions)
1463 m_reportStaticKinematicCollisions = newReportStaticKinematicCollisions;
1464 emit reportStaticKinematicCollisionsChanged();
1469#include "qphysicsworld.moc"
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
static QWorldManager worldManager
static void collectPhysicsNodes(QQuick3DObject *node, QList< QAbstractPhysicsNode * > &nodes)
static const QString qtPhysicsTimingsFile
#define QT_BEGIN_NAMESPACE
QVector< QAbstractPhysicsNode * > orphanNodes
QVector< QPhysicsWorld * > worlds