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 | |
223 | QT_BEGIN_NAMESPACE |
224 | |
225 | QQuick3DGeometryPrivate::QQuick3DGeometryPrivate() |
226 | : QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::Geometry) |
227 | { |
228 | |
229 | } |
230 | |
231 | QQuick3DGeometry::QQuick3DGeometry(QQuick3DObject *parent) |
232 | : QQuick3DObject(*new QQuick3DGeometryPrivate, parent) |
233 | { |
234 | |
235 | } |
236 | |
237 | QQuick3DGeometry::~QQuick3DGeometry() |
238 | { |
239 | |
240 | } |
241 | |
242 | /*! |
243 | Returns the vertex buffer data set by setVertexData. |
244 | */ |
245 | QByteArray 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 | */ |
256 | QByteArray QQuick3DGeometry::targetData() const |
257 | { |
258 | const Q_D(QQuick3DGeometry); |
259 | return d->m_targetBuffer; |
260 | } |
261 | |
262 | /*! |
263 | Returns the index buffer data. |
264 | */ |
265 | QByteArray 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 | */ |
276 | int 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 | */ |
287 | QQuick3DGeometry::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 | */ |
299 | int 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 | */ |
312 | QQuick3DGeometry::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 | */ |
323 | QQuick3DGeometry::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 | */ |
334 | QVector3D 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 | */ |
345 | QVector3D 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 | */ |
356 | int QQuick3DGeometry::stride() const |
357 | { |
358 | const Q_D(QQuick3DGeometry); |
359 | return d->m_stride; |
360 | } |
361 | |
362 | void 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 | */ |
375 | void 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 | */ |
396 | void 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 | */ |
416 | void 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 | */ |
439 | void 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 | */ |
456 | void 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 | */ |
477 | void 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 | */ |
503 | void 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 | */ |
516 | void 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 | */ |
548 | void 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 | */ |
608 | void 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 | */ |
644 | void 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 | */ |
674 | void 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 | */ |
702 | void 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 | */ |
718 | void 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 | */ |
737 | int 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 | */ |
748 | QVector3D 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 | */ |
761 | QVector3D 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 | */ |
774 | int 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 | */ |
787 | int 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 | */ |
798 | QString 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 | */ |
817 | void 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 | |
824 | static 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 | |
844 | static 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 | |
880 | static 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 | */ |
899 | QSSGRenderGraphObject *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 | |
977 | QQuick3DGeometry::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 | |
994 | QQuick3DGeometry::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 | |
1020 | QT_END_NAMESPACE |
1021 | |