7#include <QtQuick3D/QQuick3DGeometry>
8#include <extensions/PxExtensionsAPI.h>
10#include "foundation/PxVec3.h"
11#include "cooking/PxConvexMeshDesc.h"
12#include "extensions/PxDefaultStreams.h"
14#include <QtQml/qqml.h>
15#include <QtQml/qqmlcontext.h>
17#include <QtQuick3DUtils/private/qssgmesh_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
19#include <QtQuick3D/QQuick3DGeometry>
29 QQuick3DGeometry::Attribute::Semantic semantic)
31 for (
int i = 0; i < geometry->attributeCount(); i++) {
32 const auto attr = geometry->attribute(i);
33 if (attr.semantic == semantic)
38 return QQuick3DGeometry::Attribute();
43 if (m_convexMesh !=
nullptr)
46 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
47 if (thePhysics ==
nullptr)
51 return convexMeshGeometrySource();
52 if (!m_meshPath.isEmpty())
53 return convexMeshQmlSource();
59 if (m_triangleMesh !=
nullptr)
60 return m_triangleMesh;
62 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
63 if (thePhysics ==
nullptr)
67 return triangleMeshGeometrySource();
68 if (!m_meshPath.isEmpty())
69 return triangleMeshQmlSource();
75 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
77 m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
78 if (m_convexMesh !=
nullptr)
81 m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
82 if (m_convexMesh !=
nullptr)
87 if (!m_ssgMesh.isValid())
90 const int vStride = m_ssgMesh.vertexBuffer().stride;
91 const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
93 qCDebug(lcQuick3dPhysics) <<
"prepare cooking" << vCount <<
"verts";
95 physx::PxConvexMeshDesc convexDesc;
96 convexDesc.points.count = vCount;
97 convexDesc.points.stride = vStride;
98 convexDesc.points.data = m_ssgMesh.vertexBuffer().data.constData() + m_posOffset;
99 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
104 physx::PxDefaultMemoryOutputStream buf;
105 physx::PxConvexMeshCookingResult::Enum result;
106 const auto cooking = QPhysicsWorld::getCooking();
107 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
108 auto size = buf.getSize();
109 auto *data = buf.getData();
110 physx::PxDefaultMemoryInputData input(data, size);
111 m_convexMesh = thePhysics->createConvexMesh(input);
112 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
113 QCacheUtils::writeCachedConvexMesh(m_meshPath, buf);
115 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh from" << m_meshPath;
123 auto vertexBuffer = m_meshGeometry->vertexData();
125 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
126 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
130 if (!vertexBuffer.size()) {
131 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
135 const auto vertexAttribute =
136 attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic);
137 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
139 const auto stride = m_meshGeometry->stride();
140 const auto numVertices = vertexBuffer.size() / stride;
142 physx::PxConvexMeshDesc convexDesc;
143 convexDesc.points.count = numVertices;
144 convexDesc.points.stride = stride;
145 convexDesc.points.data = vertexBuffer.constData() + vertexAttribute.offset;
146 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
151 const auto cooking = QPhysicsWorld::getCooking();
152 physx::PxDefaultMemoryOutputStream buf;
153 physx::PxConvexMeshCookingResult::Enum result;
154 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
155 auto size = buf.getSize();
156 auto *data = buf.getData();
157 physx::PxDefaultMemoryInputData input(data, size);
158 m_convexMesh = QPhysicsWorld::getPhysics()->createConvexMesh(input);
159 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
161 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh for" <<
this;
169 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
171 m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
172 if (m_triangleMesh !=
nullptr)
173 return m_triangleMesh;
175 m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
176 if (m_triangleMesh !=
nullptr)
177 return m_triangleMesh;
180 if (!m_ssgMesh.isValid())
183 auto vertexBuffer = m_ssgMesh.vertexBuffer().data;
185 const int posOffset = m_posOffset;
186 const auto stride = m_ssgMesh.vertexBuffer().stride;
187 const auto numVertices = vertexBuffer.size() / stride;
189 physx::PxTriangleMeshDesc triangleDesc;
190 triangleDesc.points.count = numVertices;
191 triangleDesc.points.stride = stride;
192 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
194 auto indexBuffer = m_ssgMesh.indexBuffer().data;
195 if (indexBuffer.size()) {
196 const bool u16IndexType =
197 m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
199 Q_ASSERT(m_ssgMesh.indexBuffer().componentType
200 == QSSGMesh::Mesh::ComponentType::UnsignedInt16
201 || m_ssgMesh.indexBuffer().componentType
202 == QSSGMesh::Mesh::ComponentType::UnsignedInt32);
204 triangleDesc.triangles.data = indexBuffer.constData();
206 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
207 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
209 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
211 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
214 physx::PxDefaultMemoryOutputStream buf;
215 physx::PxTriangleMeshCookingResult::Enum result;
216 const auto cooking = QPhysicsWorld::getCooking();
217 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
218 auto size = buf.getSize();
219 auto *data = buf.getData();
220 physx::PxDefaultMemoryInputData input(data, size);
221 m_triangleMesh = thePhysics->createTriangleMesh(input);
222 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
224 QCacheUtils::writeCachedTriangleMesh(m_meshPath, buf);
226 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh from" << m_meshPath;
229 return m_triangleMesh;
234 auto vertexBuffer = m_meshGeometry->vertexData();
236 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
237 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
241 if (!vertexBuffer.size()) {
242 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
246 const auto vertexAttribute =
247 attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic);
248 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
250 const int posOffset = vertexAttribute.offset;
251 const auto stride = m_meshGeometry->stride();
252 const auto numVertices = vertexBuffer.size() / stride;
254 physx::PxTriangleMeshDesc triangleDesc;
255 triangleDesc.points.count = numVertices;
256 triangleDesc.points.stride = stride;
257 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
259 auto indexBuffer = m_meshGeometry->indexData();
260 if (indexBuffer.size()) {
261 const auto indexAttribute =
262 attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::IndexSemantic);
263 const bool u16IndexType =
264 indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
266 Q_ASSERT(indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type
267 || indexAttribute.componentType == QQuick3DGeometry::Attribute::U32Type);
269 triangleDesc.triangles.data = indexBuffer.constData();
271 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
272 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
274 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
276 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
279 physx::PxDefaultMemoryOutputStream buf;
280 physx::PxTriangleMeshCookingResult::Enum result;
281 const auto cooking = QPhysicsWorld::getCooking();
282 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
283 auto size = buf.getSize();
284 auto *data = buf.getData();
285 physx::PxDefaultMemoryInputData input(data, size);
286 m_triangleMesh = QPhysicsWorld::getPhysics()->createTriangleMesh(input);
287 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
290 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh for" <<
this;
293 return m_triangleMesh;
298 if (m_ssgMesh.isValid())
305 m_ssgMesh = QSSGBufferManager::loadMeshData(QSSGRenderPath(m_meshPath));
307 static const char *compTypes[] = {
"Null",
"UnsignedInt8",
"Int8",
"UnsignedInt16",
308 "Int16",
"UnsignedInt32",
"Int32",
"UnsignedInt64",
309 "Int64",
"Float16",
"Float32",
"Float64" };
311 qCDebug(lcQuick3dPhysics) <<
"Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
312 <<
"draw" <<
int(m_ssgMesh.drawMode()) <<
"wind"
313 <<
int(m_ssgMesh.winding()) <<
"subs" << m_ssgMesh.subsets().count()
314 <<
"attrs" << m_ssgMesh.vertexBuffer().entries.count()
315 << m_ssgMesh.vertexBuffer().data.size() <<
"stride"
316 << m_ssgMesh.vertexBuffer().stride <<
"verts"
317 << m_ssgMesh.vertexBuffer().data.size()
318 / m_ssgMesh.vertexBuffer().stride;
320 for (
auto &v : m_ssgMesh.vertexBuffer().entries) {
321 qCDebug(lcQuick3dPhysics) <<
" attr" << v.name << compTypes[
int(v.componentType)] <<
"cc"
322 << v.componentCount <<
"offs" << v.offset;
323 Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
324 if (v.name ==
"attr_pos")
325 m_posOffset = v.offset;
328 if (m_ssgMesh.isValid()) {
329 auto sub = m_ssgMesh.subsets().constFirst();
330 qCDebug(lcQuick3dPhysics) <<
"..." << sub.name <<
"count" << sub.count <<
"bounds"
331 << sub.bounds.min << sub.bounds.max <<
"offset" << sub.offset;
336 int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
337 int vStride = m_ssgMesh.vertexBuffer().stride;
338 qDebug() <<
"IDX" << compTypes[
int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
339 const auto ib = m_ssgMesh.indexBuffer().data;
340 const auto vb = m_ssgMesh.vertexBuffer().data;
342 auto getPoint = [&vb, vStride,
this](
int idx) -> QVector3D {
343 auto *vp = vb.constData() + vStride * idx + m_posOffset;
344 return *
reinterpret_cast<
const QVector3D *>(vp);
351 auto *ip =
reinterpret_cast<
const uint32_t *>(ib.data());
352 int n = ib.size() / iStride;
353 for (
int i = 0; i < qMin(50,n); i += 3) {
355 qDebug() <<
" " << ip [i] << ip[i+1] << ip[i+2] <<
" --- "
356 << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
360 if (!m_ssgMesh.isValid())
361 qCWarning(lcQuick3dPhysics) <<
"Could not read mesh from" << m_meshPath;
366 const QString qmlSource = QQuick3DModel::translateMeshSource(source, contextObject);
367 auto *mesh = sourceMeshHash.value(qmlSource);
370 sourceMeshHash[qmlSource] = mesh;
378 auto *mesh = geometryMeshHash.value(source);
381 geometryMeshHash.insert(source, mesh);
389 if (mesh ==
nullptr || mesh
->deref() > 0)
392 qCDebug(lcQuick3dPhysics()) <<
"deleting mesh" << mesh;
393 erase_if(sourceMeshHash, [mesh](std::pair<
const QString &, QQuick3DPhysicsMesh *&> h) {
394 return h.second == mesh;
396 erase_if(geometryMeshHash, [mesh](std::pair<QQuick3DGeometry *, QQuick3DPhysicsMesh *&> h) {
397 return h.second == mesh;
407QMeshShape::~QMeshShape()
409 delete m_convexGeometry;
411 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
414physx::PxGeometry *QMeshShape::getPhysXGeometry()
416 if (m_dirtyPhysx || m_scaleDirty)
417 updatePhysXGeometry();
418 if (shapeType() == MeshType::CONVEX)
419 return m_convexGeometry;
420 if (shapeType() == MeshType::TRIANGLE)
421 return m_triangleGeometry;
423 Q_UNREACHABLE_RETURN(
nullptr);
426void QMeshShape::updatePhysXGeometry()
428 delete m_convexGeometry;
429 delete m_triangleGeometry;
430 m_convexGeometry =
nullptr;
431 m_triangleGeometry =
nullptr;
436 auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() :
nullptr;
437 auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() :
nullptr;
438 if (!convexMesh && !triangleMesh)
441 auto meshScale = sceneScale();
442 physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
443 physx::PxQuat(physx::PxIdentity));
446 m_convexGeometry =
new physx::PxConvexMeshGeometry(convexMesh, scale);
448 m_triangleGeometry =
new physx::PxTriangleMeshGeometry(triangleMesh, scale);
450 m_dirtyPhysx =
false;
453const QUrl &QMeshShape::source()
const
458void QMeshShape::setSource(
const QUrl &newSource)
460 if (m_meshSource == newSource)
462 m_meshSource = newSource;
466 if (m_geometry ==
nullptr) {
467 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
472 if (m_geometry ==
nullptr && !newSource.isEmpty())
473 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
475 updatePhysXGeometry();
478 emit needsRebuild(
this);
479 emit sourceChanged();
482QQuick3DGeometry *QMeshShape::geometry()
const
487void QMeshShape::setGeometry(QQuick3DGeometry *newGeometry)
489 if (m_geometry == newGeometry)
492 m_geometry->disconnect(
this);
494 m_geometry = newGeometry;
496 if (m_geometry !=
nullptr) {
497 connect(m_geometry, &QObject::destroyed,
this, &QMeshShape::geometryDestroyed);
498 connect(m_geometry, &QQuick3DGeometry::geometryChanged,
this,
499 &QMeshShape::geometryContentChanged);
503 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
505 if (m_geometry !=
nullptr)
506 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
507 else if (!m_meshSource.isEmpty())
508 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
510 updatePhysXGeometry();
512 emit needsRebuild(
this);
513 emit geometryChanged();
516void QMeshShape::geometryDestroyed(QObject *geometry)
518 Q_ASSERT(m_geometry == geometry);
520 setGeometry(
nullptr);
523void QMeshShape::geometryContentChanged()
525 Q_ASSERT(m_geometry !=
nullptr);
526 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
527 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
529 updatePhysXGeometry();
531 emit needsRebuild(
this);
static void releaseMesh(QQuick3DPhysicsMesh *mesh)
static QQuick3DPhysicsMesh * getMesh(QQuick3DGeometry *source)
QQuick3DPhysicsMesh(const QQuick3DGeometry *geometrySource)
physx::PxTriangleMesh * triangleMesh()
physx::PxConvexMesh * convexMesh()
static QT_BEGIN_NAMESPACE QQuick3DGeometry::Attribute attributeBySemantic(const QQuick3DGeometry *geometry, QQuick3DGeometry::Attribute::Semantic semantic)