8#include <QtQuick3D/QQuick3DGeometry>
9#include <extensions/PxExtensionsAPI.h>
11#include "foundation/PxVec3.h"
12#include "cooking/PxConvexMeshDesc.h"
13#include "extensions/PxDefaultStreams.h"
15#include <QtQml/qqml.h>
16#include <QtQml/qqmlcontext.h>
18#include <QtQuick3DUtils/private/qssgmesh_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
20#include <QtQuick3D/QQuick3DGeometry>
30 QQuick3DGeometry::Attribute::Semantic semantic)
32 for (
int i = 0; i < geometry->attributeCount(); i++) {
33 const auto attr = geometry->attribute(i);
34 if (attr.semantic == semantic)
39 return QQuick3DGeometry::Attribute();
44 if (m_convexMesh !=
nullptr)
47 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
48 if (thePhysics ==
nullptr)
52 return convexMeshGeometrySource();
53 if (!m_meshPath.isEmpty())
54 return convexMeshQmlSource();
60 if (m_triangleMesh !=
nullptr)
61 return m_triangleMesh;
63 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
64 if (thePhysics ==
nullptr)
68 return triangleMeshGeometrySource();
69 if (!m_meshPath.isEmpty())
70 return triangleMeshQmlSource();
76 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
78 m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
79 if (m_convexMesh !=
nullptr)
82 m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
83 if (m_convexMesh !=
nullptr)
88 if (!m_ssgMesh.isValid())
91 const int vStride = m_ssgMesh.vertexBuffer().stride;
92 const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
94 qCDebug(lcQuick3dPhysics) <<
"prepare cooking" << vCount <<
"verts";
96 physx::PxConvexMeshDesc convexDesc;
97 convexDesc.points.count = vCount;
98 convexDesc.points.stride = vStride;
99 convexDesc.points.data = m_ssgMesh.vertexBuffer().data.constData() + m_posOffset;
100 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
105 physx::PxDefaultMemoryOutputStream buf;
106 physx::PxConvexMeshCookingResult::Enum result;
107 const auto cooking = QPhysicsWorld::getCooking();
108 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
109 auto size = buf.getSize();
110 auto *data = buf.getData();
111 physx::PxDefaultMemoryInputData input(data, size);
112 m_convexMesh = thePhysics->createConvexMesh(input);
113 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
116 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh from" << m_meshPath;
124 auto vertexBuffer = m_meshGeometry->vertexData();
126 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
127 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
131 if (!vertexBuffer.size()) {
132 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
136 const auto vertexAttribute =
137 attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic);
138 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
140 const auto stride = m_meshGeometry->stride();
141 const auto numVertices = vertexBuffer.size() / stride;
143 physx::PxConvexMeshDesc convexDesc;
144 convexDesc.points.count = numVertices;
145 convexDesc.points.stride = stride;
146 convexDesc.points.data = vertexBuffer.constData() + vertexAttribute.offset;
147 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
152 const auto cooking = QPhysicsWorld::getCooking();
153 physx::PxDefaultMemoryOutputStream buf;
154 physx::PxConvexMeshCookingResult::Enum result;
155 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
156 auto size = buf.getSize();
157 auto *data = buf.getData();
158 physx::PxDefaultMemoryInputData input(data, size);
159 m_convexMesh = QPhysicsWorld::getPhysics()->createConvexMesh(input);
160 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
162 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh for" <<
this;
170 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
172 m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
173 if (m_triangleMesh !=
nullptr)
174 return m_triangleMesh;
176 m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
177 if (m_triangleMesh !=
nullptr)
178 return m_triangleMesh;
181 if (!m_ssgMesh.isValid())
184 auto vertexBuffer = m_ssgMesh.vertexBuffer().data;
186 const int posOffset = m_posOffset;
187 const auto stride = m_ssgMesh.vertexBuffer().stride;
188 const auto numVertices = vertexBuffer.size() / stride;
190 physx::PxTriangleMeshDesc triangleDesc;
191 triangleDesc.points.count = numVertices;
192 triangleDesc.points.stride = stride;
193 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
195 auto indexBuffer = m_ssgMesh.indexBuffer().data;
196 if (indexBuffer.size()) {
197 const bool u16IndexType =
198 m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
200 Q_ASSERT(m_ssgMesh.indexBuffer().componentType
201 == QSSGMesh::Mesh::ComponentType::UnsignedInt16
202 || m_ssgMesh.indexBuffer().componentType
203 == QSSGMesh::Mesh::ComponentType::UnsignedInt32);
205 triangleDesc.triangles.data = indexBuffer.constData();
207 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
208 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
210 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
212 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
215 physx::PxDefaultMemoryOutputStream buf;
216 physx::PxTriangleMeshCookingResult::Enum result;
217 const auto cooking = QPhysicsWorld::getCooking();
218 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
219 auto size = buf.getSize();
220 auto *data = buf.getData();
221 physx::PxDefaultMemoryInputData input(data, size);
222 m_triangleMesh = thePhysics->createTriangleMesh(input);
223 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
227 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh from" << m_meshPath;
230 return m_triangleMesh;
235 auto vertexBuffer = m_meshGeometry->vertexData();
237 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
238 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
242 if (!vertexBuffer.size()) {
243 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
247 const auto vertexAttribute =
248 attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic);
249 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
251 const int posOffset = vertexAttribute.offset;
252 const auto stride = m_meshGeometry->stride();
253 const auto numVertices = vertexBuffer.size() / stride;
255 physx::PxTriangleMeshDesc triangleDesc;
256 triangleDesc.points.count = numVertices;
257 triangleDesc.points.stride = stride;
258 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
260 auto indexBuffer = m_meshGeometry->indexData();
261 if (indexBuffer.size()) {
262 const auto indexAttribute =
263 attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::IndexSemantic);
264 const bool u16IndexType =
265 indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
267 Q_ASSERT(indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type
268 || indexAttribute.componentType == QQuick3DGeometry::Attribute::U32Type);
270 triangleDesc.triangles.data = indexBuffer.constData();
272 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
273 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
275 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
277 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
280 physx::PxDefaultMemoryOutputStream buf;
281 physx::PxTriangleMeshCookingResult::Enum result;
282 const auto cooking = QPhysicsWorld::getCooking();
283 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
284 auto size = buf.getSize();
285 auto *data = buf.getData();
286 physx::PxDefaultMemoryInputData input(data, size);
287 m_triangleMesh = QPhysicsWorld::getPhysics()->createTriangleMesh(input);
288 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
291 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh for" <<
this;
294 return m_triangleMesh;
299 if (m_ssgMesh.isValid())
306 m_ssgMesh = QSSGBufferManager::loadMeshData(QSSGRenderPath(m_meshPath));
308 static const char *compTypes[] = {
"Null",
"UnsignedInt8",
"Int8",
"UnsignedInt16",
309 "Int16",
"UnsignedInt32",
"Int32",
"UnsignedInt64",
310 "Int64",
"Float16",
"Float32",
"Float64" };
312 qCDebug(lcQuick3dPhysics) <<
"Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
313 <<
"draw" <<
int(m_ssgMesh.drawMode()) <<
"wind"
314 <<
int(m_ssgMesh.winding()) <<
"subs" << m_ssgMesh.subsets().count()
315 <<
"attrs" << m_ssgMesh.vertexBuffer().entries.count()
316 << m_ssgMesh.vertexBuffer().data.size() <<
"stride"
317 << m_ssgMesh.vertexBuffer().stride <<
"verts"
318 << m_ssgMesh.vertexBuffer().data.size()
319 / m_ssgMesh.vertexBuffer().stride;
321 for (
auto &v : m_ssgMesh.vertexBuffer().entries) {
322 qCDebug(lcQuick3dPhysics) <<
" attr" << v.name << compTypes[
int(v.componentType)] <<
"cc"
323 << v.componentCount <<
"offs" << v.offset;
324 Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
325 if (v.name ==
"attr_pos")
326 m_posOffset = v.offset;
329 if (m_ssgMesh.isValid()) {
330 auto sub = m_ssgMesh.subsets().constFirst();
331 qCDebug(lcQuick3dPhysics) <<
"..." << sub.name <<
"count" << sub.count <<
"bounds"
332 << sub.bounds.min << sub.bounds.max <<
"offset" << sub.offset;
337 int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
338 int vStride = m_ssgMesh.vertexBuffer().stride;
339 qDebug() <<
"IDX" << compTypes[
int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
340 const auto ib = m_ssgMesh.indexBuffer().data;
341 const auto vb = m_ssgMesh.vertexBuffer().data;
343 auto getPoint = [&vb, vStride,
this](
int idx) -> QVector3D {
344 auto *vp = vb.constData() + vStride * idx + m_posOffset;
345 return *
reinterpret_cast<
const QVector3D *>(vp);
352 auto *ip =
reinterpret_cast<
const uint32_t *>(ib.data());
353 int n = ib.size() / iStride;
354 for (
int i = 0; i < qMin(50,n); i += 3) {
356 qDebug() <<
" " << ip [i] << ip[i+1] << ip[i+2] <<
" --- "
357 << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
361 if (!m_ssgMesh.isValid())
362 qCWarning(lcQuick3dPhysics) <<
"Could not read mesh from" << m_meshPath;
367 const QString qmlSource = QQuick3DModel::translateMeshSource(source, contextObject);
368 auto *mesh = sourceMeshHash.value(qmlSource);
371 sourceMeshHash[qmlSource] = mesh;
379 auto *mesh = geometryMeshHash.value(source);
382 geometryMeshHash.insert(source, mesh);
390 if (mesh ==
nullptr || mesh
->deref() > 0)
393 qCDebug(lcQuick3dPhysics()) <<
"deleting mesh" << mesh;
394 erase_if(sourceMeshHash, [mesh](std::pair<
const QString &, QQuick3DPhysicsMesh *&> h) {
395 return h.second == mesh;
397 erase_if(geometryMeshHash, [mesh](std::pair<QQuick3DGeometry *, QQuick3DPhysicsMesh *&> h) {
398 return h.second == mesh;
408QMeshShape::~QMeshShape()
410 delete m_convexGeometry;
412 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
415physx::PxGeometry *QMeshShape::getPhysXGeometry()
417 if (m_dirtyPhysx || m_scaleDirty)
418 updatePhysXGeometry();
419 if (shapeType() == MeshType::CONVEX)
420 return m_convexGeometry;
421 if (shapeType() == MeshType::TRIANGLE)
422 return m_triangleGeometry;
424 Q_UNREACHABLE_RETURN(
nullptr);
427void QMeshShape::updatePhysXGeometry()
429 delete m_convexGeometry;
430 delete m_triangleGeometry;
431 m_convexGeometry =
nullptr;
432 m_triangleGeometry =
nullptr;
437 auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() :
nullptr;
438 auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() :
nullptr;
439 if (!convexMesh && !triangleMesh)
442 auto meshScale = sceneScale();
443 physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
444 physx::PxQuat(physx::PxIdentity));
447 m_convexGeometry =
new physx::PxConvexMeshGeometry(convexMesh, scale);
449 m_triangleGeometry =
new physx::PxTriangleMeshGeometry(triangleMesh, scale);
451 m_dirtyPhysx =
false;
454const QUrl &QMeshShape::source()
const
459void QMeshShape::setSource(
const QUrl &newSource)
461 if (m_meshSource == newSource)
463 m_meshSource = newSource;
467 if (m_geometry ==
nullptr) {
468 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
473 if (m_geometry ==
nullptr && !newSource.isEmpty())
474 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
476 updatePhysXGeometry();
479 emit needsRebuild(
this);
480 emit sourceChanged();
483QQuick3DGeometry *QMeshShape::geometry()
const
488void QMeshShape::setGeometry(QQuick3DGeometry *newGeometry)
490 if (m_geometry == newGeometry)
493 m_geometry->disconnect(
this);
495 m_geometry = newGeometry;
497 if (m_geometry !=
nullptr) {
498 connect(m_geometry, &QObject::destroyed,
this, &QMeshShape::geometryDestroyed);
499 connect(m_geometry, &QQuick3DGeometry::geometryChanged,
this,
500 &QMeshShape::geometryContentChanged);
504 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
506 if (m_geometry !=
nullptr)
507 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
508 else if (!m_meshSource.isEmpty())
509 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
511 updatePhysXGeometry();
513 emit needsRebuild(
this);
514 emit geometryChanged();
517void QMeshShape::geometryDestroyed(QObject *geometry)
519 Q_ASSERT(m_geometry == geometry);
521 setGeometry(
nullptr);
524void QMeshShape::geometryContentChanged()
526 Q_ASSERT(m_geometry !=
nullptr);
527 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
528 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
530 updatePhysXGeometry();
532 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)
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE QQuick3DGeometry::Attribute attributeBySemantic(const QQuick3DGeometry *geometry, QQuick3DGeometry::Attribute::Semantic semantic)