1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qquick3dtexturedata.h" |
5 | #include "qquick3dtexturedata_p.h" |
6 | #include <QtQuick3DUtils/private/qssgutils_p.h> |
7 | #include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | /*! |
12 | \qmltype TextureData |
13 | \inherits Object3D |
14 | \inqmlmodule QtQuick3D |
15 | \instantiates QQuick3DTextureData |
16 | \brief Base type for custom texture data. |
17 | |
18 | Custom texture data allows using application-generated texture data, that |
19 | can possibly change dynamically as well. To use custom texture data set the |
20 | \l{Texture::textureData}{textureData} property of \l {Texture} to reference |
21 | a TextureData object. |
22 | |
23 | Custom Texture data is implemented in C++ by creating a QQuick3DTextureData |
24 | instance, often subclassing it. The QQuick3DTextureData type is registered to |
25 | QML under the name of TextureData. Once the subclass is registered to QML, |
26 | Texture objects can start referencing it. |
27 | |
28 | An example of when to use this API is when there is a need to procedurally |
29 | generate a texture at runtime rather than loading a static image from a file. |
30 | |
31 | \code |
32 | import MyCustomTexture 1.0 |
33 | |
34 | Model { |
35 | source: "#Cube" |
36 | materials: [ |
37 | DefaultMaterial { |
38 | diffuseMap: diffuseMapCustomTexture |
39 | } |
40 | ] |
41 | } |
42 | |
43 | Texture { |
44 | id: diffuseMapCustomTexture |
45 | textureData: MyCustomTextureData { |
46 | |
47 | } |
48 | } |
49 | |
50 | \endcode |
51 | |
52 | \sa Texture |
53 | */ |
54 | |
55 | /*! |
56 | \class QQuick3DTextureData |
57 | \inmodule QtQuick3D |
58 | \inherits QQuick3DObject |
59 | \since 6.0 |
60 | \brief Base class for defining custom texture data. |
61 | |
62 | The QQuick3DTextureData class can be used to specify custom texture data for a |
63 | \l {Texture} in a Qt Quick 3D scene. |
64 | |
65 | While not strictly required, the typical usage is to inherit from this class. |
66 | The subclass is then exposed to QML by registering it to the type system. The |
67 | \l{Texture::textureData}{textureData} property of a \l {QtQuick3D::Texture}{Texture} |
68 | can then be set to reference an instance of the registered type. |
69 | |
70 | Example implementation: |
71 | |
72 | \code |
73 | class CustomTextureData : public QQuick3DTextureData |
74 | { |
75 | Q_OBJECT |
76 | ... properties ... |
77 | |
78 | public: |
79 | CustomTextureData() { regenerateTextureData(); } |
80 | |
81 | void setProperty(...) |
82 | { |
83 | // Change relevant internal data. |
84 | // ... |
85 | |
86 | // Update the texture data |
87 | regenerateTextureData(); |
88 | |
89 | // Finally, trigger an update. This is relevant in case nothing else |
90 | // is changed in the scene; this way we make sure a new frame will |
91 | // be rendered |
92 | update(); |
93 | } |
94 | private: |
95 | void regenerateTextureData() |
96 | { |
97 | QByteArray textureData; |
98 | textureData = generateTextureData(); |
99 | setTextureData(textureData); |
100 | setSize(QSize(256, 256)); |
101 | setFormat(QQuick3DTextureData::Format::RGBA8) |
102 | setHasTransparency(true); |
103 | } |
104 | }; |
105 | \endcode |
106 | |
107 | This class can then be registered as a QML type and used with \l {QtQuick3D::Texture}{Texture}. |
108 | |
109 | In Qt 5 type registration happened with qmlRegisterType: |
110 | \code |
111 | qmlRegisterType<MyCustomTextureData>("Example", 1, 0, "MyCustomTextureData"); |
112 | \endcode |
113 | |
114 | In Qt 6 the default approach is to use automatic registration with the help |
115 | of the build system. Instead of calling qmlRegisterType, the \c{.pro} file |
116 | can now contain: |
117 | |
118 | \code |
119 | CONFIG += qmltypes |
120 | QML_IMPORT_NAME = Example |
121 | QML_IMPORT_MAJOR_VERSION = 1 |
122 | \endcode |
123 | |
124 | With CMake, automatic registration is the default behavior, so no special |
125 | settings are needed beyond basic QML module setup: |
126 | \code |
127 | qt_add_qml_module(application |
128 | URI Example |
129 | VERSION 1.0 |
130 | ) |
131 | \endcode |
132 | |
133 | The class implementation should add QML_NAMED_ELEMENT: |
134 | |
135 | \code |
136 | class CustomTextureData : public QQuick3DTextureData |
137 | { |
138 | Q_OBJECT |
139 | QML_NAMED_ELEMENT(MyCustomTextureData) |
140 | ... |
141 | }; |
142 | \endcode |
143 | |
144 | The QML code can then use the custom type: |
145 | |
146 | \code |
147 | import Example 1.0 |
148 | |
149 | Model { |
150 | source: "#Cube" |
151 | materials: [ |
152 | DefaultMaterial { |
153 | diffuseMap: diffuseMapCustomTexture |
154 | } |
155 | ] |
156 | Texture { |
157 | id: diffuseMapCustomTexture |
158 | textureData: MyCustomTextureData { } |
159 | } |
160 | } |
161 | \endcode |
162 | */ |
163 | |
164 | /*! |
165 | \enum QQuick3DTextureData::Format |
166 | Returns the color format of the texture data assigned in \l textureData property. |
167 | |
168 | \value None The color format is not defined |
169 | \value RGBA8 The color format is considered as 8-bit integer in R, G, B and alpha channels. |
170 | \value RGBA16F The color format is considered as 16-bit float in R,G,B and alpha channels. |
171 | \value RGBA32F The color format is considered as 32-bit float in R, G, B and alpha channels. |
172 | \value RGBE8 The color format is considered as 8-bit mantissa in the R, G, and B channels and 8-bit shared exponent. |
173 | \value R8 The color format is considered as 8-bit integer in R channel. |
174 | \value R16 The color format is considered as 16-bit integer in R channel. |
175 | \value R16F The color format is considered as 16-bit float in R channel. |
176 | \value R32F The color format is considered as 32-bit float R channel. |
177 | \value BC1 The color format is considred as BC1 compressed format with R, G, B, and alpha channels. |
178 | \value BC2 The color format is considred as BC2 compressed format with R, G, B, and alpha channels. |
179 | \value BC3 The color format is considred as BC3 compressed format with R, G, B, and alpha channels. |
180 | \value BC4 The color format is considred as BC4 compressed format with one color channel. |
181 | \value BC5 The color format is considred as BC5 compressed format with two color channels. |
182 | \value BC6H The color format is considred as BC6H compressed format with three high dynamic range color channels. |
183 | \value BC7 The color format is considred as BC7 compressed format with R, G, B, and alpha channels. |
184 | \value DXT1_RGBA The color format is considered as DXT1 compressed format with R, G, B and alpha channels. |
185 | \value DXT1_RGB The color format is considered as DXT1 compressed format with R, G and B channels. |
186 | \value DXT3_RGBA The color format is considered as DXT3 compressed format with R, G, B and alpha channels. |
187 | \value DXT5_RGBA The color format is considered as DXT5 compressed format with R, G, B and alpha channels. |
188 | \value ETC2_RGB8 The color format is considered as ETC2 compressed format for RGB888 data |
189 | \value ETC2_RGB8A1 The color format is considered as ETC2 compressed format for RGBA data where alpha is 1-bit. |
190 | \value ETC2_RGBA8 The color format is considered as ETC2 compressed format with RGBA8888 data. |
191 | \value ASTC_4x4 The color format is considered as ASTC compressed format with 4x4 block footprint. |
192 | \value ASTC_5x4 The color format is considered as ASTC compressed format with 5x4 block footprint. |
193 | \value ASTC_5x5 The color format is considered as ASTC compressed format with 5x5 block footprint. |
194 | \value ASTC_6x5 The color format is considered as ASTC compressed format with 6x5 block footprint. |
195 | \value ASTC_6x6 The color format is considered as ASTC compressed format with 6x6 block footprint. |
196 | \value ASTC_8x5 The color format is considered as ASTC compressed format with 8x5 block footprint. |
197 | \value ASTC_8x6 The color format is considered as ASTC compressed format with 8x6 block footprint. |
198 | \value ASTC_8x8 The color format is considered as ASTC compressed format with 8x8 block footprint. |
199 | \value ASTC_10x5 The color format is considered as ASTC compressed format with 10x5 block footprint. |
200 | \value ASTC_10x6 The color format is considered as ASTC compressed format with 10x6 block footprint. |
201 | \value ASTC_10x8 The color format is considered as ASTC compressed format with 10x8 block footprint. |
202 | \value ASTC_10x10 The color format is considered as ASTC compressed format with 10x10 block footprint. |
203 | \value ASTC_12x10 The color format is considered as ASTC compressed format with 12x10 block footprint. |
204 | \value ASTC_12x12 The color format is considered as ASTC compressed format with 12x12 block footprint. |
205 | |
206 | \note With the exception of \c RGBA8, not every format is supported at runtime as this |
207 | depends on which backend is being used as well which hardware is being used. |
208 | |
209 | \note \c RGBE is internally represented as an \c RGBA8 but is intepreted as described when used |
210 | as a lightProbe or skybox texture. |
211 | |
212 | \note Using the value \c None will assume the default value of \c RGBA8 |
213 | */ |
214 | |
215 | |
216 | QQuick3DTextureDataPrivate::QQuick3DTextureDataPrivate() |
217 | : QQuick3DObjectPrivate(QQuick3DTextureDataPrivate::Type::TextureData) |
218 | { |
219 | |
220 | } |
221 | |
222 | QQuick3DTextureData::QQuick3DTextureData(QQuick3DObject *parent) |
223 | : QQuick3DObject(*new QQuick3DTextureDataPrivate, parent) |
224 | { |
225 | |
226 | } |
227 | |
228 | QQuick3DTextureData::~QQuick3DTextureData() |
229 | { |
230 | |
231 | } |
232 | |
233 | /*! |
234 | Returns the current texture data defined by this item. |
235 | */ |
236 | const QByteArray QQuick3DTextureData::textureData() const |
237 | { |
238 | const Q_D(QQuick3DTextureData); |
239 | return d->textureData; |
240 | |
241 | } |
242 | |
243 | /*! |
244 | Sets the texture data. The contents of \a data must respect the \l size and |
245 | \l format properties as the backend will try and upload and use the data as if |
246 | it were a texture of size and format, and if there is any deviation the result |
247 | will be somewhere between incorrect rendering of the texture, or potentially a crash. |
248 | */ |
249 | |
250 | void QQuick3DTextureData::setTextureData(const QByteArray &data) |
251 | { |
252 | Q_D(QQuick3DTextureData); |
253 | d->textureData = data; |
254 | d->textureDataDirty = true; |
255 | update(); |
256 | } |
257 | |
258 | /*! |
259 | Returns the size of the texture data in pixels. |
260 | */ |
261 | QSize QQuick3DTextureData::size() const |
262 | { |
263 | const Q_D(QQuick3DTextureData); |
264 | return d->size; |
265 | } |
266 | |
267 | /*! |
268 | Sets the \a size of the texture data in pixels. |
269 | */ |
270 | void QQuick3DTextureData::setSize(const QSize &size) |
271 | { |
272 | Q_D(QQuick3DTextureData); |
273 | d->size = size; |
274 | update(); |
275 | } |
276 | |
277 | /*! |
278 | Returns the depth of the texture data in pixels. |
279 | */ |
280 | int QQuick3DTextureData::depth() const |
281 | { |
282 | const Q_D(QQuick3DTextureData); |
283 | return d->depth; |
284 | } |
285 | |
286 | /*! |
287 | Sets the \a depth of the texture data in pixels. Setting the depth above |
288 | 0 means that the texture is handled as a 3D texture. |
289 | */ |
290 | void QQuick3DTextureData::setDepth(int depth) |
291 | { |
292 | Q_D(QQuick3DTextureData); |
293 | d->depth = depth; |
294 | update(); |
295 | } |
296 | |
297 | /*! |
298 | Returns the format of the texture data. |
299 | */ |
300 | QQuick3DTextureData::Format QQuick3DTextureData::format() const |
301 | { |
302 | const Q_D(QQuick3DTextureData); |
303 | return d->format; |
304 | } |
305 | |
306 | /*! |
307 | Sets the \a format of the texture data. |
308 | |
309 | The default format is /c RGBA8 |
310 | */ |
311 | void QQuick3DTextureData::setFormat(QQuick3DTextureData::Format format) |
312 | { |
313 | Q_D(QQuick3DTextureData); |
314 | d->format = format; |
315 | update(); |
316 | } |
317 | |
318 | /*! |
319 | Returns \c true if the texture data has transparency. |
320 | |
321 | The default value is \c false. |
322 | */ |
323 | bool QQuick3DTextureData::hasTransparency() const |
324 | { |
325 | const Q_D(QQuick3DTextureData); |
326 | return d->hasTransparency; |
327 | } |
328 | |
329 | /*! |
330 | Set \a hasTransparency to true if the texture data has an active alpha |
331 | channel with non-opaque values. |
332 | |
333 | This is used as an optimization by the engine so that for formats that |
334 | do support an alpha channel do not need to have each value checked for |
335 | non-opaque values. |
336 | */ |
337 | void QQuick3DTextureData::setHasTransparency(bool hasTransparency) |
338 | { |
339 | Q_D(QQuick3DTextureData); |
340 | d->hasTransparency = hasTransparency; |
341 | update(); |
342 | } |
343 | |
344 | |
345 | static QSSGRenderTextureFormat::Format convertToBackendFormat(QQuick3DTextureData::Format format) |
346 | { |
347 | switch (format) { |
348 | case QQuick3DTextureData::None: |
349 | case QQuick3DTextureData::RGBA8: |
350 | return QSSGRenderTextureFormat::RGBA8; |
351 | case QQuick3DTextureData::RGBA16F: |
352 | return QSSGRenderTextureFormat::RGBA16F; |
353 | case QQuick3DTextureData::RGBA32F: |
354 | return QSSGRenderTextureFormat::RGBA32F; |
355 | case QQuick3DTextureData::RGBE8: |
356 | return QSSGRenderTextureFormat::RGBE8; |
357 | case QQuick3DTextureData::R8: |
358 | return QSSGRenderTextureFormat::R8; |
359 | case QQuick3DTextureData::R16: |
360 | return QSSGRenderTextureFormat::R16; |
361 | case QQuick3DTextureData::R16F: |
362 | return QSSGRenderTextureFormat::R16F; |
363 | case QQuick3DTextureData::R32F: |
364 | return QSSGRenderTextureFormat::R32F; |
365 | case QQuick3DTextureData::BC1: |
366 | return QSSGRenderTextureFormat::BC1; |
367 | case QQuick3DTextureData::BC2: |
368 | return QSSGRenderTextureFormat::BC2; |
369 | case QQuick3DTextureData::BC3: |
370 | return QSSGRenderTextureFormat::BC3; |
371 | case QQuick3DTextureData::BC4: |
372 | return QSSGRenderTextureFormat::BC4; |
373 | case QQuick3DTextureData::BC5: |
374 | return QSSGRenderTextureFormat::BC5; |
375 | case QQuick3DTextureData::BC6H: |
376 | return QSSGRenderTextureFormat::BC6H; |
377 | case QQuick3DTextureData::BC7: |
378 | return QSSGRenderTextureFormat::BC7; |
379 | case QQuick3DTextureData::DXT1_RGBA: |
380 | return QSSGRenderTextureFormat::RGBA_DXT1; |
381 | case QQuick3DTextureData::DXT1_RGB: |
382 | return QSSGRenderTextureFormat::RGB_DXT1; |
383 | case QQuick3DTextureData::DXT3_RGBA: |
384 | return QSSGRenderTextureFormat::RGBA_DXT3; |
385 | case QQuick3DTextureData::DXT5_RGBA: |
386 | return QSSGRenderTextureFormat::RGBA_DXT5; |
387 | case QQuick3DTextureData::ETC2_RGB8: |
388 | return QSSGRenderTextureFormat::RGB8_ETC2; |
389 | case QQuick3DTextureData::ETC2_RGB8A1: |
390 | return QSSGRenderTextureFormat::RGB8_PunchThrough_Alpha1_ETC2; |
391 | case QQuick3DTextureData::ETC2_RGBA8: |
392 | return QSSGRenderTextureFormat::RGBA8_ETC2_EAC; |
393 | case QQuick3DTextureData::ASTC_4x4: |
394 | return QSSGRenderTextureFormat::RGBA_ASTC_4x4; |
395 | case QQuick3DTextureData::ASTC_5x4: |
396 | return QSSGRenderTextureFormat::RGBA_ASTC_5x4; |
397 | case QQuick3DTextureData::ASTC_5x5: |
398 | return QSSGRenderTextureFormat::RGBA_ASTC_5x5; |
399 | case QQuick3DTextureData::ASTC_6x5: |
400 | return QSSGRenderTextureFormat::RGBA_ASTC_6x5; |
401 | case QQuick3DTextureData::ASTC_6x6: |
402 | return QSSGRenderTextureFormat::RGBA_ASTC_6x6; |
403 | case QQuick3DTextureData::ASTC_8x5: |
404 | return QSSGRenderTextureFormat::RGBA_ASTC_8x5; |
405 | case QQuick3DTextureData::ASTC_8x6: |
406 | return QSSGRenderTextureFormat::RGBA_ASTC_8x6; |
407 | case QQuick3DTextureData::ASTC_8x8: |
408 | return QSSGRenderTextureFormat::RGBA_ASTC_8x8; |
409 | case QQuick3DTextureData::ASTC_10x5: |
410 | return QSSGRenderTextureFormat::RGBA_ASTC_10x5; |
411 | case QQuick3DTextureData::ASTC_10x6: |
412 | return QSSGRenderTextureFormat::RGBA_ASTC_10x6; |
413 | case QQuick3DTextureData::ASTC_10x8: |
414 | return QSSGRenderTextureFormat::RGBA_ASTC_10x8; |
415 | case QQuick3DTextureData::ASTC_10x10: |
416 | return QSSGRenderTextureFormat::RGBA_ASTC_10x10; |
417 | case QQuick3DTextureData::ASTC_12x10: |
418 | return QSSGRenderTextureFormat::RGBA_ASTC_12x10; |
419 | case QQuick3DTextureData::ASTC_12x12: |
420 | return QSSGRenderTextureFormat::RGBA_ASTC_12x12; |
421 | default: |
422 | return QSSGRenderTextureFormat::RGBA8; |
423 | } |
424 | } |
425 | |
426 | /*! |
427 | \internal |
428 | */ |
429 | QSSGRenderGraphObject *QQuick3DTextureData::updateSpatialNode(QSSGRenderGraphObject *node) |
430 | { |
431 | Q_D(QQuick3DTextureData); |
432 | if (!node) { |
433 | markAllDirty(); |
434 | node = new QSSGRenderTextureData(); |
435 | } |
436 | QQuick3DObject::updateSpatialNode(node); |
437 | auto *textureData = static_cast<QSSGRenderTextureData*>(node); |
438 | |
439 | bool changed = false; |
440 | |
441 | // Use a dirty flag so we don't compare large buffer values |
442 | if (d->textureDataDirty) { |
443 | d->textureDataDirty = false; |
444 | textureData->setTextureData(d->textureData); |
445 | changed = true; |
446 | } |
447 | |
448 | // Can't use qUpdateIfNeeded unfortunately |
449 | if (d->size != textureData->size()) { |
450 | textureData->setSize(d->size); |
451 | changed = true; |
452 | } |
453 | |
454 | if (d->depth != textureData->depth()) { |
455 | textureData->setDepth(d->depth); |
456 | changed = true; |
457 | } |
458 | |
459 | QSSGRenderTextureFormat format = convertToBackendFormat(format: d->format); |
460 | if (format != textureData->format()) { |
461 | textureData->setFormat(format); |
462 | changed = true; |
463 | } |
464 | |
465 | if (d->hasTransparency != textureData->hasTransparancy()) { |
466 | textureData->setHasTransparency(d->hasTransparency); |
467 | changed = true; |
468 | } |
469 | |
470 | if (changed) |
471 | emit textureDataNodeDirty(); |
472 | |
473 | DebugViewHelpers::ensureDebugObjectName(node: textureData, src: this); |
474 | |
475 | return node; |
476 | } |
477 | |
478 | void QQuick3DTextureData::markAllDirty() |
479 | { |
480 | QQuick3DObject::markAllDirty(); |
481 | } |
482 | |
483 | |
484 | QT_END_NAMESPACE |
485 | |