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>
31 QQuick3DGeometry::Attribute::Semantic semantic,
32 QQuick3DGeometry::Attribute *result)
34 for (
int i = 0; i < geometry->attributeCount(); i++) {
35 const auto attr = geometry->attribute(i);
36 if (attr.semantic == semantic) {
47 const qsizetype positionSize = qsizetype(3 *
sizeof(
float));
48 if (stride <= 0 || offset < 0 || offset + positionSize > stride || bufferSize < stride) {
49 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, position attribute (offset"
50 << offset <<
") does not fit within stride" << stride <<
"and buffer size"
59 if (m_convexMesh !=
nullptr)
62 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
63 if (thePhysics ==
nullptr)
67 return convexMeshGeometrySource();
68 if (!m_meshPath.isEmpty())
69 return convexMeshQmlSource();
75 if (m_triangleMesh !=
nullptr)
76 return m_triangleMesh;
78 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
79 if (thePhysics ==
nullptr)
83 return triangleMeshGeometrySource();
84 if (!m_meshPath.isEmpty())
85 return triangleMeshQmlSource();
91 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
93 m_convexMesh = QCacheUtils::readCachedConvexMesh(m_meshPath, *thePhysics);
94 if (m_convexMesh !=
nullptr)
97 m_convexMesh = QCacheUtils::readCookedConvexMesh(m_meshPath, *thePhysics);
98 if (m_convexMesh !=
nullptr)
103 if (!m_ssgMesh.isValid())
106 const int vStride = m_ssgMesh.vertexBuffer().stride;
107 if (!isValidPositionLayout(m_posOffset, vStride, m_ssgMesh.vertexBuffer().data.size()))
109 const int vCount = m_ssgMesh.vertexBuffer().data.size() / vStride;
111 qCDebug(lcQuick3dPhysics) <<
"prepare cooking" << vCount <<
"verts";
113 physx::PxConvexMeshDesc convexDesc;
114 convexDesc.points.count = vCount;
115 convexDesc.points.stride = vStride;
116 convexDesc.points.data = m_ssgMesh.vertexBuffer().data.constData() + m_posOffset;
117 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
122 physx::PxDefaultMemoryOutputStream buf;
123 physx::PxConvexMeshCookingResult::Enum result;
124 const auto *cookingParams = QPhysicsWorld::getCookingParams();
125 if (cookingParams && ::PxCookConvexMesh(*cookingParams, convexDesc, buf, &result)) {
126 auto size = buf.getSize();
127 auto *data = buf.getData();
128 physx::PxDefaultMemoryInputData input(data, size);
129 m_convexMesh = thePhysics->createConvexMesh(input);
130 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
133 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh from" << m_meshPath;
141 auto vertexBuffer = m_meshGeometry->vertexData();
143 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
144 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
148 if (!vertexBuffer.size()) {
149 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
153 QQuick3DGeometry::Attribute vertexAttribute;
154 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
156 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
159 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
161 const auto stride = m_meshGeometry->stride();
162 if (!isValidPositionLayout(vertexAttribute.offset, stride, vertexBuffer.size()))
164 const auto numVertices = vertexBuffer.size() / stride;
166 physx::PxConvexMeshDesc convexDesc;
167 convexDesc.points.count = numVertices;
168 convexDesc.points.stride = stride;
169 convexDesc.points.data = vertexBuffer.constData() + vertexAttribute.offset;
170 convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX;
175 const auto *cookingParams = QPhysicsWorld::getCookingParams();
176 physx::PxDefaultMemoryOutputStream buf;
177 physx::PxConvexMeshCookingResult::Enum result;
178 if (cookingParams && ::PxCookConvexMesh(*cookingParams, convexDesc, buf, &result)) {
179 auto size = buf.getSize();
180 auto *data = buf.getData();
181 physx::PxDefaultMemoryInputData input(data, size);
182 m_convexMesh = QPhysicsWorld::getPhysics()->createConvexMesh(input);
183 qCDebug(lcQuick3dPhysics) <<
"Created convex mesh" << m_convexMesh <<
"for mesh" <<
this;
185 qCWarning(lcQuick3dPhysics) <<
"Could not create convex mesh for" <<
this;
193 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
195 m_triangleMesh = QCacheUtils::readCachedTriangleMesh(m_meshPath, *thePhysics);
196 if (m_triangleMesh !=
nullptr)
197 return m_triangleMesh;
199 m_triangleMesh = QCacheUtils::readCookedTriangleMesh(m_meshPath, *thePhysics);
200 if (m_triangleMesh !=
nullptr)
201 return m_triangleMesh;
204 if (!m_ssgMesh.isValid())
207 auto vertexBuffer = m_ssgMesh.vertexBuffer().data;
209 const int posOffset = m_posOffset;
210 const auto stride = m_ssgMesh.vertexBuffer().stride;
211 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
213 const auto numVertices = vertexBuffer.size() / stride;
215 physx::PxTriangleMeshDesc triangleDesc;
216 triangleDesc.points.count = numVertices;
217 triangleDesc.points.stride = stride;
218 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
220 auto indexBuffer = m_ssgMesh.indexBuffer().data;
221 if (indexBuffer.size()) {
222 const bool u16IndexType =
223 m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
225 Q_ASSERT(m_ssgMesh.indexBuffer().componentType
226 == QSSGMesh::Mesh::ComponentType::UnsignedInt16
227 || m_ssgMesh.indexBuffer().componentType
228 == QSSGMesh::Mesh::ComponentType::UnsignedInt32);
230 triangleDesc.triangles.data = indexBuffer.constData();
232 triangleDesc.flags |= physx::PxMeshFlag::e16_BIT_INDICES;
233 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
235 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
237 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
240 physx::PxDefaultMemoryOutputStream buf;
241 physx::PxTriangleMeshCookingResult::Enum result;
242 const auto *cookingParams = QPhysicsWorld::getCookingParams();
243 if (cookingParams && ::PxCookTriangleMesh(*cookingParams, triangleDesc, buf, &result)) {
244 auto size = buf.getSize();
245 auto *data = buf.getData();
246 physx::PxDefaultMemoryInputData input(data, size);
247 m_triangleMesh = thePhysics->createTriangleMesh(input);
248 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
252 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh from" << m_meshPath;
255 return m_triangleMesh;
260 auto vertexBuffer = m_meshGeometry->vertexData();
262 if (m_meshGeometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
263 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry primitive type, must be Triangles. ";
267 if (!vertexBuffer.size()) {
268 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, vertexData is empty. ";
272 QQuick3DGeometry::Attribute vertexAttribute;
273 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::PositionSemantic,
275 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, no position attribute found.";
278 Q_ASSERT(vertexAttribute.componentType == QQuick3DGeometry::Attribute::F32Type);
280 const int posOffset = vertexAttribute.offset;
281 const auto stride = m_meshGeometry->stride();
282 if (!isValidPositionLayout(posOffset, stride, vertexBuffer.size()))
284 const auto numVertices = vertexBuffer.size() / stride;
286 physx::PxTriangleMeshDesc triangleDesc;
287 triangleDesc.points.count = numVertices;
288 triangleDesc.points.stride = stride;
289 triangleDesc.points.data = vertexBuffer.constData() + posOffset;
291 auto indexBuffer = m_meshGeometry->indexData();
292 if (indexBuffer.size()) {
293 QQuick3DGeometry::Attribute indexAttribute;
294 if (!attributeBySemantic(m_meshGeometry, QQuick3DGeometry::Attribute::IndexSemantic,
296 qWarning() <<
"QQuick3DPhysicsMesh: Invalid geometry, index data present but no "
297 "index attribute found.";
300 const bool u16IndexType =
301 indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
303 Q_ASSERT(indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type
304 || indexAttribute.componentType == QQuick3DGeometry::Attribute::U32Type);
306 triangleDesc.triangles.data = indexBuffer.constData();
308 triangleDesc.flags |= physx::PxMeshFlag::e16_BIT_INDICES;
309 triangleDesc.triangles.stride =
sizeof(quint16) * 3;
311 triangleDesc.triangles.stride =
sizeof(quint32) * 3;
313 triangleDesc.triangles.count = indexBuffer.size() / triangleDesc.triangles.stride;
316 physx::PxDefaultMemoryOutputStream buf;
317 physx::PxTriangleMeshCookingResult::Enum result;
318 const auto *cookingParams = QPhysicsWorld::getCookingParams();
319 if (cookingParams && ::PxCookTriangleMesh(*cookingParams, triangleDesc, buf, &result)) {
320 auto size = buf.getSize();
321 auto *data = buf.getData();
322 physx::PxDefaultMemoryInputData input(data, size);
323 m_triangleMesh = QPhysicsWorld::getPhysics()->createTriangleMesh(input);
324 qCDebug(lcQuick3dPhysics) <<
"Created triangle mesh" << m_triangleMesh <<
"for mesh"
327 qCWarning(lcQuick3dPhysics) <<
"Could not create triangle mesh for" <<
this;
330 return m_triangleMesh;
335 if (m_ssgMesh.isValid())
342 m_ssgMesh = QSSGBufferManager::loadMeshData(QSSGRenderPath(m_meshPath));
344 static const char *compTypes[] = {
"Null",
"UnsignedInt8",
"Int8",
"UnsignedInt16",
345 "Int16",
"UnsignedInt32",
"Int32",
"UnsignedInt64",
346 "Int64",
"Float16",
"Float32",
"Float64" };
348 qCDebug(lcQuick3dPhysics) <<
"Loaded SSG mesh from" << m_meshPath << m_ssgMesh.isValid()
349 <<
"draw" <<
int(m_ssgMesh.drawMode()) <<
"wind"
350 <<
int(m_ssgMesh.winding()) <<
"subs" << m_ssgMesh.subsets().count()
351 <<
"attrs" << m_ssgMesh.vertexBuffer().entries.count()
352 << m_ssgMesh.vertexBuffer().data.size() <<
"stride"
353 << m_ssgMesh.vertexBuffer().stride <<
"verts"
354 << m_ssgMesh.vertexBuffer().data.size()
355 / m_ssgMesh.vertexBuffer().stride;
357 for (
auto &v : m_ssgMesh.vertexBuffer().entries) {
358 qCDebug(lcQuick3dPhysics) <<
" attr" << v.name << compTypes[
int(v.componentType)] <<
"cc"
359 << v.componentCount <<
"offs" << v.offset;
360 Q_ASSERT(v.componentType == QSSGMesh::Mesh::ComponentType::Float32);
361 if (v.name ==
"attr_pos")
362 m_posOffset = v.offset;
365 if (m_ssgMesh.isValid()) {
366 auto sub = m_ssgMesh.subsets().constFirst();
367 qCDebug(lcQuick3dPhysics) <<
"..." << sub.name <<
"count" << sub.count <<
"bounds"
368 << sub.bounds.min << sub.bounds.max <<
"offset" << sub.offset;
373 int iStride = m_ssgMesh.indexBuffer().componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16 ? 2 : 4;
374 int vStride = m_ssgMesh.vertexBuffer().stride;
375 qDebug() <<
"IDX" << compTypes[
int(m_ssgMesh.indexBuffer().componentType)] << m_ssgMesh.indexBuffer().data.size() / iStride;
376 const auto ib = m_ssgMesh.indexBuffer().data;
377 const auto vb = m_ssgMesh.vertexBuffer().data;
379 auto getPoint = [&vb, vStride,
this](
int idx) -> QVector3D {
380 auto *vp = vb.constData() + vStride * idx + m_posOffset;
381 return *
reinterpret_cast<
const QVector3D *>(vp);
388 auto *ip =
reinterpret_cast<
const uint32_t *>(ib.data());
389 int n = ib.size() / iStride;
390 for (
int i = 0; i < qMin(50,n); i += 3) {
392 qDebug() <<
" " << ip [i] << ip[i+1] << ip[i+2] <<
" --- "
393 << getPoint(ip[i]) << getPoint(ip[i+1]) << getPoint(ip[i+2]);
397 if (!m_ssgMesh.isValid())
398 qCWarning(lcQuick3dPhysics) <<
"Could not read mesh from" << m_meshPath;
403 const QString qmlSource = QQuick3DModel::translateMeshSource(source, contextObject);
404 auto *mesh = sourceMeshHash.value(qmlSource);
407 sourceMeshHash[qmlSource] = mesh;
415 auto *mesh = geometryMeshHash.value(source);
418 geometryMeshHash.insert(source, mesh);
426 if (mesh ==
nullptr || mesh
->deref() > 0)
429 qCDebug(lcQuick3dPhysics()) <<
"deleting mesh" << mesh;
430 erase_if(sourceMeshHash, [mesh](std::pair<
const QString &, QQuick3DPhysicsMesh *&> h) {
431 return h.second == mesh;
433 erase_if(geometryMeshHash, [mesh](std::pair<QQuick3DGeometry *, QQuick3DPhysicsMesh *&> h) {
434 return h.second == mesh;
444QMeshShape::~QMeshShape()
446 delete m_convexGeometry;
448 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
451physx::PxGeometry *QMeshShape::getPhysXGeometry()
453 if (m_dirtyPhysx || m_scaleDirty)
454 updatePhysXGeometry();
455 if (shapeType() == MeshType::CONVEX)
456 return m_convexGeometry;
457 if (shapeType() == MeshType::TRIANGLE)
458 return m_triangleGeometry;
460 Q_UNREACHABLE_RETURN(
nullptr);
463void QMeshShape::updatePhysXGeometry()
465 delete m_convexGeometry;
466 delete m_triangleGeometry;
467 m_convexGeometry =
nullptr;
468 m_triangleGeometry =
nullptr;
473 auto *convexMesh = shapeType() == MeshType::CONVEX ? m_mesh->convexMesh() :
nullptr;
474 auto *triangleMesh = shapeType() == MeshType::TRIANGLE ? m_mesh->triangleMesh() :
nullptr;
475 if (!convexMesh && !triangleMesh)
478 auto meshScale = sceneScale();
479 physx::PxMeshScale scale(physx::PxVec3(meshScale.x(), meshScale.y(), meshScale.z()),
480 physx::PxQuat(physx::PxIdentity));
482 if (!QPhysicsUtils::isFinite(meshScale)
483 || !(convexMesh ? scale.isValidForConvexMesh() : scale.isValidForTriangleMesh())) {
484 qWarning() <<
"MeshShape: scaled mesh scale" << meshScale <<
"is invalid, ignoring.";
485 m_dirtyPhysx =
false;
490 m_convexGeometry =
new physx::PxConvexMeshGeometry(convexMesh, scale);
492 m_triangleGeometry =
new physx::PxTriangleMeshGeometry(triangleMesh, scale);
494 m_dirtyPhysx =
false;
497const QUrl &QMeshShape::source()
const
502void QMeshShape::setSource(
const QUrl &newSource)
504 if (m_meshSource == newSource)
506 m_meshSource = newSource;
510 if (m_geometry ==
nullptr) {
511 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
516 if (m_geometry ==
nullptr && !newSource.isEmpty())
517 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
519 updatePhysXGeometry();
522 emit needsRebuild(
this);
523 emit sourceChanged();
526QQuick3DGeometry *QMeshShape::geometry()
const
531void QMeshShape::setGeometry(QQuick3DGeometry *newGeometry)
533 if (m_geometry == newGeometry)
536 m_geometry->disconnect(
this);
538 m_geometry = newGeometry;
540 if (m_geometry !=
nullptr) {
541 connect(m_geometry, &QObject::destroyed,
this, &QMeshShape::geometryDestroyed);
542 connect(m_geometry, &QQuick3DGeometry::geometryChanged,
this,
543 &QMeshShape::geometryContentChanged);
547 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
549 if (m_geometry !=
nullptr)
550 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
551 else if (!m_meshSource.isEmpty())
552 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_meshSource,
this);
554 updatePhysXGeometry();
556 emit needsRebuild(
this);
557 emit geometryChanged();
560void QMeshShape::geometryDestroyed(QObject *geometry)
562 Q_ASSERT(m_geometry == geometry);
564 setGeometry(
nullptr);
567void QMeshShape::geometryContentChanged()
569 Q_ASSERT(m_geometry !=
nullptr);
570 QQuick3DPhysicsMeshManager::releaseMesh(m_mesh);
571 m_mesh = QQuick3DPhysicsMeshManager::getMesh(m_geometry);
573 updatePhysXGeometry();
575 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)