| 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 | \nativetype 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 | emit geometryChanged(); |
| 955 | } |
| 956 | if (d->m_geometryBoundsChanged) { |
| 957 | geometry->setBounds(min: d->m_min, max: d->m_max); |
| 958 | emit geometryNodeDirty(); |
| 959 | d->m_geometryBoundsChanged = false; |
| 960 | } |
| 961 | if (d->m_targetChanged) { |
| 962 | geometry->clearTarget(); |
| 963 | geometry->setTargetData(d->m_usesOldTargetSemantics ? d->m_vertexBuffer : d->m_targetBuffer); |
| 964 | for (int i = 0; i < d->m_targetAttributeCount; ++i) { |
| 965 | geometry->addTargetAttribute(targetId: d->m_targetAttributes[i].targetId, |
| 966 | semantic: mapSemantic(s: d->m_targetAttributes[i].attr.semantic), |
| 967 | offset: d->m_targetAttributes[i].attr.offset, |
| 968 | stride: d->m_usesOldTargetSemantics ? d->m_stride : d->m_targetAttributes[i].stride); |
| 969 | } |
| 970 | d->m_targetChanged = false; |
| 971 | } |
| 972 | |
| 973 | DebugViewHelpers::ensureDebugObjectName(node: geometry, src: this); |
| 974 | |
| 975 | return node; |
| 976 | } |
| 977 | |
| 978 | QQuick3DGeometry::Attribute::Semantic QQuick3DGeometryPrivate::semanticFromName(const QByteArray &name) |
| 979 | { |
| 980 | static QMap<const QByteArray, QQuick3DGeometry::Attribute::Semantic> semanticMap; |
| 981 | if (semanticMap.isEmpty()) { |
| 982 | semanticMap[QSSGMesh::MeshInternal::getPositionAttrName()] = QQuick3DGeometry::Attribute::PositionSemantic; |
| 983 | semanticMap[QSSGMesh::MeshInternal::getNormalAttrName()] = QQuick3DGeometry::Attribute::NormalSemantic; |
| 984 | semanticMap[QSSGMesh::MeshInternal::getUV0AttrName()] = QQuick3DGeometry::Attribute::TexCoord0Semantic; |
| 985 | semanticMap[QSSGMesh::MeshInternal::getUV1AttrName()] = QQuick3DGeometry::Attribute::TexCoord1Semantic; |
| 986 | semanticMap[QSSGMesh::MeshInternal::getTexTanAttrName()] = QQuick3DGeometry::Attribute::TangentSemantic; |
| 987 | semanticMap[QSSGMesh::MeshInternal::getTexBinormalAttrName()] = QQuick3DGeometry::Attribute::BinormalSemantic; |
| 988 | semanticMap[QSSGMesh::MeshInternal::getColorAttrName()] = QQuick3DGeometry::Attribute::ColorSemantic; |
| 989 | semanticMap[QSSGMesh::MeshInternal::getWeightAttrName()] = QQuick3DGeometry::Attribute::WeightSemantic; |
| 990 | semanticMap[QSSGMesh::MeshInternal::getJointAttrName()] = QQuick3DGeometry::Attribute::JointSemantic; |
| 991 | } |
| 992 | return semanticMap[name]; |
| 993 | } |
| 994 | |
| 995 | QQuick3DGeometry::Attribute::ComponentType QQuick3DGeometryPrivate::toComponentType(QSSGMesh::Mesh::ComponentType ctype) |
| 996 | { |
| 997 | switch (ctype) { |
| 998 | case QSSGMesh::Mesh::ComponentType::Float32: |
| 999 | return QQuick3DGeometry::Attribute::F32Type; |
| 1000 | case QSSGMesh::Mesh::ComponentType::Int32: |
| 1001 | return QQuick3DGeometry::Attribute::I32Type; |
| 1002 | case QSSGMesh::Mesh::ComponentType::UnsignedInt16: |
| 1003 | return QQuick3DGeometry::Attribute::U16Type; |
| 1004 | case QSSGMesh::Mesh::ComponentType::UnsignedInt32: |
| 1005 | return QQuick3DGeometry::Attribute::U32Type; |
| 1006 | |
| 1007 | case QSSGMesh::Mesh::ComponentType::Float16: |
| 1008 | case QSSGMesh::Mesh::ComponentType::Float64: |
| 1009 | case QSSGMesh::Mesh::ComponentType::UnsignedInt8: |
| 1010 | case QSSGMesh::Mesh::ComponentType::Int8: |
| 1011 | case QSSGMesh::Mesh::ComponentType::Int16: |
| 1012 | case QSSGMesh::Mesh::ComponentType::UnsignedInt64: |
| 1013 | case QSSGMesh::Mesh::ComponentType::Int64: |
| 1014 | default: |
| 1015 | Q_ASSERT_X(0, "Incorrect datatype" , "QQuick3DGeometryPrivate::toComponentType" ); |
| 1016 | break; |
| 1017 | } |
| 1018 | return QQuick3DGeometry::Attribute::F32Type; |
| 1019 | } |
| 1020 | |
| 1021 | QT_END_NAMESPACE |
| 1022 | |