9#include <QtQuick3D/QQuick3DGeometry>
10#include <extensions/PxExtensionsAPI.h>
12#include "foundation/PxVec3.h"
13#include "cooking/PxConvexMeshDesc.h"
14#include "extensions/PxDefaultStreams.h"
16#include <QtQml/qqml.h>
17#include <QtQml/qqmlcontext.h>
19#include <QtQuick3DUtils/private/qssgmesh_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
21#include <QtQuick3D/QQuick3DGeometry>
30 QQuick3DGeometry::Attribute::Semantic semantic,
31 QQuick3DGeometry::Attribute *result)
33 for (
int i = 0; i < geometry->attributeCount(); i++) {
34 const auto attr = geometry->attribute(i);
35 if (attr.semantic == semantic) {
46 const qsizetype positionSize = qsizetype(3 *
sizeof(
float));
47 if (stride <= 0 || offset < 0 || offset + positionSize > stride || bufferSize < stride) {
48 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, position attribute (offset"
49 << offset <<
") does not fit within stride" << stride <<
"and buffer size"
58 if (m_convexMesh !=
nullptr)
61 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
62 if (thePhysics ==
nullptr)
66 return convexMeshGeometrySource();
67 if (!m_meshPath.isEmpty())
68 return convexMeshQmlSource();
74 if (m_triangleMesh !=
nullptr)
75 return m_triangleMesh;
77 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
78 if (thePhysics ==
nullptr)
82 return triangleMeshGeometrySource();
83 if (!m_meshPath.isEmpty())
84 return triangleMeshQmlSource();
90 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
92 m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
93 if (m_convexMesh !=
nullptr)
96 m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
97 if (m_convexMesh !=
nullptr)
102 if (!m_ssgMesh.isValid())
105 const int vStride = m_ssgMesh.vertexBuffer().stride;
106 if (!isValidPositionLayout(m_posOffset, vStride, m_ssgMesh.vertexBuffer().data.size()))
108 const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
110 qCDebug(lcQuick3dPhysics) <<
"prepare cooking" << vCount <<
"verts";
112 physx::PxConvexMeshDesc convexDesc;
113 convexDesc.points.count = vCount;
114 convexDesc.points.stride = vStride;
115 convexDesc.points.data = m_ssgMesh.vertexBuffer().data.constData() + m_posOffset;
116 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
121 physx::PxDefaultMemoryOutputStream buf;
122 physx::PxConvexMeshCookingResult::Enum result;
123 const auto cooking = QPhysicsWorld::getCooking();
124 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
125 auto size = buf.getSize();
126 auto *data = buf.getData();
127 physx::PxDefaultMemoryInputData input(data, size);
128 m_convexMesh = thePhysics->createConvexMesh(input);
129 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
132 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh from" << m_meshPath;
140 auto vertexBuffer = m_meshGeometry->vertexData();
142 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
143 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
147 if (!vertexBuffer.size()) {
148 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
152 QQuick3DGeometry::Attribute vertexAttribute;
153 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
155 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
158 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
160 const auto stride = m_meshGeometry->stride();
161 if (!isValidPositionLayout(vertexAttribute.offset, stride, vertexBuffer.size()))
163 const auto numVertices = vertexBuffer.size() / stride;
165 physx::PxConvexMeshDesc convexDesc;
166 convexDesc.points.count = numVertices;
167 convexDesc.points.stride = stride;
168 convexDesc.points.data = vertexBuffer.constData() + vertexAttribute.offset;
169 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
174 const auto cooking = QPhysicsWorld::getCooking();
175 physx::PxDefaultMemoryOutputStream buf;
176 physx::PxConvexMeshCookingResult::Enum result;
177 if (cooking && cooking->cookConvexMesh(convexDesc, buf, &result)) {
178 auto size = buf.getSize();
179 auto *data = buf.getData();
180 physx::PxDefaultMemoryInputData input(data, size);
181 m_convexMesh = QPhysicsWorld::getPhysics()->createConvexMesh(input);
182 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
184 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh for" <<
this;
192 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
194 m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
195 if (m_triangleMesh !=
nullptr)
196 return m_triangleMesh;
198 m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
199 if (m_triangleMesh !=
nullptr)
200 return m_triangleMesh;
203 if (!m_ssgMesh.isValid())
206 auto vertexBuffer = m_ssgMesh.vertexBuffer().data;
208 const int posOffset = m_posOffset;
209 const auto stride = m_ssgMesh.vertexBuffer().stride;
210 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
212 const auto numVertices = vertexBuffer.size() / stride;
214 physx::PxTriangleMeshDesc triangleDesc;
215 triangleDesc.points.count = numVertices;
216 triangleDesc.points.stride = stride;
217 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
219 auto indexBuffer = m_ssgMesh.indexBuffer().data;
220 if (indexBuffer.size()) {
221 const bool u16IndexType =
222 m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
224 Q_ASSERT(m_ssgMesh.indexBuffer().componentType
225 == QSSGMesh::Mesh::ComponentType::UnsignedInt16
226 || m_ssgMesh.indexBuffer().componentType
227 == QSSGMesh::Mesh::ComponentType::UnsignedInt32);
229 triangleDesc.triangles.data = indexBuffer.constData();
231 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
232 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
234 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
236 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
239 physx::PxDefaultMemoryOutputStream buf;
240 physx::PxTriangleMeshCookingResult::Enum result;
241 const auto cooking = QPhysicsWorld::getCooking();
242 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
243 auto size = buf.getSize();
244 auto *data = buf.getData();
245 physx::PxDefaultMemoryInputData input(data, size);
246 m_triangleMesh = thePhysics->createTriangleMesh(input);
247 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
251 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh from" << m_meshPath;
254 return m_triangleMesh;
259 auto vertexBuffer = m_meshGeometry->vertexData();
261 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
262 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
266 if (!vertexBuffer.size()) {
267 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
271 QQuick3DGeometry::Attribute vertexAttribute;
272 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
274 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
277 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
279 const int posOffset = vertexAttribute.offset;
280 const auto stride = m_meshGeometry->stride();
281 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
283 const auto numVertices = vertexBuffer.size() / stride;
285 physx::PxTriangleMeshDesc triangleDesc;
286 triangleDesc.points.count = numVertices;
287 triangleDesc.points.stride = stride;
288 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
290 auto indexBuffer = m_meshGeometry->indexData();
291 if (indexBuffer.size()) {
292 QQuick3DGeometry::Attribute indexAttribute;
293 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::IndexSemantic,
295 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, index data present but no "
296 "index attribute found.";
299 const bool u16IndexType =
300 indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
302 Q_ASSERT(indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type
303 || indexAttribute.componentType == QQuick3DGeometry::Attribute::U32Type);
305 triangleDesc.triangles.data = indexBuffer.constData();
307 triangleDesc.flags.set(physx::PxMeshFlag::e16_BIT_INDICES);
308 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
310 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
312 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
315 physx::PxDefaultMemoryOutputStream buf;
316 physx::PxTriangleMeshCookingResult::Enum result;
317 const auto cooking = QPhysicsWorld::getCooking();
318 if (cooking && cooking->cookTriangleMesh(triangleDesc, buf, &result)) {
319 auto size = buf.getSize();
320 auto *data = buf.getData();
321 physx::PxDefaultMemoryInputData input(data, size);
322 m_triangleMesh = QPhysicsWorld::getPhysics()->createTriangleMesh(input);
323 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
326 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh for" <<
this;
329 return m_triangleMesh;
334 if (m_ssgMesh.isValid())
341 m_ssgMesh = QSSGBufferManager::loadMeshData(QSSGRenderPath(m_meshPath));
343 static const char *compTypes[] = {
"Null",
"UnsignedInt8",
"Int8",
"UnsignedInt16",
344 "Int16",
"UnsignedInt32",
"Int32",
"UnsignedInt64",
345 "Int64",
"Float16",
"Float32",
"Float64" };
347 qCDebug(lcQuick3dPhysics) <<
"Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
348 <<
"draw" <<
int(m_ssgMesh.drawMode()) <<
"wind"
349 <<
int(m_ssgMesh.winding()) <<
"subs" << m_ssgMesh.subsets().count()
350 <<
"attrs" << m_ssgMesh.vertexBuffer().entries.count()
351 << m_ssgMesh.vertexBuffer().data.size() <<
"stride"
352 << m_ssgMesh.vertexBuffer().stride <<
"verts"
353 << m_ssgMesh.vertexBuffer().data.size()
354 / m_ssgMesh.vertexBuffer().stride;
356 for (
auto &v : m_ssgMesh.vertexBuffer().entries) {
357 qCDebug(lcQuick3dPhysics) <<
" attr" << v.name << compTypes[
int(v.componentType)] <<
"cc"
358 << v.componentCount <<
"offs" << v.offset;
359 Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
360 if (v.name ==
"attr_pos")
361 m_posOffset = v.offset;
364 if (m_ssgMesh.isValid()) {
365 auto sub = m_ssgMesh.subsets().constFirst();
366 qCDebug(lcQuick3dPhysics) <<
"..." << sub.name <<
"count" << sub.count <<
"bounds"
367 << sub.bounds.min << sub.bounds.max <<
"offset" << sub.offset;
372 int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
373 int vStride = m_ssgMesh.vertexBuffer().stride;
374 qDebug() <<
"IDX" << compTypes[
int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
375 const auto ib = m_ssgMesh.indexBuffer().data;
376 const auto vb = m_ssgMesh.vertexBuffer().data;
378 auto getPoint = [&vb, vStride,
this](
int idx) -> QVector3D {
379 auto *vp = vb.constData() + vStride * idx + m_posOffset;
380 return *
reinterpret_cast<
const QVector3D *>(vp);
387 auto *ip =
reinterpret_cast<
const uint32_t *>(ib.data());
388 int n = ib.size() / iStride;
389 for (
int i = 0; i < qMin(50,n); i += 3) {
391 qDebug() <<
" " << ip [i] << ip[i+1] << ip[i+2] <<
" --- "
392 << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
396 if (!m_ssgMesh.isValid())
397 qCWarning(lcQuick3dPhysics) <<
"Could not read mesh from" << m_meshPath;
402 const QString qmlSource = QQuick3DModel::translateMeshSource(source, contextObject);
403 auto *mesh = sourceMeshHash.value(qmlSource);
406 sourceMeshHash[qmlSource] = mesh;
414 auto *mesh = geometryMeshHash.value(source);
417 geometryMeshHash.insert(source, mesh);
425 if (mesh ==
nullptr || mesh
->deref() > 0)
428 qCDebug(lcQuick3dPhysics()) <<
"deleting mesh" << mesh;
429 erase_if(sourceMeshHash, [mesh](std::pair<
const QString &, QQuick3DPhysicsMesh *&> h) {
430 return h.second == mesh;
432 erase_if(geometryMeshHash, [mesh](std::pair<QQuick3DGeometry *, QQuick3DPhysicsMesh *&> h) {
433 return h.second == mesh;
443QMeshShape::~QMeshShape()
445 delete m_convexGeometry;
447 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
450physx::PxGeometry *QMeshShape::getPhysXGeometry()
452 if (m_dirtyPhysx || m_scaleDirty)
453 updatePhysXGeometry();
454 if (shapeType() == MeshType::CONVEX)
455 return m_convexGeometry;
456 if (shapeType() == MeshType::TRIANGLE)
457 return m_triangleGeometry;
459 Q_UNREACHABLE_RETURN(
nullptr);
462void QMeshShape::updatePhysXGeometry()
464 delete m_convexGeometry;
465 delete m_triangleGeometry;
466 m_convexGeometry =
nullptr;
467 m_triangleGeometry =
nullptr;
472 auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() :
nullptr;
473 auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() :
nullptr;
474 if (!convexMesh && !triangleMesh)
477 auto meshScale = sceneScale();
478 physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
479 physx::PxQuat(physx::PxIdentity));
481 if (!QPhysicsUtils::isFinite(meshScale)
482 || !(convexMesh ? scale.isValidForConvexMesh() : scale.isValidForTriangleMesh())) {
483 qWarning() <<
"MeshShape: scaled mesh scale" << meshScale <<
"is invalid, ignoring.";
484 m_dirtyPhysx =
false;
489 m_convexGeometry =
new physx::PxConvexMeshGeometry(convexMesh, scale);
491 m_triangleGeometry =
new physx::PxTriangleMeshGeometry(triangleMesh, scale);
493 m_dirtyPhysx =
false;
496const QUrl &QMeshShape::source()
const
501void QMeshShape::setSource(
const QUrl &newSource)
503 if (m_meshSource == newSource)
505 m_meshSource = newSource;
509 if (m_geometry ==
nullptr) {
510 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
515 if (m_geometry ==
nullptr && !newSource.isEmpty())
516 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
518 updatePhysXGeometry();
521 emit needsRebuild(
this);
522 emit sourceChanged();
525QQuick3DGeometry *QMeshShape::geometry()
const
530void QMeshShape::setGeometry(QQuick3DGeometry *newGeometry)
532 if (m_geometry == newGeometry)
535 m_geometry->disconnect(
this);
537 m_geometry = newGeometry;
539 if (m_geometry !=
nullptr) {
540 connect(m_geometry, &QObject::destroyed,
this, &QMeshShape::geometryDestroyed);
541 connect(m_geometry, &QQuick3DGeometry::geometryChanged,
this,
542 &QMeshShape::geometryContentChanged);
546 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
548 if (m_geometry !=
nullptr)
549 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
550 else if (!m_meshSource.isEmpty())
551 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
553 updatePhysXGeometry();
555 emit needsRebuild(
this);
556 emit geometryChanged();
559void QMeshShape::geometryDestroyed(QObject *geometry)
561 Q_ASSERT(m_geometry == geometry);
563 setGeometry(
nullptr);
566void QMeshShape::geometryContentChanged()
568 Q_ASSERT(m_geometry !=
nullptr);
569 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
570 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
572 updatePhysXGeometry();
574 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 bool isValidPositionLayout(int offset, int stride, qsizetype bufferSize)
static QT_BEGIN_NAMESPACE bool attributeBySemantic(const QQuick3DGeometry *geometry, QQuick3DGeometry::Attribute::Semantic semantic, QQuick3DGeometry::Attribute *result)