10#include <QtCore/qdir.h>
11#include <QtQml/qqmlfile.h>
13#include <QtQuick3D/private/qquick3dobject_p.h>
14#include <QtQuick3D/private/qquick3dgeometry_p.h>
16#include <QtQuick3DUtils/private/qssgutils_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrenderparticles_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
20#include <QtQuick3DUtils/private/qssgmesh_p.h>
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
59QQuick3DParticleModelBlendParticle::QQuick3DParticleModelBlendParticle(QQuick3DNode *parent)
60 : QQuick3DParticle(*
new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::ModelBlendParticle), parent)
62 setFadeInEffect(QQuick3DParticle::FadeNone);
63 setFadeOutEffect(QQuick3DParticle::FadeNone);
64 QQuick3DParticle::doSetMaxAmount(0);
67QQuick3DParticleModelBlendParticle::~QQuick3DParticleModelBlendParticle()
70 delete m_modelGeometry;
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96QQmlComponent *QQuick3DParticleModelBlendParticle::delegate()
const
101void QQuick3DParticleModelBlendParticle::setDelegate(QQmlComponent *delegate)
103 if (delegate == m_delegate)
105 m_delegate = delegate;
109 Q_EMIT delegateChanged();
113
114
115
116
117
118
119QQuick3DNode *QQuick3DParticleModelBlendParticle::endNode()
const
125
126
127
128
129
130
131
132
133
134
135
137
138
139
140
141QQuick3DParticleModelBlendParticle::ModelBlendMode QQuick3DParticleModelBlendParticle::modelBlendMode()
const
143 return m_modelBlendMode;
147
148
149
150
151
152
153
154
155int QQuick3DParticleModelBlendParticle::endTime()
const
161
162
163
164
165
166
167
168
169
170QQuick3DNode *QQuick3DParticleModelBlendParticle::activationNode()
const
172 return m_activationNode;
176
177
178
179
180
181
182
183
184
185
186
188
189
190
191
192QQuick3DParticleModelBlendParticle::ModelBlendEmitMode QQuick3DParticleModelBlendParticle::emitMode()
const
197void QQuick3DParticleModelBlendParticle::setEndNode(QQuick3DNode *node)
199 if (m_endNode == node)
202 QObject::disconnect(
this);
207 QObject::connect(m_endNode, &QQuick3DNode::positionChanged,
this, &QQuick3DParticleModelBlendParticle::handleEndNodeChanged);
208 QObject::connect(m_endNode, &QQuick3DNode::rotationChanged,
this, &QQuick3DParticleModelBlendParticle::handleEndNodeChanged);
209 QObject::connect(m_endNode, &QQuick3DNode::scaleChanged,
this, &QQuick3DParticleModelBlendParticle::handleEndNodeChanged);
211 handleEndNodeChanged();
212 Q_EMIT endNodeChanged();
215void QQuick3DParticleModelBlendParticle::setModelBlendMode(ModelBlendMode mode)
217 if (m_modelBlendMode == mode)
219 m_modelBlendMode = mode;
221 Q_EMIT modelBlendModeChanged();
224void QQuick3DParticleModelBlendParticle::setEndTime(
int endTime)
226 if (endTime == m_endTime)
229 Q_EMIT endTimeChanged();
232void QQuick3DParticleModelBlendParticle::setActivationNode(QQuick3DNode *activationNode)
234 if (m_activationNode == activationNode)
237 m_activationNode = activationNode;
238 Q_EMIT activationNodeChanged();
241void QQuick3DParticleModelBlendParticle::setEmitMode(ModelBlendEmitMode emitMode)
243 if (m_emitMode == emitMode)
246 m_emitMode = emitMode;
247 Q_EMIT emitModeChanged();
250void QQuick3DParticleModelBlendParticle::regenerate()
255 if (!isComponentComplete())
261 if (QQuick3DParticleSystem::isGloballyDisabled())
264 auto *obj = m_delegate->create(m_delegate->creationContext());
266 m_model = qobject_cast<QQuick3DModel *>(obj);
269 auto *psystem = QQuick3DParticle::system();
270 m_model->setParent(psystem);
271 m_model->setParentItem(psystem);
275 handleEndNodeChanged();
280 QString src = source;
281 if (source.startsWith(QLatin1Char(
'#'))) {
282 src = QSSGBufferManager::primitivePath(source);
283 src.prepend(QLatin1String(
":/"));
285 src = QDir::cleanPath(src);
286 if (src.startsWith(QLatin1String(
"qrc:/")))
290 if (!file.open(QFile::ReadOnly))
292 return QSSGMesh::Mesh::loadMesh(&file);
297 const quint8 *vertex = srcVertices + idx * vertexStride;
298 return *
reinterpret_cast<
const QVector3D *>(vertex + posOffset);
301static float calcTriangleRadius(
const QVector3D ¢er,
const QVector3D &p0,
const QVector3D &p1,
const QVector3D &p2)
303 return qMax(center.distanceToPoint(p1), qMax(center.distanceToPoint(p2), center.distanceToPoint(p0)));
307 QVector<QVector3D> ¢erData,
308 float &maxTriangleRadius,
309 const QByteArray &vertexBufferData,
310 quint32 vertexStride,
312 const QByteArray &indexBufferData,
314 quint32 primitiveCount)
316 const quint8 *srcVertices =
reinterpret_cast<
const quint8 *>(vertexBufferData.data());
317 quint8 *dst =
reinterpret_cast<quint8 *>(unindexedVertexData.data());
318 const quint16 *indexData16 =
reinterpret_cast<
const quint16 *>(indexBufferData.begin());
319 const quint32 *indexData32 =
reinterpret_cast<
const quint32 *>(indexBufferData.begin());
320 const float c_div3 = 1.0f / 3.0f;
321 for (quint32 i = 0; i < primitiveCount; i++) {
324 i0 = indexData16[3 * i];
325 i1 = indexData16[3 * i + 1];
326 i2 = indexData16[3 * i + 2];
328 i0 = indexData32[3 * i];
329 i1 = indexData32[3 * i + 1];
330 i2 = indexData32[3 * i + 2];
332 QVector3D p0 = getPosition(srcVertices, i0, vertexStride, posOffset);
333 QVector3D p1 = getPosition(srcVertices, i1, vertexStride, posOffset);
334 QVector3D p2 = getPosition(srcVertices, i2, vertexStride, posOffset);
335 QVector3D center = (p0 + p1 + p2) * c_div3;
336 centerData[i] = center;
337 maxTriangleRadius = qMax(maxTriangleRadius, calcTriangleRadius(center, p0, p1, p2));
338 memcpy(dst, srcVertices + i0 * vertexStride, vertexStride);
340 memcpy(dst, srcVertices + i1 * vertexStride, vertexStride);
342 memcpy(dst, srcVertices + i2 * vertexStride, vertexStride);
348 float &maxTriangleRadius,
349 const QByteArray &vertexBufferData,
350 quint32 vertexStride,
352 quint32 primitiveCount)
354 const quint8 *srcVertices =
reinterpret_cast<
const quint8 *>(vertexBufferData.data());
355 const float c_div3 = 1.0f / 3.0f;
356 for (quint32 i = 0; i < primitiveCount; i++) {
357 QVector3D p0 = getPosition(srcVertices, 3 * i, vertexStride, posOffset);
358 QVector3D p1 = getPosition(srcVertices, 3 * i + 1, vertexStride, posOffset);
359 QVector3D p2 = getPosition(srcVertices, 3 * i + 2, vertexStride, posOffset);
360 QVector3D center = (p0 + p1 + p2) * c_div3;
361 centerData[i] = center;
362 maxTriangleRadius = qMax(maxTriangleRadius, calcTriangleRadius(center, p0, p1, p2));
366void QQuick3DParticleModelBlendParticle::updateParticles()
368 m_maxTriangleRadius = 0.f;
373 if (m_model->geometry()) {
374 QQuick3DGeometry *geometry = m_model->geometry();
375 if (geometry->primitiveType() != QQuick3DGeometry::PrimitiveType::Triangles) {
376 qWarning () <<
"ModelBlendParticle3D: Invalid geometry primitive type, must be Triangles. ";
379 auto vertexBuffer = geometry->vertexData();
380 auto indexBuffer = geometry->indexData();
382 if (!vertexBuffer.size()) {
383 qWarning () <<
"ModelBlendParticle3D: Invalid geometry, vertexData is empty. ";
387 const auto attributeBySemantic = [&](
const QQuick3DGeometry *geometry, QQuick3DGeometry::Attribute::Semantic semantic) {
388 for (
int i = 0; i < geometry->attributeCount(); i++) {
389 const auto attr = geometry->attribute(i);
390 if (attr.semantic == semantic)
394 return QQuick3DGeometry::Attribute();
397 if (indexBuffer.size()) {
398 m_modelGeometry =
new QQuick3DGeometry;
400 m_modelGeometry->setBounds(geometry->boundsMin(), geometry->boundsMax());
401 m_modelGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
402 m_modelGeometry->setStride(geometry->stride());
404 for (
int i = 0; i < geometry->attributeCount(); i++) {
405 auto attr = geometry->attribute(i);
406 if (attr.semantic != QQuick3DGeometry::Attribute::IndexSemantic)
407 m_modelGeometry->addAttribute(attr);
411 QByteArray unindexedVertexData;
412 quint32 primitiveCount = indexBuffer.size();
413 auto indexAttribute = attributeBySemantic(geometry, QQuick3DGeometry::Attribute::IndexSemantic);
414 bool u16IndexType = indexAttribute.componentType == QQuick3DGeometry::Attribute::U16Type;
418 primitiveCount /= 12;
420 unindexedVertexData.resize(geometry->stride() * primitiveCount * 3);
421 m_centerData.resize(primitiveCount);
422 m_particleCount = primitiveCount;
423 copyToUnindexedVertices(unindexedVertexData,
428 attributeBySemantic(geometry, QQuick3DGeometry::Attribute::PositionSemantic).offset,
433 m_modelGeometry->setVertexData(unindexedVertexData);
434 m_model->setGeometry(m_modelGeometry);
437 quint32 primitiveCount = vertexBuffer.size() / geometry->stride() / 3;
438 m_centerData.resize(primitiveCount);
439 m_particleCount = primitiveCount;
440 getVertexCenterData(m_centerData,
444 attributeBySemantic(geometry, QQuick3DGeometry::Attribute::PositionSemantic).offset,
448 const QQmlContext *context = qmlContext(
this);
449 QString src = m_model->source().toString();
450 if (context && !src.startsWith(QLatin1Char(
'#')))
451 src = QQmlFile::urlToLocalFileOrQrc(context->resolvedUrl(m_model->source()));
452 QSSGMesh::Mesh mesh = loadModelBlendParticleMesh(src);
453 if (!mesh.isValid()) {
454 qWarning () <<
"ModelBlendParticle3D: Unable to load mesh: " << src;
457 if (mesh.drawMode() != QSSGMesh::Mesh::DrawMode::Triangles) {
458 qWarning () <<
"ModelBlendParticle3D: Invalid mesh primitive type, must be Triangles. ";
462 m_modelGeometry =
new QQuick3DGeometry;
464 const auto vertexBuffer = mesh.vertexBuffer();
465 const auto indexBuffer = mesh.indexBuffer();
467 const auto entryOffset = [&](
const QSSGMesh::Mesh::VertexBuffer &vb,
const QByteArray &name) ->
int {
468 for (
const auto &e : vb.entries) {
469 if (e.name == name) {
470 Q_ASSERT(e.componentType == QSSGMesh::Mesh::ComponentType::Float32);
477 const auto toAttribute = [&](
const QSSGMesh::Mesh::VertexBufferEntry &e) -> QQuick3DGeometry::Attribute {
478 QQuick3DGeometry::Attribute a;
479 a.componentType = QQuick3DGeometryPrivate::toComponentType(e.componentType);
481 a.semantic = QQuick3DGeometryPrivate::semanticFromName(e.name);
485 const auto indexedPrimitiveCount = [&](
const QSSGMesh::Mesh::IndexBuffer &indexBuffer) -> quint32 {
486 if (indexBuffer.componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16)
487 return quint32(indexBuffer.data.size() /
sizeof(quint16) / 3);
488 return quint32(indexBuffer.data.size() /
sizeof(quint32) / 3);
491 if (indexBuffer.data.size()) {
493 QByteArray unindexedVertexData;
494 quint32 primitiveCount = indexedPrimitiveCount(indexBuffer);
495 bool u16IndexType = indexBuffer.componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16;
496 unindexedVertexData.resize(vertexBuffer.stride * primitiveCount * 3);
497 m_centerData.resize(primitiveCount);
498 m_particleCount = primitiveCount;
500 copyToUnindexedVertices(unindexedVertexData,
505 entryOffset(vertexBuffer, QByteArray(QSSGMesh::MeshInternal::getPositionAttrName())),
509 m_modelGeometry->setBounds(mesh.subsets().first().bounds.min, mesh.subsets().first().bounds.max);
510 m_modelGeometry->setStride(vertexBuffer.stride);
511 m_modelGeometry->setVertexData(unindexedVertexData);
512 m_modelGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
515 quint32 primitiveCount = vertexBuffer.data.size() / vertexBuffer.stride / 3;
516 m_centerData.resize(primitiveCount);
517 m_particleCount = primitiveCount;
518 getVertexCenterData(m_centerData,
522 entryOffset(vertexBuffer, QByteArray(QSSGMesh::MeshInternal::getPositionAttrName())),
524 m_modelGeometry->setBounds(mesh.subsets().first().bounds.min, mesh.subsets().first().bounds.max);
525 m_modelGeometry->setStride(vertexBuffer.stride);
526 m_modelGeometry->setVertexData(vertexBuffer.data);
527 m_modelGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
529 for (
auto &e : vertexBuffer.entries)
530 m_modelGeometry->addAttribute(toAttribute(e));
531 for (
auto &s : mesh.subsets())
532 m_modelGeometry->addSubset(s.offset, s.count, s.bounds.min, s.bounds.max, s.name);
534 m_model->setSource({});
535 m_model->setGeometry(m_modelGeometry);
538 QMatrix4x4 transform = m_model->sceneTransform();
539 if (m_model->parentNode())
540 transform = m_model->parentNode()->sceneTransform().inverted() * transform;
541 const QVector3D scale = QSSGUtils::mat44::getScale(transform);
543 const float scaleMax = qMax(scale.x(), qMax(scale.y(), scale.z()));
544 m_maxTriangleRadius *= scaleMax;
546 m_triangleParticleData.resize(m_particleCount);
547 m_particleData.resize(m_particleCount);
548 m_particleData.fill({});
549 for (
int i = 0; i < m_particleCount; i++) {
550 m_triangleParticleData[i].center = m_centerData[i];
551 m_centerData[i] = transform.map(m_centerData[i]);
552 if (m_modelBlendMode == Construct) {
553 m_triangleParticleData[i].size = 0.0f;
555 m_triangleParticleData[i].size = 1.0f;
556 m_triangleParticleData[i].position = m_centerData[i];
559 QQuick3DParticle::doSetMaxAmount(m_particleCount);
562QSSGRenderGraphObject *QQuick3DParticleModelBlendParticle::updateSpatialNode(QSSGRenderGraphObject *node)
566 auto *spatialNode = QQuick3DObjectPrivate::get(m_model)->spatialNode;
568 spatialNode = QQuick3DObjectPrivate::updateSpatialNode(m_model,
nullptr);
569 QQuick3DObjectPrivate::get(m_model)->spatialNode = spatialNode;
570 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(
this, spatialNode);
572#if QT_CONFIG(qml_debug)
573 if (m_modelGeometry) {
574 auto *geometrySpatialNode = QQuick3DObjectPrivate::get(m_modelGeometry)->spatialNode;
575 if (geometrySpatialNode)
576 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(
this, geometrySpatialNode);
580 QSSGRenderModel *model =
static_cast<QSSGRenderModel *>(spatialNode);
582 if (!model->particleBuffer) {
583 QSSGParticleBuffer *buffer = model->particleBuffer =
new QSSGParticleBuffer;
584 buffer->resize(m_particleCount,
sizeof(QSSGTriangleParticle));
586 QQuick3DParticleSystem *psystem = QQuick3DParticle::system();
587 QMatrix4x4 particleMatrix = psystem->sceneTransform().inverted() * m_model->sceneTransform();
588 model->particleMatrix = particleMatrix.inverted();
589 model->hasTransparency = fadeInEffect() == QQuick3DParticle::FadeOpacity || fadeOutEffect() == QQuick3DParticle::FadeOpacity;
590 updateParticleBuffer(model->particleBuffer, psystem->sceneTransform());
595void QQuick3DParticleModelBlendParticle::componentComplete()
597 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
598 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
601 QQuick3DObject::componentComplete();
605void QQuick3DParticleModelBlendParticle::doSetMaxAmount(
int)
607 qWarning() <<
"ModelBlendParticle3D.maxAmount: Unable to set maximum amount, because it is set from the model.";
611int QQuick3DParticleModelBlendParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
613 if (!m_perEmitterData.contains(emitter)) {
614 m_perEmitterData.insert(emitter, PerEmitterData());
615 auto &perEmitter = m_perEmitterData[emitter];
616 perEmitter.emitter = emitter;
617 perEmitter.emitterIndex = m_nextEmitterIndex++;
619 auto &perEmitter = m_perEmitterData[emitter];
620 int index = QQuick3DParticle::nextCurrentIndex(emitter);
621 if (m_triangleParticleData[index].emitterIndex != perEmitter.emitterIndex) {
622 if (m_triangleParticleData[index].emitterIndex >= 0)
623 perEmitterData(m_triangleParticleData[index].emitterIndex).particleCount--;
624 perEmitter.particleCount++;
626 m_triangleParticleData[index].emitterIndex = perEmitter.emitterIndex;
631void QQuick3DParticleModelBlendParticle::setParticleData(
int particleIndex,
632 const QVector3D &position,
633 const QVector3D &rotation,
634 const QVector4D &color,
635 float size,
float age)
637 auto &dst = m_triangleParticleData[particleIndex];
638 dst = {position, rotation, dst.center, color, age, size, dst.emitterIndex};
639 m_dataChanged =
true;
642QQuick3DParticleModelBlendParticle::PerEmitterData &QQuick3DParticleModelBlendParticle::perEmitterData(
int emitterIndex)
644 for (
auto &perEmitter : m_perEmitterData) {
645 if (perEmitter.emitterIndex == emitterIndex)
648 return n_noPerEmitterData;
651void QQuick3DParticleModelBlendParticle::updateParticleBuffer(QSSGParticleBuffer *buffer,
const QMatrix4x4 &sceneTransform)
653 const auto &particles = m_triangleParticleData;
655 if (!buffer || !m_dataChanged)
658 const int particleCount = m_particleCount;
660 char *dest = buffer->pointer();
661 const TriangleParticleData *src = particles.data();
662 const int pps = buffer->particlesPerSlice();
663 const int ss = buffer->sliceStride();
664 const int slices = buffer->sliceCount();
665 const float c_degToRad =
float(M_PI / 180.0f);
668 for (
int s = 0; s < slices; s++) {
669 QSSGTriangleParticle *dp =
reinterpret_cast<QSSGTriangleParticle *>(dest);
670 for (
int p = 0; p < pps && i < particleCount; ) {
671 if (src->size > 0.0f)
672 bounds.include(src->position);
673 dp->position = src->position;
674 dp->rotation = src->rotation * c_degToRad;
675 dp->color = src->color;
677 dp->center = src->center;
678 dp->size = src->size;
687 bounds.fatten(m_maxTriangleRadius);
688 bounds.transform(sceneTransform);
689 buffer->setBounds(bounds);
690 m_dataChanged =
false;
693void QQuick3DParticleModelBlendParticle::itemChange(QQuick3DObject::ItemChange change,
694 const QQuick3DObject::ItemChangeData &value)
696 QQuick3DObject::itemChange(change, value);
697 if (change == ItemParentHasChanged && value.sceneManager)
701void QQuick3DParticleModelBlendParticle::reset()
703 QQuick3DParticle::reset();
704 if (m_particleCount) {
705 for (
int i = 0; i < m_particleCount; i++) {
706 if (m_modelBlendMode == Construct) {
707 m_triangleParticleData[i].size = 0.0f;
709 m_triangleParticleData[i].size = 1.0f;
710 m_triangleParticleData[i].position = m_triangleParticleData[i].center;
716QVector3D QQuick3DParticleModelBlendParticle::particleCenter(
int particleIndex)
const
718 return m_centerData[particleIndex];
721bool QQuick3DParticleModelBlendParticle::lastParticle()
const
723 return m_currentIndex >= m_maxAmount - 1;
728 float x = qDegreesToRadians(eulerRotation.x());
729 float y = qDegreesToRadians(eulerRotation.y());
730 float z = qDegreesToRadians(eulerRotation.z());
743 ret(1,0) = bd * e + a * f;
744 ret(1,1) = a * e - bd * f;
746 ret(2,0) = b * f - ad * e;
747 ret(2,1) = ad * f + b * e;
752void QQuick3DParticleModelBlendParticle::handleEndNodeChanged()
754 if (m_endNode && m_model) {
755 if (!m_model->rotation().isIdentity()) {
757 QMatrix3x3 r1 = qt_fromEulerRotation(m_endNode->eulerRotation());
758 QMatrix3x3 r2 = m_model->rotation().toRotationMatrix();
759 QMatrix3x3 r = r2 * r1.transposed() * r2.transposed();
760 m_endNodeRotation = m_endNode->eulerRotation();
761 m_endRotationMatrix = QMatrix4x4(r);
763 m_endNodeRotation = m_endNode->eulerRotation();
764 m_endRotationMatrix = QMatrix4x4(m_endNode->rotation().toRotationMatrix().transposed());
766 m_endNodePosition = m_endNode->position();
767 m_endNodeScale = m_endNode->scale();
769 m_endNodePosition = QVector3D();
770 m_endNodeRotation = QVector3D();
771 m_endNodeScale = QVector3D(1.0f, 1.0f, 1.0f);
772 m_endRotationMatrix.setToIdentity();
776QVector3D QQuick3DParticleModelBlendParticle::particleEndPosition(
int idx)
const
778 return m_endRotationMatrix.map(QVector3D(m_endNodeScale * m_centerData[idx])) + m_endNodePosition;
781QVector3D QQuick3DParticleModelBlendParticle::particleEndRotation(
int)
const
783 return m_endNodeRotation;
786int QQuick3DParticleModelBlendParticle::randomIndex(
int particleIndex)
788 if (m_randomParticles.isEmpty()) {
789 m_randomParticles.resize(m_maxAmount);
790 for (
int i = 0; i < m_maxAmount; i++)
791 m_randomParticles[i] = i;
794 QRandomGenerator rand(system()->rand()->generator());
795 for (
int i = 0; i < m_maxAmount; i++) {
796 int ridx = rand.generate() % m_maxAmount;
798 qSwap(m_randomParticles[i], m_randomParticles[ridx]);
801 return m_randomParticles[particleIndex];
Combined button and popup list for selecting options.
static float calcTriangleRadius(const QVector3D ¢er, const QVector3D &p0, const QVector3D &p1, const QVector3D &p2)
static QVector3D getPosition(const quint8 *srcVertices, quint32 idx, quint32 vertexStride, quint32 posOffset)
static void getVertexCenterData(QVector< QVector3D > ¢erData, float &maxTriangleRadius, const QByteArray &vertexBufferData, quint32 vertexStride, quint32 posOffset, quint32 primitiveCount)
static void copyToUnindexedVertices(QByteArray &unindexedVertexData, QVector< QVector3D > ¢erData, float &maxTriangleRadius, const QByteArray &vertexBufferData, quint32 vertexStride, quint32 posOffset, const QByteArray &indexBufferData, bool u16Indices, quint32 primitiveCount)
static QMatrix3x3 qt_fromEulerRotation(const QVector3D &eulerRotation)
static QSSGMesh::Mesh loadModelBlendParticleMesh(const QString &source)