Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dgeometry.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
8#include <QtQuick3DUtils/private/qssgutils_p.h>
9
10/*!
11 \qmltype Geometry
12 \inherits Object3D
13 \inqmlmodule QtQuick3D
14 \nativetype QQuick3DGeometry
15 \brief Base type for custom geometry.
16
17 Custom geometry allows using application-generated vertex and index data,
18 that can possibly change dynamically as well. To use custom geometry, do
19 not assign a \c{.mesh} file as the \l{Model::source}{source} to a Model.
20 Instead, set its \l{Model::geometry}{geometry} property to reference a
21 Geometry object.
22
23 A typical way of implementing custom geometry is by creating a
24 \l QQuick3DGeometry subclass in C++ and registering the new type for use
25 with QML.
26
27 It is also possible to use the built-in custom geometry provider
28 \l GridGeometry in the \c Helpers module. The following is an example of
29 \l GridGeometry. Any application-provided Geometry subclass can be taken into
30 use in the same manner.
31
32 \code
33 import QtQuick3D.Helpers
34
35 Model {
36 geometry: GridGeometry {
37 }
38 materials: [
39 DefaultMaterial {
40 diffuseColor: "white"
41 lighting: DefaultMaterial.NoLighting
42 }
43 ]
44 }
45 \endcode
46
47 \sa {Qt Quick 3D - Custom Geometry Example}, Model, QQuick3DGeometry
48*/
49
50/*!
51 \class QQuick3DGeometry
52 \inmodule QtQuick3D
53 \inherits QQuick3DObject
54 \since 5.15
55 \brief Base class for defining custom geometry.
56
57 The QQuick3DGeometry can be used to specify custom geometry for a Model in
58 the Qt Quick 3D scene.
59
60 While not strictly required, the typical usage is to inherit from this
61 class. The subclass is then exposed to QML by registering it to the type
62 system. The \l{Model::geometry}{geometry} property of a Model can then be
63 set to reference an instance of the registered type.
64
65 The high-level structure of such a class is typically similar to the following:
66
67 \code
68 class CustomGeometry : public QQuick3DGeometry
69 {
70 public:
71 CustomGeometry() { rebuildGeometry(); }
72
73 void setSomething() {
74 // Change relevant internal data.
75 // ...
76
77 // Then rebuild the vertex and index data and pass it to QQuick3DGeometry.
78 rebuildGeometry();
79
80 // Finally, trigger an update. This is relevant in case nothing else
81 // is changing in the scene; this way we make sure a new frame will
82 // be rendered.
83 update();
84 }
85
86 private:
87 void rebuildGeometry()
88 {
89 QByteArray vertices;
90 QByteArray indices;
91 ...
92 setPrimitiveType(Lines);
93 setVertexBuffer(vertices);
94 setIndexBuffer(indices);
95 setStride(3 * sizeof(float)); // e.g. when having 3 components per vertex
96 setBounds(...); // minimum and maximum extents, for picking
97 addAttribute(PositionSemantic, 0, F32Type);
98 ...
99 }
100 };
101 \endcode
102
103 This class can then be registered as a QML type and used with \l {QtQuick3D::Model}{Model}.
104
105 In Qt 5 type registration happened with qmlRegisterType:
106 \code
107 qmlRegisterType<CustomGeometry>("Example", 1, 0, "CustomGeometry");
108 \endcode
109
110 In Qt 6 the default approach is to use automatic registration with the help
111 of the build system. Instead of calling qmlRegisterType, the \c{.pro} file
112 can now contain:
113
114 \code
115 CONFIG += qmltypes
116 QML_IMPORT_NAME = Example
117 QML_IMPORT_MAJOR_VERSION = 1
118 \endcode
119
120 With CMake, automatic registration is the default behavior, so no special
121 settings are needed beyond basic QML module setup:
122 \code
123 qt_add_qml_module(application
124 URI Example
125 VERSION 1.0
126 )
127 \endcode
128
129 The class implementation should add QML_NAMED_ELEMENT:
130
131 \code
132 class CustomGeometry : public QQuick3DGeometry
133 {
134 Q_OBJECT
135 QML_NAMED_ELEMENT(CustomGeometry)
136 ...
137 };
138 \endcode
139
140 The QML code can then use the custom type:
141
142 \code
143 import Example 1.0
144
145 Model {
146 id: customModel
147 geometry: CustomGeometry {
148 }
149 }
150 \endcode
151
152 At minimum, a custom geometry should have the following specified:
153
154 \list
155 \li vertex data,
156 \li vertex stride,
157 \li primitive type,
158 \li an attribute with PositionSemantic.
159 \endlist
160
161 These are sufficient to render the mesh. For indexed drawing, the index
162 buffer data and an attribute with IndexSemantic needs to be specified as
163 well. In order to support picking (input), the class must specify the bounding volume using setBounds().
164 For proper lighting, an attribute with NormalSemantic is needed. When the
165 material uses texturing, at least one set of UV coordinates must be
166 provided and described in an TexCoord0Semantic or TexCoord1Semantic attribute. Some materials may
167 require tangents and binormals as well.
168
169 As a concrete, minimal example, the following class would provide geometry
170 for a single triangle:
171
172 \code
173 class ExampleGeometry : public QQuick3DGeometry
174 {
175 Q_OBJECT
176 QML_NAMED_ELEMENT(ExampleGeometry)
177
178 public:
179 ExampleGeometry();
180
181 private:
182 void updateData();
183 };
184
185 ExampleGeometry::ExampleGeometry()
186 {
187 updateData();
188 }
189
190 void ExampleGeometry::updateData()
191 {
192 QByteArray v;
193 v.resize(3 * 3 * sizeof(float));
194 float *p = reinterpret_cast<float *>(v.data());
195
196 // a triangle, front face = counter-clockwise
197 *p++ = -1.0f; *p++ = -1.0f; *p++ = 0.0f;
198 *p++ = 1.0f; *p++ = -1.0f; *p++ = 0.0f;
199 *p++ = 0.0f; *p++ = 1.0f; *p++ = 0.0f;
200
201 setVertexData(v);
202 setStride(3 * sizeof(float));
203
204 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
205
206 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
207 0,
208 QQuick3DGeometry::Attribute::F32Type);
209 }
210 \endcode
211
212 Depending on the lighting in the scene, the result of referencing this
213 geometry from a Model:
214
215 \image customgeometry.jpg
216 {Black triangle with gray background showing custom geometry}
217
218 \note Vertex data is expected to follow OpenGL conventions. This means the
219 data must be provided with the assumption that the Y axis is pointing up in
220 the normalized device coordinate system, and that front faces have a
221 counter clockwise winding.
222
223 \sa Model, Geometry
224*/
225
226QT_BEGIN_NAMESPACE
227
228QQuick3DGeometryPrivate::QQuick3DGeometryPrivate()
229 : QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::Geometry)
230{
231
232}
233
234QQuick3DGeometry::QQuick3DGeometry(QQuick3DObject *parent)
235 : QQuick3DObject(*new QQuick3DGeometryPrivate, parent)
236{
237
238}
239
240QQuick3DGeometry::~QQuick3DGeometry()
241{
242
243}
244
245/*!
246 Returns the vertex buffer data set by setVertexData.
247*/
248QByteArray QQuick3DGeometry::vertexData() const
249{
250 const Q_D(QQuick3DGeometry);
251 return d->m_vertexBuffer;
252}
253
254/*!
255 \since 6.6
256
257 Returns the target buffer data set by setTargetData.
258*/
259QByteArray QQuick3DGeometry::targetData() const
260{
261 const Q_D(QQuick3DGeometry);
262 return d->m_targetBuffer;
263}
264
265/*!
266 Returns the index buffer data.
267*/
268QByteArray QQuick3DGeometry::indexData() const
269{
270 const Q_D(QQuick3DGeometry);
271 return d->m_indexBuffer;
272}
273
274/*!
275 Returns the number of attributes defined for this geometry.
276
277 \sa attribute
278*/
279int QQuick3DGeometry::attributeCount() const
280{
281 const Q_D(QQuick3DGeometry);
282 return d->m_attributeCount;
283}
284
285/*!
286 Returns attribute definition number \a index
287
288 The attribute definitions are numbered from 0 to \c {attributeCount() - 1}
289*/
290QQuick3DGeometry::Attribute QQuick3DGeometry::attribute(int index) const
291{
292 const Q_D(QQuick3DGeometry);
293 return d->m_attributes[index];
294}
295
296/*!
297 \since 6.6
298 Returns the number of morph target attributes defined for this geometry.
299
300 \sa targetAttribute
301*/
302int QQuick3DGeometry::targetAttributeCount() const
303{
304 const Q_D(QQuick3DGeometry);
305 return d->m_targetAttributeCount;
306}
307
308/*!
309 \since 6.6
310
311 Returns morph target attribute definition number \a index
312
313 The attribute definitions are numbered from 0 to \c {attributeCount() - 1}
314*/
315QQuick3DGeometry::TargetAttribute QQuick3DGeometry::targetAttribute(int index) const
316{
317 const Q_D(QQuick3DGeometry);
318 return d->m_targetAttributes[index];
319}
320
321/*!
322 Returns the primitive type used when rendering. The default is \c Triangles.
323
324 \sa setPrimitiveType
325*/
326QQuick3DGeometry::PrimitiveType QQuick3DGeometry::primitiveType() const
327{
328 const Q_D(QQuick3DGeometry);
329 return d->m_primitiveType;
330}
331
332/*!
333 Returns the minimum coordinate of the bounding volume.
334
335 \sa setBounds
336*/
337QVector3D QQuick3DGeometry::boundsMin() const
338{
339 const Q_D(QQuick3DGeometry);
340 return d->m_min;
341}
342
343/*!
344 Returns the maximum coordinate of the bounding volume.
345
346 \sa setBounds
347*/
348QVector3D QQuick3DGeometry::boundsMax() const
349{
350 const Q_D(QQuick3DGeometry);
351 return d->m_max;
352}
353
354/*!
355 Returns the byte stride of the vertex buffer.
356
357 \sa setStride
358*/
359int QQuick3DGeometry::stride() const
360{
361 const Q_D(QQuick3DGeometry);
362 return d->m_stride;
363}
364
365void QQuick3DGeometry::markAllDirty()
366{
367 QQuick3DObject::markAllDirty();
368}
369
370/*!
371 Sets the vertex buffer \a data. The buffer should hold all the vertex data
372 packed in the array, as described by the attribute definitions. Note that
373 this does not include attributes with \c IndexSemantic, which belong in the
374 index buffer.
375
376 \sa addAttribute, setStride, setIndexData
377*/
378void QQuick3DGeometry::setVertexData(const QByteArray &data)
379{
380 Q_D(QQuick3DGeometry);
381 d->m_vertexBuffer = data;
382 d->m_geometryChanged = true;
383}
384
385/*!
386 \overload
387 Updates a subset of the vertex buffer. \a offset specifies the offset in
388 bytes, \a data specifies the size and the data.
389
390 This function will not resize the buffer. If \c {offset + data.size()} is
391 greater than the current size of the buffer, the overshooting data will
392 be ignored.
393
394 \note The partial update functions for vertex, index and morph target data
395 do not offer any guarantee on how such changes are implemented internally.
396 depending on the underlying implementation, even partial changes may lead
397 to updating the entire graphics resource.
398*/
399void QQuick3DGeometry::setVertexData(int offset, const QByteArray &data)
400{
401 Q_D(QQuick3DGeometry);
402 if (offset >= d->m_vertexBuffer.size())
403 return;
404
405 const size_t len = qMin(d->m_vertexBuffer.size() - offset, data.size());
406 memcpy(d->m_vertexBuffer.data() + offset, data.data(), len);
407
408 d->m_geometryChanged = true;
409}
410
411/*!
412 \since 6.6
413
414 Sets the morph target buffer \a data. The buffer should hold all the
415 morph target data.
416
417 \sa addTargetAttribute
418*/
419void QQuick3DGeometry::setTargetData(const QByteArray &data)
420{
421 Q_D(QQuick3DGeometry);
422 d->m_targetBuffer = data;
423 d->m_targetChanged = true;
424}
425
426/*!
427 \since 6.6
428 \overload
429
430 Updates a subset of the morph target buffer. \a offset specifies the offset in
431 bytes, \a data specifies the size and the data.
432
433 This function will not resize the buffer. If \c {offset + data.size()} is
434 greater than the current size of the buffer, the overshooting data will
435 be ignored.
436
437 \note The partial update functions for vertex, index and morph target data
438 do not offer any guarantee on how such changes are implemented internally.
439 Depending on the underlying implementation, even partial changes may lead
440 to updating the entire graphics resource.
441*/
442void QQuick3DGeometry::setTargetData(int offset, const QByteArray &data)
443{
444 Q_D(QQuick3DGeometry);
445 if (offset >= d->m_targetBuffer.size())
446 return;
447
448 const size_t len = qMin(d->m_targetBuffer.size() - offset, data.size());
449 memcpy(d->m_targetBuffer.data() + offset, data.data(), len);
450
451 d->m_targetChanged = true;
452}
453
454/*!
455 Sets the index buffer to \a data. To use indexed drawing, add an attribute with \c IndexSemantic
456
457 \sa addAttribute
458*/
459void QQuick3DGeometry::setIndexData(const QByteArray &data)
460{
461 Q_D(QQuick3DGeometry);
462 d->m_indexBuffer = data;
463 d->m_geometryChanged = true;
464}
465
466/*!
467 \overload
468 Updates a subset of the index buffer. \a offset specifies the offset in
469 bytes, \a data specifies the size and the data.
470
471 This function will not resize the buffer. If \c {offset + data.size()} is
472 greater than the current size of the buffer, the overshooting data will
473 be ignored.
474
475 \note The partial update functions for vertex, index and morph target data
476 do not offer any guarantee on how such changes are implemented internally.
477 Depending on the underlying implementation, even partial changes may lead
478 to updating the entire graphics resource.
479*/
480void QQuick3DGeometry::setIndexData(int offset, const QByteArray &data)
481{
482 Q_D(QQuick3DGeometry);
483 if (offset >= d->m_indexBuffer.size())
484 return;
485
486 const size_t len = qMin(d->m_indexBuffer.size() - offset, data.size());
487 memcpy(d->m_indexBuffer.data() + offset, data.data(), len);
488
489 d->m_geometryChanged = true;
490}
491
492/*!
493 Sets the stride of the vertex buffer to \a stride, measured in bytes.
494 This is the distance between two consecutive vertices in the buffer.
495
496 For example, a tightly packed, interleaved vertex buffer for a geometry using
497 \c PositionSemantic, \c IndexSemantic, and \c ColorSemantic will have a stride of
498 \c 28 (Seven floats in total: Three for position, four for color, and none for indexes,
499 which do not go in the vertex buffer.)
500
501 \note QQuick3DGeometry expects, and works only with, vertex data with an
502 interleaved attribute layout.
503
504 \sa addAttribute
505*/
506void QQuick3DGeometry::setStride(int stride)
507{
508 Q_D(QQuick3DGeometry);
509 if (stride != d->m_stride) {
510 d->m_stride = stride;
511 d->m_geometryChanged = true;
512 }
513}
514
515/*!
516 Sets the bounding volume of the geometry to the cube defined by the points \a min and \a max.
517 This is used for \l {View3D::pick}{picking}.
518*/
519void QQuick3DGeometry::setBounds(const QVector3D &min, const QVector3D &max)
520{
521 Q_D(QQuick3DGeometry);
522 d->m_max = max;
523 d->m_min = min;
524 d->m_geometryBoundsChanged = true;
525}
526
527/*!
528 Sets the primitive type used for rendering to \a type.
529
530 \value Points The primitives are points.
531 \value LineStrip The primitives are lines in a strip.
532 \value Lines The primitives are lines in a list.
533 \value TriangleStrip The primitives are triangles in a strip.
534 \value TriangleFan The primitives are triangles in a fan. Be aware that
535 triangle fans may not be supported at run time, depending on the underlying
536 graphics API.
537 \value Triangles The primitives are triangles in a list.
538
539 The initial value is \c Triangles.
540
541 \note Be aware that triangle fans (TriangleFan) may not be supported at run
542 time, depending on the underlying graphics API. For example, with Direct 3D
543 this topology will not be functional at all.
544
545 \note The point size for Points and the line width for Lines and LineStrip
546 are controlled by the \l{PrincipledMaterial::pointSize}{material}. Be aware
547 however that sizes other than 1 may not be supported at run time, depending
548 on the underlying graphics API.
549
550*/
551void QQuick3DGeometry::setPrimitiveType(PrimitiveType type)
552{
553 Q_D(QQuick3DGeometry);
554 if (d->m_primitiveType != type) {
555 d->m_primitiveType = type;
556 d->m_geometryChanged = true;
557 }
558}
559
560/*!
561 Adds vertex attribute description. Each attribute has a \a semantic, which specifies
562 the usage of the attribute and the number of components it has, an \a offset from the
563 beginning to the vertex to the attribute location inside a vertex and a \a componentType
564 specifying the datatype and size of the attribute.
565
566 The semantic can be one of the following:
567
568 \value PositionSemantic The attribute is a position. 3 components: \e x, \e y, and \e z
569 \value NormalSemantic The attribute is a normal vector. 3 components: \e x, \e y, and \e z
570 \value TexCoord0Semantic The attribute is a texture coordinate. 2 components: \e u and \e v
571 \value TexCoord1Semantic The attribute is a texture coordinate. 2 components: \e u and \e v
572 \value TangentSemantic The attribute is a tangent vector. 3 components: \e x, \e y, and \e z
573 \value BinormalSemantic The attribute is a binormal vector. 3 components: \e x, \e y, and \e z
574 \value JointSemantic The attribute is a joint index vector for \l {Vertex Skinning}{skinning}. 4 components: joint index 1-4
575 \value WeightSemantic The attribute is a weight vector for \l {Vertex Skinning}{skinning}. 4 components: joint weight 1-4
576 \value ColorSemantic The attribute is a vertex color vector. 4 components: \e r, \e g, \e b, and \e a
577 \value TargetPositionSemantic The attribute is a position for the first \l {Morphing Animation}{morph target}. 3 components: \e x, \e y, and \e z
578 \value TargetNormalSemantic The attribute is a normal vector for the first \l {Morphing Animation}{morph target}. 3 components: \e x, \e y, and \e z
579 \value TargetTangentSemantic The attribute is a tangent vector for the first \l {Morphing Animation}{morph target}. 3 components: \e x, \e y, and \e z
580 \value TargetBinormalSemantic The attribute is a binormal vector for the first \l {Morphing Animation}{morph target}. 3 components: \e x, \e y, and \e z
581
582 In addition, \a semantic can be \c IndexSemantic. In this case the attribute does not represent an entry in the vertex
583 buffer, but rather describes the index data in the index buffer. Since there is always just one index per vertex, \a offset
584 makes no sense for the index buffer, and should be left at zero.
585
586 The component type can be one of the following:
587
588 \value U16Type The index component type is unsigned 16-bit integer. Only
589 supported for \c IndexSemantic.
590
591 \value U32Type The attribute (or index component) is an unsigned 32-bit
592 integer.
593
594 \value I32Type The attribute is a signed 32-bit integer. Be aware that old
595 OpenGL versions (such as, 2.1 or OpenGL ES 2.0) may not support this data
596 type.
597
598 \value F32Type The attribute is a single-precision float.
599
600 \note The joint index data is typically \c I32Type. \c F32Type is also supported
601 in order to enable functioning with APIs, such as OpenGL ES 2.0, that do not
602 support integer vertex input attributes.
603
604 \note For index data (\c IndexSemantic) only U16Type and U32Type are
605 sensible and supported.
606
607 \note TargetXXXSemantics will be deprecated. \l addTargetAttribute can be used for the morph targets.
608 Now these semantics are just supported for backward compatibility. If they are mixed-used with
609 addTargetAttribute and setTargetData, the result cannot be quaranteed.
610*/
611void QQuick3DGeometry::addAttribute(Attribute::Semantic semantic, int offset,
612 Attribute::ComponentType componentType)
613{
614 Q_D(QQuick3DGeometry);
615 if (semantic != Attribute::TargetPositionSemantic
616 && semantic != Attribute::TargetNormalSemantic
617 && semantic != Attribute::TargetTangentSemantic
618 && semantic != Attribute::TargetBinormalSemantic) {
619 if (d->m_attributeCount >= QQuick3DGeometryPrivate::MAX_ATTRIBUTE_COUNT)
620 return;
621 d->m_attributes[d->m_attributeCount].semantic = semantic;
622 d->m_attributes[d->m_attributeCount].offset = offset;
623 d->m_attributes[d->m_attributeCount].componentType = componentType;
624 d->m_attributeCount++;
625 d->m_geometryChanged = true;
626 } else {
627 if (d->m_targetAttributeCount >= QQuick3DGeometryPrivate::MAX_TARGET_ATTRIBUTE_COUNT)
628 return;
629 d->m_targetAttributes[d->m_targetAttributeCount].targetId = 0;
630 d->m_targetAttributes[d->m_targetAttributeCount].attr.semantic = semantic;
631 d->m_targetAttributes[d->m_targetAttributeCount].attr.offset = offset;
632 // m_stride and m_vertexBuffer will be used for targetBuffer.
633 d->m_targetAttributeCount++;
634 d->m_targetChanged = true;
635 d->m_usesOldTargetSemantics = true;
636 }
637}
638
639/*!
640 \overload
641
642 Adds vertex attribute description. Each attribute has a semantic, which specifies
643 the usage of the attribute and the number of components it has, an offset from the
644 beginning to the vertex to the attribute location inside a vertex and a componentType
645 specifying the datatype and size of the attribute.
646*/
647void QQuick3DGeometry::addAttribute(const Attribute &attribute)
648{
649 Q_D(QQuick3DGeometry);
650 if (d->m_attributeCount >= QQuick3DGeometryPrivate::MAX_ATTRIBUTE_COUNT)
651 return;
652 d->m_attributes[d->m_attributeCount++] = attribute;
653 d->m_geometryChanged = true;
654}
655
656/*!
657 \since 6.6
658
659 Adds morph target attribute description. Each attribute has a \a targetId which the
660 attribute belongs to, a \a semantic, which specifies the usage of the attribute and the
661 number of components it has, an \a offset from the beginning to the vertex to the attribute
662 location inside a vertex, and a \a stride which is a byte size between the elements.
663
664 \note The targetId should be increased from 0 without skipping any number and all the
665 targets should have the same attributes.
666
667 \note The semantic is the same as the vertex attribute but IndexSemantic, JointSementic
668 and WeightSemantic are not allowed for target attributes.
669
670 \note The componentTypes of all the target attributes must be F32Type.
671
672 \note If the stride is not given or less than or equal to zero, the attribute is
673 considered to be tightly packed.
674
675 \sa addAttribute
676*/
677void QQuick3DGeometry::addTargetAttribute(quint32 targetId,
678 Attribute::Semantic semantic, int offset,
679 int stride)
680{
681 Q_D(QQuick3DGeometry);
682 if (d->m_targetAttributeCount >= QQuick3DGeometryPrivate::MAX_TARGET_ATTRIBUTE_COUNT)
683 return;
684 if (semantic == Attribute::IndexSemantic
685 || semantic == Attribute::JointSemantic
686 || semantic == Attribute::WeightSemantic)
687 return;
688 d->m_targetAttributes[d->m_targetAttributeCount].targetId = targetId;
689 d->m_targetAttributes[d->m_targetAttributeCount].attr.semantic = semantic;
690 d->m_targetAttributes[d->m_targetAttributeCount].attr.offset = offset;
691 d->m_targetAttributes[d->m_targetAttributeCount].stride = stride;
692 d->m_targetAttributeCount++;
693 d->m_targetChanged = true;
694}
695
696/*!
697 \since 6.6
698 \overload
699
700 Adds morph target attribute description. Each attribute has a targetId which the
701 attribute belongs to, a semantic, which specifies the usage of the attribute and the
702 number of components it has, an offset from the beginning to the vertex to the attribute
703 location inside a vertex, and a stride which is a byte size between the elements.
704*/
705void QQuick3DGeometry::addTargetAttribute(const TargetAttribute &attribute)
706{
707 Q_D(QQuick3DGeometry);
708 if (d->m_targetAttributeCount >= QQuick3DGeometryPrivate::MAX_TARGET_ATTRIBUTE_COUNT)
709 return;
710 if (attribute.attr.semantic == Attribute::IndexSemantic ||
711 attribute.attr.semantic == Attribute::JointSemantic ||
712 attribute.attr.semantic == Attribute::WeightSemantic)
713 return;
714 d->m_targetAttributes[d->m_targetAttributeCount++] = attribute;
715 d->m_targetChanged = true;
716}
717
718/*!
719 Resets the geometry to its initial state, clearing previously set vertex and index data as well as attributes.
720*/
721void QQuick3DGeometry::clear()
722{
723 Q_D(QQuick3DGeometry);
724 d->m_vertexBuffer.clear();
725 d->m_targetBuffer.clear();
726 d->m_indexBuffer.clear();
727 d->m_attributeCount = 0;
728 d->m_targetAttributeCount = 0;
729 d->m_subsets.clear();
730 d->m_primitiveType = PrimitiveType::Triangles;
731 d->m_geometryChanged = true;
732 d->m_targetChanged = true;
733 d->m_min = {};
734 d->m_max = {};
735}
736
737/*!
738 Returns the number of subsets.
739*/
740int QQuick3DGeometry::subsetCount() const
741{
742 Q_D(const QQuick3DGeometry);
743 return d->m_subsets.size();
744}
745
746/*!
747 Returns the number of minimum bounds of a \a subset.
748
749 \sa subsetBoundsMax
750*/
751QVector3D QQuick3DGeometry::subsetBoundsMin(int subset) const
752{
753 Q_D(const QQuick3DGeometry);
754 if (subset >= 0 && subset < d->m_subsets.size())
755 return d->m_subsets[subset].boundsMin;
756 return {};
757}
758
759/*!
760 Returns the number of maximum bounds of a \a subset.
761
762 \sa subsetBoundsMin
763*/
764QVector3D QQuick3DGeometry::subsetBoundsMax(int subset) const
765{
766 Q_D(const QQuick3DGeometry);
767 if (subset >= 0 && subset < d->m_subsets.size())
768 return d->m_subsets[subset].boundsMax;
769 return {};
770}
771
772/*!
773 Returns the \a subset offset to the vertex or index buffer.
774
775 \sa subsetCount
776*/
777int QQuick3DGeometry::subsetOffset(int subset) const
778{
779 Q_D(const QQuick3DGeometry);
780 if (subset >= 0 && subset < d->m_subsets.size())
781 return d->m_subsets[subset].offset;
782 return 0;
783}
784
785/*!
786 Returns the subset primitive count.
787
788 \sa subsetOffset
789*/
790int QQuick3DGeometry::subsetCount(int subset) const
791{
792 Q_D(const QQuick3DGeometry);
793 if (subset >= 0 && subset < d->m_subsets.size())
794 return d->m_subsets[subset].count;
795 return 0;
796}
797
798/*!
799 Returns the \a subset name.
800*/
801QString QQuick3DGeometry::subsetName(int subset) const
802{
803 Q_D(const QQuick3DGeometry);
804 if (subset >= 0 && subset < d->m_subsets.size())
805 return d->m_subsets[subset].name;
806 return {};
807}
808
809/*!
810 Adds new subset to the geometry. Subsets allow rendering parts of the geometry with different
811 materials. The materials are specified in the \l {Model::materials}{model}.
812
813 If the geometry has index buffer, then the \a offset and \a count are the primitive offset and
814 count of indices in the subset. If the geometry has only vertex buffer,
815 the offset is the vertex offset and count is the number of vertices in the subset.
816
817 The bounds \a boundsMin and \a boundsMax should enclose the subset just like geometry bounds.
818 Also the subset can have a \a name.
819*/
820void QQuick3DGeometry::addSubset(int offset, int count, const QVector3D &boundsMin, const QVector3D &boundsMax, const QString &name)
821{
822 Q_D(QQuick3DGeometry);
823 d->m_subsets.append({name, boundsMin, boundsMax, quint32(offset), quint32(count)});
824 d->m_geometryChanged = true;
825}
826
827static inline QSSGMesh::Mesh::DrawMode mapPrimitiveType(QQuick3DGeometry::PrimitiveType t)
828{
829 switch (t) {
830 case QQuick3DGeometry::PrimitiveType::Points:
831 return QSSGMesh::Mesh::DrawMode::Points;
832 case QQuick3DGeometry::PrimitiveType::LineStrip:
833 return QSSGMesh::Mesh::DrawMode::LineStrip;
834 case QQuick3DGeometry::PrimitiveType::Lines:
835 return QSSGMesh::Mesh::DrawMode::Lines;
836 case QQuick3DGeometry::PrimitiveType::TriangleStrip:
837 return QSSGMesh::Mesh::DrawMode::TriangleStrip;
838 case QQuick3DGeometry::PrimitiveType::TriangleFan:
839 return QSSGMesh::Mesh::DrawMode::TriangleFan;
840 case QQuick3DGeometry::PrimitiveType::Triangles:
841 return QSSGMesh::Mesh::DrawMode::Triangles;
842 }
843
844 Q_UNREACHABLE_RETURN(QSSGMesh::Mesh::DrawMode::Triangles);
845}
846
847static inline QSSGMesh::RuntimeMeshData::Attribute::Semantic mapSemantic(QQuick3DGeometry::Attribute::Semantic s)
848{
849 switch (s) {
850 case QQuick3DGeometry::Attribute::IndexSemantic:
851 return QSSGMesh::RuntimeMeshData::Attribute::IndexSemantic;
852 case QQuick3DGeometry::Attribute::PositionSemantic:
853 return QSSGMesh::RuntimeMeshData::Attribute::PositionSemantic;
854 case QQuick3DGeometry::Attribute::NormalSemantic:
855 return QSSGMesh::RuntimeMeshData::Attribute::NormalSemantic;
856 case QQuick3DGeometry::Attribute::TexCoord0Semantic:
857 return QSSGMesh::RuntimeMeshData::Attribute::TexCoord0Semantic;
858 case QQuick3DGeometry::Attribute::TexCoord1Semantic:
859 return QSSGMesh::RuntimeMeshData::Attribute::TexCoord1Semantic;
860 case QQuick3DGeometry::Attribute::TangentSemantic:
861 return QSSGMesh::RuntimeMeshData::Attribute::TangentSemantic;
862 case QQuick3DGeometry::Attribute::BinormalSemantic:
863 return QSSGMesh::RuntimeMeshData::Attribute::BinormalSemantic;
864 case QQuick3DGeometry::Attribute::JointSemantic:
865 return QSSGMesh::RuntimeMeshData::Attribute::JointSemantic;
866 case QQuick3DGeometry::Attribute::WeightSemantic:
867 return QSSGMesh::RuntimeMeshData::Attribute::WeightSemantic;
868 case QQuick3DGeometry::Attribute::ColorSemantic:
869 return QSSGMesh::RuntimeMeshData::Attribute::ColorSemantic;
870 case QQuick3DGeometry::Attribute::TargetPositionSemantic:
871 return QSSGMesh::RuntimeMeshData::Attribute::PositionSemantic;
872 case QQuick3DGeometry::Attribute::TargetNormalSemantic:
873 return QSSGMesh::RuntimeMeshData::Attribute::NormalSemantic;
874 case QQuick3DGeometry::Attribute::TargetTangentSemantic:
875 return QSSGMesh::RuntimeMeshData::Attribute::TangentSemantic;
876 case QQuick3DGeometry::Attribute::TargetBinormalSemantic:
877 return QSSGMesh::RuntimeMeshData::Attribute::BinormalSemantic;
878 }
879
880 Q_UNREACHABLE_RETURN(QSSGMesh::RuntimeMeshData::Attribute::PositionSemantic);
881}
882
883static inline QSSGMesh::Mesh::ComponentType mapComponentType(QQuick3DGeometry::Attribute::ComponentType t)
884{
885 switch (t) {
886 case QQuick3DGeometry::Attribute::U16Type:
887 return QSSGMesh::Mesh::ComponentType::UnsignedInt16;
888 case QQuick3DGeometry::Attribute::U32Type:
889 return QSSGMesh::Mesh::ComponentType::UnsignedInt32;
890 case QQuick3DGeometry::Attribute::I32Type:
891 return QSSGMesh::Mesh::ComponentType::Int32;
892 case QQuick3DGeometry::Attribute::F32Type:
893 return QSSGMesh::Mesh::ComponentType::Float32;
894 }
895
896 Q_UNREACHABLE_RETURN(QSSGMesh::Mesh::ComponentType::Float32);
897}
898
899/*!
900 \internal
901 */
902QSSGRenderGraphObject *QQuick3DGeometry::updateSpatialNode(QSSGRenderGraphObject *node)
903{
904 Q_D(QQuick3DGeometry);
905 if (!node) {
906 markAllDirty();
907 node = new QSSGRenderGeometry();
908 emit geometryNodeDirty();
909 }
910 QQuick3DObject::updateSpatialNode(node);
911 QSSGRenderGeometry *geometry = static_cast<QSSGRenderGeometry *>(node);
912 if (d->m_geometryChanged) {
913 geometry->clearVertexAndIndex();
914 geometry->setBounds(d->m_min, d->m_max);
915 geometry->setStride(d->m_stride);
916 // If there is vertex data but no stride is set, the user likely forgot to set the stride.
917 if (d->m_stride < 1 && !d->m_vertexBuffer.isEmpty())
918 qWarning("%d is an invalid stride, was QQuick3DGeometry::setStride() called?", d->m_stride);
919 geometry->setIndexData(d->m_indexBuffer);
920 geometry->setVertexData(d->m_vertexBuffer);
921 geometry->setPrimitiveType(mapPrimitiveType(d->m_primitiveType));
922 quint32 indexBufferComponentSize = 0;
923 for (int i = 0; i < d->m_attributeCount; ++i) {
924 const auto componentType = mapComponentType(d->m_attributes[i].componentType);
925 geometry->addAttribute(mapSemantic(d->m_attributes[i].semantic),
926 d->m_attributes[i].offset,
927 componentType);
928 if (d->m_attributes[i].semantic == Attribute::IndexSemantic) {
929 if (componentType != QSSGMesh::Mesh::ComponentType::UnsignedInt16
930 && componentType != QSSGMesh::Mesh::ComponentType::UnsignedInt32)
931 {
932 qWarning("Index data can only be uint16 or uint32");
933 }
934 indexBufferComponentSize = QSSGMesh::MeshInternal::byteSizeForComponentType(componentType);
935 } else if (componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt16) {
936 qWarning("Attributes cannot be uint16, only index data");
937 }
938 }
939 if (!d->m_indexBuffer.isEmpty() && !indexBufferComponentSize) {
940 qWarning("IndexData has been set, but no index attribute found.");
941 geometry->setIndexData({});
942 }
943 // Implicitely add subset if none set for backwards compatibility
944 if (d->m_subsets.isEmpty()) {
945 quint32 offset = 0;
946 quint32 count = 0;
947 if (!d->m_indexBuffer.isEmpty() && indexBufferComponentSize)
948 count = d->m_indexBuffer.size() / indexBufferComponentSize;
949 else if (d->m_stride)
950 count = d->m_vertexBuffer.size() / d->m_stride;
951 geometry->addSubset(offset, count, d->m_min, d->m_max);
952 } else {
953 for (auto &s : d->m_subsets)
954 geometry->addSubset(s.offset, s.count, s.boundsMin, s.boundsMax, s.name);
955 }
956 d->m_geometryChanged = false;
957 emit geometryChanged();
958 }
959 if (d->m_geometryBoundsChanged) {
960 geometry->setBounds(d->m_min, d->m_max);
961 emit geometryNodeDirty();
962 d->m_geometryBoundsChanged = false;
963 }
964 if (d->m_targetChanged) {
965 geometry->clearTarget();
966 geometry->setTargetData(d->m_usesOldTargetSemantics ? d->m_vertexBuffer : d->m_targetBuffer);
967 for (int i = 0; i < d->m_targetAttributeCount; ++i) {
968 geometry->addTargetAttribute(d->m_targetAttributes[i].targetId,
969 mapSemantic(d->m_targetAttributes[i].attr.semantic),
970 d->m_targetAttributes[i].attr.offset,
971 d->m_usesOldTargetSemantics ? d->m_stride : d->m_targetAttributes[i].stride);
972 }
973 d->m_targetChanged = false;
974 }
975
976 DebugViewHelpers::ensureDebugObjectName(geometry, this);
977
978 return node;
979}
980
981QQuick3DGeometry::Attribute::Semantic QQuick3DGeometryPrivate::semanticFromName(const QByteArray &name)
982{
983 static QMap<const QByteArray, QQuick3DGeometry::Attribute::Semantic> semanticMap;
984 if (semanticMap.isEmpty()) {
985 semanticMap[QSSGMesh::MeshInternal::getPositionAttrName()] = QQuick3DGeometry::Attribute::PositionSemantic;
986 semanticMap[QSSGMesh::MeshInternal::getNormalAttrName()] = QQuick3DGeometry::Attribute::NormalSemantic;
987 semanticMap[QSSGMesh::MeshInternal::getUV0AttrName()] = QQuick3DGeometry::Attribute::TexCoord0Semantic;
988 semanticMap[QSSGMesh::MeshInternal::getUV1AttrName()] = QQuick3DGeometry::Attribute::TexCoord1Semantic;
989 semanticMap[QSSGMesh::MeshInternal::getTexTanAttrName()] = QQuick3DGeometry::Attribute::TangentSemantic;
990 semanticMap[QSSGMesh::MeshInternal::getTexBinormalAttrName()] = QQuick3DGeometry::Attribute::BinormalSemantic;
991 semanticMap[QSSGMesh::MeshInternal::getColorAttrName()] = QQuick3DGeometry::Attribute::ColorSemantic;
992 semanticMap[QSSGMesh::MeshInternal::getWeightAttrName()] = QQuick3DGeometry::Attribute::WeightSemantic;
993 semanticMap[QSSGMesh::MeshInternal::getJointAttrName()] = QQuick3DGeometry::Attribute::JointSemantic;
994 }
995 return semanticMap[name];
996}
997
998QQuick3DGeometry::Attribute::ComponentType QQuick3DGeometryPrivate::toComponentType(QSSGMesh::Mesh::ComponentType ctype)
999{
1000 switch (ctype) {
1001 case QSSGMesh::Mesh::ComponentType::Float32:
1002 return QQuick3DGeometry::Attribute::F32Type;
1003 case QSSGMesh::Mesh::ComponentType::Int32:
1004 return QQuick3DGeometry::Attribute::I32Type;
1005 case QSSGMesh::Mesh::ComponentType::UnsignedInt16:
1006 return QQuick3DGeometry::Attribute::U16Type;
1007 case QSSGMesh::Mesh::ComponentType::UnsignedInt32:
1008 return QQuick3DGeometry::Attribute::U32Type;
1009
1010 case QSSGMesh::Mesh::ComponentType::Float16:
1011 case QSSGMesh::Mesh::ComponentType::Float64:
1012 case QSSGMesh::Mesh::ComponentType::UnsignedInt8:
1013 case QSSGMesh::Mesh::ComponentType::Int8:
1014 case QSSGMesh::Mesh::ComponentType::Int16:
1015 case QSSGMesh::Mesh::ComponentType::UnsignedInt64:
1016 case QSSGMesh::Mesh::ComponentType::Int64:
1017 default:
1018 Q_ASSERT_X(0, "Incorrect datatype", "QQuick3DGeometryPrivate::toComponentType");
1019 break;
1020 }
1021 return QQuick3DGeometry::Attribute::F32Type;
1022}
1023
1024QT_END_NAMESPACE
static QSSGMesh::Mesh::DrawMode mapPrimitiveType(QQuick3DGeometry::PrimitiveType t)
static QSSGMesh::RuntimeMeshData::Attribute::Semantic mapSemantic(QQuick3DGeometry::Attribute::Semantic s)
static QSSGMesh::Mesh::ComponentType mapComponentType(QQuick3DGeometry::Attribute::ComponentType t)