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 : 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 : m_physXBodies) {
419 body->cleanup(m_physx);
422 m_physx->deleteWorld();
424 worldManager.worlds.removeAll(
this);
426 if (!qtPhysicsTimingsFile.isEmpty()) {
427 if (m_frameTimings.isEmpty()) {
428 qWarning() <<
"No frame timings saved.";
429 }
else if (
auto csvFile = QFile(qtPhysicsTimingsFile); csvFile.open(QIODevice::WriteOnly)) {
430 QTextStream out(&csvFile);
431 for (
int i = 1; i < m_frameTimings.size(); i++) {
432 out << i <<
"," << m_frameTimings[i] <<
'\n';
436 qWarning() <<
"Could not open timings file " << qtPhysicsTimingsFile;
441void QPhysicsWorld::classBegin() {}
443void QPhysicsWorld::componentComplete()
445 if ((!m_running && !m_inDesignStudio) || m_physicsInitialized)
450QVector3D QPhysicsWorld::gravity()
const
455bool QPhysicsWorld::running()
const
460bool QPhysicsWorld::forceDebugDraw()
const
462 return m_forceDebugDraw;
465bool QPhysicsWorld::enableCCD()
const
470float QPhysicsWorld::typicalLength()
const
472 return m_typicalLength;
475float QPhysicsWorld::typicalSpeed()
const
477 return m_typicalSpeed;
480bool QPhysicsWorld::isNodeRemoved(QAbstractPhysicsNode *object)
482 return m_removedPhysicsNodes.contains(object);
485void QPhysicsWorld::setGravity(QVector3D gravity)
487 if (m_gravity == gravity)
491 if (m_physx->scene) {
492 m_physx->scene->setGravity(QPhysicsUtils::toPhysXType(m_gravity));
494 emit gravityChanged(m_gravity);
497void QPhysicsWorld::setRunning(
bool running)
499 if (m_running == running)
503 if (!m_inDesignStudio && m_running && !m_physicsInitialized)
507 m_frameAnimator->start();
509 m_frameAnimator->stop();
511 emit runningChanged(m_running);
514void QPhysicsWorld::setForceDebugDraw(
bool forceDebugDraw)
516 if (m_forceDebugDraw == forceDebugDraw)
519 m_forceDebugDraw = forceDebugDraw;
520 if (!m_forceDebugDraw)
524 emit forceDebugDrawChanged(m_forceDebugDraw);
527QQuick3DNode *QPhysicsWorld::viewport()
const
532void QPhysicsWorld::setHasIndividualDebugDraw()
534 m_hasIndividualDebugDraw =
true;
537void QPhysicsWorld::setViewport(QQuick3DNode *viewport)
539 if (m_viewport == viewport)
542 m_viewport = viewport;
545 for (
auto material : m_debugMaterials)
547 m_debugMaterials.clear();
549 for (
auto &holder : m_collisionShapeDebugModels) {
550 holder.releaseMeshPointer();
553 m_collisionShapeDebugModels.clear();
555 emit viewportChanged(m_viewport);
558void QPhysicsWorld::setMinimumTimestep(
float minTimestep)
560 if (qFuzzyCompare(m_minTimestep, minTimestep))
563 if (minTimestep > m_maxTimestep) {
564 qWarning(
"Minimum timestep greater than maximum timestep, value clamped");
565 minTimestep = qMin(minTimestep, m_maxTimestep);
568 if (minTimestep < 0.f) {
569 qWarning(
"Minimum timestep less than zero, value clamped");
570 minTimestep = qMax(minTimestep, 0.f);
573 if (qFuzzyCompare(m_minTimestep, minTimestep))
576 m_minTimestep = minTimestep;
577 emit minimumTimestepChanged(m_minTimestep);
580void QPhysicsWorld::setMaximumTimestep(
float maxTimestep)
582 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
585 if (maxTimestep < 0.f) {
586 qWarning(
"Maximum timestep less than zero, value clamped");
587 maxTimestep = qMax(maxTimestep, 0.f);
590 if (qFuzzyCompare(m_maxTimestep, maxTimestep))
593 m_maxTimestep = maxTimestep;
594 emit maximumTimestepChanged(maxTimestep);
597void QPhysicsWorld::setupDebugMaterials(QQuick3DNode *sceneNode)
599 if (!m_debugMaterials.isEmpty())
602 const int lineWidth = m_inDesignStudio ? 1 : 3;
605 for (
auto color : { QColorConstants::Svg::chartreuse, QColorConstants::Svg::cyan,
606 QColorConstants::Svg::lightsalmon, QColorConstants::Svg::red,
607 QColorConstants::Svg::blueviolet, QColorConstants::Svg::black }) {
608 auto debugMaterial =
new QQuick3DPrincipledMaterial();
609 debugMaterial->setLineWidth(lineWidth);
610 debugMaterial->setParentItem(sceneNode);
611 debugMaterial->setParent(sceneNode);
612 debugMaterial->setBaseColor(color);
613 debugMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
614 debugMaterial->setCullMode(QQuick3DMaterial::NoCulling);
615 m_debugMaterials.push_back(debugMaterial);
619void QPhysicsWorld::updateDebugDraw()
621 if (!(m_forceDebugDraw || m_hasIndividualDebugDraw)) {
623 for (
auto &holder : m_collisionShapeDebugModels) {
624 holder.releaseMeshPointer();
627 m_collisionShapeDebugModels.clear();
632 auto sceneNode = m_viewport ? m_viewport : m_scene;
634 if (sceneNode ==
nullptr)
637 setupDebugMaterials(sceneNode);
638 m_hasIndividualDebugDraw =
false;
641 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>> currentCollisionShapes;
642 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
644 for (QAbstractPhysXNode *node : m_physXBodies) {
645 if (!node->debugGeometryCapability())
648 const auto &collisionShapes = node->frontendNode->getCollisionShapesList();
649 const int materialIdx =
static_cast<
int>(node->getDebugDrawBodyType());
650 const int length = collisionShapes.length();
651 for (
int idx = 0; idx < length; idx++) {
652 const auto collisionShape = collisionShapes[idx];
654 if (!m_forceDebugDraw && !collisionShape->enableDebugDraw())
657 DebugModelHolder &holder =
658 m_collisionShapeDebugModels[std::make_pair(collisionShape, node)];
659 auto &model = holder.model;
661 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
663 m_hasIndividualDebugDraw =
664 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
668 model =
new QQuick3DModel();
669 model->setParentItem(sceneNode);
670 model->setParent(sceneNode);
671 model->setCastsShadows(
false);
672 model->setReceivesShadows(
false);
673 model->setCastsReflections(
false);
676 model->setVisible(
true);
679 auto material = m_debugMaterials[materialIdx];
680 QQmlListReference materialsRef(model,
"materials");
681 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
682 materialsRef.clear();
683 materialsRef.append(material);
689 if (qobject_cast<QCharacterController *>(node->frontendNode)) {
690 QCapsuleShape *capsuleShape = qobject_cast<QCapsuleShape *>(collisionShape);
694 const float radius = capsuleShape->diameter() * 0.5;
695 const float halfHeight = capsuleShape->height() * 0.5;
697 if (!qFuzzyCompare(radius, holder.radius())
698 || !qFuzzyCompare(halfHeight, holder.halfHeight())) {
699 auto geom = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
700 geom->setParent(model);
701 model->setGeometry(geom);
702 holder.setRadius(radius);
703 holder.setHalfHeight(halfHeight);
706 model->setPosition(node->frontendNode->scenePosition());
707 model->setRotation(node->frontendNode->sceneRotation()
708 * QQuaternion::fromEulerAngles(0, 0, 90));
712 if (node->shapes.length() < length)
715 const auto physXShape = node->shapes[idx];
716 auto localPose = physXShape->getLocalPose();
718 switch (physXShape->getGeometryType()) {
719 case physx::PxGeometryType::eBOX: {
720 physx::PxBoxGeometry boxGeometry;
721 physXShape->getBoxGeometry(boxGeometry);
722 const auto &halfExtentsOld = holder.halfExtents();
723 const auto halfExtents = QPhysicsUtils::toQtType(boxGeometry.halfExtents);
724 if (!qFuzzyCompare(halfExtentsOld, halfExtents)) {
725 auto geom = QDebugDrawHelper::generateBoxGeometry(halfExtents);
726 geom->setParent(model);
727 model->setGeometry(geom);
728 holder.setHalfExtents(halfExtents);
734 case physx::PxGeometryType::eSPHERE: {
735 physx::PxSphereGeometry sphereGeometry;
736 physXShape->getSphereGeometry(sphereGeometry);
737 const float radius = holder.radius();
738 if (!qFuzzyCompare(sphereGeometry.radius, radius)) {
739 auto geom = QDebugDrawHelper::generateSphereGeometry(sphereGeometry.radius);
740 geom->setParent(model);
741 model->setGeometry(geom);
742 holder.setRadius(sphereGeometry.radius);
747 case physx::PxGeometryType::eCAPSULE: {
748 physx::PxCapsuleGeometry capsuleGeometry;
749 physXShape->getCapsuleGeometry(capsuleGeometry);
750 const float radius = holder.radius();
751 const float halfHeight = holder.halfHeight();
753 if (!qFuzzyCompare(capsuleGeometry.radius, radius)
754 || !qFuzzyCompare(capsuleGeometry.halfHeight, halfHeight)) {
755 auto geom = QDebugDrawHelper::generateCapsuleGeometry(
756 capsuleGeometry.radius, capsuleGeometry.halfHeight);
757 geom->setParent(model);
758 model->setGeometry(geom);
759 holder.setRadius(capsuleGeometry.radius);
760 holder.setHalfHeight(capsuleGeometry.halfHeight);
765 case physx::PxGeometryType::ePLANE:{
766 physx::PxPlaneGeometry planeGeometry;
767 physXShape->getPlaneGeometry(planeGeometry);
769 const QQuaternion rotation =
770 QPhysicsUtils::kMinus90YawRotation * QPhysicsUtils::toQtType(localPose.q);
771 localPose = physx::PxTransform(localPose.p, QPhysicsUtils::toPhysXType(rotation));
773 if (model->geometry() ==
nullptr) {
774 auto geom = QDebugDrawHelper::generatePlaneGeometry();
775 geom->setParent(model);
776 model->setGeometry(geom);
784 case physx::PxGeometryType::eHEIGHTFIELD: {
785 physx::PxHeightFieldGeometry heightFieldGeometry;
786 bool success = physXShape->getHeightFieldGeometry(heightFieldGeometry);
788 const float heightScale = holder.heightScale();
789 const float rowScale = holder.rowScale();
790 const float columnScale = holder.columnScale();
792 if (
auto heightField = holder.getHeightField();
793 heightField && heightField != heightFieldGeometry.heightField) {
794 heightField->release();
795 holder.setHeightField(
nullptr);
798 if (!qFuzzyCompare(heightFieldGeometry.heightScale, heightScale)
799 || !qFuzzyCompare(heightFieldGeometry.rowScale, rowScale)
800 || !qFuzzyCompare(heightFieldGeometry.columnScale, columnScale)
801 || !holder.getHeightField()) {
802 if (!holder.getHeightField()) {
803 heightFieldGeometry.heightField->acquireReference();
804 holder.setHeightField(heightFieldGeometry.heightField);
806 auto geom = QDebugDrawHelper::generateHeightFieldGeometry(
807 heightFieldGeometry.heightField, heightFieldGeometry.heightScale,
808 heightFieldGeometry.rowScale, heightFieldGeometry.columnScale);
809 geom->setParent(model);
810 model->setGeometry(geom);
811 holder.setHeightScale(heightFieldGeometry.heightScale);
812 holder.setRowScale(heightFieldGeometry.rowScale);
813 holder.setColumnScale(heightFieldGeometry.columnScale);
818 case physx::PxGeometryType::eCONVEXMESH: {
819 physx::PxConvexMeshGeometry convexMeshGeometry;
820 const bool success = physXShape->getConvexMeshGeometry(convexMeshGeometry);
822 const auto rotation = convexMeshGeometry.scale.rotation * localPose.q;
823 localPose = physx::PxTransform(localPose.p, rotation);
824 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry.scale.scale));
826 if (
auto convexMesh = holder.getConvexMesh();
827 convexMesh && convexMesh != convexMeshGeometry.convexMesh) {
828 convexMesh->release();
829 holder.setConvexMesh(
nullptr);
832 if (!model->geometry() || !holder.getConvexMesh()) {
833 if (!holder.getConvexMesh()) {
834 convexMeshGeometry.convexMesh->acquireReference();
835 holder.setConvexMesh(convexMeshGeometry.convexMesh);
837 auto geom = QDebugDrawHelper::generateConvexMeshGeometry(
838 convexMeshGeometry.convexMesh);
839 geom->setParent(model);
840 model->setGeometry(geom);
845 case physx::PxGeometryType::eTRIANGLEMESH: {
846 physx::PxTriangleMeshGeometry triangleMeshGeometry;
847 const bool success = physXShape->getTriangleMeshGeometry(triangleMeshGeometry);
849 const auto rotation = triangleMeshGeometry.scale.rotation * localPose.q;
850 localPose = physx::PxTransform(localPose.p, rotation);
851 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry.scale.scale));
853 if (
auto triangleMesh = holder.getTriangleMesh();
854 triangleMesh && triangleMesh != triangleMeshGeometry.triangleMesh) {
855 triangleMesh->release();
856 holder.setTriangleMesh(
nullptr);
859 if (!model->geometry() || !holder.getTriangleMesh()) {
860 if (!holder.getTriangleMesh()) {
861 triangleMeshGeometry.triangleMesh->acquireReference();
862 holder.setTriangleMesh(triangleMeshGeometry.triangleMesh);
864 auto geom = QDebugDrawHelper::generateTriangleMeshGeometry(
865 triangleMeshGeometry.triangleMesh);
866 geom->setParent(model);
867 model->setGeometry(geom);
872 case physx::PxGeometryType::eINVALID:
873 case physx::PxGeometryType::eGEOMETRY_COUNT:
878 auto globalPose = node->getGlobalPose();
879 auto finalPose = globalPose.transform(localPose);
881 model->setRotation(QPhysicsUtils::toQtType(finalPose.q));
882 model->setPosition(QPhysicsUtils::toQtType(finalPose.p));
887 m_collisionShapeDebugModels.removeIf(
888 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysXNode *>,
889 DebugModelHolder>::iterator it) {
890 if (!currentCollisionShapes.contains(it.key())) {
891 auto holder = it.value();
892 holder.releaseMeshPointer();
903 if (
auto shape = qobject_cast<QAbstractPhysicsNode *>(node)) {
904 nodes.push_back(shape);
908 for (QQuick3DObject *child : node->childItems())
909 collectPhysicsNodes(child, nodes);
912void QPhysicsWorld::updateDebugDrawDesignStudio()
915 auto sceneNode = m_viewport ? m_viewport : m_scene;
917 if (sceneNode ==
nullptr)
920 setupDebugMaterials(sceneNode);
923 QSet<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>> currentCollisionShapes;
924 currentCollisionShapes.reserve(m_collisionShapeDebugModels.size());
926 QList<QAbstractPhysicsNode *> activePhysicsNodes;
927 activePhysicsNodes.reserve(m_collisionShapeDebugModels.size());
928 collectPhysicsNodes(m_scene, activePhysicsNodes);
930 for (QAbstractPhysicsNode *node : activePhysicsNodes) {
932 const auto &collisionShapes = node->getCollisionShapesList();
933 const int materialIdx = 0;
934 const int length = collisionShapes.length();
936 const bool isCharacterController = qobject_cast<QCharacterController *>(node) !=
nullptr;
938 for (
int idx = 0; idx < length; idx++) {
939 QAbstractCollisionShape *collisionShape = collisionShapes[idx];
940 DebugModelHolder &holder =
941 m_DesignStudioDebugModels[std::make_pair(collisionShape, node)];
942 auto &model = holder.model;
944 currentCollisionShapes.insert(std::make_pair(collisionShape, node));
946 m_hasIndividualDebugDraw =
947 m_hasIndividualDebugDraw || collisionShape->enableDebugDraw();
954 model =
new QQuick3DModel();
955 model->setParentItem(sceneNode);
956 model->setParent(sceneNode);
957 model->setCastsShadows(
false);
958 model->setReceivesShadows(
false);
959 model->setCastsReflections(
false);
962 const bool hasGeometry = holder.geometry !=
nullptr;
963 QVector3D scenePosition = collisionShape->scenePosition();
964 QQuaternion sceneRotation = collisionShape->sceneRotation();
965 QQuick3DGeometry *newGeometry =
nullptr;
967 if (isCharacterController)
968 sceneRotation = sceneRotation * QQuaternion::fromEulerAngles(QVector3D(0, 0, 90));
971 auto material = m_debugMaterials[materialIdx];
972 QQmlListReference materialsRef(model,
"materials");
973 if (materialsRef.count() == 0 || materialsRef.at(0) != material) {
974 materialsRef.clear();
975 materialsRef.append(material);
979 if (
auto shape = qobject_cast<QBoxShape *>(collisionShape)) {
980 const auto &halfExtentsOld = holder.halfExtents();
981 const auto halfExtents = shape->sceneScale() * shape->extents() * 0.5f;
982 if (!qFuzzyCompare(halfExtentsOld, halfExtents) || !hasGeometry) {
983 newGeometry = QDebugDrawHelper::generateBoxGeometry(halfExtents);
984 holder.setHalfExtents(halfExtents);
986 }
else if (
auto shape = qobject_cast<QSphereShape *>(collisionShape)) {
987 const float radiusOld = holder.radius();
988 const float radius = shape->sceneScale().x() * shape->diameter() * 0.5f;
989 if (!qFuzzyCompare(radiusOld, radius) || !hasGeometry) {
990 newGeometry = QDebugDrawHelper::generateSphereGeometry(radius);
991 holder.setRadius(radius);
993 }
else if (
auto shape = qobject_cast<QCapsuleShape *>(collisionShape)) {
994 const float radiusOld = holder.radius();
995 const float halfHeightOld = holder.halfHeight();
996 const float radius = shape->sceneScale().y() * shape->diameter() * 0.5f;
997 const float halfHeight = shape->sceneScale().x() * shape->height() * 0.5f;
999 if ((!qFuzzyCompare(radiusOld, radius) || !qFuzzyCompare(halfHeightOld, halfHeight))
1001 newGeometry = QDebugDrawHelper::generateCapsuleGeometry(radius, halfHeight);
1002 holder.setRadius(radius);
1003 holder.setHalfHeight(halfHeight);
1005 }
else if (qobject_cast<QPlaneShape *>(collisionShape)) {
1007 newGeometry = QDebugDrawHelper::generatePlaneGeometry();
1008 }
else if (
auto shape = qobject_cast<QHeightFieldShape *>(collisionShape)) {
1009 physx::PxHeightFieldGeometry *heightFieldGeometry =
1010 static_cast<physx::PxHeightFieldGeometry *>(shape->getPhysXGeometry());
1011 const float heightScale = holder.heightScale();
1012 const float rowScale = holder.rowScale();
1013 const float columnScale = holder.columnScale();
1014 scenePosition += shape->hfOffset();
1015 if (!heightFieldGeometry) {
1016 qWarning() <<
"Could not get height field";
1017 }
else if (!qFuzzyCompare(heightFieldGeometry->heightScale, heightScale)
1018 || !qFuzzyCompare(heightFieldGeometry->rowScale, rowScale)
1019 || !qFuzzyCompare(heightFieldGeometry->columnScale, columnScale)
1021 newGeometry = QDebugDrawHelper::generateHeightFieldGeometry(
1022 heightFieldGeometry->heightField, heightFieldGeometry->heightScale,
1023 heightFieldGeometry->rowScale, heightFieldGeometry->columnScale);
1024 holder.setHeightScale(heightFieldGeometry->heightScale);
1025 holder.setRowScale(heightFieldGeometry->rowScale);
1026 holder.setColumnScale(heightFieldGeometry->columnScale);
1028 }
else if (
auto shape = qobject_cast<QConvexMeshShape *>(collisionShape)) {
1029 auto convexMeshGeometry =
1030 static_cast<physx::PxConvexMeshGeometry *>(shape->getPhysXGeometry());
1031 if (!convexMeshGeometry) {
1032 qWarning() <<
"Could not get convex mesh";
1034 model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry->scale.scale));
1037 newGeometry = QDebugDrawHelper::generateConvexMeshGeometry(
1038 convexMeshGeometry->convexMesh);
1041 }
else if (
auto shape = qobject_cast<QTriangleMeshShape *>(collisionShape)) {
1042 physx::PxTriangleMeshGeometry *triangleMeshGeometry =
1043 static_cast<physx::PxTriangleMeshGeometry *>(shape->getPhysXGeometry());
1044 if (!triangleMeshGeometry) {
1045 qWarning() <<
"Could not get triangle mesh";
1047 model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry->scale.scale));
1050 newGeometry = QDebugDrawHelper::generateTriangleMeshGeometry(
1051 triangleMeshGeometry->triangleMesh);
1057 delete holder.geometry;
1058 holder.geometry = newGeometry;
1061 model->setGeometry(holder.geometry);
1062 model->setVisible(
true);
1064 model->setRotation(sceneRotation);
1065 model->setPosition(scenePosition);
1070 m_DesignStudioDebugModels.removeIf(
1071 [&](QHash<QPair<QAbstractCollisionShape *, QAbstractPhysicsNode *>,
1072 DebugModelHolder>::iterator it) {
1073 if (!currentCollisionShapes.contains(it.key())) {
1074 auto holder = it.value();
1075 holder.releaseMeshPointer();
1077 delete holder.geometry;
1078 delete holder.model;
1086void QPhysicsWorld::disableDebugDraw()
1088 m_hasIndividualDebugDraw =
false;
1090 for (QAbstractPhysXNode *body : m_physXBodies) {
1091 const auto &collisionShapes = body->frontendNode->getCollisionShapesList();
1092 const int length = collisionShapes.length();
1093 for (
int idx = 0; idx < length; idx++) {
1094 const auto collisionShape = collisionShapes[idx];
1095 if (collisionShape->enableDebugDraw()) {
1096 m_hasIndividualDebugDraw =
true;
1103void QPhysicsWorld::setEnableCCD(
bool enableCCD)
1105 if (m_enableCCD == enableCCD)
1108 if (m_physicsInitialized) {
1110 <<
"Warning: Changing 'enableCCD' after physics is initialized will have no effect";
1114 m_enableCCD = enableCCD;
1115 emit enableCCDChanged(m_enableCCD);
1118void QPhysicsWorld::setTypicalLength(
float typicalLength)
1120 if (qFuzzyCompare(typicalLength, m_typicalLength))
1123 if (typicalLength <= 0.f) {
1124 qWarning() <<
"Warning: 'typicalLength' value less than zero, ignored";
1128 if (m_physicsInitialized) {
1129 qWarning() <<
"Warning: Changing 'typicalLength' after physics is initialized will have "
1134 m_typicalLength = typicalLength;
1136 emit typicalLengthChanged(typicalLength);
1139void QPhysicsWorld::setTypicalSpeed(
float typicalSpeed)
1141 if (qFuzzyCompare(typicalSpeed, m_typicalSpeed))
1144 if (m_physicsInitialized) {
1145 qWarning() <<
"Warning: Changing 'typicalSpeed' after physics is initialized will have "
1150 m_typicalSpeed = typicalSpeed;
1152 emit typicalSpeedChanged(typicalSpeed);
1155float QPhysicsWorld::defaultDensity()
const
1157 return m_defaultDensity;
1160float QPhysicsWorld::minimumTimestep()
const
1162 return m_minTimestep;
1165float QPhysicsWorld::maximumTimestep()
const
1167 return m_maxTimestep;
1170void QPhysicsWorld::setDefaultDensity(
float defaultDensity)
1172 if (qFuzzyCompare(m_defaultDensity, defaultDensity))
1174 m_defaultDensity = defaultDensity;
1177 for (QAbstractPhysXNode *body : m_physXBodies)
1178 body->updateDefaultDensity(m_defaultDensity);
1180 emit defaultDensityChanged(defaultDensity);
1185void QPhysicsWorld::cleanupRemovedNodes()
1187 m_physXBodies.removeIf([
this](QAbstractPhysXNode *body) {
1188 return body->cleanupIfRemoved(m_physx);
1190 m_removedPhysicsNodes.clear();
1193void QPhysicsWorld::initPhysics()
1195 Q_ASSERT(!m_physicsInitialized);
1197 const unsigned int numThreads = m_numThreads >= 0 ? m_numThreads : qMax(0, QThread::idealThreadCount());
1198 m_physx->createScene(m_typicalLength, m_typicalSpeed, m_gravity, m_enableCCD,
this, numThreads);
1199 m_frameAnimator->start();
1200 m_physicsInitialized =
true;
1203void QPhysicsWorld::simulateFrame()
1205 constexpr double MILLIONTH = 0.000001;
1206 constexpr double THOUSANDTH = 0.001;
1208 if (m_inDesignStudio) {
1209 frameFinishedDesignStudio();
1213 if (!m_physx->isRunning) {
1215 m_physx->isRunning =
true;
1216 const double minTimestepSecs = m_minTimestep * 0.001;
1217 m_physx->scene->simulate(minTimestepSecs);
1218 m_currTimeStep = minTimestepSecs;
1223 if (!m_frameFetched && !m_physx->scene->checkResults()) {
1228 if (!m_frameFetched) {
1229 m_physx->scene->fetchResults(
true);
1230 frameFinished(m_currTimeStep);
1231 m_frameFetched =
true;
1232 if (Q_UNLIKELY(!qtPhysicsTimingsFile.isEmpty())) {
1233 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1234 m_frameTimings.append(deltaMS);
1239 const double deltaMS = m_timer.nsecsElapsed() * MILLIONTH;
1240 if (deltaMS < m_minTimestep)
1242 const double deltaSecs = qMin<
double>(deltaMS, m_maxTimestep) * THOUSANDTH;
1244 m_physx->scene->simulate(deltaSecs);
1245 m_frameFetched =
false;
1246 m_currTimeStep = deltaSecs;
1249void QPhysicsWorld::frameFinished(
float deltaTime)
1252 emitContactCallbacks();
1253 cleanupRemovedNodes();
1254 for (
auto *node : std::as_const(m_newPhysicsNodes)) {
1255 auto *body = node->createPhysXBackend();
1256 body->init(
this, m_physx);
1257 m_physXBodies.push_back(body);
1259 m_newPhysicsNodes.clear();
1261 QHash<QQuick3DNode *, QMatrix4x4> transformCache;
1264 for (
auto *physXBody : std::as_const(m_physXBodies)) {
1265 physXBody->markDirtyShapes();
1266 physXBody->rebuildDirtyShapes(
this, m_physx);
1267 physXBody->updateFilters();
1270 physXBody->sync(deltaTime, transformCache);
1274 emit frameDone(deltaTime * 1000);
1277void QPhysicsWorld::frameFinishedDesignStudio()
1281 emitContactCallbacks();
1282 cleanupRemovedNodes();
1284 m_newPhysicsNodes.clear();
1286 updateDebugDrawDesignStudio();
1289QPhysicsWorld *QPhysicsWorld::getWorld(QQuick3DNode *node)
1291 for (QPhysicsWorld *world : worldManager.worlds) {
1292 if (!world->m_scene) {
1296 QQuick3DNode *nodeCurr = node;
1299 if (nodeCurr == world->m_scene)
1302 while (nodeCurr->parentNode()) {
1303 nodeCurr = nodeCurr->parentNode();
1304 if (nodeCurr == world->m_scene)
1312void QPhysicsWorld::matchOrphanNodes()
1315 if (worldManager.orphanNodes.isEmpty())
1318 qsizetype numNodes = worldManager.orphanNodes.length();
1321 while (idx < numNodes) {
1322 auto node = worldManager.orphanNodes[idx];
1323 auto world = getWorld(node);
1324 if (world ==
this) {
1325 world->m_newPhysicsNodes.push_back(node);
1327 worldManager.orphanNodes.swapItemsAt(idx, numNodes - 1);
1328 worldManager.orphanNodes.pop_back();
1336void QPhysicsWorld::findPhysicsNodes()
1341 if (m_scene ==
nullptr)
1345 QList<QQuick3DObject *> children = m_scene->childItems();
1346 while (!children.empty()) {
1347 auto child = children.takeFirst();
1348 if (
auto converted = qobject_cast<QAbstractPhysicsNode *>(child); converted !=
nullptr) {
1350 if (converted->m_backendObject !=
nullptr) {
1351 qWarning() <<
"Warning: physics node already associated with a backend node.";
1355 m_newPhysicsNodes.push_back(converted);
1356 worldManager.orphanNodes.removeAll(converted);
1358 children.append(child->childItems());
1362void QPhysicsWorld::emitContactCallbacks()
1364 for (
const QPhysicsWorld::BodyContact &contact : m_registeredContacts) {
1365 if (m_removedPhysicsNodes.contains(contact.sender)
1366 || m_removedPhysicsNodes.contains(contact.receiver))
1368 contact.receiver->registerContact(contact.sender, contact.positions, contact.impulses,
1372 m_registeredContacts.clear();
1375physx::PxPhysics *QPhysicsWorld::getPhysics()
1377 return StaticPhysXObjects::getReference().physics;
1380physx::PxCooking *QPhysicsWorld::getCooking()
1382 return StaticPhysXObjects::getReference().cooking;
1385physx::PxControllerManager *QPhysicsWorld::controllerManager()
1387 if (m_physx->scene && !m_physx->controllerManager) {
1388 m_physx->controllerManager = PxCreateControllerManager(*m_physx->scene);
1389 qCDebug(lcQuick3dPhysics) <<
"Created controller manager" << m_physx->controllerManager;
1391 return m_physx->controllerManager;
1394QQuick3DNode *QPhysicsWorld::scene()
const
1399void QPhysicsWorld::setScene(QQuick3DNode *newScene)
1401 if (m_scene == newScene)
1407 for (
auto body : m_physXBodies) {
1408 deregisterNode(body->frontendNode);
1412 bool sceneOK =
true;
1413 for (QPhysicsWorld *world : worldManager.worlds) {
1414 if (world !=
this && world->scene() == newScene) {
1416 qWarning() <<
"Warning: scene already associated with physics world";
1422 emit sceneChanged();
1425int QPhysicsWorld::numThreads()
const
1427 return m_numThreads;
1430void QPhysicsWorld::setNumThreads(
int newNumThreads)
1432 if (m_numThreads == newNumThreads)
1434 m_numThreads = newNumThreads;
1435 emit numThreadsChanged();
1438bool QPhysicsWorld::reportKinematicKinematicCollisions()
const
1440 return m_reportKinematicKinematicCollisions;
1443void QPhysicsWorld::setReportKinematicKinematicCollisions(
1444 bool newReportKinematicKinematicCollisions)
1446 if (m_reportKinematicKinematicCollisions == newReportKinematicKinematicCollisions)
1448 m_reportKinematicKinematicCollisions = newReportKinematicKinematicCollisions;
1449 emit reportKinematicKinematicCollisionsChanged();
1452bool QPhysicsWorld::reportStaticKinematicCollisions()
const
1454 return m_reportStaticKinematicCollisions;
1457void QPhysicsWorld::setReportStaticKinematicCollisions(
bool newReportStaticKinematicCollisions)
1459 if (m_reportStaticKinematicCollisions == newReportStaticKinematicCollisions)
1461 m_reportStaticKinematicCollisions = newReportStaticKinematicCollisions;
1462 emit reportStaticKinematicCollisionsChanged();
1467#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