9#include <QtQuick3D/QQuick3DGeometry>
10#include <extensions/PxExtensionsAPI.h>
12#include "foundation/PxVec3.h"
13#include "cooking/PxConvexMeshDesc.h"
14#include "cooking/PxCooking.h"
15#include "extensions/PxDefaultStreams.h"
17#include <QtQml/qqml.h>
18#include <QtQml/qqmlcontext.h>
20#include <QtQuick3DUtils/private/qssgmesh_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
22#include <QtQuick3D/QQuick3DGeometry>
32 QQuick3DGeometry::Attribute::Semantic semantic,
33 QQuick3DGeometry::Attribute *result)
35 for (
int i = 0; i < geometry->attributeCount(); i++) {
36 const auto attr = geometry->attribute(i);
37 if (attr.semantic == semantic) {
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"
60 forgetStalePhysXMeshes();
62 m_convexMesh->release();
64 m_triangleMesh->release();
73 if (m_physicsGeneration == s_physx.physicsGeneration)
75 m_convexMesh =
nullptr;
76 m_triangleMesh =
nullptr;
77 m_physicsGeneration = s_physx.physicsGeneration;
82 forgetStalePhysXMeshes();
83 if (m_convexMesh !=
nullptr)
86 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
87 if (thePhysics ==
nullptr)
91 return convexMeshGeometrySource();
92 if (!m_meshPath.isEmpty())
93 return convexMeshQmlSource();
99 forgetStalePhysXMeshes();
100 if (m_triangleMesh !=
nullptr)
101 return m_triangleMesh;
103 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
104 if (thePhysics ==
nullptr)
108 return triangleMeshGeometrySource();
109 if (!m_meshPath.isEmpty())
110 return triangleMeshQmlSource();
116 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
118 m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
119 if (m_convexMesh !=
nullptr)
122 m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
123 if (m_convexMesh !=
nullptr)
128 if (!m_ssgMesh.isValid())
131 const int vStride = m_ssgMesh.vertexBuffer().stride;
132 if (!isValidPositionLayout(m_posOffset, vStride, m_ssgMesh.vertexBuffer().data.size()))
134 const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
136 qCDebug(lcQuick3dPhysics) <<
"prepare cooking" << vCount <<
"verts";
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;
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;
158 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh from" << m_meshPath;
166 auto vertexBuffer = m_meshGeometry->vertexData();
168 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
169 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
173 if (!vertexBuffer.size()) {
174 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
178 QQuick3DGeometry::Attribute vertexAttribute;
179 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
181 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
184 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
186 const auto stride = m_meshGeometry->stride();
187 if (!isValidPositionLayout(vertexAttribute.offset, stride, vertexBuffer.size()))
189 const auto numVertices = vertexBuffer.size() / stride;
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;
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;
210 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh for" <<
this;
218 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
220 m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
221 if (m_triangleMesh !=
nullptr)
222 return m_triangleMesh;
224 m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
225 if (m_triangleMesh !=
nullptr)
226 return m_triangleMesh;
229 if (!m_ssgMesh.isValid())
232 auto vertexBuffer = m_ssgMesh.vertexBuffer().data;
234 const int posOffset = m_posOffset;
235 const auto stride = m_ssgMesh.vertexBuffer().stride;
236 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
238 const auto numVertices = vertexBuffer.size() / stride;
240 physx::PxTriangleMeshDesc triangleDesc;
241 triangleDesc.points.count = numVertices;
242 triangleDesc.points.stride = stride;
243 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
245 auto indexBuffer = m_ssgMesh.indexBuffer().data;
246 if (indexBuffer.size()) {
247 const bool u16IndexType =
248 m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
250 Q_ASSERT(m_ssgMesh.indexBuffer().componentType
251 == QSSGMesh::Mesh::ComponentType::UnsignedInt16
252 || m_ssgMesh.indexBuffer().componentType
253 == QSSGMesh::Mesh::ComponentType::UnsignedInt32);
255 triangleDesc.triangles.data = indexBuffer.constData();
257 triangleDesc.flags |= physx::PxMeshFlag::e16_BIT_INDICES;
258 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
260 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
262 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
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"
277 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh from" << m_meshPath;
280 return m_triangleMesh;
285 auto vertexBuffer = m_meshGeometry->vertexData();
287 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
288 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
292 if (!vertexBuffer.size()) {
293 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
297 QQuick3DGeometry::Attribute vertexAttribute;
298 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
300 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
303 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
305 const int posOffset = vertexAttribute.offset;
306 const auto stride = m_meshGeometry->stride();
307 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
309 const auto numVertices = vertexBuffer.size() / stride;
311 physx::PxTriangleMeshDesc triangleDesc;
312 triangleDesc.points.count = numVertices;
313 triangleDesc.points.stride = stride;
314 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
316 auto indexBuffer = m_meshGeometry->indexData();
317 if (indexBuffer.size()) {
318 QQuick3DGeometry::Attribute indexAttribute;
319 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::IndexSemantic,
321 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, index data present but no "
322 "index attribute found.";
325 const bool u16IndexType =
326 indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
328 Q_ASSERT(indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type
329 || indexAttribute.componentType == QQuick3DGeometry::Attribute::U32Type);
331 triangleDesc.triangles.data = indexBuffer.constData();
333 triangleDesc.flags |= physx::PxMeshFlag::e16_BIT_INDICES;
334 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
336 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
338 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
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"
352 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh for" <<
this;
355 return m_triangleMesh;
360 if (m_ssgMesh.isValid())
367 m_ssgMesh = QSSGBufferManager::loadMeshData(QSSGRenderPath(m_meshPath));
369 static const char *compTypes[] = {
"Null",
"UnsignedInt8",
"Int8",
"UnsignedInt16",
370 "Int16",
"UnsignedInt32",
"Int32",
"UnsignedInt64",
371 "Int64",
"Float16",
"Float32",
"Float64" };
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;
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;
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;
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;
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);
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) {
417 qDebug() <<
" " << ip [i] << ip[i+1] << ip[i+2] <<
" --- "
418 << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
422 if (!m_ssgMesh.isValid())
423 qCWarning(lcQuick3dPhysics) <<
"Could not read mesh from" << m_meshPath;
428 const QString qmlSource = QQuick3DModel::translateMeshSource(source, contextObject);
429 auto *mesh = sourceMeshHash.value(qmlSource);
432 sourceMeshHash[qmlSource] = mesh;
440 auto *mesh = geometryMeshHash.value(source);
443 geometryMeshHash.insert(source, mesh);
451 if (mesh ==
nullptr || mesh
->deref() > 0)
454 qCDebug(lcQuick3dPhysics()) <<
"deleting mesh" << mesh;
455 erase_if(sourceMeshHash, [mesh](std::pair<
const QString &, QQuick3DPhysicsMesh *&> h) {
456 return h.second == mesh;
458 erase_if(geometryMeshHash, [mesh](std::pair<QQuick3DGeometry *, QQuick3DPhysicsMesh *&> h) {
459 return h.second == mesh;
469QMeshShape::~QMeshShape()
471 delete m_convexGeometry;
472 delete m_triangleGeometry;
474 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
477physx::PxGeometry *QMeshShape::getPhysXGeometry()
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;
489 Q_UNREACHABLE_RETURN(
nullptr);
492void QMeshShape::updatePhysXGeometry()
494 delete m_convexGeometry;
495 delete m_triangleGeometry;
496 m_convexGeometry =
nullptr;
497 m_triangleGeometry =
nullptr;
498 m_physicsGeneration = StaticPhysXObjects::getReference().physicsGeneration;
503 auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() :
nullptr;
504 auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() :
nullptr;
505 if (!convexMesh && !triangleMesh)
508 auto meshScale = sceneScale();
509 physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
510 physx::PxQuat(physx::PxIdentity));
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;
520 m_convexGeometry =
new physx::PxConvexMeshGeometry(convexMesh, scale);
522 m_triangleGeometry =
new physx::PxTriangleMeshGeometry(triangleMesh, scale);
524 m_dirtyPhysx =
false;
527const QUrl &QMeshShape::source()
const
532void QMeshShape::setSource(
const QUrl &newSource)
534 if (m_meshSource == newSource)
536 m_meshSource = newSource;
540 if (m_geometry ==
nullptr) {
541 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
546 if (m_geometry ==
nullptr && !newSource.isEmpty())
547 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
549 updatePhysXGeometry();
552 emit needsRebuild(
this);
553 emit sourceChanged();
556QQuick3DGeometry *QMeshShape::geometry()
const
561void QMeshShape::setGeometry(QQuick3DGeometry *newGeometry)
563 if (m_geometry == newGeometry)
566 m_geometry->disconnect(
this);
568 m_geometry = newGeometry;
570 if (m_geometry !=
nullptr) {
571 connect(m_geometry, &QObject::destroyed,
this, &QMeshShape::geometryDestroyed);
572 connect(m_geometry, &QQuick3DGeometry::geometryChanged,
this,
573 &QMeshShape::geometryContentChanged);
577 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
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);
584 updatePhysXGeometry();
586 emit needsRebuild(
this);
587 emit geometryChanged();
590void QMeshShape::geometryDestroyed(QObject *geometry)
592 Q_ASSERT(m_geometry == geometry);
594 setGeometry(
nullptr);
597void QMeshShape::geometryContentChanged()
599 Q_ASSERT(m_geometry !=
nullptr);
600 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
601 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
603 updatePhysXGeometry();
605 emit needsRebuild(
this);
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
static StaticPhysXObjects & getReference()