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>
29 QQuick3DGeometry::Attribute::Semantic semantic,
30 QQuick3DGeometry::Attribute *result)
32 for (
int i = 0; i < geometry->attributeCount(); i++) {
33 const auto attr = geometry->attribute(i);
34 if (attr.semantic == semantic) {
45 if (m_convexMesh !=
nullptr)
48 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
49 if (thePhysics ==
nullptr)
53 return convexMeshGeometrySource();
54 if (!m_meshPath.isEmpty())
55 return convexMeshQmlSource();
61 if (m_triangleMesh !=
nullptr)
62 return m_triangleMesh;
64 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
65 if (thePhysics ==
nullptr)
69 return triangleMeshGeometrySource();
70 if (!m_meshPath.isEmpty())
71 return triangleMeshQmlSource();
77 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
79 m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
80 if (m_convexMesh !=
nullptr)
83 m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
84 if (m_convexMesh !=
nullptr)
89 if (!m_ssgMesh.isValid())
92 const int vStride = m_ssgMesh.vertexBuffer().stride;
93 const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
95 qCDebug(lcQuick3dPhysics) <<
"prepare cooking" << vCount <<
"verts";
97 physx::PxConvexMeshDesc convexDesc;
98 convexDesc.points.count = vCount;
99 convexDesc.points.stride = vStride;
100 convexDesc.points.data = m_ssgMesh.vertexBuffer().data.constData() + m_posOffset;
101 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
106 physx::PxDefaultMemoryOutputStream buf;
107 physx::PxConvexMeshCookingResult::Enum result;
108 const auto cooking = QPhysicsWorld::getCooking();
109 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
110 auto size = buf.getSize();
111 auto *data = buf.getData();
112 physx::PxDefaultMemoryInputData input(data, size);
113 m_convexMesh = thePhysics->createConvexMesh(input);
114 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
117 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh from" << m_meshPath;
125 auto vertexBuffer = m_meshGeometry->vertexData();
127 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
128 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
132 if (!vertexBuffer.size()) {
133 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
137 QQuick3DGeometry::Attribute vertexAttribute;
138 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
140 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
143 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
145 const auto stride = m_meshGeometry->stride();
146 const auto numVertices = vertexBuffer.size() / stride;
148 physx::PxConvexMeshDesc convexDesc;
149 convexDesc.points.count = numVertices;
150 convexDesc.points.stride = stride;
151 convexDesc.points.data = vertexBuffer.constData() + vertexAttribute.offset;
152 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
157 const auto cooking = QPhysicsWorld::getCooking();
158 physx::PxDefaultMemoryOutputStream buf;
159 physx::PxConvexMeshCookingResult::Enum result;
160 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
161 auto size = buf.getSize();
162 auto *data = buf.getData();
163 physx::PxDefaultMemoryInputData input(data, size);
164 m_convexMesh = QPhysicsWorld::getPhysics()->createConvexMesh(input);
165 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
167 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh for" <<
this;
175 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
177 m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
178 if (m_triangleMesh !=
nullptr)
179 return m_triangleMesh;
181 m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
182 if (m_triangleMesh !=
nullptr)
183 return m_triangleMesh;
186 if (!m_ssgMesh.isValid())
189 auto vertexBuffer = m_ssgMesh.vertexBuffer().data;
191 const int posOffset = m_posOffset;
192 const auto stride = m_ssgMesh.vertexBuffer().stride;
193 const auto numVertices = vertexBuffer.size() / stride;
195 physx::PxTriangleMeshDesc triangleDesc;
196 triangleDesc.points.count = numVertices;
197 triangleDesc.points.stride = stride;
198 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
200 auto indexBuffer = m_ssgMesh.indexBuffer().data;
201 if (indexBuffer.size()) {
202 const bool u16IndexType =
203 m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
205 Q_ASSERT(m_ssgMesh.indexBuffer().componentType
206 == QSSGMesh::Mesh::ComponentType::UnsignedInt16
207 || m_ssgMesh.indexBuffer().componentType
208 == QSSGMesh::Mesh::ComponentType::UnsignedInt32);
210 triangleDesc.triangles.data = indexBuffer.constData();
212 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
213 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
215 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
217 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
220 physx::PxDefaultMemoryOutputStream buf;
221 physx::PxTriangleMeshCookingResult::Enum result;
222 const auto cooking = QPhysicsWorld::getCooking();
223 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
224 auto size = buf.getSize();
225 auto *data = buf.getData();
226 physx::PxDefaultMemoryInputData input(data, size);
227 m_triangleMesh = thePhysics->createTriangleMesh(input);
228 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
232 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh from" << m_meshPath;
235 return m_triangleMesh;
240 auto vertexBuffer = m_meshGeometry->vertexData();
242 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
243 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
247 if (!vertexBuffer.size()) {
248 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
252 QQuick3DGeometry::Attribute vertexAttribute;
253 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
255 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
258 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
260 const int posOffset = vertexAttribute.offset;
261 const auto stride = m_meshGeometry->stride();
262 const auto numVertices = vertexBuffer.size() / stride;
264 physx::PxTriangleMeshDesc triangleDesc;
265 triangleDesc.points.count = numVertices;
266 triangleDesc.points.stride = stride;
267 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
269 auto indexBuffer = m_meshGeometry->indexData();
270 if (indexBuffer.size()) {
271 QQuick3DGeometry::Attribute indexAttribute;
272 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::IndexSemantic,
274 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, index data present but no "
275 "index attribute found.";
278 const bool u16IndexType =
279 indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
281 Q_ASSERT(indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type
282 || indexAttribute.componentType == QQuick3DGeometry::Attribute::U32Type);
284 triangleDesc.triangles.data = indexBuffer.constData();
286 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
287 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
289 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
291 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
294 physx::PxDefaultMemoryOutputStream buf;
295 physx::PxTriangleMeshCookingResult::Enum result;
296 const auto cooking = QPhysicsWorld::getCooking();
297 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
298 auto size = buf.getSize();
299 auto *data = buf.getData();
300 physx::PxDefaultMemoryInputData input(data, size);
301 m_triangleMesh = QPhysicsWorld::getPhysics()->createTriangleMesh(input);
302 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
305 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh for" <<
this;
308 return m_triangleMesh;
313 if (m_ssgMesh.isValid())
320 m_ssgMesh = QSSGBufferManager::loadMeshData(QSSGRenderPath(m_meshPath));
322 static const char *compTypes[] = {
"Null",
"UnsignedInt8",
"Int8",
"UnsignedInt16",
323 "Int16",
"UnsignedInt32",
"Int32",
"UnsignedInt64",
324 "Int64",
"Float16",
"Float32",
"Float64" };
326 qCDebug(lcQuick3dPhysics) <<
"Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
327 <<
"draw" <<
int(m_ssgMesh.drawMode()) <<
"wind"
328 <<
int(m_ssgMesh.winding()) <<
"subs" << m_ssgMesh.subsets().count()
329 <<
"attrs" << m_ssgMesh.vertexBuffer().entries.count()
330 << m_ssgMesh.vertexBuffer().data.size() <<
"stride"
331 << m_ssgMesh.vertexBuffer().stride <<
"verts"
332 << m_ssgMesh.vertexBuffer().data.size()
333 / m_ssgMesh.vertexBuffer().stride;
335 for (
auto &v : m_ssgMesh.vertexBuffer().entries) {
336 qCDebug(lcQuick3dPhysics) <<
" attr" << v.name << compTypes[
int(v.componentType)] <<
"cc"
337 << v.componentCount <<
"offs" << v.offset;
338 Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
339 if (v.name ==
"attr_pos")
340 m_posOffset = v.offset;
343 if (m_ssgMesh.isValid()) {
344 auto sub = m_ssgMesh.subsets().constFirst();
345 qCDebug(lcQuick3dPhysics) <<
"..." << sub.name <<
"count" << sub.count <<
"bounds"
346 << sub.bounds.min << sub.bounds.max <<
"offset" << sub.offset;
351 int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
352 int vStride = m_ssgMesh.vertexBuffer().stride;
353 qDebug() <<
"IDX" << compTypes[
int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
354 const auto ib = m_ssgMesh.indexBuffer().data;
355 const auto vb = m_ssgMesh.vertexBuffer().data;
357 auto getPoint = [&vb, vStride,
this](
int idx) -> QVector3D {
358 auto *vp = vb.constData() + vStride * idx + m_posOffset;
359 return *
reinterpret_cast<
const QVector3D *>(vp);
366 auto *ip =
reinterpret_cast<
const uint32_t *>(ib.data());
367 int n = ib.size() / iStride;
368 for (
int i = 0; i < qMin(50,n); i += 3) {
370 qDebug() <<
" " << ip [i] << ip[i+1] << ip[i+2] <<
" --- "
371 << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
375 if (!m_ssgMesh.isValid())
376 qCWarning(lcQuick3dPhysics) <<
"Could not read mesh from" << m_meshPath;
381 const QString qmlSource = QQuick3DModel::translateMeshSource(source, contextObject);
382 auto *mesh = sourceMeshHash.value(qmlSource);
385 sourceMeshHash[qmlSource] = mesh;
393 auto *mesh = geometryMeshHash.value(source);
396 geometryMeshHash.insert(source, mesh);
404 if (mesh ==
nullptr || mesh
->deref() > 0)
407 qCDebug(lcQuick3dPhysics()) <<
"deleting mesh" << mesh;
408 erase_if(sourceMeshHash, [mesh](std::pair<
const QString &, QQuick3DPhysicsMesh *&> h) {
409 return h.second == mesh;
411 erase_if(geometryMeshHash, [mesh](std::pair<QQuick3DGeometry *, QQuick3DPhysicsMesh *&> h) {
412 return h.second == mesh;
422QMeshShape::~QMeshShape()
424 delete m_convexGeometry;
426 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
429physx::PxGeometry *QMeshShape::getPhysXGeometry()
431 if (m_dirtyPhysx || m_scaleDirty)
432 updatePhysXGeometry();
433 if (shapeType() == MeshType::CONVEX)
434 return m_convexGeometry;
435 if (shapeType() == MeshType::TRIANGLE)
436 return m_triangleGeometry;
438 Q_UNREACHABLE_RETURN(
nullptr);
441void QMeshShape::updatePhysXGeometry()
443 delete m_convexGeometry;
444 delete m_triangleGeometry;
445 m_convexGeometry =
nullptr;
446 m_triangleGeometry =
nullptr;
451 auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() :
nullptr;
452 auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() :
nullptr;
453 if (!convexMesh && !triangleMesh)
456 auto meshScale = sceneScale();
457 physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
458 physx::PxQuat(physx::PxIdentity));
461 m_convexGeometry =
new physx::PxConvexMeshGeometry(convexMesh, scale);
463 m_triangleGeometry =
new physx::PxTriangleMeshGeometry(triangleMesh, scale);
465 m_dirtyPhysx =
false;
468const QUrl &QMeshShape::source()
const
473void QMeshShape::setSource(
const QUrl &newSource)
475 if (m_meshSource == newSource)
477 m_meshSource = newSource;
481 if (m_geometry ==
nullptr) {
482 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
487 if (m_geometry ==
nullptr && !newSource.isEmpty())
488 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
490 updatePhysXGeometry();
493 emit needsRebuild(
this);
494 emit sourceChanged();
497QQuick3DGeometry *QMeshShape::geometry()
const
502void QMeshShape::setGeometry(QQuick3DGeometry *newGeometry)
504 if (m_geometry == newGeometry)
507 m_geometry->disconnect(
this);
509 m_geometry = newGeometry;
511 if (m_geometry !=
nullptr) {
512 connect(m_geometry, &QObject::destroyed,
this, &QMeshShape::geometryDestroyed);
513 connect(m_geometry, &QQuick3DGeometry::geometryChanged,
this,
514 &QMeshShape::geometryContentChanged);
518 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
520 if (m_geometry !=
nullptr)
521 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
522 else if (!m_meshSource.isEmpty())
523 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
525 updatePhysXGeometry();
527 emit needsRebuild(
this);
528 emit geometryChanged();
531void QMeshShape::geometryDestroyed(QObject *geometry)
533 Q_ASSERT(m_geometry == geometry);
535 setGeometry(
nullptr);
538void QMeshShape::geometryContentChanged()
540 Q_ASSERT(m_geometry !=
nullptr);
541 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
542 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
544 updatePhysXGeometry();
546 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 bool attributeBySemantic(const QQuick3DGeometry *geometry, QQuick3DGeometry::Attribute::Semantic semantic, QQuick3DGeometry::Attribute *result)