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 const double minTimestepSecs = m_minTimestep * 0.001;
1216 m_physx->scene->simulate(minTimestepSecs);
1217 m_currTimeStep = minTimestepSecs;
1222 if (!m_frameFetched && !m_physx->scene->checkResults()) {
1227 if (!m_frameFetched) {
1228 m_physx->scene->fetchResults(
true);
1229 frameFinished(m_currTimeStep);
1230 m_frameFetched =
true;
1231 if (Q_UNLIKELY(!qtPhysicsTimingsFile.isEmpty())) {
1232 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1233 m_frameTimings.append(deltaMS);
1238 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1239 if (deltaMS < m_minTimestep)
1241 const double deltaSecs = qMin<
double>(deltaMS, m_maxTimestep) * THOUSANDTH;
1243 m_physx->scene->simulate(deltaSecs);
1244 m_frameFetched =
false;
1245 m_currTimeStep = deltaSecs;
1248void QPhysicsWorld::frameFinished(
float deltaTime)
1251 emitContactCallbacks();
1252 cleanupRemovedNodes();
1253 for (
auto *node : std::as_const(m_newPhysicsNodes)) {
1254 auto *body = node->createPhysXBackend();
1255 body->init(
this, m_physx);
1256 m_physXBodies.push_back(body);
1258 m_newPhysicsNodes.clear();
1260 QHash<QQuick3DNode *, QMatrix4x4> transformCache;
1263 for (
auto *physXBody : std::as_const(m_physXBodies)) {
1264 physXBody->markDirtyShapes();
1265 physXBody->rebuildDirtyShapes(
this, m_physx);
1266 physXBody->updateFilters();
1269 physXBody->sync(deltaTime, transformCache);
1273 emit frameDone(deltaTime * 1000);
1276void QPhysicsWorld::frameFinishedDesignStudio()
1280 emitContactCallbacks();
1281 cleanupRemovedNodes();
1283 m_newPhysicsNodes.clear();
1285 updateDebugDrawDesignStudio();
1288QPhysicsWorld *QPhysicsWorld::getWorld(QQuick3DNode *node)
1290 for (QPhysicsWorld *world : worldManager.worlds) {
1291 if (!world->m_scene) {
1295 QQuick3DNode *nodeCurr = node;
1298 if (nodeCurr == world->m_scene)
1301 while (nodeCurr->parentNode()) {
1302 nodeCurr = nodeCurr->parentNode();
1303 if (nodeCurr == world->m_scene)
1311void QPhysicsWorld::matchOrphanNodes()
1314 if (worldManager.orphanNodes.isEmpty())
1317 qsizetype numNodes = worldManager.orphanNodes.length();
1320 while (idx < numNodes) {
1321 auto node = worldManager.orphanNodes[idx];
1322 auto world = getWorld(node);
1323 if (world ==
this) {
1324 world->m_newPhysicsNodes.push_back(node);
1326 worldManager.orphanNodes.swapItemsAt(idx, numNodes - 1);
1327 worldManager.orphanNodes.pop_back();
1335void QPhysicsWorld::findPhysicsNodes()
1340 if (m_scene ==
nullptr)
1344 QList<QQuick3DObject *> children = m_scene->childItems();
1345 while (!children.empty()) {
1346 auto child = children.takeFirst();
1347 if (
auto converted = qobject_cast<QAbstractPhysicsNode *>(child); converted !=
nullptr) {
1349 if (converted->m_backendObject !=
nullptr) {
1350 qWarning() <<
"Warning: physics node already associated with a backend node.";
1354 m_newPhysicsNodes.push_back(converted);
1355 worldManager.orphanNodes.removeAll(converted);
1357 children.append(child->childItems());
1361void QPhysicsWorld::emitContactCallbacks()
1363 for (
const QPhysicsWorld::BodyContact &contact : m_registeredContacts) {
1364 if (m_removedPhysicsNodes.contains(contact.sender)
1365 || m_removedPhysicsNodes.contains(contact.receiver))
1367 contact.receiver->registerContact(contact.sender, contact.positions, contact.impulses,
1371 m_registeredContacts.clear();
1374physx::PxPhysics *QPhysicsWorld::getPhysics()
1376 return StaticPhysXObjects::getReference().physics;
1379physx::PxCooking *QPhysicsWorld::getCooking()
1381 return StaticPhysXObjects::getReference().cooking;
1384physx::PxControllerManager *QPhysicsWorld::controllerManager()
1386 if (m_physx->scene && !m_physx->controllerManager) {
1387 m_physx->controllerManager = PxCreateControllerManager(*m_physx->scene);
1388 qCDebug(lcQuick3dPhysics) <<
"Created controller manager" << m_physx->controllerManager;
1390 return m_physx->controllerManager;
1393QQuick3DNode *QPhysicsWorld::scene()
const
1398void QPhysicsWorld::setScene(QQuick3DNode *newScene)
1400 if (m_scene == newScene)
1406 for (
auto body : m_physXBodies) {
1407 deregisterNode(body->frontendNode);
1411 bool sceneOK =
true;
1412 for (QPhysicsWorld *world : worldManager.worlds) {
1413 if (world !=
this && world->scene() == newScene) {
1415 qWarning() <<
"Warning: scene already associated with physics world";
1421 emit sceneChanged();
1424int QPhysicsWorld::numThreads()
const
1426 return m_numThreads;
1429void QPhysicsWorld::setNumThreads(
int newNumThreads)
1431 if (m_numThreads == newNumThreads)
1433 m_numThreads = newNumThreads;
1434 emit numThreadsChanged();
1437bool QPhysicsWorld::reportKinematicKinematicCollisions()
const
1439 return m_reportKinematicKinematicCollisions;
1442void QPhysicsWorld::setReportKinematicKinematicCollisions(
1443 bool newReportKinematicKinematicCollisions)
1445 if (m_reportKinematicKinematicCollisions == newReportKinematicKinematicCollisions)
1447 m_reportKinematicKinematicCollisions = newReportKinematicKinematicCollisions;
1448 emit reportKinematicKinematicCollisionsChanged();
1451bool QPhysicsWorld::reportStaticKinematicCollisions()
const
1453 return m_reportStaticKinematicCollisions;
1456void QPhysicsWorld::setReportStaticKinematicCollisions(
bool newReportStaticKinematicCollisions)
1458 if (m_reportStaticKinematicCollisions == newReportStaticKinematicCollisions)
1460 m_reportStaticKinematicCollisions = newReportStaticKinematicCollisions;
1461 emit reportStaticKinematicCollisionsChanged();
1466#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