8#include <QtQuick3D/private/qquick3dobject_p.h>
10#include <QtQuick/QQuickWindow>
17
18
19
20
21
22
23
24
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
96
97
98
99
102
103
104
107
108
109
112
113
114
117
118
119
122
123
124
127
128
129
132
133
134
137
138
139
142
143
144
145
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
165
166
167
168
169
170
171
172
173
174
175
178
179
180
181
182
183
184
185
186
187
188
189
192
193
194
197
198
199
200
201
204
205
206
207
209ProceduralMesh::ProceduralMesh()
214QList<QVector3D> ProceduralMesh::positions()
const
219void ProceduralMesh::setPositions(
const QList<QVector3D> &newPositions)
221 if (m_positions == newPositions)
223 m_positions = newPositions;
224 Q_EMIT positionsChanged();
228ProceduralMesh::PrimitiveMode ProceduralMesh::primitiveMode()
const
230 return m_primitiveMode;
233void ProceduralMesh::setPrimitiveMode(PrimitiveMode newPrimitiveMode)
235 if (m_primitiveMode == newPrimitiveMode)
239 if (newPrimitiveMode < Points || newPrimitiveMode > Triangles) {
240 qWarning() <<
"Invalid primitive mode specified";
244 if (newPrimitiveMode == PrimitiveMode::TriangleFan) {
245 if (!supportsTriangleFanPrimitive()) {
246 qWarning() <<
"TriangleFan is not supported by the current backend";
251 m_primitiveMode = newPrimitiveMode;
252 Q_EMIT primitiveModeChanged();
256void ProceduralMesh::requestUpdate()
258 if (!m_updateRequested) {
259 QMetaObject::invokeMethod(
this,
"updateGeometry", Qt::QueuedConnection);
260 m_updateRequested =
true;
264void ProceduralMesh::updateGeometry()
266 m_updateRequested =
false;
270 setPrimitiveType(PrimitiveType(m_primitiveMode));
273 const auto expectedLength = m_positions.size();
274 bool hasPositions = !m_positions.isEmpty();
280 bool hasNormals = m_normals.size() >= expectedLength;
281 bool hasTangents = m_tangents.size() >= expectedLength;
282 bool hasBinormals = m_binormals.size() >= expectedLength;
283 bool hasUV0s = m_uv0s.size() >= expectedLength;
284 bool hasUV1s = m_uv1s.size() >= expectedLength;
285 bool hasColors = m_colors.size() >= expectedLength;
286 bool hasJoints = m_joints.size() >= expectedLength;
287 bool hasWeights = m_weights.size() >= expectedLength;
288 bool hasIndexes = !m_indexes.isEmpty();
292 addAttribute(Attribute::Semantic::PositionSemantic, offset, Attribute::ComponentType::F32Type);
293 offset += 3 *
sizeof(
float);
297 addAttribute(Attribute::Semantic::NormalSemantic, offset, Attribute::ComponentType::F32Type);
298 offset += 3 *
sizeof(
float);
302 addAttribute(Attribute::Semantic::TangentSemantic, offset, Attribute::ComponentType::F32Type);
303 offset += 3 *
sizeof(
float);
307 addAttribute(Attribute::Semantic::BinormalSemantic, offset, Attribute::ComponentType::F32Type);
308 offset += 3 *
sizeof(
float);
312 addAttribute(Attribute::Semantic::TexCoord0Semantic, offset, Attribute::ComponentType::F32Type);
313 offset += 2 *
sizeof(
float);
317 addAttribute(Attribute::Semantic::TexCoord1Semantic, offset, Attribute::ComponentType::F32Type);
318 offset += 2 *
sizeof(
float);
322 addAttribute(Attribute::Semantic::ColorSemantic, offset, Attribute::ComponentType::F32Type);
323 offset += 4 *
sizeof(
float);
327 addAttribute(Attribute::Semantic::JointSemantic, offset, Attribute::ComponentType::F32Type);
328 offset += 4 *
sizeof(
float);
332 addAttribute(Attribute::Semantic::WeightSemantic, offset, Attribute::ComponentType::F32Type);
333 offset += 4 *
sizeof(
float);
337 addAttribute(Attribute::Semantic::IndexSemantic, 0, Attribute::ComponentType::U32Type);
340 const int stride = offset;
341 const qsizetype bufferSize = expectedLength * stride;
344 QVector<
float> vertexBufferData;
345 vertexBufferData.reserve(bufferSize /
sizeof(
float));
350 for (qsizetype i = 0; i < expectedLength; ++i) {
353 const auto &position = m_positions[i];
354 vertexBufferData.append(position.x());
355 vertexBufferData.append(position.y());
356 vertexBufferData.append(position.z());
357 minBounds.setX(qMin(minBounds.x(), position.x()));
358 maxBounds.setX(qMax(maxBounds.x(), position.x()));
359 minBounds.setY(qMin(minBounds.y(), position.y()));
360 maxBounds.setY(qMax(maxBounds.y(), position.y()));
361 minBounds.setZ(qMin(minBounds.z(), position.z()));
362 maxBounds.setZ(qMax(maxBounds.z(), position.z()));
365 const auto &normal = m_normals[i];
366 vertexBufferData.append(normal.x());
367 vertexBufferData.append(normal.y());
368 vertexBufferData.append(normal.z());
372 const auto &binormal = m_binormals[i];
373 vertexBufferData.append(binormal.x());
374 vertexBufferData.append(binormal.y());
375 vertexBufferData.append(binormal.z());
379 const auto &tangent = m_tangents[i];
380 vertexBufferData.append(tangent.x());
381 vertexBufferData.append(tangent.y());
382 vertexBufferData.append(tangent.z());
386 const auto &uv0 = m_uv0s[i];
387 vertexBufferData.append(uv0.x());
388 vertexBufferData.append(uv0.y());
392 const auto &uv1 = m_uv1s[i];
393 vertexBufferData.append(uv1.x());
394 vertexBufferData.append(uv1.y());
398 const auto &color = m_colors[i];
399 vertexBufferData.append(color.x());
400 vertexBufferData.append(color.y());
401 vertexBufferData.append(color.z());
402 vertexBufferData.append(color.w());
406 const auto &joint = m_joints[i];
407 vertexBufferData.append(joint.x());
408 vertexBufferData.append(joint.y());
409 vertexBufferData.append(joint.z());
410 vertexBufferData.append(joint.w());
414 const auto &weight = m_weights[i];
415 vertexBufferData.append(weight.x());
416 vertexBufferData.append(weight.y());
417 vertexBufferData.append(weight.z());
418 vertexBufferData.append(weight.w());
422 setBounds(minBounds, maxBounds);
423 QByteArray vertexBuffer(
reinterpret_cast<
char *>(vertexBufferData.data()), bufferSize);
424 setVertexData(vertexBuffer);
428 const qsizetype indexLength = m_indexes.size();
429 QByteArray indexBuffer;
430 indexBuffer.reserve(indexLength *
sizeof(
unsigned int));
431 for (qsizetype i = 0; i < indexLength; ++i) {
432 const auto &index = m_indexes[i];
433 indexBuffer.append(
reinterpret_cast<
const char *>(&index),
sizeof(
unsigned int));
435 setIndexData(indexBuffer);
440 if (!m_subsets.isEmpty()) {
441 for (
const auto &subset : m_subsets) {
442 QVector3D subsetMinBounds;
443 QVector3D subsetMaxBounds;
446 bool outOfRange =
false;
447 for (qsizetype i = subset->offset(); i < subset->offset() + subset->count(); ++i) {
451 if (i < m_indexes.size()) {
452 index = m_indexes[i];
458 if (index < m_positions.size()) {
459 const auto &position = m_positions[index];
460 subsetMinBounds.setX(qMin(subsetMinBounds.x(), position.x()));
461 subsetMaxBounds.setX(qMax(subsetMaxBounds.x(), position.x()));
462 subsetMinBounds.setY(qMin(subsetMinBounds.y(), position.y()));
463 subsetMaxBounds.setY(qMax(subsetMaxBounds.y(), position.y()));
464 subsetMinBounds.setZ(qMin(subsetMinBounds.z(), position.z()));
465 subsetMaxBounds.setZ(qMax(subsetMaxBounds.z(), position.z()));
473 addSubset(subset->offset(), subset->count(), subsetMinBounds, subsetMaxBounds, subset->name());
475 qWarning(
"Skipping invalid subset: Out of Range");
480 emit geometryChanged();
483void ProceduralMesh::subsetDestroyed(QObject *subset)
485 if (m_subsets.removeAll(subset))
489bool ProceduralMesh::supportsTriangleFanPrimitive()
const
491 static bool supportQueried =
false;
492 static bool triangleFanSupported =
false;
493 if (!supportQueried) {
494 const auto &manager = QQuick3DObjectPrivate::get(
this)->sceneManager;
496 auto window = manager->window();
498 auto rhi = window->rhi();
500 triangleFanSupported = rhi->isFeatureSupported(QRhi::TriangleFanTopology);
501 supportQueried =
true;
507 return triangleFanSupported;
510void ProceduralMesh::qmlAppendProceduralMeshSubset(QQmlListProperty<ProceduralMeshSubset> *list, ProceduralMeshSubset *subset)
512 if (subset ==
nullptr)
514 ProceduralMesh *self =
static_cast<ProceduralMesh *>(list->object);
515 self->m_subsets.push_back(subset);
517 connect(subset, &ProceduralMeshSubset::isDirty, self, &ProceduralMesh::requestUpdate);
518 connect(subset, &QObject::destroyed, self, &ProceduralMesh::subsetDestroyed);
520 self->requestUpdate();
523ProceduralMeshSubset *ProceduralMesh::qmlProceduralMeshSubsetAt(QQmlListProperty<ProceduralMeshSubset> *list, qsizetype index)
525 ProceduralMesh *self =
static_cast<ProceduralMesh *>(list->object);
526 return self->m_subsets.at(index);
530qsizetype ProceduralMesh::qmlProceduralMeshSubsetCount(QQmlListProperty<ProceduralMeshSubset> *list)
532 ProceduralMesh *self =
static_cast<ProceduralMesh *>(list->object);
533 return self->m_subsets.count();
536void ProceduralMesh::qmlClearProceduralMeshSubset(QQmlListProperty<ProceduralMeshSubset> *list)
538 ProceduralMesh *self =
static_cast<ProceduralMesh *>(list->object);
539 self->m_subsets.clear();
540 self->requestUpdate();
543QList<
unsigned int> ProceduralMesh::indexes()
const
548void ProceduralMesh::setIndexes(
const QList<
unsigned int> &newIndexes)
550 if (m_indexes == newIndexes)
552 m_indexes = newIndexes;
553 Q_EMIT indexesChanged();
557QList<QVector3D> ProceduralMesh::normals()
const
562void ProceduralMesh::setNormals(
const QList<QVector3D> &newNormals)
564 if (m_normals == newNormals)
566 m_normals = newNormals;
567 Q_EMIT normalsChanged();
571QList<QVector3D> ProceduralMesh::tangents()
const
576void ProceduralMesh::setTangents(
const QList<QVector3D> &newTangents)
578 if (m_tangents == newTangents)
580 m_tangents = newTangents;
581 Q_EMIT tangentsChanged();
585QList<QVector3D> ProceduralMesh::binormals()
const
590void ProceduralMesh::setBinormals(
const QList<QVector3D> &newBinormals)
592 if (m_binormals == newBinormals)
594 m_binormals = newBinormals;
595 Q_EMIT binormalsChanged();
599QList<QVector2D> ProceduralMesh::uv0s()
const
604void ProceduralMesh::setUv0s(
const QList<QVector2D> &newUv0s)
606 if (m_uv0s == newUv0s)
609 Q_EMIT uv0sChanged();
613QList<QVector2D> ProceduralMesh::uv1s()
const
618void ProceduralMesh::setUv1s(
const QList<QVector2D> &newUv1s)
620 if (m_uv1s == newUv1s)
623 Q_EMIT uv1sChanged();
627QList<QVector4D> ProceduralMesh::colors()
const
632void ProceduralMesh::setColors(
const QList<QVector4D> &newColors)
634 if (m_colors == newColors)
636 m_colors = newColors;
637 Q_EMIT colorsChanged();
641QList<QVector4D> ProceduralMesh::joints()
const
646void ProceduralMesh::setJoints(
const QList<QVector4D> &newJoints)
648 if (m_joints == newJoints)
650 m_joints = newJoints;
651 Q_EMIT jointsChanged();
655QList<QVector4D> ProceduralMesh::weights()
const
660void ProceduralMesh::setWeights(
const QList<QVector4D> &newWeights)
662 if (m_weights == newWeights)
664 m_weights = newWeights;
665 Q_EMIT weightsChanged();
669QQmlListProperty<ProceduralMeshSubset> ProceduralMesh::subsets()
671 return QQmlListProperty<ProceduralMeshSubset>(
this,
673 ProceduralMesh::qmlAppendProceduralMeshSubset,
674 ProceduralMesh::qmlProceduralMeshSubsetCount,
675 ProceduralMesh::qmlProceduralMeshSubsetAt,
676 ProceduralMesh::qmlClearProceduralMeshSubset);
679int ProceduralMeshSubset::offset()
const
684void ProceduralMeshSubset::setOffset(
int newOffset)
686 if (m_offset == newOffset)
689 m_offset = newOffset;
690 Q_EMIT offsetChanged();
694int ProceduralMeshSubset::count()
const
699void ProceduralMeshSubset::setCount(
int newCount)
701 if (m_count == newCount)
705 Q_EMIT countChanged();
709QString ProceduralMeshSubset::name()
const
714void ProceduralMeshSubset::setName(
const QString &newName)
716 if (m_name == newName)
720 Q_EMIT nameChanged();
Combined button and popup list for selecting options.