diff options
Diffstat (limited to 'examples/quick3d/proceduraltexture')
8 files changed, 352 insertions, 0 deletions
diff --git a/examples/quick3d/proceduraltexture/ProceduralTextureModule/Main.qml b/examples/quick3d/proceduraltexture/ProceduralTextureModule/Main.qml new file mode 100644 index 000000000..610c08a27 --- /dev/null +++ b/examples/quick3d/proceduraltexture/ProceduralTextureModule/Main.qml @@ -0,0 +1,188 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick3D +import QtQuick3D.Helpers +import QtQuick.Controls +import QtQuick.Layouts + +import ProceduralTextureModule + +ApplicationWindow { + id: window + width: 480 + height: 320 + visible: true + title: "Procedural Texture Example" + + QtObject { + id: applicationState + property int size: size256.checked ? 256 : 16 + property color startColor: "#00dbde" + property color endColor: "#fc00ff" + property int filterMode: size === 256 ? Texture.Linear : Texture.Nearest + property Texture texture: pythonModeRadio.checked ? textureFromPython : textureFromQML + + function randomColor() : color { + return Qt.rgba(Math.random(), + Math.random(), + Math.random(), + 1.0); + } + } + + View3D { + anchors.fill: parent + + DirectionalLight { + } + + PerspectiveCamera { + z: 300 + } + + Texture { + id: textureFromPython + + minFilter: applicationState.filterMode + magFilter: applicationState.filterMode + textureData: gradientTexture + + GradientTexture { + id: gradientTexture + startColor: applicationState.startColor + endColor: applicationState.endColor + width: applicationState.size + height: width + } + } + + Texture { + id: textureFromQML + minFilter: applicationState.filterMode + magFilter: applicationState.filterMode + textureData: gradientTextureDataQML + + ProceduralTextureData { + id: gradientTextureDataQML + + property color startColor: applicationState.startColor + property color endColor: applicationState.endColor + width: applicationState.size + height: width + textureData: generateTextureData() + + function linearInterpolate(startColor : color, endColor : color, fraction : real) : color{ + return Qt.rgba( + startColor.r + (endColor.r - startColor.r) * fraction, + startColor.g + (endColor.g - startColor.g) * fraction, + startColor.b + (endColor.b - startColor.b) * fraction, + startColor.a + (endColor.a - startColor.a) * fraction + ); + } + + function generateTextureData() { + let dataBuffer = new ArrayBuffer(width * height * 4) + let data = new Uint8Array(dataBuffer) + + let gradientScanline = new Uint8Array(width * 4); + + for (let x = 0; x < width; ++x) { + let color = linearInterpolate(startColor, endColor, x / width); + let offset = x * 4; + gradientScanline[offset + 0] = color.r * 255; + gradientScanline[offset + 1] = color.g * 255; + gradientScanline[offset + 2] = color.b * 255; + gradientScanline[offset + 3] = color.a * 255; + } + + for (let y = 0; y < height; ++y) { + data.set(gradientScanline, y * width * 4); + } + + return dataBuffer; + } + } + } + + Model { + source: "#Cube" + + materials: [ + PrincipledMaterial { + baseColorMap: applicationState.texture + } + ] + + PropertyAnimation on eulerRotation.y { + from: 0 + to: 360 + duration: 5000 + loops: Animation.Infinite + running: true + } + } + } + + Pane { + ColumnLayout { + + GroupBox { + title: "Size:" + + ButtonGroup { + id: sizeGroup + } + + ColumnLayout { + RadioButton { + id: size256 + text: "256x256" + checked: true + ButtonGroup.group: sizeGroup + } + RadioButton { + id: size512 + text: "16x16" + checked: false + ButtonGroup.group: sizeGroup + } + } + } + + GroupBox { + title: "Backend:" + + ButtonGroup { + id: backendGroup + } + + ColumnLayout { + RadioButton { + id: pythonModeRadio + text: "Python" + checked: true + ButtonGroup.group: backendGroup + } + RadioButton { + id: qmlModeRadio + text: "QML" + checked: false + ButtonGroup.group: backendGroup + } + } + + } + + Button { + text: "Random Start Color" + onClicked: applicationState.startColor = applicationState.randomColor(); + } + Button { + text: "Random End Color" + onClicked: applicationState.endColor = applicationState.randomColor(); + } + } + } +} diff --git a/examples/quick3d/proceduraltexture/ProceduralTextureModule/app.qrc b/examples/quick3d/proceduraltexture/ProceduralTextureModule/app.qrc new file mode 100644 index 000000000..f0719ad5b --- /dev/null +++ b/examples/quick3d/proceduraltexture/ProceduralTextureModule/app.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/qt/qml/ProceduralTextureModule"> + <file>qmldir</file> + <file>Main.qml</file> + </qresource> +</RCC> diff --git a/examples/quick3d/proceduraltexture/ProceduralTextureModule/qmldir b/examples/quick3d/proceduraltexture/ProceduralTextureModule/qmldir new file mode 100644 index 000000000..7a5644075 --- /dev/null +++ b/examples/quick3d/proceduraltexture/ProceduralTextureModule/qmldir @@ -0,0 +1,2 @@ +module ProceduralTextureModule +Main 1.0 Main.qml diff --git a/examples/quick3d/proceduraltexture/doc/proceduraltexture-example.webp b/examples/quick3d/proceduraltexture/doc/proceduraltexture-example.webp Binary files differnew file mode 100644 index 000000000..60bc9a3df --- /dev/null +++ b/examples/quick3d/proceduraltexture/doc/proceduraltexture-example.webp diff --git a/examples/quick3d/proceduraltexture/doc/proceduraltexture.rst b/examples/quick3d/proceduraltexture/doc/proceduraltexture.rst new file mode 100644 index 000000000..f2af3ee52 --- /dev/null +++ b/examples/quick3d/proceduraltexture/doc/proceduraltexture.rst @@ -0,0 +1,12 @@ +Procedural Texture Example +========================== + +Demonstrates how to provide custom texture data from Python. + +.. image:: proceduraltexture-example.webp + :width: 400 + :alt: QtQuick3D Procedural Texture Example + +In this example, we leverage `QQuick3DTextureData` and the `textureData` +property of `Texture` to produce texture data dynamically from Python, rather +than sourcing it from a static asset. diff --git a/examples/quick3d/proceduraltexture/gradienttexture.py b/examples/quick3d/proceduraltexture/gradienttexture.py new file mode 100644 index 000000000..a577f7ebd --- /dev/null +++ b/examples/quick3d/proceduraltexture/gradienttexture.py @@ -0,0 +1,105 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtCore import Signal, Property, QSize +from PySide6.QtGui import QColor +from PySide6.QtQuick3D import QQuick3DTextureData +from PySide6.QtQml import QmlElement + +QML_IMPORT_NAME = "ProceduralTextureModule" +QML_IMPORT_MAJOR_VERSION = 1 + + +@QmlElement +class GradientTexture(QQuick3DTextureData): + + heightChanged = Signal(int) + widthChanged = Signal(int) + startColorChanged = Signal(QColor) + endColorChanged = Signal(QColor) + + def __init__(self, parent=None): + super().__init__(parent=parent) + self._height = 256 + self._width = 256 + self._startcolor = QColor("#d4fc79") + self._endcolor = QColor("#96e6a1") + self.updateTexture() + + @Property(int, notify=heightChanged) + def height(self): + return self._height + + @height.setter + def height(self, val): + if self._height == val: + return + self._height = val + self.updateTexture() + self.heightChanged.emit(self._height) + + @Property(int, notify=widthChanged) + def width(self): + return self._width + + @width.setter + def width(self, val): + if self._width == val: + return + self._width = val + self.updateTexture() + self.widthChanged.emit(self._width) + + @Property(QColor, notify=startColorChanged) + def startColor(self): + return self._startcolor + + @startColor.setter + def startColor(self, val): + if self._startcolor == val: + return + self._startcolor = val + self.updateTexture() + self.startColorChanged.emit(self._startcolor) + + @Property(QColor, notify=endColorChanged) + def endColor(self): + return self._endcolor + + @endColor.setter + def endColor(self, val): + if self._endcolor == val: + return + self._endcolor = val + self.updateTexture() + self.endColorChanged.emit(self._endcolor) + + def updateTexture(self): + self.setSize(QSize(self._width, self._height)) + self.setFormat(QQuick3DTextureData.RGBA8) + self.setHasTransparency(False) + self.setTextureData(self.generate_texture()) + + def generate_texture(self): + # Generate a horizontal gradient by interpolating between start and end colors. + gradientScanline = [ + self.linear_interpolate(self._startcolor, self._endcolor, x / self._width) + for x in range(self._width) + ] + # Convert the gradient colors to a flattened list of RGBA values. + flattenedGradient = [ + component + for color in gradientScanline + for component in (color.red(), color.green(), color.blue(), 255) + ] + # Repeat the gradient vertically to form the texture. + return bytearray(flattenedGradient * self._height) + + def linear_interpolate(self, color1, color2, value): + output = QColor() + + output.setRedF(color1.redF() + (value * (color2.redF() - color1.redF()))) + output.setGreenF(color1.greenF() + (value * (color2.greenF() - color1.greenF()))) + output.setBlueF(color1.blueF() + (value * (color2.blueF() - color1.blueF()))) + + return output diff --git a/examples/quick3d/proceduraltexture/main.py b/examples/quick3d/proceduraltexture/main.py new file mode 100644 index 000000000..a732cc015 --- /dev/null +++ b/examples/quick3d/proceduraltexture/main.py @@ -0,0 +1,30 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtGui import QGuiApplication +from PySide6.QtQml import QQmlApplicationEngine + +from gradienttexture import GradientTexture # noqa: F401 + +from pathlib import Path + +import os +import sys + +if __name__ == "__main__": + app = QGuiApplication(sys.argv) + app.setOrganizationName("QtProject") + app.setApplicationName("ProceduralTexture") + + engine = QQmlApplicationEngine() + app_dir = Path(__file__).parent + engine.addImportPath(os.fspath(app_dir)) + engine.loadFromModule("ProceduralTextureModule", "Main") + + if not engine.rootObjects(): + sys.exit(-1) + + ex = app.exec() + del engine + + sys.exit(ex) diff --git a/examples/quick3d/proceduraltexture/proceduraltexture.pyproject b/examples/quick3d/proceduraltexture/proceduraltexture.pyproject new file mode 100644 index 000000000..0815cd8b8 --- /dev/null +++ b/examples/quick3d/proceduraltexture/proceduraltexture.pyproject @@ -0,0 +1,9 @@ +{ + "files": [ + "main.py", + "gradienttexture.py", + "ProceduralTextureModule/qmldir", + "ProceduralTextureModule/Main.qml", + "ProceduralTextureModule/app.qrc" + ] +} |