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

source code of qtquick3d/src/quick3d/qquick3dgeometry.cpp