aboutsummaryrefslogtreecommitdiffstats
diff options
authorTarja Sundqvist <tarja.sundqvist@qt.io>2025-03-04 15:48:16 +0200
committerTarja Sundqvist <tarja.sundqvist@qt.io>2025-03-04 15:48:16 +0200
commitd8004c7ef00cf19a35f151956d9dbd97dac4fd72 (patch)
treea64cad250c7b585a13e3fec3b6e9004cfc7d9654
parent20cb76bd79e66311545ebae7ec14d2f1c9e4d631 (diff)
parent6dab0079351fe3c3383f4f2957f424bc4a8d0242 (diff)
Merge tag 'v6.5.5-lts-lgpl' into 6.56.5
Qt 6.5.5-lts-lgpl release
-rw-r--r--.cmake.conf2
-rw-r--r--dependencies.yaml8
-rw-r--r--examples/quick3dphysics/cannon/main.qml4
-rw-r--r--examples/quick3dphysics/material/main.qml2
-rw-r--r--src/3rdparty/PhysX/CMakeLists.txt2
-rw-r--r--src/quick3dphysics/CMakeLists.txt3
-rw-r--r--src/quick3dphysics/physxnode/qphysxactorbody.cpp13
-rw-r--r--src/quick3dphysics/physxnode/qphysxstaticbody.cpp9
-rw-r--r--src/quick3dphysics/physxnode/qphysxworld.cpp9
-rw-r--r--src/quick3dphysics/qconvexmeshshape.cpp289
-rw-r--r--src/quick3dphysics/qconvexmeshshape_p.h38
-rw-r--r--src/quick3dphysics/qmeshshape.cpp310
-rw-r--r--src/quick3dphysics/qmeshshape_p.h68
-rw-r--r--src/quick3dphysics/qphysicsutils_p.h9
-rw-r--r--src/quick3dphysics/qphysicsworld.cpp185
-rw-r--r--src/quick3dphysics/qphysicsworld_p.h54
-rw-r--r--src/quick3dphysics/qtrianglemeshshape.cpp63
-rw-r--r--src/quick3dphysics/qtrianglemeshshape_p.h39
-rw-r--r--tests/auto/CMakeLists.txt1
-rw-r--r--tests/auto/callback_create_delete_node/Box.qml21
-rw-r--r--tests/auto/callback_create_delete_node/CMakeLists.txt22
-rw-r--r--tests/auto/callback_create_delete_node/tst_callback_create_delete_node.cpp21
-rw-r--r--tests/auto/callback_create_delete_node/tst_callback_create_delete_node.qml104
-rw-r--r--tests/baseline/data/DebugDrawSharedShapes.qml2
-rw-r--r--tests/baseline/data/ScaledPosition.qml5
-rw-r--r--tests/baseline/data/UpdateScaleHeightfield.qml2
-rw-r--r--tools/cooker/CMakeLists.txt2
27 files changed, 812 insertions, 475 deletions
diff --git a/.cmake.conf b/.cmake.conf
index b46669a..faef28e 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,2 +1,2 @@
-set(QT_REPO_MODULE_VERSION "6.5.4")
+set(QT_REPO_MODULE_VERSION "6.5.5")
set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
diff --git a/dependencies.yaml b/dependencies.yaml
index 4e49c7e..20783f2 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,13 +1,13 @@
dependencies:
../tqtc-qtbase:
- ref: 8ff0b254e4c3db81254782262d827f7831d15f6b
+ ref: fdf57f5df57e7d12cf871699d857a71acf272e0c
required: true
../tqtc-qtdeclarative:
- ref: 9edb471d3a35b3dc40def86c395789086edaa983
+ ref: 7ac842cba18be081ac835bf40ac475ec4c47d30b
required: true
../tqtc-qtquick3d:
- ref: 2c64e3fbf973f0db7b33d18220b0ad84a47c11d0
+ ref: d3cf878098cf399b00ecfa741e3d9d7d7964e42b
required: true
../tqtc-qtshadertools:
- ref: 5bf9de4a39fe2385998307c3e5ae16df6c0a5bd7
+ ref: 8191dce7e16f9bf42476c41a3b7aab9a62b26daa
required: true
diff --git a/examples/quick3dphysics/cannon/main.qml b/examples/quick3dphysics/cannon/main.qml
index bf11e61..6a16d7e 100644
--- a/examples/quick3dphysics/cannon/main.qml
+++ b/examples/quick3dphysics/cannon/main.qml
@@ -117,11 +117,11 @@ Window {
return
instancesSpheres.forEach(sphere => {
- sphere.collisionShapes = {}
+ sphere.collisionShapes = []
sphere.destroy()
})
instancesBoxes.forEach(box => {
- box.object.collisionShapes = {}
+ box.object.collisionShapes = []
box.object.destroy()
})
instancesSpheres = []
diff --git a/examples/quick3dphysics/material/main.qml b/examples/quick3dphysics/material/main.qml
index 0e770e0..49a8d71 100644
--- a/examples/quick3dphysics/material/main.qml
+++ b/examples/quick3dphysics/material/main.qml
@@ -63,6 +63,8 @@ Window {
materials: DefaultMaterial {
diffuseColor: "green"
}
+ castsShadows: false
+ receivesShadows: true
}
}
//! [floor]
diff --git a/src/3rdparty/PhysX/CMakeLists.txt b/src/3rdparty/PhysX/CMakeLists.txt
index 366a9c0..1fb4923 100644
--- a/src/3rdparty/PhysX/CMakeLists.txt
+++ b/src/3rdparty/PhysX/CMakeLists.txt
@@ -1345,7 +1345,7 @@ if (MSYS OR MINGW)
qt_internal_extend_target(BundledPhysX DEFINES PX_SIMD_DISABLED PX_GCC_FAMILY)
endif()
-if (UNIX)
+if (UNIX OR MINGW)
# Needed for PxPreprocessor.h error
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
qt_internal_extend_target(BundledPhysX DEFINES _DEBUG)
diff --git a/src/quick3dphysics/CMakeLists.txt b/src/quick3dphysics/CMakeLists.txt
index 350a4bd..76b123d 100644
--- a/src/quick3dphysics/CMakeLists.txt
+++ b/src/quick3dphysics/CMakeLists.txt
@@ -34,6 +34,7 @@ qt_internal_add_qml_module(Quick3DPhysics
qdebugdrawhelper.cpp qdebugdrawhelper_p.h
qdynamicrigidbody.cpp qdynamicrigidbody_p.h
qheightfieldshape.cpp qheightfieldshape_p.h
+ qmeshshape.cpp qmeshshape_p.h
qphysicscommands.cpp qphysicscommands_p.h
qphysicsmaterial.cpp qphysicsmaterial_p.h
qphysicsmeshutils_p_p.h
@@ -81,7 +82,7 @@ qt_internal_extend_target(qquick3dphysicsplugin
Qt::Quick3DPhysicsPrivate
)
-if (UNIX)
+if (UNIX OR MINGW)
# Needed for PxPreprocessor.h error
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
qt_internal_extend_target(Quick3DPhysics DEFINES _DEBUG)
diff --git a/src/quick3dphysics/physxnode/qphysxactorbody.cpp b/src/quick3dphysics/physxnode/qphysxactorbody.cpp
index 8d952d0..88a7b18 100644
--- a/src/quick3dphysics/physxnode/qphysxactorbody.cpp
+++ b/src/quick3dphysics/physxnode/qphysxactorbody.cpp
@@ -24,21 +24,12 @@
QT_BEGIN_NAMESPACE
-static const QQuaternion kMinus90YawRotation = QQuaternion::fromEulerAngles(0, -90, 0);
-
-static inline bool fuzzyEquals(const physx::PxTransform &a, const physx::PxTransform &b)
-{
- return qFuzzyCompare(a.p.x, b.p.x) && qFuzzyCompare(a.p.y, b.p.y) && qFuzzyCompare(a.p.z, b.p.z)
- && qFuzzyCompare(a.q.x, b.q.x) && qFuzzyCompare(a.q.y, b.q.y)
- && qFuzzyCompare(a.q.z, b.q.z) && qFuzzyCompare(a.q.w, b.q.w);
-}
-
static physx::PxTransform getPhysXLocalTransform(const QQuick3DNode *node)
{
// Modify transforms to make the PhysX shapes match the QtQuick3D conventions
if (qobject_cast<const QPlaneShape *>(node) != nullptr) {
// Rotate the plane to make it match the built-in rectangle
- const QQuaternion rotation = kMinus90YawRotation * node->rotation();
+ const QQuaternion rotation = QPhysicsUtils::kMinus90YawRotation * node->rotation();
return physx::PxTransform(QPhysicsUtils::toPhysXType(node->position()),
QPhysicsUtils::toPhysXType(rotation));
} else if (auto *hf = qobject_cast<const QHeightFieldShape *>(node)) {
@@ -115,7 +106,7 @@ void QPhysXActorBody::markDirtyShapes()
auto poseNew = getPhysXLocalTransform(collisionShapes[i]);
auto poseOld = physXShapes[i]->getLocalPose();
- if (!fuzzyEquals(poseNew, poseOld)) {
+ if (!QPhysicsUtils::fuzzyEquals(poseNew, poseOld)) {
setShapesDirty(true);
break;
}
diff --git a/src/quick3dphysics/physxnode/qphysxstaticbody.cpp b/src/quick3dphysics/physxnode/qphysxstaticbody.cpp
index 083cc64..18f5c37 100644
--- a/src/quick3dphysics/physxnode/qphysxstaticbody.cpp
+++ b/src/quick3dphysics/physxnode/qphysxstaticbody.cpp
@@ -14,13 +14,6 @@
QT_BEGIN_NAMESPACE
-static inline bool fuzzyEquals(const physx::PxTransform &a, const physx::PxTransform &b)
-{
- return qFuzzyCompare(a.p.x, b.p.x) && qFuzzyCompare(a.p.y, b.p.y) && qFuzzyCompare(a.p.z, b.p.z)
- && qFuzzyCompare(a.q.x, b.q.x) && qFuzzyCompare(a.q.y, b.q.y)
- && qFuzzyCompare(a.q.z, b.q.z) && qFuzzyCompare(a.q.w, b.q.w);
-}
-
QPhysXStaticBody::QPhysXStaticBody(QStaticRigidBody *frontEnd) : QPhysXRigidBody(frontEnd) { }
DebugDrawBodyType QPhysXStaticBody::getDebugDrawBodyType()
@@ -36,7 +29,7 @@ void QPhysXStaticBody::sync(float deltaTime, QHash<QQuick3DNode *, QMatrix4x4> &
const physx::PxTransform poseOld = actor->getGlobalPose();
// For performance we only update static objects if they have been moved
- if (!fuzzyEquals(poseNew, poseOld))
+ if (!QPhysicsUtils::fuzzyEquals(poseNew, poseOld))
actor->setGlobalPose(poseNew);
QPhysXActorBody::sync(deltaTime, transformCache);
}
diff --git a/src/quick3dphysics/physxnode/qphysxworld.cpp b/src/quick3dphysics/physxnode/qphysxworld.cpp
index 9d348d3..e5731bb 100644
--- a/src/quick3dphysics/physxnode/qphysxworld.cpp
+++ b/src/quick3dphysics/physxnode/qphysxworld.cpp
@@ -91,8 +91,9 @@ public:
QAbstractPhysicsNode *other =
static_cast<QAbstractPhysicsNode *>(pairHeader.actors[1]->userData);
- if (!trigger || !other || !trigger->m_backendObject || !other->m_backendObject
- || world->isNodeRemoved(trigger) || world->isNodeRemoved(other))
+ if (!trigger || !other || world->isNodeRemoved(trigger)
+ || world->isNodeRemoved(other) || !trigger->m_backendObject
+ || !other->m_backendObject)
continue;
const bool triggerReceive =
@@ -130,9 +131,9 @@ public:
}
if (triggerReceive)
- trigger->registerContact(other, positions, impulses, normals);
+ world->registerContact(other, trigger, positions, impulses, normals);
if (otherReceive)
- other->registerContact(trigger, positions, impulses, normalsInverted);
+ world->registerContact(trigger, other, positions, impulses, normalsInverted);
}
}
};
diff --git a/src/quick3dphysics/qconvexmeshshape.cpp b/src/quick3dphysics/qconvexmeshshape.cpp
index a8a420e..56b6d3f 100644
--- a/src/quick3dphysics/qconvexmeshshape.cpp
+++ b/src/quick3dphysics/qconvexmeshshape.cpp
@@ -1,248 +1,10 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include "qcacheutils_p.h"
#include "qconvexmeshshape_p.h"
-#include <QFile>
-#include <QFileInfo>
-#include <QtQuick3D/QQuick3DGeometry>
-#include <extensions/PxExtensionsAPI.h>
-
-#include "foundation/PxVec3.h"
-#include "cooking/PxConvexMeshDesc.h"
-#include "extensions/PxDefaultStreams.h"
-
-#include <QtQml/qqml.h>
-#include <QtQml/QQmlFile>
-#include <QtQml/qqmlcontext.h>
-
-#include <QtQuick3DUtils/private/qssgmesh_p.h>
-#include "qphysicsworld_p.h"
-#include "qphysicsmeshutils_p_p.h"
-#include "qphysicsutils_p.h"
-
QT_BEGIN_NAMESPACE
-physx::PxConvexMesh *QQuick3DPhysicsMesh::convexMesh()
-{
- if (m_convexMesh != nullptr)
- return m_convexMesh;
-
- physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
- if (thePhysics == nullptr)
- return nullptr;
-
- m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
- if (m_convexMesh != nullptr)
- return m_convexMesh;
-
- m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
- if (m_convexMesh != nullptr)
- return m_convexMesh;
-
- loadSsgMesh();
-
- if (!m_ssgMesh.isValid())
- return nullptr;
-
- physx::PxDefaultMemoryOutputStream buf;
- physx::PxConvexMeshCookingResult::Enum result;
- int vStride = m_ssgMesh.vertexBuffer().stride;
- int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
- const auto *vd = m_ssgMesh.vertexBuffer().data.constData();
-
- qCDebug(lcQuick3dPhysics) << "prepare cooking" << vCount << "verts";
-
- QVector<physx::PxVec3> verts;
-
- for (int i = 0; i < vCount; ++i) {
- auto *vp = reinterpret_cast<const QVector3D *>(vd + vStride * i + m_posOffset);
- verts << physx::PxVec3 { vp->x(), vp->y(), vp->z() };
- }
-
- const auto *convexVerts = verts.constData();
-
- physx::PxConvexMeshDesc convexDesc;
- convexDesc.points.count = vCount;
- convexDesc.points.stride = sizeof(physx::PxVec3);
- convexDesc.points.data = convexVerts;
- convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
-
- const auto cooking = QPhysicsWorld::getCooking();
- if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
- auto size = buf.getSize();
- auto *data = buf.getData();
- physx::PxDefaultMemoryInputData input(data, size);
- m_convexMesh = thePhysics->createConvexMesh(input);
- qCDebug(lcQuick3dPhysics) << "Created convex mesh" << m_convexMesh << "for mesh" << this;
- QCacheUtils::writeCachedConvexMesh(m_meshPath, buf);
- } else {
- qCWarning(lcQuick3dPhysics) << "Could not create convex mesh from" << m_meshPath;
- }
-
- return m_convexMesh;
-}
-
-physx::PxTriangleMesh *QQuick3DPhysicsMesh::triangleMesh()
-{
-
- if (m_triangleMesh != nullptr)
- return m_triangleMesh;
-
- physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
- if (thePhysics == nullptr)
- return nullptr;
-
- m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
- if (m_triangleMesh != nullptr)
- return m_triangleMesh;
-
- m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
- if (m_triangleMesh != nullptr)
- return m_triangleMesh;
-
- loadSsgMesh();
- if (!m_ssgMesh.isValid())
- return nullptr;
-
- physx::PxDefaultMemoryOutputStream buf;
- physx::PxTriangleMeshCookingResult::Enum result;
- const int vStride = m_ssgMesh.vertexBuffer().stride;
- const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
- const auto *vd = m_ssgMesh.vertexBuffer().data.constData();
-
- const int iStride =
- m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16
- ? 2
- : 4;
- const int iCount = m_ssgMesh.indexBuffer().data.size() / iStride;
-
- qCDebug(lcQuick3dPhysics) << "prepare cooking" << vCount << "verts" << iCount << "idxs";
-
- physx::PxTriangleMeshDesc triangleDesc;
- triangleDesc.points.count = vCount;
- triangleDesc.points.stride = vStride;
- triangleDesc.points.data = vd + m_posOffset;
-
- triangleDesc.flags = {}; //??? physx::PxMeshFlag::eFLIPNORMALS or
- // physx::PxMeshFlag::e16_BIT_INDICES
- triangleDesc.triangles.count = iCount / 3;
- triangleDesc.triangles.stride = iStride * 3;
- triangleDesc.triangles.data = m_ssgMesh.indexBuffer().data.constData();
-
- const auto cooking = QPhysicsWorld::getCooking();
- if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
- auto size = buf.getSize();
- auto *data = buf.getData();
- physx::PxDefaultMemoryInputData input(data, size);
- m_triangleMesh = thePhysics->createTriangleMesh(input);
- qCDebug(lcQuick3dPhysics) << "Created triangle mesh" << m_triangleMesh << "for mesh"
- << this;
- QCacheUtils::writeCachedTriangleMesh(m_meshPath, buf);
- } else {
- qCWarning(lcQuick3dPhysics) << "Could not create triangle mesh from" << m_meshPath;
- }
-
- return m_triangleMesh;
-}
-
-void QQuick3DPhysicsMesh::loadSsgMesh()
-{
- if (m_ssgMesh.isValid())
- return;
-
- static const char *compTypes[] = { "Null", "UnsignedInt8", "Int8", "UnsignedInt16",
- "Int16", "UnsignedInt32", "Int32", "UnsignedInt64",
- "Int64", "Float16", "Float32", "Float64" };
-
- QFileInfo fileInfo = QFileInfo(m_meshPath);
- if (fileInfo.exists()) {
- QFile file(fileInfo.absoluteFilePath());
- if (file.open(QFile::ReadOnly))
- m_ssgMesh = QSSGMesh::Mesh::loadMesh(&file);
- }
- qCDebug(lcQuick3dPhysics) << "Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
- << "draw" << int(m_ssgMesh.drawMode()) << "wind"
- << int(m_ssgMesh.winding()) << "subs" << m_ssgMesh.subsets().count()
- << "attrs" << m_ssgMesh.vertexBuffer().entries.count()
- << m_ssgMesh.vertexBuffer().data.size() << "stride"
- << m_ssgMesh.vertexBuffer().stride << "verts"
- << m_ssgMesh.vertexBuffer().data.size()
- / m_ssgMesh.vertexBuffer().stride;
-
- for (auto &v : m_ssgMesh.vertexBuffer().entries) {
- qCDebug(lcQuick3dPhysics) << " attr" << v.name << compTypes[int(v.componentType)] << "cc"
- << v.componentCount << "offs" << v.offset;
- Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
- if (v.name == "attr_pos")
- m_posOffset = v.offset;
- }
-
- if (m_ssgMesh.isValid()) {
- auto sub = m_ssgMesh.subsets().constFirst();
- qCDebug(lcQuick3dPhysics) << "..." << sub.name << "count" << sub.count << "bounds"
- << sub.bounds.min << sub.bounds.max << "offset" << sub.offset;
- }
-
-#if 0 // EXTRA_DEBUG
-
- int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
- int vStride = m_ssgMesh.vertexBuffer().stride;
- qDebug() << "IDX" << compTypes[int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
- const auto ib = m_ssgMesh.indexBuffer().data;
- const auto vb = m_ssgMesh.vertexBuffer().data;
-
- auto getPoint = [&vb, vStride, this](int idx) -> QVector3D {
- auto *vp = vb.constData() + vStride * idx + m_posOffset;
- return *reinterpret_cast<const QVector3D *>(vp);
- return {};
- };
-
- if (iStride == 2) {
-
- } else {
- auto *ip = reinterpret_cast<const uint32_t *>(ib.data());
- int n = ib.size() / iStride;
- for (int i = 0; i < qMin(50,n); i += 3) {
-
- qDebug() << " " << ip [i] << ip[i+1] << ip[i+2] << " --- "
- << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
- }
- }
-#endif
- if (!m_ssgMesh.isValid())
- qCWarning(lcQuick3dPhysics) << "Could not read mesh from" << m_meshPath;
-}
-
-QQuick3DPhysicsMesh *QQuick3DPhysicsMeshManager::getMesh(const QUrl &source,
- const QObject *contextObject)
-{
- const QQmlContext *context = qmlContext(contextObject);
- const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
- const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
- auto *mesh = meshHash.value(qmlSource);
- if (!mesh) {
- mesh = new QQuick3DPhysicsMesh(qmlSource);
- meshHash[qmlSource] = mesh;
- }
- mesh->ref();
- return mesh;
-}
-
-void QQuick3DPhysicsMeshManager::releaseMesh(QQuick3DPhysicsMesh *mesh)
-{
- if (mesh->deref() == 0) {
- qCDebug(lcQuick3dPhysics()) << "deleting mesh" << mesh;
- erase_if(meshHash, [mesh](std::pair<const QString &, QQuick3DPhysicsMesh *&> h) {
- return h.second == mesh;
- });
- delete mesh;
- }
-}
-
-QHash<QString, QQuick3DPhysicsMesh *> QQuick3DPhysicsMeshManager::meshHash;
-
/*!
\qmltype ConvexMeshShape
\inherits CollisionShape
@@ -268,57 +30,14 @@ QHash<QString, QQuick3DPhysicsMesh *> QQuick3DPhysicsMeshManager::meshHash;
for details.
*/
-QConvexMeshShape::QConvexMeshShape() = default;
-
-QConvexMeshShape::~QConvexMeshShape()
+QMeshShape::MeshType QConvexMeshShape::shapeType() const
{
- delete m_meshGeometry;
- if (m_mesh)
- QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
+ return QMeshShape::MeshType::CONVEX;
}
-physx::PxGeometry *QConvexMeshShape::getPhysXGeometry()
+bool QConvexMeshShape::isStaticShape() const
{
- if (m_dirtyPhysx || m_scaleDirty) {
- updatePhysXGeometry();
- }
- return m_meshGeometry;
-}
-
-void QConvexMeshShape::updatePhysXGeometry()
-{
- delete m_meshGeometry;
- m_meshGeometry = nullptr;
-
- auto *convexMesh = m_mesh->convexMesh();
- if (!convexMesh)
- return;
-
- auto meshScale = sceneScale();
- physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
- physx::PxQuat(physx::PxIdentity));
-
- m_meshGeometry = new physx::PxConvexMeshGeometry(convexMesh, scale);
- m_dirtyPhysx = false;
-}
-
-const QUrl &QConvexMeshShape::source() const
-{
- return m_meshSource;
-}
-
-void QConvexMeshShape::setSource(const QUrl &newSource)
-{
- if (m_meshSource == newSource)
- return;
- m_meshSource = newSource;
- m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource, this);
- updatePhysXGeometry();
-
- m_dirtyPhysx = true;
-
- emit needsRebuild(this);
- emit sourceChanged();
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/quick3dphysics/qconvexmeshshape_p.h b/src/quick3dphysics/qconvexmeshshape_p.h
index 75ddad9..de07690 100644
--- a/src/quick3dphysics/qconvexmeshshape_p.h
+++ b/src/quick3dphysics/qconvexmeshshape_p.h
@@ -15,46 +15,16 @@
// We mean it.
//
-#include <QtQuick3DPhysics/qtquick3dphysicsglobal.h>
-#include <QtQuick3DPhysics/private/qabstractcollisionshape_p.h>
-#include <QtCore/QObject>
-#include <QtGui/QVector3D>
-#include <QtQml/QQmlEngine>
-
-namespace physx {
-class PxBoxGeometry;
-class PxConvexMesh;
-class PxConvexMeshGeometry;
-}
+#include "qmeshshape_p.h"
QT_BEGIN_NAMESPACE
-class QQuick3DPhysicsMesh;
-class Q_QUICK3DPHYSICS_EXPORT QConvexMeshShape : public QAbstractCollisionShape
+class Q_QUICK3DPHYSICS_EXPORT QConvexMeshShape : public QMeshShape
{
Q_OBJECT
- Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged REVISION(6, 5))
QML_NAMED_ELEMENT(ConvexMeshShape)
-public:
- QConvexMeshShape();
- ~QConvexMeshShape();
-
- physx::PxGeometry *getPhysXGeometry() override;
-
- Q_REVISION(6, 5) const QUrl &source() const;
- Q_REVISION(6, 5) void setSource(const QUrl &newSource);
- bool isStaticShape() const override { return false; }
-
-signals:
- Q_REVISION(6, 5) void sourceChanged();
-
-private:
- void updatePhysXGeometry();
-
- bool m_dirtyPhysx = false;
- physx::PxConvexMeshGeometry *m_meshGeometry = nullptr;
- QUrl m_meshSource;
- QQuick3DPhysicsMesh *m_mesh = nullptr;
+ virtual QMeshShape::MeshType shapeType() const override;
+ virtual bool isStaticShape() const override;
};
QT_END_NAMESPACE
diff --git a/src/quick3dphysics/qmeshshape.cpp b/src/quick3dphysics/qmeshshape.cpp
new file mode 100644
index 0000000..f7d38da
--- /dev/null
+++ b/src/quick3dphysics/qmeshshape.cpp
@@ -0,0 +1,310 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qcacheutils_p.h"
+#include "qmeshshape_p.h"
+
+#include <QFile>
+#include <QFileInfo>
+#include <QtQuick3D/QQuick3DGeometry>
+#include <extensions/PxExtensionsAPI.h>
+
+#include "foundation/PxVec3.h"
+#include "cooking/PxConvexMeshDesc.h"
+#include "extensions/PxDefaultStreams.h"
+
+#include <QtQml/qqml.h>
+#include <QtQml/QQmlFile>
+#include <QtQml/qqmlcontext.h>
+
+#include <QtQuick3DUtils/private/qssgmesh_p.h>
+#include "qmeshshape_p.h"
+#include "qphysicsworld_p.h"
+#include "qphysicsmeshutils_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+physx::PxConvexMesh *QQuick3DPhysicsMesh::convexMesh()
+{
+ if (m_convexMesh != nullptr)
+ return m_convexMesh;
+
+ physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
+ if (thePhysics == nullptr)
+ return nullptr;
+
+ m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
+ if (m_convexMesh != nullptr)
+ return m_convexMesh;
+
+ m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
+ if (m_convexMesh != nullptr)
+ return m_convexMesh;
+
+ loadSsgMesh();
+
+ if (!m_ssgMesh.isValid())
+ return nullptr;
+
+ physx::PxDefaultMemoryOutputStream buf;
+ physx::PxConvexMeshCookingResult::Enum result;
+ int vStride = m_ssgMesh.vertexBuffer().stride;
+ int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
+ const auto *vd = m_ssgMesh.vertexBuffer().data.constData();
+
+ qCDebug(lcQuick3dPhysics) << "prepare cooking" << vCount << "verts";
+
+ QVector<physx::PxVec3> verts;
+
+ for (int i = 0; i < vCount; ++i) {
+ auto *vp = reinterpret_cast<const QVector3D *>(vd + vStride * i + m_posOffset);
+ verts << physx::PxVec3 { vp->x(), vp->y(), vp->z() };
+ }
+
+ const auto *convexVerts = verts.constData();
+
+ physx::PxConvexMeshDesc convexDesc;
+ convexDesc.points.count = vCount;
+ convexDesc.points.stride = sizeof(physx::PxVec3);
+ convexDesc.points.data = convexVerts;
+ convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
+
+ const auto cooking = QPhysicsWorld::getCooking();
+ if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
+ auto size = buf.getSize();
+ auto *data = buf.getData();
+ physx::PxDefaultMemoryInputData input(data, size);
+ m_convexMesh = thePhysics->createConvexMesh(input);
+ qCDebug(lcQuick3dPhysics) << "Created convex mesh" << m_convexMesh << "for mesh" << this;
+ QCacheUtils::writeCachedConvexMesh(m_meshPath, buf);
+ } else {
+ qCWarning(lcQuick3dPhysics) << "Could not create convex mesh from" << m_meshPath;
+ }
+
+ return m_convexMesh;
+}
+
+physx::PxTriangleMesh *QQuick3DPhysicsMesh::triangleMesh()
+{
+ if (m_triangleMesh != nullptr)
+ return m_triangleMesh;
+
+ physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
+ if (thePhysics == nullptr)
+ return nullptr;
+
+ m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
+ if (m_triangleMesh != nullptr)
+ return m_triangleMesh;
+
+ m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
+ if (m_triangleMesh != nullptr)
+ return m_triangleMesh;
+
+ loadSsgMesh();
+ if (!m_ssgMesh.isValid())
+ return nullptr;
+
+ physx::PxDefaultMemoryOutputStream buf;
+ physx::PxTriangleMeshCookingResult::Enum result;
+ const int vStride = m_ssgMesh.vertexBuffer().stride;
+ const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
+ const auto *vd = m_ssgMesh.vertexBuffer().data.constData();
+
+ const int iStride =
+ m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16
+ ? 2
+ : 4;
+ const int iCount = m_ssgMesh.indexBuffer().data.size() / iStride;
+
+ qCDebug(lcQuick3dPhysics) << "prepare cooking" << vCount << "verts" << iCount << "idxs";
+
+ physx::PxTriangleMeshDesc triangleDesc;
+ triangleDesc.points.count = vCount;
+ triangleDesc.points.stride = vStride;
+ triangleDesc.points.data = vd + m_posOffset;
+
+ triangleDesc.flags = {}; //??? physx::PxMeshFlag::eFLIPNORMALS or
+ // physx::PxMeshFlag::e16_BIT_INDICES
+ triangleDesc.triangles.count = iCount / 3;
+ triangleDesc.triangles.stride = iStride * 3;
+ triangleDesc.triangles.data = m_ssgMesh.indexBuffer().data.constData();
+
+ const auto cooking = QPhysicsWorld::getCooking();
+ if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
+ auto size = buf.getSize();
+ auto *data = buf.getData();
+ physx::PxDefaultMemoryInputData input(data, size);
+ m_triangleMesh = thePhysics->createTriangleMesh(input);
+ qCDebug(lcQuick3dPhysics) << "Created triangle mesh" << m_triangleMesh << "for mesh"
+ << this;
+ QCacheUtils::writeCachedTriangleMesh(m_meshPath, buf);
+ } else {
+ qCWarning(lcQuick3dPhysics) << "Could not create triangle mesh from" << m_meshPath;
+ }
+
+ return m_triangleMesh;
+}
+
+void QQuick3DPhysicsMesh::loadSsgMesh()
+{
+ if (m_ssgMesh.isValid())
+ return;
+
+ static const char *compTypes[] = { "Null", "UnsignedInt8", "Int8", "UnsignedInt16",
+ "Int16", "UnsignedInt32", "Int32", "UnsignedInt64",
+ "Int64", "Float16", "Float32", "Float64" };
+
+ QFileInfo fileInfo = QFileInfo(m_meshPath);
+ if (fileInfo.exists()) {
+ QFile file(fileInfo.absoluteFilePath());
+ if (file.open(QFile::ReadOnly))
+ m_ssgMesh = QSSGMesh::Mesh::loadMesh(&file);
+ }
+ qCDebug(lcQuick3dPhysics) << "Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
+ << "draw" << int(m_ssgMesh.drawMode()) << "wind"
+ << int(m_ssgMesh.winding()) << "subs" << m_ssgMesh.subsets().count()
+ << "attrs" << m_ssgMesh.vertexBuffer().entries.count()
+ << m_ssgMesh.vertexBuffer().data.size() << "stride"
+ << m_ssgMesh.vertexBuffer().stride << "verts"
+ << m_ssgMesh.vertexBuffer().data.size()
+ / m_ssgMesh.vertexBuffer().stride;
+
+ for (auto &v : m_ssgMesh.vertexBuffer().entries) {
+ qCDebug(lcQuick3dPhysics) << " attr" << v.name << compTypes[int(v.componentType)] << "cc"
+ << v.componentCount << "offs" << v.offset;
+ Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
+ if (v.name == "attr_pos")
+ m_posOffset = v.offset;
+ }
+
+ if (m_ssgMesh.isValid()) {
+ auto sub = m_ssgMesh.subsets().constFirst();
+ qCDebug(lcQuick3dPhysics) << "..." << sub.name << "count" << sub.count << "bounds"
+ << sub.bounds.min << sub.bounds.max << "offset" << sub.offset;
+ }
+
+#if 0 // EXTRA_DEBUG
+
+ int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
+ int vStride = m_ssgMesh.vertexBuffer().stride;
+ qDebug() << "IDX" << compTypes[int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
+ const auto ib = m_ssgMesh.indexBuffer().data;
+ const auto vb = m_ssgMesh.vertexBuffer().data;
+
+ auto getPoint = [&vb, vStride, this](int idx) -> QVector3D {
+ auto *vp = vb.constData() + vStride * idx + m_posOffset;
+ return *reinterpret_cast<const QVector3D *>(vp);
+ return {};
+ };
+
+ if (iStride == 2) {
+
+ } else {
+ auto *ip = reinterpret_cast<const uint32_t *>(ib.data());
+ int n = ib.size() / iStride;
+ for (int i = 0; i < qMin(50,n); i += 3) {
+
+ qDebug() << " " << ip [i] << ip[i+1] << ip[i+2] << " --- "
+ << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
+ }
+ }
+#endif
+ if (!m_ssgMesh.isValid())
+ qCWarning(lcQuick3dPhysics) << "Could not read mesh from" << m_meshPath;
+}
+
+QQuick3DPhysicsMesh *QQuick3DPhysicsMeshManager::getMesh(const QUrl &source,
+ const QObject *contextObject)
+{
+ const QQmlContext *context = qmlContext(contextObject);
+ const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
+ const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
+ auto *mesh = meshHash.value(qmlSource);
+ if (!mesh) {
+ mesh = new QQuick3DPhysicsMesh(qmlSource);
+ meshHash[qmlSource] = mesh;
+ }
+ mesh->ref();
+ return mesh;
+}
+
+void QQuick3DPhysicsMeshManager::releaseMesh(QQuick3DPhysicsMesh *mesh)
+{
+ if (mesh->deref() == 0) {
+ qCDebug(lcQuick3dPhysics()) << "deleting mesh" << mesh;
+ erase_if(meshHash, [mesh](std::pair<const QString &, QQuick3DPhysicsMesh *&> h) {
+ return h.second == mesh;
+ });
+ delete mesh;
+ }
+}
+
+QHash<QString, QQuick3DPhysicsMesh *> QQuick3DPhysicsMeshManager::meshHash;
+
+QMeshShape::~QMeshShape()
+{
+ delete m_convexGeometry;
+ if (m_mesh)
+ QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
+}
+
+physx::PxGeometry *QMeshShape::getPhysXGeometry()
+{
+ if (m_dirtyPhysx || m_scaleDirty)
+ updatePhysXGeometry();
+ if (shapeType() == MeshType::CONVEX)
+ return m_convexGeometry;
+ if (shapeType() == MeshType::TRIANGLE)
+ return m_triangleGeometry;
+
+ Q_UNREACHABLE_RETURN(nullptr);
+}
+
+void QMeshShape::updatePhysXGeometry()
+{
+ delete m_convexGeometry;
+ delete m_triangleGeometry;
+ m_convexGeometry = nullptr;
+ m_triangleGeometry = nullptr;
+
+ if (!m_mesh)
+ return;
+
+ auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() : nullptr;
+ auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() : nullptr;
+ if (!convexMesh && !triangleMesh)
+ return;
+
+ auto meshScale = sceneScale();
+ physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
+ physx::PxQuat(physx::PxIdentity));
+
+ if (convexMesh)
+ m_convexGeometry = new physx::PxConvexMeshGeometry(convexMesh, scale);
+ if (triangleMesh)
+ m_triangleGeometry = new physx::PxTriangleMeshGeometry(triangleMesh, scale);
+
+ m_dirtyPhysx = false;
+}
+
+const QUrl &QMeshShape::source() const
+{
+ return m_meshSource;
+}
+
+void QMeshShape::setSource(const QUrl &newSource)
+{
+ if (m_meshSource == newSource)
+ return;
+ m_meshSource = newSource;
+ m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource, this);
+ updatePhysXGeometry();
+
+ m_dirtyPhysx = true;
+
+ emit needsRebuild(this);
+ emit sourceChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick3dphysics/qmeshshape_p.h b/src/quick3dphysics/qmeshshape_p.h
new file mode 100644
index 0000000..5ce34b2
--- /dev/null
+++ b/src/quick3dphysics/qmeshshape_p.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MESHSHAPE_H
+#define MESHSHAPE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick3DPhysics/qtquick3dphysicsglobal.h>
+#include <QtQuick3DPhysics/private/qabstractcollisionshape_p.h>
+#include <QtCore/QObject>
+#include <QtGui/QVector3D>
+#include <QtQml/QQmlEngine>
+
+namespace physx {
+class PxBoxGeometry;
+class PxConvexMesh;
+class PxConvexMeshGeometry;
+class PxTriangleMesh;
+class PxTriangleMeshGeometry;
+}
+
+QT_BEGIN_NAMESPACE
+class QQuick3DPhysicsMesh;
+
+class Q_QUICK3DPHYSICS_EXPORT QMeshShape : public QAbstractCollisionShape
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged REVISION(6, 5))
+ QML_NAMED_ELEMENT(MeshShape)
+ QML_UNCREATABLE("abstract interface")
+
+public:
+ ~QMeshShape();
+
+ enum class MeshType { TRIANGLE, CONVEX };
+ virtual MeshType shapeType() const = 0;
+
+ physx::PxGeometry *getPhysXGeometry() override;
+
+ Q_REVISION(6, 5) const QUrl &source() const;
+ Q_REVISION(6, 5) void setSource(const QUrl &newSource);
+
+signals:
+ Q_REVISION(6, 5) void sourceChanged();
+
+private:
+ void updatePhysXGeometry();
+
+ bool m_dirtyPhysx = false;
+ physx::PxConvexMeshGeometry *m_convexGeometry = nullptr;
+ physx::PxTriangleMeshGeometry *m_triangleGeometry = nullptr;
+ QUrl m_meshSource;
+ QQuick3DPhysicsMesh *m_mesh = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // MESHSHAPE_H
diff --git a/src/quick3dphysics/qphysicsutils_p.h b/src/quick3dphysics/qphysicsutils_p.h
index 8f2a0bb..ad1d679 100644
--- a/src/quick3dphysics/qphysicsutils_p.h
+++ b/src/quick3dphysics/qphysicsutils_p.h
@@ -59,6 +59,15 @@ Q_ALWAYS_INLINE physx::PxTransform toPhysXTransform(const QVector3D &position,
return physx::PxTransform(QPhysicsUtils::toPhysXType(position),
QPhysicsUtils::toPhysXType(rotation));
}
+
+Q_ALWAYS_INLINE bool fuzzyEquals(const physx::PxTransform &a, const physx::PxTransform &b)
+{
+ return qFuzzyCompare(a.p.x, b.p.x) && qFuzzyCompare(a.p.y, b.p.y) && qFuzzyCompare(a.p.z, b.p.z)
+ && qFuzzyCompare(a.q.x, b.q.x) && qFuzzyCompare(a.q.y, b.q.y)
+ && qFuzzyCompare(a.q.z, b.q.z) && qFuzzyCompare(a.q.w, b.q.w);
+}
+
+inline const QQuaternion kMinus90YawRotation = QQuaternion::fromEulerAngles(0, -90, 0);
}
#endif // QPHYSICSUTILS_P_H
diff --git a/src/quick3dphysics/qphysicsworld.cpp b/src/quick3dphysics/qphysicsworld.cpp
index 2376edd..be7c30d 100644
--- a/src/quick3dphysics/qphysicsworld.cpp
+++ b/src/quick3dphysics/qphysicsworld.cpp
@@ -146,8 +146,6 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQuick3dPhysics, "qt.quick3d.physics");
-static const QQuaternion kMinus90YawRotation = QQuaternion::fromEulerAngles(0, -90, 0);
-
/////////////////////////////////////////////////////////////////////////////
class SimulationWorker : public QObject
@@ -204,6 +202,88 @@ private:
/////////////////////////////////////////////////////////////////////////////
+void QPhysicsWorld::DebugModelHolder::releaseMeshPointer()
+{
+ if (auto base = static_cast<physx::PxBase *>(ptr); base)
+ base->release();
+ ptr = nullptr;
+}
+
+const QVector3D &QPhysicsWorld::DebugModelHolder::halfExtents() const
+{
+ return data;
+}
+void QPhysicsWorld::DebugModelHolder::setHalfExtents(const QVector3D &halfExtents)
+{
+ data = halfExtents;
+}
+float QPhysicsWorld::DebugModelHolder::radius() const
+{
+ return data.x();
+}
+void QPhysicsWorld::DebugModelHolder::setRadius(float radius)
+{
+ data.setX(radius);
+}
+float QPhysicsWorld::DebugModelHolder::heightScale() const
+{
+ return data.x();
+}
+void QPhysicsWorld::DebugModelHolder::setHeightScale(float heightScale)
+{
+ data.setX(heightScale);
+}
+float QPhysicsWorld::DebugModelHolder::halfHeight() const
+{
+ return data.y();
+}
+void QPhysicsWorld::DebugModelHolder::setHalfHeight(float halfHeight)
+{
+ data.setY(halfHeight);
+}
+float QPhysicsWorld::DebugModelHolder::rowScale() const
+{
+ return data.y();
+}
+void QPhysicsWorld::DebugModelHolder::setRowScale(float rowScale)
+{
+ data.setY(rowScale);
+}
+float QPhysicsWorld::DebugModelHolder::columnScale() const
+{
+ return data.z();
+}
+void QPhysicsWorld::DebugModelHolder::setColumnScale(float columnScale)
+{
+ data.setZ(columnScale);
+}
+physx::PxConvexMesh *QPhysicsWorld::DebugModelHolder::getConvexMesh()
+{
+ return static_cast<physx::PxConvexMesh *>(ptr);
+}
+void QPhysicsWorld::DebugModelHolder::setConvexMesh(physx::PxConvexMesh *mesh)
+{
+ ptr = static_cast<void *>(mesh);
+}
+physx::PxTriangleMesh *QPhysicsWorld::DebugModelHolder::getTriangleMesh()
+{
+ return static_cast<physx::PxTriangleMesh *>(ptr);
+}
+void QPhysicsWorld::DebugModelHolder::setTriangleMesh(physx::PxTriangleMesh *mesh)
+{
+ ptr = static_cast<void *>(mesh);
+}
+physx::PxHeightField *QPhysicsWorld::DebugModelHolder::getHeightField()
+{
+ return static_cast<physx::PxHeightField *>(ptr);
+}
+void QPhysicsWorld::DebugModelHolder::setHeightField(physx::PxHeightField *hf)
+{
+ ptr = static_cast<physx::PxHeightField *>(hf);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
struct QWorldManager
{
QVector<QPhysicsWorld *> worlds;
@@ -226,18 +306,39 @@ void QPhysicsWorld::deregisterNode(QAbstractPhysicsNode *physicsNode)
{
for (auto world : worldManager.worlds) {
world->m_newPhysicsNodes.removeAll(physicsNode);
+ QMutexLocker locker(&world->m_removedPhysicsNodesMutex);
if (physicsNode->m_backendObject) {
Q_ASSERT(physicsNode->m_backendObject->frontendNode == physicsNode);
physicsNode->m_backendObject->frontendNode = nullptr;
physicsNode->m_backendObject->isRemoved = true;
physicsNode->m_backendObject = nullptr;
}
- QMutexLocker locker(&world->m_removedPhysicsNodesMutex);
world->m_removedPhysicsNodes.insert(physicsNode);
}
worldManager.orphanNodes.removeAll(physicsNode);
}
+void QPhysicsWorld::registerContact(QAbstractPhysicsNode *sender, QAbstractPhysicsNode *receiver,
+ const QVector<QVector3D> &positions,
+ const QVector<QVector3D> &impulses,
+ const QVector<QVector3D> &normals)
+{
+ // Since collision callbacks happen in the physx simulation thread we need
+ // to store these callbacks. Otherwise, if an object is deleted in the same
+ // frame a 'onBodyContact' signal is enqueued and a crash will happen.
+ // Therefore we save these contact callbacks and run them at the end of the
+ // physics frame when we know if the objects are deleted or not.
+
+ BodyContact contact;
+ contact.sender = sender;
+ contact.receiver = receiver;
+ contact.positions = positions;
+ contact.impulses = impulses;
+ contact.normals = normals;
+
+ m_registeredContacts.push_back(contact);
+}
+
QPhysicsWorld::QPhysicsWorld(QObject *parent) : QObject(parent)
{
m_inDesignStudio = !qEnvironmentVariableIsEmpty("QML_PUPPET_MODE");
@@ -369,6 +470,7 @@ void QPhysicsWorld::setViewport(QQuick3DNode *viewport)
m_debugMaterials.clear();
for (auto &holder : m_collisionShapeDebugModels) {
+ holder.releaseMeshPointer();
delete holder.model;
}
m_collisionShapeDebugModels.clear();
@@ -441,11 +543,11 @@ void QPhysicsWorld::updateDebugDraw()
{
if (!(m_forceDebugDraw || m_hasIndividualDebugDraw)) {
// Nothing to draw, trash all previous models (if any) and return
- if (!m_collisionShapeDebugModels.isEmpty()) {
- for (const auto& holder : std::as_const(m_collisionShapeDebugModels))
- delete holder.model;
- m_collisionShapeDebugModels.clear();
+ for (auto &holder : m_collisionShapeDebugModels) {
+ holder.releaseMeshPointer();
+ delete holder.model;
}
+ m_collisionShapeDebugModels.clear();
return;
}
@@ -560,7 +662,7 @@ void QPhysicsWorld::updateDebugDraw()
physXShape->getPlaneGeometry(planeGeometry);
// Special rotation
const QQuaternion rotation =
- kMinus90YawRotation * QPhysicsUtils::toQtType(localPose.q);
+ QPhysicsUtils::kMinus90YawRotation * QPhysicsUtils::toQtType(localPose.q);
localPose = physx::PxTransform(localPose.p, QPhysicsUtils::toPhysXType(rotation));
if (model->geometry() == nullptr) {
@@ -571,17 +673,31 @@ void QPhysicsWorld::updateDebugDraw()
}
break;
+ // For heightfield, convex mesh and triangle mesh we increase its reference count
+ // to make sure it does not get dereferenced and deleted so that the new mesh will
+ // have another memory address so we know when it has changed.
case physx::PxGeometryType::eHEIGHTFIELD: {
physx::PxHeightFieldGeometry heightFieldGeometry;
- physXShape->getHeightFieldGeometry(heightFieldGeometry);
+ bool success = physXShape->getHeightFieldGeometry(heightFieldGeometry);
+ Q_ASSERT(success);
const float heightScale = holder.heightScale();
const float rowScale = holder.rowScale();
const float columnScale = holder.columnScale();
+ if (auto heightField = holder.getHeightField();
+ heightField && heightField != heightFieldGeometry.heightField) {
+ heightField->release();
+ holder.setHeightField(nullptr);
+ }
+
if (!qFuzzyCompare(heightFieldGeometry.heightScale, heightScale)
|| !qFuzzyCompare(heightFieldGeometry.rowScale, rowScale)
- || !qFuzzyCompare(heightFieldGeometry.columnScale, columnScale)) {
-
+ || !qFuzzyCompare(heightFieldGeometry.columnScale, columnScale)
+ || !holder.getHeightField()) {
+ if (!holder.getHeightField()) {
+ heightFieldGeometry.heightField->acquireReference();
+ holder.setHeightField(heightFieldGeometry.heightField);
+ }
auto geom = QDebugDrawHelper::generateHeightFieldGeometry(
heightFieldGeometry.heightField, heightFieldGeometry.heightScale,
heightFieldGeometry.rowScale, heightFieldGeometry.columnScale);
@@ -596,12 +712,23 @@ void QPhysicsWorld::updateDebugDraw()
case physx::PxGeometryType::eCONVEXMESH: {
physx::PxConvexMeshGeometry convexMeshGeometry;
- physXShape->getConvexMeshGeometry(convexMeshGeometry);
+ const bool success = physXShape->getConvexMeshGeometry(convexMeshGeometry);
+ Q_ASSERT(success);
const auto rotation = convexMeshGeometry.scale.rotation * localPose.q;
localPose = physx::PxTransform(localPose.p, rotation);
model->setScale(QPhysicsUtils::toQtType(convexMeshGeometry.scale.scale));
- if (model->geometry() == nullptr) {
+ if (auto convexMesh = holder.getConvexMesh();
+ convexMesh && convexMesh != convexMeshGeometry.convexMesh) {
+ convexMesh->release();
+ holder.setConvexMesh(nullptr);
+ }
+
+ if (!model->geometry() || !holder.getConvexMesh()) {
+ if (!holder.getConvexMesh()) {
+ convexMeshGeometry.convexMesh->acquireReference();
+ holder.setConvexMesh(convexMeshGeometry.convexMesh);
+ }
auto geom = QDebugDrawHelper::generateConvexMeshGeometry(
convexMeshGeometry.convexMesh);
geom->setParent(model);
@@ -612,12 +739,23 @@ void QPhysicsWorld::updateDebugDraw()
case physx::PxGeometryType::eTRIANGLEMESH: {
physx::PxTriangleMeshGeometry triangleMeshGeometry;
- physXShape->getTriangleMeshGeometry(triangleMeshGeometry);
+ const bool success = physXShape->getTriangleMeshGeometry(triangleMeshGeometry);
+ Q_ASSERT(success);
const auto rotation = triangleMeshGeometry.scale.rotation * localPose.q;
localPose = physx::PxTransform(localPose.p, rotation);
model->setScale(QPhysicsUtils::toQtType(triangleMeshGeometry.scale.scale));
- if (model->geometry() == nullptr) {
+ if (auto triangleMesh = holder.getTriangleMesh();
+ triangleMesh && triangleMesh != triangleMeshGeometry.triangleMesh) {
+ triangleMesh->release();
+ holder.setTriangleMesh(nullptr);
+ }
+
+ if (!model->geometry() || !holder.getTriangleMesh()) {
+ if (!holder.getTriangleMesh()) {
+ triangleMeshGeometry.triangleMesh->acquireReference();
+ holder.setTriangleMesh(triangleMeshGeometry.triangleMesh);
+ }
auto geom = QDebugDrawHelper::generateTriangleMeshGeometry(
triangleMeshGeometry.triangleMesh);
geom->setParent(model);
@@ -648,6 +786,7 @@ void QPhysicsWorld::updateDebugDraw()
DebugModelHolder>::iterator it) {
if (!currentCollisionShapes.contains(it.key())) {
auto holder = it.value();
+ holder.releaseMeshPointer();
if (holder.model)
delete holder.model;
return true;
@@ -830,6 +969,7 @@ void QPhysicsWorld::updateDebugDrawDesignStudio()
DebugModelHolder>::iterator it) {
if (!currentCollisionShapes.contains(it.key())) {
auto holder = it.value();
+ holder.releaseMeshPointer();
if (holder.model) {
delete holder.geometry;
delete holder.model;
@@ -976,6 +1116,7 @@ void QPhysicsWorld::initPhysics()
void QPhysicsWorld::frameFinished(float deltaTime)
{
matchOrphanNodes();
+ emitContactCallbacks();
cleanupRemovedNodes();
for (auto *node : std::as_const(m_newPhysicsNodes)) {
auto *body = node->createPhysXBackend();
@@ -1006,6 +1147,7 @@ void QPhysicsWorld::frameFinishedDesignStudio()
{
// Note sure if this is needed but do it anyway
matchOrphanNodes();
+ emitContactCallbacks();
cleanupRemovedNodes();
// Ignore new physics nodes, we find them from the scene node anyway
m_newPhysicsNodes.clear();
@@ -1088,6 +1230,19 @@ void QPhysicsWorld::findPhysicsNodes()
}
}
+void QPhysicsWorld::emitContactCallbacks()
+{
+ for (const QPhysicsWorld::BodyContact &contact : m_registeredContacts) {
+ if (m_removedPhysicsNodes.contains(contact.sender)
+ || m_removedPhysicsNodes.contains(contact.receiver))
+ continue;
+ contact.receiver->registerContact(contact.sender, contact.positions, contact.impulses,
+ contact.normals);
+ }
+
+ m_registeredContacts.clear();
+}
+
physx::PxPhysics *QPhysicsWorld::getPhysics()
{
return StaticPhysXObjects::getReference().physics;
diff --git a/src/quick3dphysics/qphysicsworld_p.h b/src/quick3dphysics/qphysicsworld_p.h
index f36a7b2..f749721 100644
--- a/src/quick3dphysics/qphysicsworld_p.h
+++ b/src/quick3dphysics/qphysicsworld_p.h
@@ -36,6 +36,9 @@ class PxRigidActor;
class PxRigidStatic;
class PxCooking;
class PxControllerManager;
+class PxConvexMesh;
+class PxTriangleMesh;
+class PxHeightField;
}
QT_BEGIN_NAMESPACE
@@ -101,6 +104,10 @@ public:
static void registerNode(QAbstractPhysicsNode *physicsNode);
static void deregisterNode(QAbstractPhysicsNode *physicsNode);
+ void registerContact(QAbstractPhysicsNode *sender, QAbstractPhysicsNode *receiver,
+ const QVector<QVector3D> &positions, const QVector<QVector3D> &impulses,
+ const QVector<QVector3D> &normals);
+
Q_REVISION(6, 5) QQuick3DNode *viewport() const;
void setHasIndividualDebugDraw();
physx::PxControllerManager *controllerManager();
@@ -145,30 +152,52 @@ private:
void disableDebugDraw();
void matchOrphanNodes();
void findPhysicsNodes();
+ void emitContactCallbacks();
+
+ struct BodyContact
+ {
+ QAbstractPhysicsNode *sender = nullptr;
+ QAbstractPhysicsNode *receiver = nullptr;
+ QVector<QVector3D> positions;
+ QVector<QVector3D> impulses;
+ QVector<QVector3D> normals;
+ };
struct DebugModelHolder
{
QQuick3DModel *model = nullptr;
QQuick3DGeometry *geometry = nullptr;
QVector3D data;
+ void *ptr = nullptr;
+
+ void releaseMeshPointer();
+
+ const QVector3D &halfExtents() const;
+ void setHalfExtents(const QVector3D &halfExtents);
+
+ float radius() const;
+ void setRadius(float radius);
+
+ float heightScale() const;
+ void setHeightScale(float heightScale);
- const QVector3D &halfExtents() const { return data; }
- void setHalfExtents(const QVector3D &halfExtents) { data = halfExtents; }
+ float halfHeight() const;
+ void setHalfHeight(float halfHeight);
- float radius() const { return data.x(); }
- void setRadius(float radius) { data.setX(radius); }
+ float rowScale() const;
+ void setRowScale(float rowScale);
- float heightScale() const { return data.x(); }
- void setHeightScale(float heightScale) { data.setX(heightScale); }
+ float columnScale() const;
+ void setColumnScale(float columnScale);
- float halfHeight() const { return data.y(); }
- void setHalfHeight(float halfHeight) { data.setY(halfHeight); }
+ physx::PxConvexMesh *getConvexMesh();
+ void setConvexMesh(physx::PxConvexMesh *mesh);
- float rowScale() const { return data.y(); }
- void setRowScale(float rowScale) { data.setY(rowScale); }
+ physx::PxTriangleMesh *getTriangleMesh();
+ void setTriangleMesh(physx::PxTriangleMesh *mesh);
- float columnScale() const { return data.z(); }
- void setColumnScale(float columnScale) { data.setZ(columnScale); }
+ physx::PxHeightField *getHeightField();
+ void setHeightField(physx::PxHeightField *hf);
};
QList<QAbstractPhysXNode *> m_physXBodies;
@@ -179,6 +208,7 @@ private:
m_collisionShapeDebugModels;
QSet<QAbstractPhysicsNode *> m_removedPhysicsNodes;
QMutex m_removedPhysicsNodesMutex;
+ QList<BodyContact> m_registeredContacts;
QVector3D m_gravity = QVector3D(0.f, -981.f, 0.f);
float m_typicalLength = 100.f; // 100 cm
diff --git a/src/quick3dphysics/qtrianglemeshshape.cpp b/src/quick3dphysics/qtrianglemeshshape.cpp
index f804d48..c8cde78 100644
--- a/src/quick3dphysics/qtrianglemeshshape.cpp
+++ b/src/quick3dphysics/qtrianglemeshshape.cpp
@@ -3,17 +3,6 @@
#include "qtrianglemeshshape_p.h"
-#include <QtQuick3D/QQuick3DGeometry>
-#include <extensions/PxExtensionsAPI.h>
-
-#include "qphysicsmeshutils_p_p.h"
-
-//########################################################################################
-// NOTE:
-// Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are
-// not supported for non-kinematic PxRigidDynamic instances.
-//########################################################################################
-
QT_BEGIN_NAMESPACE
/*!
@@ -42,58 +31,14 @@ QT_BEGIN_NAMESPACE
for details.
*/
-QTriangleMeshShape::QTriangleMeshShape() = default;
-
-QTriangleMeshShape::~QTriangleMeshShape()
-{
- delete m_meshGeometry;
- if (m_mesh)
- QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
-}
-
-physx::PxGeometry *QTriangleMeshShape::getPhysXGeometry()
-{
- if (m_dirtyPhysx || m_scaleDirty) {
- updatePhysXGeometry();
- }
- return m_meshGeometry;
-}
-
-void QTriangleMeshShape::updatePhysXGeometry()
+QMeshShape::MeshType QTriangleMeshShape::shapeType() const
{
- delete m_meshGeometry;
- m_meshGeometry = nullptr;
-
- if (!m_mesh)
- return;
- auto *triangleMesh = m_mesh->triangleMesh();
- if (!triangleMesh)
- return;
-
- auto meshScale = sceneScale();
- physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
- physx::PxQuat(physx::PxIdentity));
-
- m_meshGeometry = new physx::PxTriangleMeshGeometry(triangleMesh, scale);
- m_dirtyPhysx = false;
+ return QMeshShape::MeshType::TRIANGLE;
}
-const QUrl &QTriangleMeshShape::source() const
+bool QTriangleMeshShape::isStaticShape() const
{
- return m_meshSource;
-}
-
-void QTriangleMeshShape::setSource(const QUrl &newSource)
-{
- if (m_meshSource == newSource)
- return;
- m_meshSource = newSource;
- m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource, this);
-
- updatePhysXGeometry();
-
- emit needsRebuild(this);
- emit sourceChanged();
+ return true;
}
QT_END_NAMESPACE
diff --git a/src/quick3dphysics/qtrianglemeshshape_p.h b/src/quick3dphysics/qtrianglemeshshape_p.h
index a974e50..c4f7e36 100644
--- a/src/quick3dphysics/qtrianglemeshshape_p.h
+++ b/src/quick3dphysics/qtrianglemeshshape_p.h
@@ -15,47 +15,16 @@
// We mean it.
//
-#include <QtQuick3DPhysics/qtquick3dphysicsglobal.h>
-#include <QtQuick3DPhysics/private/qabstractcollisionshape_p.h>
-#include <QtCore/QObject>
-#include <QtGui/QVector3D>
-#include <QtQml/QQmlEngine>
-
-namespace physx {
-class PxBoxGeometry;
-class PxTriangleMesh;
-class PxTriangleMeshGeometry;
-}
+#include "qmeshshape_p.h"
QT_BEGIN_NAMESPACE
-class QQuick3DPhysicsMesh;
-class Q_QUICK3DPHYSICS_EXPORT QTriangleMeshShape : public QAbstractCollisionShape
+class Q_QUICK3DPHYSICS_EXPORT QTriangleMeshShape : public QMeshShape
{
Q_OBJECT
- Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged REVISION(6, 5))
-
QML_NAMED_ELEMENT(TriangleMeshShape)
-public:
- QTriangleMeshShape();
- ~QTriangleMeshShape();
-
- physx::PxGeometry *getPhysXGeometry() override;
- Q_REVISION(6, 5) const QUrl &source() const;
- Q_REVISION(6, 5) void setSource(const QUrl &newSource);
- bool isStaticShape() const override { return true; }
-
-signals:
- Q_REVISION(6, 5) void sourceChanged();
-
-private:
- void updatePhysXGeometry();
-
- bool m_dirtyPhysx = false;
- QUrl m_meshSource;
- QQuick3DPhysicsMesh *m_mesh = nullptr;
-
- physx::PxTriangleMeshGeometry *m_meshGeometry = nullptr;
+ virtual QMeshShape::MeshType shapeType() const override;
+ virtual bool isStaticShape() const override;
};
QT_END_NAMESPACE
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index 7a23a0d..17f692b 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(callback_create_delete_node)
add_subdirectory(changescene)
add_subdirectory(character)
add_subdirectory(character_remove)
diff --git a/tests/auto/callback_create_delete_node/Box.qml b/tests/auto/callback_create_delete_node/Box.qml
new file mode 100644
index 0000000..a8137b3
--- /dev/null
+++ b/tests/auto/callback_create_delete_node/Box.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick3D
+import QtQuick3D.Physics
+
+DynamicRigidBody {
+ Model {
+ source: "#Cube"
+ materials: PrincipledMaterial {
+ baseColor: "red"
+ }
+ }
+
+ sendContactReports: true
+ receiveContactReports: true
+ onBodyContact: (body, positions, impulses, normals) => {}
+
+ collisionShapes: BoxShape {}
+}
diff --git a/tests/auto/callback_create_delete_node/CMakeLists.txt b/tests/auto/callback_create_delete_node/CMakeLists.txt
new file mode 100644
index 0000000..2cc871e
--- /dev/null
+++ b/tests/auto/callback_create_delete_node/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+set(PROJECT_NAME "test_auto_callback_create_delete_node")
+
+qt_internal_add_test(${PROJECT_NAME}
+ GUI
+ QMLTEST
+ SOURCES
+ tst_callback_create_delete_node.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Qml
+ TESTDATA
+ tst_callback_create_delete_node.qml
+ Box.qml
+ BUILTIN_TESTDATA
+)
+
+if(QT_BUILD_STANDALONE_TESTS)
+ qt_import_qml_plugins(${PROJECT_NAME})
+endif()
diff --git a/tests/auto/callback_create_delete_node/tst_callback_create_delete_node.cpp b/tests/auto/callback_create_delete_node/tst_callback_create_delete_node.cpp
new file mode 100644
index 0000000..1e4324f
--- /dev/null
+++ b/tests/auto/callback_create_delete_node/tst_callback_create_delete_node.cpp
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtQuickTest/quicktest.h>
+class tst_callback_create_delete_node : public QObject
+{
+ Q_OBJECT
+private slots:
+ void skiptest() { QSKIP("This test will fail, skipping."); };
+};
+int main(int argc, char **argv)
+{
+ if (!qEnvironmentVariableIsEmpty("QEMU_LD_PREFIX")) {
+ qWarning("This test is unstable on QEMU, so it will be skipped.");
+ tst_callback_create_delete_node skip;
+ return QTest::qExec(&skip, argc, argv);
+ }
+ QTEST_SET_MAIN_SOURCE_PATH
+ return quick_test_main(argc, argv, "tst_callback_create_delete_node", QUICK_TEST_SOURCE_DIR);
+}
+#include "tst_callback_create_delete_node.moc"
diff --git a/tests/auto/callback_create_delete_node/tst_callback_create_delete_node.qml b/tests/auto/callback_create_delete_node/tst_callback_create_delete_node.qml
new file mode 100644
index 0000000..c43db93
--- /dev/null
+++ b/tests/auto/callback_create_delete_node/tst_callback_create_delete_node.qml
@@ -0,0 +1,104 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+// Tests that removing and adding objects with active contact callbacks
+// does not crash. QTBUG-121033
+
+import QtCore
+import QtTest
+import QtQuick3D
+import QtQuick3D.Physics
+import QtQuick3D.Physics.Helpers
+import QtQuick
+
+Item {
+ width: 800
+ height: 600
+ visible: true
+
+ PhysicsWorld {
+ scene: viewport.scene
+ }
+
+ View3D {
+ id: viewport
+ width: parent.width
+ height: parent.height
+ focus: true
+
+ environment: SceneEnvironment {
+ antialiasingMode: SceneEnvironment.MSAA
+ backgroundMode: SceneEnvironment.Color
+ clearColor: "#f0f0f0"
+ }
+
+ PerspectiveCamera {
+ id: camera
+ position: Qt.vector3d(-400, 500, 1000)
+ eulerRotation: Qt.vector3d(-20, -20, 0)
+ clipFar: 5000
+ clipNear: 1
+ }
+
+ DirectionalLight {
+ eulerRotation: Qt.vector3d(-45, 45, 0)
+ }
+
+ Node {
+ id: shapeSpawner
+ property var instancesBoxes: []
+ property var boxComponent: Qt.createComponent("Box.qml")
+ property int numSpawns: 0
+
+ function createStack() {
+ let size = 10
+
+ for (var i = 0; i < 3; i++) {
+ for (var j = 0; j < 3; j++) {
+ let center = Qt.vector3d(j*100, 100*i, 0)
+ let box = boxComponent.incubateObject(shapeSpawner, {
+ "position": center,
+ })
+ instancesBoxes.push(box)
+ }
+ }
+
+ numSpawns = numSpawns + 1;
+ }
+
+ function reset() {
+ // Only run method if previous stack has been created fully
+ for (var i = 0; i < instancesBoxes.length; i++)
+ if (!instancesBoxes[i].object)
+ return
+
+ instancesBoxes.forEach(box => {
+ box.object.collisionShapes = []
+ box.object.destroy()
+ })
+ instancesBoxes = []
+
+ shapeSpawner.createStack()
+ }
+ }
+ }
+
+ FrameAnimation {
+ property int frame: 0
+ running: true
+ onTriggered: {
+ frame = frame + 1;
+ if (frame % 2 == 0) {
+ shapeSpawner.reset()
+ }
+ }
+ }
+
+ TestCase {
+ name: "100 cycles"
+ when: shapeSpawner.numSpawns > 100
+ function triggered() {}
+ }
+
+}
+
diff --git a/tests/baseline/data/DebugDrawSharedShapes.qml b/tests/baseline/data/DebugDrawSharedShapes.qml
index a3fd43a..0b9cc3a 100644
--- a/tests/baseline/data/DebugDrawSharedShapes.qml
+++ b/tests/baseline/data/DebugDrawSharedShapes.qml
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick3D
import QtQuick3D.Physics
-Window {
+Rectangle {
width: 640
height: 480
visible: true
diff --git a/tests/baseline/data/ScaledPosition.qml b/tests/baseline/data/ScaledPosition.qml
index 0aa4320..b51dc81 100644
--- a/tests/baseline/data/ScaledPosition.qml
+++ b/tests/baseline/data/ScaledPosition.qml
@@ -2,6 +2,9 @@ import QtQuick
import QtQuick3D
import QtQuick3D.Physics
+// Two bodies where one is scaled 1.5x*2x and the other 3x
+// and they should have the same size.
+
Rectangle {
width: 640
height: 480
@@ -37,6 +40,7 @@ Rectangle {
position: Qt.vector3d(0, -100, 0)
}
Model {
+ scale: Qt.vector3d(0.99, 0.99, 0.99) // just to avoid z-fight
position: Qt.vector3d(0, -100, 0)
source: "#Cube"
materials: PrincipledMaterial {
@@ -54,6 +58,7 @@ Rectangle {
position: Qt.vector3d(0, 100, 0)
}
Model {
+ scale: Qt.vector3d(0.99, 0.99, 0.99) // just to avoid z-fight
position: Qt.vector3d(0, 100, 0)
source: "#Cube"
materials: PrincipledMaterial {
diff --git a/tests/baseline/data/UpdateScaleHeightfield.qml b/tests/baseline/data/UpdateScaleHeightfield.qml
index 181b863..5f41353 100644
--- a/tests/baseline/data/UpdateScaleHeightfield.qml
+++ b/tests/baseline/data/UpdateScaleHeightfield.qml
@@ -13,7 +13,7 @@ Rectangle {
}
Timer {
- interval: 500; running: true; repeat: false
+ interval: 1; running: true; repeat: false
onTriggered: {
tablecloth.scale = Qt.vector3d(10, 10, 10)
}
diff --git a/tools/cooker/CMakeLists.txt b/tools/cooker/CMakeLists.txt
index e956e0e..b2bddc2 100644
--- a/tools/cooker/CMakeLists.txt
+++ b/tools/cooker/CMakeLists.txt
@@ -18,7 +18,7 @@ target_include_directories(${target_name} SYSTEM
../../src/3rdparty/PhysX/include
../../src/3rdparty/PhysX/pxshared/include
)
-if (UNIX)
+if (UNIX OR MINGW)
# Needed for PxPreprocessor.h error
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
qt_internal_extend_target(${target_name} DEFINES _DEBUG)