Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qmeshshape.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include "qmeshshape_p.h"
8
9#include <QtQuick3D/QQuick3DGeometry>
10#include <extensions/PxExtensionsAPI.h>
11
12#include "foundation/PxVec3.h"
13#include "cooking/PxConvexMeshDesc.h"
14#include "cooking/PxCooking.h"
15#include "extensions/PxDefaultStreams.h"
16
17#include <QtQml/qqml.h>
18#include <QtQml/qqmlcontext.h>
19
20#include <QtQuick3DUtils/private/qssgmesh_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
22#include <QtQuick3D/QQuick3DGeometry>
23
24#include "qmeshshape_p.h"
28
30
31static bool attributeBySemantic(const QQuick3DGeometry *geometry,
32 QQuick3DGeometry::Attribute::Semantic semantic,
33 QQuick3DGeometry::Attribute *result)
34{
35 for (int i = 0; i < geometry->attributeCount(); i++) {
36 const auto attr = geometry->attribute(i);
37 if (attr.semantic == semantic) {
38 *result = attr;
39 return true;
40 }
41 }
42
43 return false;
44};
45
46static bool isValidPositionLayout(int offset, int stride, qsizetype bufferSize)
47{
48 const qsizetype positionSize = qsizetype(3 * sizeof(float));
49 if (stride <= 0 || offset < 0 || offset + positionSize > stride || bufferSize < stride) {
50 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry, position attribute (offset"
51 << offset << ") does not fit within stride" << stride << "and buffer size"
52 << bufferSize;
53 return false;
54 }
55 return true;
56}
57
59{
60 forgetStalePhysXMeshes();
61 if (m_convexMesh)
62 m_convexMesh->release();
63 if (m_triangleMesh)
64 m_triangleMesh->release();
65}
66
67// Drops the PhysX meshes if they were destroyed together with the PxPhysics that created them.
68// That happens when the last PhysicsWorld is destroyed while shapes using this mesh remain, so
69// the destructor must not release the meshes again, and the next world must not get them.
70void QQuick3DPhysicsMesh::forgetStalePhysXMeshes()
71{
72 const auto &s_physx = StaticPhysXObjects::getReference();
73 if (m_physicsGeneration == s_physx.physicsGeneration)
74 return;
75 m_convexMesh = nullptr;
76 m_triangleMesh = nullptr;
77 m_physicsGeneration = s_physx.physicsGeneration;
78}
79
81{
82 forgetStalePhysXMeshes();
83 if (m_convexMesh != nullptr)
84 return m_convexMesh;
85
86 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
87 if (thePhysics == nullptr)
88 return nullptr;
89
90 if (m_meshGeometry)
91 return convexMeshGeometrySource();
92 if (!m_meshPath.isEmpty())
93 return convexMeshQmlSource();
94 return nullptr;
95}
96
98{
99 forgetStalePhysXMeshes();
100 if (m_triangleMesh != nullptr)
101 return m_triangleMesh;
102
103 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
104 if (thePhysics == nullptr)
105 return nullptr;
106
107 if (m_meshGeometry)
108 return triangleMeshGeometrySource();
109 if (!m_meshPath.isEmpty())
110 return triangleMeshQmlSource();
111 return nullptr;
112}
113
114physx::PxConvexMesh *QQuick3DPhysicsMesh::convexMeshQmlSource()
115{
116 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
117
118 m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
119 if (m_convexMesh != nullptr)
120 return m_convexMesh;
121
122 m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
123 if (m_convexMesh != nullptr)
124 return m_convexMesh;
125
126 loadSsgMesh();
127
128 if (!m_ssgMesh.isValid())
129 return nullptr;
130
131 const int vStride = m_ssgMesh.vertexBuffer().stride;
132 if (!isValidPositionLayout(m_posOffset, vStride, m_ssgMesh.vertexBuffer().data.size()))
133 return nullptr;
134 const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
135
136 qCDebug(lcQuick3dPhysics) << "prepare cooking" << vCount << "verts";
137
138 physx::PxConvexMeshDesc convexDesc;
139 convexDesc.points.count = vCount;
140 convexDesc.points.stride = vStride;
141 convexDesc.points.data = m_ssgMesh.vertexBuffer().data.constData() + m_posOffset;
142 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
143
144 // NOTE: Since we are making a mesh for the convex hull and are only
145 // interested in the positions we can Skip the index array.
146
147 physx::PxDefaultMemoryOutputStream buf;
148 physx::PxConvexMeshCookingResult::Enum result;
149 const auto *cookingParams = QPhysicsWorld::getCookingParams();
150 if (cookingParams && ::PxCookConvexMesh(*cookingParams, convexDesc, buf, &result)) {
151 auto size = buf.getSize();
152 auto *data = buf.getData();
153 physx::PxDefaultMemoryInputData input(data, size);
154 m_convexMesh = thePhysics->createConvexMesh(input);
155 qCDebug(lcQuick3dPhysics) << "Created convex mesh" << m_convexMesh << "for mesh" << this;
156 QCacheUtils::writeCachedConvexMesh(m_meshPath, buf);
157 } else {
158 qCWarning(lcQuick3dPhysics) << "Could not create convex mesh from" << m_meshPath;
159 }
160
161 return m_convexMesh;
162}
163
164physx::PxConvexMesh *QQuick3DPhysicsMesh::convexMeshGeometrySource()
165{
166 auto vertexBuffer = m_meshGeometry->vertexData();
167
168 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
169 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
170 return nullptr;
171 }
172
173 if (!vertexBuffer.size()) {
174 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
175 return nullptr;
176 }
177
178 QQuick3DGeometry::Attribute vertexAttribute;
179 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
180 &vertexAttribute)) {
181 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
182 return nullptr;
183 }
184 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
185
186 const auto stride = m_meshGeometry->stride();
187 if (!isValidPositionLayout(vertexAttribute.offset, stride, vertexBuffer.size()))
188 return nullptr;
189 const auto numVertices = vertexBuffer.size() / stride;
190
191 physx::PxConvexMeshDesc convexDesc;
192 convexDesc.points.count = numVertices;
193 convexDesc.points.stride = stride;
194 convexDesc.points.data = vertexBuffer.constData() + vertexAttribute.offset;
195 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
196
197 // NOTE: Since we are making a mesh for the convex hull and are only
198 // interested in the positions we can Skip the index array.
199
200 const auto *cookingParams = QPhysicsWorld::getCookingParams();
201 physx::PxDefaultMemoryOutputStream buf;
202 physx::PxConvexMeshCookingResult::Enum result;
203 if (cookingParams && ::PxCookConvexMesh(*cookingParams, convexDesc, buf, &result)) {
204 auto size = buf.getSize();
205 auto *data = buf.getData();
206 physx::PxDefaultMemoryInputData input(data, size);
207 m_convexMesh = QPhysicsWorld::getPhysics()->createConvexMesh(input);
208 qCDebug(lcQuick3dPhysics) << "Created convex mesh" << m_convexMesh << "for mesh" << this;
209 } else {
210 qCWarning(lcQuick3dPhysics) << "Could not create convex mesh for" << this;
211 }
212
213 return m_convexMesh;
214}
215
216physx::PxTriangleMesh *QQuick3DPhysicsMesh::triangleMeshQmlSource()
217{
218 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
219
220 m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
221 if (m_triangleMesh != nullptr)
222 return m_triangleMesh;
223
224 m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
225 if (m_triangleMesh != nullptr)
226 return m_triangleMesh;
227
228 loadSsgMesh();
229 if (!m_ssgMesh.isValid())
230 return nullptr;
231
232 auto vertexBuffer = m_ssgMesh.vertexBuffer().data;
233
234 const int posOffset = m_posOffset;
235 const auto stride = m_ssgMesh.vertexBuffer().stride;
236 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
237 return nullptr;
238 const auto numVertices = vertexBuffer.size() / stride;
239
240 physx::PxTriangleMeshDesc triangleDesc;
241 triangleDesc.points.count = numVertices;
242 triangleDesc.points.stride = stride;
243 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
244
245 auto indexBuffer = m_ssgMesh.indexBuffer().data;
246 if (indexBuffer.size()) {
247 const bool u16IndexType =
248 m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
249
250 Q_ASSERT(m_ssgMesh.indexBuffer().componentType
251 == QSSGMesh::Mesh::ComponentType::UnsignedInt16
252 || m_ssgMesh.indexBuffer().componentType
253 == QSSGMesh::Mesh::ComponentType::UnsignedInt32);
254
255 triangleDesc.triangles.data = indexBuffer.constData();
256 if (u16IndexType) {
257 triangleDesc.flags |= physx::PxMeshFlag::e16_BIT_INDICES;
258 triangleDesc.triangles.stride = sizeof(quint16) * 3;
259 } else {
260 triangleDesc.triangles.stride = sizeof(quint32) * 3;
261 }
262 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
263 }
264
265 physx::PxDefaultMemoryOutputStream buf;
266 physx::PxTriangleMeshCookingResult::Enum result;
267 const auto *cookingParams = QPhysicsWorld::getCookingParams();
268 if (cookingParams && ::PxCookTriangleMesh(*cookingParams, triangleDesc, buf, &result)) {
269 auto size = buf.getSize();
270 auto *data = buf.getData();
271 physx::PxDefaultMemoryInputData input(data, size);
272 m_triangleMesh = thePhysics->createTriangleMesh(input);
273 qCDebug(lcQuick3dPhysics) << "Created triangle mesh" << m_triangleMesh << "for mesh"
274 << this;
276 } else {
277 qCWarning(lcQuick3dPhysics) << "Could not create triangle mesh from" << m_meshPath;
278 }
279
280 return m_triangleMesh;
281}
282
283physx::PxTriangleMesh *QQuick3DPhysicsMesh::triangleMeshGeometrySource()
284{
285 auto vertexBuffer = m_meshGeometry->vertexData();
286
287 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
288 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
289 return nullptr;
290 }
291
292 if (!vertexBuffer.size()) {
293 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
294 return nullptr;
295 }
296
297 QQuick3DGeometry::Attribute vertexAttribute;
298 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
299 &vertexAttribute)) {
300 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
301 return nullptr;
302 }
303 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
304
305 const int posOffset = vertexAttribute.offset;
306 const auto stride = m_meshGeometry->stride();
307 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
308 return nullptr;
309 const auto numVertices = vertexBuffer.size() / stride;
310
311 physx::PxTriangleMeshDesc triangleDesc;
312 triangleDesc.points.count = numVertices;
313 triangleDesc.points.stride = stride;
314 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
315
316 auto indexBuffer = m_meshGeometry->indexData();
317 if (indexBuffer.size()) {
318 QQuick3DGeometry::Attribute indexAttribute;
319 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::IndexSemantic,
320 &indexAttribute)) {
321 qWarning() << "QQuick3DPhysicsMesh: Invalid geometry, index data present but no "
322 "index attribute found.";
323 return nullptr;
324 }
325 const bool u16IndexType =
326 indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
327
328 Q_ASSERT(indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type
329 || indexAttribute.componentType == QQuick3DGeometry::Attribute::U32Type);
330
331 triangleDesc.triangles.data = indexBuffer.constData();
332 if (u16IndexType) {
333 triangleDesc.flags |= physx::PxMeshFlag::e16_BIT_INDICES;
334 triangleDesc.triangles.stride = sizeof(quint16) * 3;
335 } else {
336 triangleDesc.triangles.stride = sizeof(quint32) * 3;
337 }
338 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
339 }
340
341 physx::PxDefaultMemoryOutputStream buf;
342 physx::PxTriangleMeshCookingResult::Enum result;
343 const auto *cookingParams = QPhysicsWorld::getCookingParams();
344 if (cookingParams && ::PxCookTriangleMesh(*cookingParams, triangleDesc, buf, &result)) {
345 auto size = buf.getSize();
346 auto *data = buf.getData();
347 physx::PxDefaultMemoryInputData input(data, size);
348 m_triangleMesh = QPhysicsWorld::getPhysics()->createTriangleMesh(input);
349 qCDebug(lcQuick3dPhysics) << "Created triangle mesh" << m_triangleMesh << "for mesh"
350 << this;
351 } else {
352 qCWarning(lcQuick3dPhysics) << "Could not create triangle mesh for" << this;
353 }
354
355 return m_triangleMesh;
356}
357
358void QQuick3DPhysicsMesh::loadSsgMesh()
359{
360 if (m_ssgMesh.isValid())
361 return;
362
363 // A bit ugly to use QSSGRenderPath here but it is just a wrapper for
364 // a QString and its hash.
365 // Security note: m_meshPath is a user provided file but QSSGBufferManager::loadMeshData is
366 // assumed to handle invalid meshes
367 m_ssgMesh = QSSGBufferManager::loadMeshData(QSSGRenderPath(m_meshPath));
368
369 static const char *compTypes[] = { "Null", "UnsignedInt8", "Int8", "UnsignedInt16",
370 "Int16", "UnsignedInt32", "Int32", "UnsignedInt64",
371 "Int64", "Float16", "Float32", "Float64" };
372
373 qCDebug(lcQuick3dPhysics) << "Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
374 << "draw" << int(m_ssgMesh.drawMode()) << "wind"
375 << int(m_ssgMesh.winding()) << "subs" << m_ssgMesh.subsets().count()
376 << "attrs" << m_ssgMesh.vertexBuffer().entries.count()
377 << m_ssgMesh.vertexBuffer().data.size() << "stride"
378 << m_ssgMesh.vertexBuffer().stride << "verts"
379 << m_ssgMesh.vertexBuffer().data.size()
380 / m_ssgMesh.vertexBuffer().stride;
381
382 for (auto &v : m_ssgMesh.vertexBuffer().entries) {
383 qCDebug(lcQuick3dPhysics) << " attr" << v.name << compTypes[int(v.componentType)] << "cc"
384 << v.componentCount << "offs" << v.offset;
385 Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
386 if (v.name == "attr_pos")
387 m_posOffset = v.offset;
388 }
389
390 if (m_ssgMesh.isValid()) {
391 auto sub = m_ssgMesh.subsets().constFirst();
392 qCDebug(lcQuick3dPhysics) << "..." << sub.name << "count" << sub.count << "bounds"
393 << sub.bounds.min << sub.bounds.max << "offset" << sub.offset;
394 }
395
396#if 0 // EXTRA_DEBUG
397
398 int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
399 int vStride = m_ssgMesh.vertexBuffer().stride;
400 qDebug() << "IDX" << compTypes[int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
401 const auto ib = m_ssgMesh.indexBuffer().data;
402 const auto vb = m_ssgMesh.vertexBuffer().data;
403
404 auto getPoint = [&vb, vStride, this](int idx) -> QVector3D {
405 auto *vp = vb.constData() + vStride * idx + m_posOffset;
406 return *reinterpret_cast<const QVector3D *>(vp);
407 return {};
408 };
409
410 if (iStride == 2) {
411
412 } else {
413 auto *ip = reinterpret_cast<const uint32_t *>(ib.data());
414 int n = ib.size() / iStride;
415 for (int i = 0; i < qMin(50,n); i += 3) {
416
417 qDebug() << " " << ip [i] << ip[i+1] << ip[i+2] << " --- "
418 << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
419 }
420 }
421#endif
422 if (!m_ssgMesh.isValid())
423 qCWarning(lcQuick3dPhysics) << "Could not read mesh from" << m_meshPath;
424}
425
426QQuick3DPhysicsMesh *QQuick3DPhysicsMeshManager::getMesh(const QUrl &source, QObject *contextObject)
427{
428 const QString qmlSource = QQuick3DModel::translateMeshSource(source, contextObject);
429 auto *mesh = sourceMeshHash.value(qmlSource);
430 if (!mesh) {
431 mesh = new QQuick3DPhysicsMesh(qmlSource);
432 sourceMeshHash[qmlSource] = mesh;
433 }
434 mesh->ref();
435 return mesh;
436}
437
439{
440 auto *mesh = geometryMeshHash.value(source);
441 if (!mesh) {
442 mesh = new QQuick3DPhysicsMesh(source);
443 geometryMeshHash.insert(source, mesh);
444 }
445 mesh->ref();
446 return mesh;
447}
448
450{
451 if (mesh == nullptr || mesh->deref() > 0)
452 return;
453
454 qCDebug(lcQuick3dPhysics()) << "deleting mesh" << mesh;
455 erase_if(sourceMeshHash, [mesh](std::pair<const QString &, QQuick3DPhysicsMesh *&> h) {
456 return h.second == mesh;
457 });
458 erase_if(geometryMeshHash, [mesh](std::pair<QQuick3DGeometry *, QQuick3DPhysicsMesh *&> h) {
459 return h.second == mesh;
460 });
461 delete mesh;
462}
463
464QHash<QString, QQuick3DPhysicsMesh *> QQuick3DPhysicsMeshManager::sourceMeshHash;
465QHash<QQuick3DGeometry *, QQuick3DPhysicsMesh *> QQuick3DPhysicsMeshManager::geometryMeshHash;
466
467/////////////////////////////////////////////////////////////////////////////
468
469QMeshShape::~QMeshShape()
470{
471 delete m_convexGeometry;
472 delete m_triangleGeometry;
473 if (m_mesh)
474 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
475}
476
477physx::PxGeometry *QMeshShape::getPhysXGeometry()
478{
479 // The geometry points at a PhysX mesh that dies with the PxPhysics that created it, so a new
480 // world needs a new geometry even when nothing else is dirty.
481 const auto &s_physx = StaticPhysXObjects::getReference();
482 if (m_dirtyPhysx || m_scaleDirty || m_physicsGeneration != s_physx.physicsGeneration)
483 updatePhysXGeometry();
484 if (shapeType() == MeshType::CONVEX)
485 return m_convexGeometry;
486 if (shapeType() == MeshType::TRIANGLE)
487 return m_triangleGeometry;
488
489 Q_UNREACHABLE_RETURN(nullptr);
490}
491
492void QMeshShape::updatePhysXGeometry()
493{
494 delete m_convexGeometry;
495 delete m_triangleGeometry;
496 m_convexGeometry = nullptr;
497 m_triangleGeometry = nullptr;
498 m_physicsGeneration = StaticPhysXObjects::getReference().physicsGeneration;
499
500 if (!m_mesh)
501 return;
502
503 auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() : nullptr;
504 auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() : nullptr;
505 if (!convexMesh && !triangleMesh)
506 return;
507
508 auto meshScale = sceneScale();
509 physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
510 physx::PxQuat(physx::PxIdentity));
511
512 if (!QPhysicsUtils::isFinite(meshScale)
513 || !(convexMesh ? scale.isValidForConvexMesh() : scale.isValidForTriangleMesh())) {
514 qWarning() << "MeshShape: scaled mesh scale" << meshScale << "is invalid, ignoring.";
515 m_dirtyPhysx = false;
516 return;
517 }
518
519 if (convexMesh)
520 m_convexGeometry = new physx::PxConvexMeshGeometry(convexMesh, scale);
521 if (triangleMesh)
522 m_triangleGeometry = new physx::PxTriangleMeshGeometry(triangleMesh, scale);
523
524 m_dirtyPhysx = false;
525}
526
527const QUrl &QMeshShape::source() const
528{
529 return m_meshSource;
530}
531
532void QMeshShape::setSource(const QUrl &newSource)
533{
534 if (m_meshSource == newSource)
535 return;
536 m_meshSource = newSource;
537
538 // If we get a new source and our mesh was from the old source
539 // (meaning it was NOT from a geometry) we deref
540 if (m_geometry == nullptr) {
541 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
542 m_mesh = nullptr;
543 }
544
545 // Load new mesh only if we don't have a geometry as source
546 if (m_geometry == nullptr && !newSource.isEmpty())
547 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource, this);
548
549 updatePhysXGeometry();
550 m_dirtyPhysx = true;
551
552 emit needsRebuild(this);
553 emit sourceChanged();
554}
555
556QQuick3DGeometry *QMeshShape::geometry() const
557{
558 return m_geometry;
559}
560
561void QMeshShape::setGeometry(QQuick3DGeometry *newGeometry)
562{
563 if (m_geometry == newGeometry)
564 return;
565 if (m_geometry)
566 m_geometry->disconnect(this);
567
568 m_geometry = newGeometry;
569
570 if (m_geometry != nullptr) {
571 connect(m_geometry, &QObject::destroyed, this, &QMeshShape::geometryDestroyed);
572 connect(m_geometry, &QQuick3DGeometry::geometryChanged, this,
573 &QMeshShape::geometryContentChanged);
574 }
575
576 // New geometry means we get a new mesh so deref the old one
577 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
578 m_mesh = nullptr;
579 if (m_geometry != nullptr)
580 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
581 else if (!m_meshSource.isEmpty())
582 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource, this);
583
584 updatePhysXGeometry();
585 m_dirtyPhysx = true;
586 emit needsRebuild(this);
587 emit geometryChanged();
588}
589
590void QMeshShape::geometryDestroyed(QObject *geometry)
591{
592 Q_ASSERT(m_geometry == geometry);
593 // Set geometry to null and the old one will be disconnected and dereferenced
594 setGeometry(nullptr);
595}
596
597void QMeshShape::geometryContentChanged()
598{
599 Q_ASSERT(m_geometry != nullptr);
600 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
601 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
602
603 updatePhysXGeometry();
604 m_dirtyPhysx = true;
605 emit needsRebuild(this);
606}
607
static void releaseMesh(QQuick3DPhysicsMesh *mesh)
static QQuick3DPhysicsMesh * getMesh(QQuick3DGeometry *source)
QQuick3DPhysicsMesh(const QQuick3DGeometry *geometrySource)
physx::PxTriangleMesh * triangleMesh()
physx::PxConvexMesh * convexMesh()
void writeCachedConvexMesh(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
void writeCachedTriangleMesh(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
static bool isValidPositionLayout(int offset, int stride, qsizetype bufferSize)
static QT_BEGIN_NAMESPACE bool attributeBySemantic(const QQuick3DGeometry *geometry, QQuick3DGeometry::Attribute::Semantic semantic, QQuick3DGeometry::Attribute *result)
#define QT_BEGIN_NAMESPACE
#define QT_END_NAMESPACE
static StaticPhysXObjects & getReference()