6#include "physxnode/qabstractphysxnode_p.h"
7#include "physxnode/qphysxworld_p.h"
21#include "PxPhysicsAPI.h"
22#include "cooking/PxCooking.h"
24#include <QtQuick/private/qquickframeanimation_p.h>
25#include <QtQuick3D/private/qquick3dobject_p.h>
26#include <QtQuick3D/private/qquick3dnode_p.h>
27#include <QtQuick3D/private/qquick3dmodel_p.h>
28#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
29#include <QtQuick3DUtils/private/qssgutils_p.h>
31#include <QtGui/qquaternion.h>
33#include <QtEnvironmentVariables>
35#define PHYSX_ENABLE_PVD 0
40
41
42
43
44
45
46
47
48
51
52
53
54
55
58
59
60
63
64
65
66
69
70
71
72
73
74
75
76
79
80
81
82
83
84
85
88
89
90
91
92
93
94
95
96
97
100
101
102
103
104
105
106
107
108
109
112
113
114
115
116
117
120
121
122
123
124
125
126
127
128
129
130
133
134
135
136
137
138
139
140
141
142
143
146
147
148
149
150
151
152
153
156
157
158
159
160
161
164
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
196
197
198
199
200
201
202
203
204
205
206
207
208
211
212
213
214
215
216
217
218
219
220
221
222
223
224
256void QPhysicsWorld::DebugModelHolder::releaseMeshPointer()
258 if (
auto base =
static_cast<physx::PxBase *>(ptr); base)
263const QVector3D &QPhysicsWorld::DebugModelHolder::halfExtents()
const
267void QPhysicsWorld::DebugModelHolder::setHalfExtents(
const QVector3D &halfExtents)
271float QPhysicsWorld::DebugModelHolder::radius()
const
275void QPhysicsWorld::DebugModelHolder::setRadius(
float radius)
279float QPhysicsWorld::DebugModelHolder::heightScale()
const
283void QPhysicsWorld::DebugModelHolder::setHeightScale(
float heightScale)
285 data.setX(heightScale);
287float QPhysicsWorld::DebugModelHolder::halfHeight()
const
291void QPhysicsWorld::DebugModelHolder::setHalfHeight(
float halfHeight)
293 data.setY(halfHeight);
295float QPhysicsWorld::DebugModelHolder::rowScale()
const
299void QPhysicsWorld::DebugModelHolder::setRowScale(
float rowScale)
303float QPhysicsWorld::DebugModelHolder::columnScale()
const
307void QPhysicsWorld::DebugModelHolder::setColumnScale(
float columnScale)
309 data.setZ(columnScale);
311physx::PxConvexMesh *QPhysicsWorld::DebugModelHolder::getConvexMesh()
313 return static_cast<physx::PxConvexMesh *>(ptr);
315void QPhysicsWorld::DebugModelHolder::setConvexMesh(physx::PxConvexMesh *mesh)
317 ptr =
static_cast<
void *>(mesh);
319physx::PxTriangleMesh *QPhysicsWorld::DebugModelHolder::getTriangleMesh()
321 return static_cast<physx::PxTriangleMesh *>(ptr);
323void QPhysicsWorld::DebugModelHolder::setTriangleMesh(physx::PxTriangleMesh *mesh)
325 ptr =
static_cast<
void *>(mesh);
327physx::PxHeightField *QPhysicsWorld::DebugModelHolder::getHeightField()
329 return static_cast<physx::PxHeightField *>(ptr);
331void QPhysicsWorld::DebugModelHolder::setHeightField(physx::PxHeightField *hf)
333 ptr =
static_cast<physx::PxHeightField *>(hf);
346void QPhysicsWorld::registerNode(QAbstractPhysicsNode *physicsNode)
348 auto world = getWorld(physicsNode);
350 world->m_newPhysicsNodes.push_back(physicsNode);
352 worldManager.orphanNodes.push_back(physicsNode);
356void QPhysicsWorld::deregisterNode(QAbstractPhysicsNode *physicsNode)
358 for (
auto world : worldManager.worlds) {
359 world->m_newPhysicsNodes.removeAll(physicsNode);
360 QMutexLocker locker(&world->m_removedPhysicsNodesMutex);
361 if (physicsNode->m_backendObject) {
362 Q_ASSERT(physicsNode->m_backendObject->frontendNode == physicsNode);
363 physicsNode->m_backendObject->frontendNode =
nullptr;
364 physicsNode->m_backendObject->isRemoved =
true;
365 physicsNode->m_backendObject =
nullptr;
367 world->m_removedPhysicsNodes.insert(physicsNode);
369 worldManager.orphanNodes.removeAll(physicsNode);
372void QPhysicsWorld::registerContact(QAbstractPhysicsNode *sender, QAbstractPhysicsNode *receiver,
373 const QVector<QVector3D> &positions,
374 const QVector<QVector3D> &impulses,
375 const QVector<QVector3D> &normals)
384 contact.sender = sender;
385 contact.receiver = receiver;
386 contact.positions = positions;
387 contact.impulses = impulses;
388 contact.normals = normals;
390 m_registeredContacts.push_back(contact);
393QPhysicsWorld::QPhysicsWorld(QObject *parent) : QObject(parent)
395 m_inDesignStudio = !qEnvironmentVariableIsEmpty(
"QML_PUPPET_MODE");
396 m_physx =
new QPhysXWorld;
397 m_physx->createWorld();
399 worldManager.worlds.push_back(
this);
402 m_frameAnimator =
new FrameAnimator;
403 connect(m_frameAnimator, &QQuickFrameAnimation::triggered,
this,
404 &QPhysicsWorld::simulateFrame);
407QPhysicsWorld::~QPhysicsWorld()
409 if (m_frameAnimator) {
410 m_frameAnimator->stop();
411 delete m_frameAnimator;
415 m_physx->scene->fetchResults(
true);
417 for (
auto body : m_physXBodies) {
418 body->cleanup(m_physx);
421 m_physx->deleteWorld();
423 worldManager.worlds.removeAll(
this);
425 if (!qtPhysicsTimingsFile.isEmpty()) {
426 if (m_frameTimings.isEmpty()) {
427 qWarning() <<
"No frame timings saved.";
428 }
else if (
auto csvFile = QFile(qtPhysicsTimingsFile); csvFile.open(QIODevice::WriteOnly)) {
429 QTextStream out(&csvFile);
430 for (
int i = 1; i < m_frameTimings.size(); i++) {
431 out << i <<
"," << m_frameTimings[i] <<
'\n';
435 qWarning() <<
"Could not open timings file " << qtPhysicsTimingsFile;
440void QPhysicsWorld::classBegin() {}
442void QPhysicsWorld::componentComplete()
444 if ((!m_running && !m_inDesignStudio) || m_physicsInitialized)
449QVector3D QPhysicsWorld::gravity()
const
454bool QPhysicsWorld::running()
const
459bool QPhysicsWorld::forceDebugDraw()
const
461 return m_forceDebugDraw;
464bool QPhysicsWorld::enableCCD()
const
469float QPhysicsWorld::typicalLength()
const
471 return m_typicalLength;
474float QPhysicsWorld::typicalSpeed()
const
476 return m_typicalSpeed;
479bool QPhysicsWorld::isNodeRemoved(QAbstractPhysicsNode *object)
481 return m_removedPhysicsNodes.contains(object);
484void QPhysicsWorld::setGravity(QVector3D gravity)
486 if (m_gravity == gravity)
490 if (m_physx->scene) {
491 m_physx->scene->setGravity(QPhysicsUtils::toPhysXType(m_gravity));
493 emit gravityChanged(m_gravity);
496void QPhysicsWorld::setRunning(
bool running)
498 if (m_running == running)
502 if (!m_inDesignStudio && m_running && !m_physicsInitialized)
506 m_frameAnimator->start();
508 m_frameAnimator->stop();
510 emit runningChanged(m_running);
513void QPhysicsWorld::setForceDebugDraw(
bool forceDebugDraw)
515 if (m_forceDebugDraw == forceDebugDraw)
518 m_forceDebugDraw = forceDebugDraw;
519 if (!m_forceDebugDraw)
523 emit forceDebugDrawChanged(m_forceDebugDraw);
526QQuick3DNode *QPhysicsWorld::viewport()
const
531void QPhysicsWorld::setHasIndividualDebugDraw()
533 m_hasIndividualDebugDraw =
true;
536void QPhysicsWorld::setViewport(QQuick3DNode *viewport)
538 if (m_viewport == viewport)
541 m_viewport = viewport;
544 for (
auto material : m_debugMaterials)
546 m_debugMaterials.clear();
548 for (
auto &holder : m_collisionShapeDebugModels) {
549 holder.releaseMeshPointer();
552 m_collisionShapeDebugModels.clear();
554 emit viewportChanged(m_viewport);
557void QPhysicsWorld::setMinimumTimestep(
float minTimestep)
559 if (qFuzzyCompare(m_minTimestep, minTimestep))
562 if (minTimestep > m_maxTimestep) {
563 qWarning(
"Minimum timestep greater than maximum timestep, value clamped");
564 minTimestep = qMin(minTimestep, m_maxTimestep);
567 if (minTimestep < 0.f) {
568 qWarning(
"Minimum timestep less than zero, value clamped");
569 minTimestep = qMax(minTimestep, 0.f);
572 if (qFuzzyCompare(m_minTimestep, minTimestep))
575 m_minTimestep = minTimestep;
576 emit minimumTimestepChanged(m_minTimestep);
579void QPhysicsWorld::setMaximumTimestep(
float maxTimestep)
581 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
584 if (maxTimestep < 0.f) {
585 qWarning(
"Maximum timestep less than zero, value clamped");
586 maxTimestep = qMax(maxTimestep, 0.f);
589 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
592 m_maxTimestep = maxTimestep;
593 emit maximumTimestepChanged(maxTimestep);
596void QPhysicsWorld::setupDebugMaterials(QQuick3DNode *sceneNode)
598 if (!m_debugMaterials.isEmpty())
601 const int lineWidth = m_inDesignStudio ? 1 : 3;
604 for (
auto color : { QColorConstants::Svg::chartreuse, QColorConstants::Svg::cyan,
605 QColorConstants::Svg::lightsalmon, QColorConstants::Svg::red,
606 QColorConstants::Svg::blueviolet, QColorConstants::Svg::black }) {
607 auto debugMaterial =
new QQuick3DPrincipledMaterial();
608 debugMaterial->setLineWidth(lineWidth);
609 debugMaterial->setParentItem(sceneNode);
610 debugMaterial->setParent(sceneNode);
611 debugMaterial->setBaseColor(color);
612 debugMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
613 debugMaterial->setCullMode(QQuick3DMaterial::NoCulling);
614 m_debugMaterials.push_back(debugMaterial);
618void QPhysicsWorld::updateDebugDraw()
620 if (!(m_forceDebugDraw || m_hasIndividualDebugDraw)) {
622 for (
auto &holder : m_collisionShapeDebugModels) {
623 holder.releaseMeshPointer();
626 m_collisionShapeDebugModels.clear();
631 auto sceneNode = m_viewport ? m_viewport : m_scene;
633 if (sceneNode ==
nullptr)
636 setupDebugMaterials(sceneNode);
637 m_hasIndividualDebugDraw =
false;
640 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>> currentCollisionShapes;
641 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
643 for (QAbstractPhysXNode *node : m_physXBodies) {
644 if (!node->debugGeometryCapability())
647 const auto &collisionShapes = node->frontendNode->getCollisionShapesList();
648 const int materialIdx =
static_cast<
int>(node->getDebugDrawBodyType());
649 const int length = collisionShapes.length();
650 for (
int idx = 0; idx < length; idx++) {
651 const auto collisionShape = collisionShapes[idx];
653 if (!m_forceDebugDraw && !collisionShape->enableDebugDraw())
656 DebugModelHolder &holder =
657 m_collisionShapeDebugModels[std::make_pair(collisionShape, node)];
658 auto &model = holder.model;
660 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
662 m_hasIndividualDebugDraw =
663 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
667 model =
new QQuick3DModel();
668 model->setParentItem(sceneNode);
669 model->setParent(sceneNode);
670 model->setCastsShadows(
false);
671 model->setReceivesShadows(
false);
672 model->setCastsReflections(
false);
675 model->setVisible(
true);
678 auto material = m_debugMaterials[materialIdx];
679 QQmlListReference materialsRef(model,
"materials");
680 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
681 materialsRef.clear();
682 materialsRef.append(material);
688 if (qobject_cast<QCharacterController *>(node->frontendNode)) {
689 QCapsuleShape *capsuleShape = qobject_cast<QCapsuleShape *>(collisionShape);
693 const float radius = capsuleShape->diameter() * 0.5;
694 const float halfHeight = capsuleShape->height() * 0.5;
696 if (!qFuzzyCompare(radius, holder.radius())
697 || !qFuzzyCompare(halfHeight, holder.halfHeight())) {
698 auto geom = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
699 geom->setParent(model);
700 model->setGeometry(geom);
701 holder.setRadius(radius);
702 holder.setHalfHeight(halfHeight);
705 model->setPosition(node->frontendNode->scenePosition());
706 model->setRotation(node->frontendNode->sceneRotation()
707 * QQuaternion::fromEulerAngles(0, 0, 90));
711 if (node->shapes.length() < length)
714 const auto physXShape = node->shapes[idx];
715 auto localPose = physXShape->getLocalPose();
717 switch (physXShape->getGeometryType()) {
718 case physx::PxGeometryType::eBOX: {
719 physx::PxBoxGeometry boxGeometry;
720 physXShape->getBoxGeometry(boxGeometry);
721 const auto &halfExtentsOld = holder.halfExtents();
722 const auto halfExtents = QPhysicsUtils::toQtType(boxGeometry.halfExtents);
723 if (!qFuzzyCompare(halfExtentsOld, halfExtents)) {
724 auto geom = QDebugDrawHelper::generateBoxGeometry(halfExtents);
725 geom->setParent(model);
726 model->setGeometry(geom);
727 holder.setHalfExtents(halfExtents);
733 case physx::PxGeometryType::eSPHERE: {
734 physx::PxSphereGeometry sphereGeometry;
735 physXShape->getSphereGeometry(sphereGeometry);
736 const float radius = holder.radius();
737 if (!qFuzzyCompare(sphereGeometry.radius, radius)) {
738 auto geom = QDebugDrawHelper::generateSphereGeometry(sphereGeometry.radius);
739 geom->setParent(model);
740 model->setGeometry(geom);
741 holder.setRadius(sphereGeometry.radius);
746 case physx::PxGeometryType::eCAPSULE: {
747 physx::PxCapsuleGeometry capsuleGeometry;
748 physXShape->getCapsuleGeometry(capsuleGeometry);
749 const float radius = holder.radius();
750 const float halfHeight = holder.halfHeight();
752 if (!qFuzzyCompare(capsuleGeometry.radius, radius)
753 || !qFuzzyCompare(capsuleGeometry.halfHeight, halfHeight)) {
754 auto geom = QDebugDrawHelper::generateCapsuleGeometry(
755 capsuleGeometry.radius, capsuleGeometry.halfHeight);
756 geom->setParent(model);
757 model->setGeometry(geom);
758 holder.setRadius(capsuleGeometry.radius);
759 holder.setHalfHeight(capsuleGeometry.halfHeight);
764 case physx::PxGeometryType::ePLANE:{
765 physx::PxPlaneGeometry planeGeometry;
766 physXShape->getPlaneGeometry(planeGeometry);
768 const QQuaternion rotation =
769 QPhysicsUtils::kMinus90YawRotation * QPhysicsUtils::toQtType(localPose.q);
770 localPose = physx::PxTransform(localPose.p, QPhysicsUtils::toPhysXType(rotation));
772 if (model->geometry() ==
nullptr) {
773 auto geom = QDebugDrawHelper::generatePlaneGeometry();
774 geom->setParent(model);
775 model->setGeometry(geom);
783 case physx::PxGeometryType::eHEIGHTFIELD: {
784 physx::PxHeightFieldGeometry heightFieldGeometry;
785 bool success = physXShape->getHeightFieldGeometry(heightFieldGeometry);
787 const float heightScale = holder.heightScale();
788 const float rowScale = holder.rowScale();
789 const float columnScale = holder.columnScale();
791 if (
auto heightField = holder.getHeightField();
792 heightField && heightField != heightFieldGeometry.heightField) {
793 heightField->release();
794 holder.setHeightField(
nullptr);
797 if (!qFuzzyCompare(heightFieldGeometry.heightScale, heightScale)
798 || !qFuzzyCompare(heightFieldGeometry.rowScale, rowScale)
799 || !qFuzzyCompare(heightFieldGeometry.columnScale, columnScale)
800 || !holder.getHeightField()) {
801 if (!holder.getHeightField()) {
802 heightFieldGeometry.heightField->acquireReference();
803 holder.setHeightField(heightFieldGeometry.heightField);
805 auto geom = QDebugDrawHelper::generateHeightFieldGeometry(
806 heightFieldGeometry.heightField, heightFieldGeometry.heightScale,
807 heightFieldGeometry.rowScale, heightFieldGeometry.columnScale);
808 geom->setParent(model);
809 model->setGeometry(geom);
810 holder.setHeightScale(heightFieldGeometry.heightScale);
811 holder.setRowScale(heightFieldGeometry.rowScale);
812 holder.setColumnScale(heightFieldGeometry.columnScale);
817 case physx::PxGeometryType::eCONVEXMESH: {
818 physx::PxConvexMeshGeometry convexMeshGeometry;
819 const bool success = physXShape->getConvexMeshGeometry(convexMeshGeometry);
821 const auto rotation = convexMeshGeometry.scale.rotation * localPose.q;
822 localPose = physx::PxTransform(localPose.p, rotation);
823 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry.scale.scale));
825 if (
auto convexMesh = holder.getConvexMesh();
826 convexMesh && convexMesh != convexMeshGeometry.convexMesh) {
827 convexMesh->release();
828 holder.setConvexMesh(
nullptr);
831 if (!model->geometry() || !holder.getConvexMesh()) {
832 if (!holder.getConvexMesh()) {
833 convexMeshGeometry.convexMesh->acquireReference();
834 holder.setConvexMesh(convexMeshGeometry.convexMesh);
836 auto geom = QDebugDrawHelper::generateConvexMeshGeometry(
837 convexMeshGeometry.convexMesh);
838 geom->setParent(model);
839 model->setGeometry(geom);
844 case physx::PxGeometryType::eTRIANGLEMESH: {
845 physx::PxTriangleMeshGeometry triangleMeshGeometry;
846 const bool success = physXShape->getTriangleMeshGeometry(triangleMeshGeometry);
848 const auto rotation = triangleMeshGeometry.scale.rotation * localPose.q;
849 localPose = physx::PxTransform(localPose.p, rotation);
850 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry.scale.scale));
852 if (
auto triangleMesh = holder.getTriangleMesh();
853 triangleMesh && triangleMesh != triangleMeshGeometry.triangleMesh) {
854 triangleMesh->release();
855 holder.setTriangleMesh(
nullptr);
858 if (!model->geometry() || !holder.getTriangleMesh()) {
859 if (!holder.getTriangleMesh()) {
860 triangleMeshGeometry.triangleMesh->acquireReference();
861 holder.setTriangleMesh(triangleMeshGeometry.triangleMesh);
863 auto geom = QDebugDrawHelper::generateTriangleMeshGeometry(
864 triangleMeshGeometry.triangleMesh);
865 geom->setParent(model);
866 model->setGeometry(geom);
871 case physx::PxGeometryType::eINVALID:
872 case physx::PxGeometryType::eGEOMETRY_COUNT:
877 auto globalPose = node->getGlobalPose();
878 auto finalPose = globalPose.transform(localPose);
880 model->setRotation(QPhysicsUtils::toQtType(finalPose.q));
881 model->setPosition(QPhysicsUtils::toQtType(finalPose.p));
886 m_collisionShapeDebugModels.removeIf(
887 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>,
888 DebugModelHolder>::iterator it) {
889 if (!currentCollisionShapes.contains(it.key())) {
890 auto holder = it.value();
891 holder.releaseMeshPointer();
902 if (
auto shape = qobject_cast<QAbstractPhysicsNode *>(node)) {
903 nodes.push_back(shape);
907 for (QQuick3DObject *child : node->childItems())
908 collectPhysicsNodes(child, nodes);
911void QPhysicsWorld::updateDebugDrawDesignStudio()
914 auto sceneNode = m_viewport ? m_viewport : m_scene;
916 if (sceneNode ==
nullptr)
919 setupDebugMaterials(sceneNode);
922 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>> currentCollisionShapes;
923 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
925 QList<QAbstractPhysicsNode *> activePhysicsNodes;
926 activePhysicsNodes.reserve(m_collisionShapeDebugModels.size());
927 collectPhysicsNodes(m_scene, activePhysicsNodes);
929 for (QAbstractPhysicsNode *node : activePhysicsNodes) {
931 const auto &collisionShapes = node->getCollisionShapesList();
932 const int materialIdx = 0;
933 const int length = collisionShapes.length();
935 const bool isCharacterController = qobject_cast<QCharacterController *>(node) !=
nullptr;
937 for (
int idx = 0; idx < length; idx++) {
938 QAbstractCollisionShape *collisionShape = collisionShapes[idx];
939 DebugModelHolder &holder =
940 m_DesignStudioDebugModels[std::make_pair(collisionShape, node)];
941 auto &model = holder.model;
943 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
945 m_hasIndividualDebugDraw =
946 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
953 model =
new QQuick3DModel();
954 model->setParentItem(sceneNode);
955 model->setParent(sceneNode);
956 model->setCastsShadows(
false);
957 model->setReceivesShadows(
false);
958 model->setCastsReflections(
false);
961 const bool hasGeometry = holder.geometry !=
nullptr;
962 QVector3D scenePosition = collisionShape->scenePosition();
963 QQuaternion sceneRotation = collisionShape->sceneRotation();
964 QQuick3DGeometry *newGeometry =
nullptr;
966 if (isCharacterController)
967 sceneRotation = sceneRotation * QQuaternion::fromEulerAngles(QVector3D(0, 0, 90));
970 auto material = m_debugMaterials[materialIdx];
971 QQmlListReference materialsRef(model,
"materials");
972 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
973 materialsRef.clear();
974 materialsRef.append(material);
978 if (
auto shape = qobject_cast<QBoxShape *>(collisionShape)) {
979 const auto &halfExtentsOld = holder.halfExtents();
980 const auto halfExtents = shape->sceneScale() * shape->extents() * 0.5f;
981 if (!qFuzzyCompare(halfExtentsOld, halfExtents) || !hasGeometry) {
982 newGeometry = QDebugDrawHelper::generateBoxGeometry(halfExtents);
983 holder.setHalfExtents(halfExtents);
985 }
else if (
auto shape = qobject_cast<QSphereShape *>(collisionShape)) {
986 const float radiusOld = holder.radius();
987 const float radius = shape->sceneScale().x() * shape->diameter() * 0.5f;
988 if (!qFuzzyCompare(radiusOld, radius) || !hasGeometry) {
989 newGeometry = QDebugDrawHelper::generateSphereGeometry(radius);
990 holder.setRadius(radius);
992 }
else if (
auto shape = qobject_cast<QCapsuleShape *>(collisionShape)) {
993 const float radiusOld = holder.radius();
994 const float halfHeightOld = holder.halfHeight();
995 const float radius = shape->sceneScale().y() * shape->diameter() * 0.5f;
996 const float halfHeight = shape->sceneScale().x() * shape->height() * 0.5f;
998 if ((!qFuzzyCompare(radiusOld, radius) || !qFuzzyCompare(halfHeightOld, halfHeight))
1000 newGeometry = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
1001 holder.setRadius(radius);
1002 holder.setHalfHeight(halfHeight);
1004 }
else if (qobject_cast<QPlaneShape *>(collisionShape)) {
1006 newGeometry = QDebugDrawHelper::generatePlaneGeometry();
1007 }
else if (
auto shape = qobject_cast<QHeightFieldShape *>(collisionShape)) {
1008 physx::PxHeightFieldGeometry *heightFieldGeometry =
1009 static_cast<physx::PxHeightFieldGeometry *>(shape->getPhysXGeometry());
1010 const float heightScale = holder.heightScale();
1011 const float rowScale = holder.rowScale();
1012 const float columnScale = holder.columnScale();
1013 scenePosition += shape->hfOffset();
1014 if (!heightFieldGeometry) {
1015 qWarning() <<
"Could not get height field";
1016 }
else if (!qFuzzyCompare(heightFieldGeometry->heightScale, heightScale)
1017 || !qFuzzyCompare(heightFieldGeometry->rowScale, rowScale)
1018 || !qFuzzyCompare(heightFieldGeometry->columnScale, columnScale)
1020 newGeometry = QDebugDrawHelper::generateHeightFieldGeometry(
1021 heightFieldGeometry->heightField, heightFieldGeometry->heightScale,
1022 heightFieldGeometry->rowScale, heightFieldGeometry->columnScale);
1023 holder.setHeightScale(heightFieldGeometry->heightScale);
1024 holder.setRowScale(heightFieldGeometry->rowScale);
1025 holder.setColumnScale(heightFieldGeometry->columnScale);
1027 }
else if (
auto shape = qobject_cast<QConvexMeshShape *>(collisionShape)) {
1028 auto convexMeshGeometry =
1029 static_cast<physx::PxConvexMeshGeometry *>(shape->getPhysXGeometry());
1030 if (!convexMeshGeometry) {
1031 qWarning() <<
"Could not get convex mesh";
1033 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry->scale.scale));
1036 newGeometry = QDebugDrawHelper::generateConvexMeshGeometry(
1037 convexMeshGeometry->convexMesh);
1040 }
else if (
auto shape = qobject_cast<QTriangleMeshShape *>(collisionShape)) {
1041 physx::PxTriangleMeshGeometry *triangleMeshGeometry =
1042 static_cast<physx::PxTriangleMeshGeometry *>(shape->getPhysXGeometry());
1043 if (!triangleMeshGeometry) {
1044 qWarning() <<
"Could not get triangle mesh";
1046 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry->scale.scale));
1049 newGeometry = QDebugDrawHelper::generateTriangleMeshGeometry(
1050 triangleMeshGeometry->triangleMesh);
1056 delete holder.geometry;
1057 holder.geometry = newGeometry;
1060 model->setGeometry(holder.geometry);
1061 model->setVisible(
true);
1063 model->setRotation(sceneRotation);
1064 model->setPosition(scenePosition);
1069 m_DesignStudioDebugModels.removeIf(
1070 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>,
1071 DebugModelHolder>::iterator it) {
1072 if (!currentCollisionShapes.contains(it.key())) {
1073 auto holder = it.value();
1074 holder.releaseMeshPointer();
1076 delete holder.geometry;
1077 delete holder.model;
1085void QPhysicsWorld::disableDebugDraw()
1087 m_hasIndividualDebugDraw =
false;
1089 for (QAbstractPhysXNode *body : m_physXBodies) {
1090 const auto &collisionShapes = body->frontendNode->getCollisionShapesList();
1091 const int length = collisionShapes.length();
1092 for (
int idx = 0; idx < length; idx++) {
1093 const auto collisionShape = collisionShapes[idx];
1094 if (collisionShape->enableDebugDraw()) {
1095 m_hasIndividualDebugDraw =
true;
1102void QPhysicsWorld::setEnableCCD(
bool enableCCD)
1104 if (m_enableCCD == enableCCD)
1107 if (m_physicsInitialized) {
1109 <<
"Warning: Changing 'enableCCD' after physics is initialized will have no effect";
1113 m_enableCCD = enableCCD;
1114 emit enableCCDChanged(m_enableCCD);
1117void QPhysicsWorld::setTypicalLength(
float typicalLength)
1119 if (qFuzzyCompare(typicalLength, m_typicalLength))
1122 if (typicalLength <= 0.f) {
1123 qWarning() <<
"Warning: 'typicalLength' value less than zero, ignored";
1127 if (m_physicsInitialized) {
1128 qWarning() <<
"Warning: Changing 'typicalLength' after physics is initialized will have "
1133 m_typicalLength = typicalLength;
1135 emit typicalLengthChanged(typicalLength);
1138void QPhysicsWorld::setTypicalSpeed(
float typicalSpeed)
1140 if (qFuzzyCompare(typicalSpeed, m_typicalSpeed))
1143 if (m_physicsInitialized) {
1144 qWarning() <<
"Warning: Changing 'typicalSpeed' after physics is initialized will have "
1149 m_typicalSpeed = typicalSpeed;
1151 emit typicalSpeedChanged(typicalSpeed);
1154float QPhysicsWorld::defaultDensity()
const
1156 return m_defaultDensity;
1159float QPhysicsWorld::minimumTimestep()
const
1161 return m_minTimestep;
1164float QPhysicsWorld::maximumTimestep()
const
1166 return m_maxTimestep;
1169void QPhysicsWorld::setDefaultDensity(
float defaultDensity)
1171 if (qFuzzyCompare(m_defaultDensity, defaultDensity))
1173 m_defaultDensity = defaultDensity;
1176 for (QAbstractPhysXNode *body : m_physXBodies)
1177 body->updateDefaultDensity(m_defaultDensity);
1179 emit defaultDensityChanged(defaultDensity);
1184void QPhysicsWorld::cleanupRemovedNodes()
1186 m_physXBodies.removeIf([
this](QAbstractPhysXNode *body) {
1187 return body->cleanupIfRemoved(m_physx);
1189 m_removedPhysicsNodes.clear();
1192void QPhysicsWorld::initPhysics()
1194 Q_ASSERT(!m_physicsInitialized);
1196 const unsigned int numThreads = m_numThreads >= 0 ? m_numThreads : qMax(0, QThread::idealThreadCount());
1197 m_physx->createScene(m_typicalLength, m_typicalSpeed, m_gravity, m_enableCCD,
this, numThreads);
1198 m_frameAnimator->start();
1199 m_physicsInitialized =
true;
1202void QPhysicsWorld::simulateFrame()
1204 constexpr double MILLIONTH = 0.000001;
1205 constexpr double THOUSANDTH = 0.001;
1207 if (m_inDesignStudio) {
1208 frameFinishedDesignStudio();
1212 if (!m_physx->isRunning) {
1214 m_physx->isRunning =
true;
1215 m_physx->scene->simulate(m_minTimestep);
1216 m_currTimeStep = m_minTimestep;
1221 if (!m_frameFetched && !m_physx->scene->checkResults()) {
1226 if (!m_frameFetched) {
1227 m_physx->scene->fetchResults(
true);
1228 frameFinished(m_currTimeStep);
1229 m_frameFetched =
true;
1230 if (Q_UNLIKELY(!qtPhysicsTimingsFile.isEmpty())) {
1231 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1232 m_frameTimings.append(deltaMS);
1237 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1238 if (deltaMS < m_minTimestep)
1240 const double deltaSecs = qMin<
double>(deltaMS, m_maxTimestep) * THOUSANDTH;
1242 m_physx->scene->simulate(deltaSecs);
1243 m_frameFetched =
false;
1244 m_currTimeStep = deltaSecs;
1247void QPhysicsWorld::frameFinished(
float deltaTime)
1250 emitContactCallbacks();
1251 cleanupRemovedNodes();
1252 for (
auto *node : std::as_const(m_newPhysicsNodes)) {
1253 auto *body = node->createPhysXBackend();
1254 body->init(
this, m_physx);
1255 m_physXBodies.push_back(body);
1257 m_newPhysicsNodes.clear();
1259 QHash<QQuick3DNode *, QMatrix4x4> transformCache;
1262 for (
auto *physXBody : std::as_const(m_physXBodies)) {
1263 physXBody->markDirtyShapes();
1264 physXBody->rebuildDirtyShapes(
this, m_physx);
1265 physXBody->updateFilters();
1268 physXBody->sync(deltaTime, transformCache);
1272 emit frameDone(deltaTime * 1000);
1275void QPhysicsWorld::frameFinishedDesignStudio()
1279 emitContactCallbacks();
1280 cleanupRemovedNodes();
1282 m_newPhysicsNodes.clear();
1284 updateDebugDrawDesignStudio();
1287QPhysicsWorld *QPhysicsWorld::getWorld(QQuick3DNode *node)
1289 for (QPhysicsWorld *world : worldManager.worlds) {
1290 if (!world->m_scene) {
1294 QQuick3DNode *nodeCurr = node;
1297 if (nodeCurr == world->m_scene)
1300 while (nodeCurr->parentNode()) {
1301 nodeCurr = nodeCurr->parentNode();
1302 if (nodeCurr == world->m_scene)
1310void QPhysicsWorld::matchOrphanNodes()
1313 if (worldManager.orphanNodes.isEmpty())
1316 qsizetype numNodes = worldManager.orphanNodes.length();
1319 while (idx < numNodes) {
1320 auto node = worldManager.orphanNodes[idx];
1321 auto world = getWorld(node);
1322 if (world ==
this) {
1323 world->m_newPhysicsNodes.push_back(node);
1325 worldManager.orphanNodes.swapItemsAt(idx, numNodes - 1);
1326 worldManager.orphanNodes.pop_back();
1334void QPhysicsWorld::findPhysicsNodes()
1339 if (m_scene ==
nullptr)
1343 QList<QQuick3DObject *> children = m_scene->childItems();
1344 while (!children.empty()) {
1345 auto child = children.takeFirst();
1346 if (
auto converted = qobject_cast<QAbstractPhysicsNode *>(child); converted !=
nullptr) {
1348 if (converted->m_backendObject !=
nullptr) {
1349 qWarning() <<
"Warning: physics node already associated with a backend node.";
1353 m_newPhysicsNodes.push_back(converted);
1354 worldManager.orphanNodes.removeAll(converted);
1356 children.append(child->childItems());
1360void QPhysicsWorld::emitContactCallbacks()
1362 for (
const QPhysicsWorld::BodyContact &contact : m_registeredContacts) {
1363 if (m_removedPhysicsNodes.contains(contact.sender)
1364 || m_removedPhysicsNodes.contains(contact.receiver))
1366 contact.receiver->registerContact(contact.sender, contact.positions, contact.impulses,
1370 m_registeredContacts.clear();
1373physx::PxPhysics *QPhysicsWorld::getPhysics()
1375 return StaticPhysXObjects::getReference().physics;
1378physx::PxCooking *QPhysicsWorld::getCooking()
1380 return StaticPhysXObjects::getReference().cooking;
1383physx::PxControllerManager *QPhysicsWorld::controllerManager()
1385 if (m_physx->scene && !m_physx->controllerManager) {
1386 m_physx->controllerManager = PxCreateControllerManager(*m_physx->scene);
1387 qCDebug(lcQuick3dPhysics) <<
"Created controller manager" << m_physx->controllerManager;
1389 return m_physx->controllerManager;
1392QQuick3DNode *QPhysicsWorld::scene()
const
1397void QPhysicsWorld::setScene(QQuick3DNode *newScene)
1399 if (m_scene == newScene)
1405 for (
auto body : m_physXBodies) {
1406 deregisterNode(body->frontendNode);
1410 bool sceneOK =
true;
1411 for (QPhysicsWorld *world : worldManager.worlds) {
1412 if (world !=
this && world->scene() == newScene) {
1414 qWarning() <<
"Warning: scene already associated with physics world";
1420 emit sceneChanged();
1423int QPhysicsWorld::numThreads()
const
1425 return m_numThreads;
1428void QPhysicsWorld::setNumThreads(
int newNumThreads)
1430 if (m_numThreads == newNumThreads)
1432 m_numThreads = newNumThreads;
1433 emit numThreadsChanged();
1436bool QPhysicsWorld::reportKinematicKinematicCollisions()
const
1438 return m_reportKinematicKinematicCollisions;
1441void QPhysicsWorld::setReportKinematicKinematicCollisions(
1442 bool newReportKinematicKinematicCollisions)
1444 if (m_reportKinematicKinematicCollisions == newReportKinematicKinematicCollisions)
1446 m_reportKinematicKinematicCollisions = newReportKinematicKinematicCollisions;
1447 emit reportKinematicKinematicCollisionsChanged();
1450bool QPhysicsWorld::reportStaticKinematicCollisions()
const
1452 return m_reportStaticKinematicCollisions;
1455void QPhysicsWorld::setReportStaticKinematicCollisions(
bool newReportStaticKinematicCollisions)
1457 if (m_reportStaticKinematicCollisions == newReportStaticKinematicCollisions)
1459 m_reportStaticKinematicCollisions = newReportStaticKinematicCollisions;
1460 emit reportStaticKinematicCollisionsChanged();
1465#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