aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorCristián Maureira-Fredes <Cristian.Maureira-Fredes@qt.io>2021-11-19 21:56:06 +0100
committerCristián Maureira-Fredes <Cristian.Maureira-Fredes@qt.io>2023-11-15 14:57:42 +0100
commitb78a848801da0446728162400160380aa2f49337 (patch)
tree5fb76631759055a3820ce286d89ac3d06ee05293 /examples
parent9557565b48b2a72b7697443930234f9f92c17781 (diff)
Add QtQuick3D Procedural texture example
Pick-to: 6.6 Task-number: PYSIDE-841 Co-authored-by: Dennis Oberst <dennis.oberst@qt.io> Change-Id: I191965e81aa93b812b128ad2045da1ef13f157b5 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'examples')
-rw-r--r--examples/quick3d/proceduraltexture/ProceduralTextureModule/Main.qml188
-rw-r--r--examples/quick3d/proceduraltexture/ProceduralTextureModule/app.qrc6
-rw-r--r--examples/quick3d/proceduraltexture/ProceduralTextureModule/qmldir2
-rw-r--r--examples/quick3d/proceduraltexture/doc/proceduraltexture-example.webpbin0 -> 5166 bytes
-rw-r--r--examples/quick3d/proceduraltexture/doc/proceduraltexture.rst12
-rw-r--r--examples/quick3d/proceduraltexture/gradienttexture.py106
-rw-r--r--examples/quick3d/proceduraltexture/main.py27
-rw-r--r--examples/quick3d/proceduraltexture/proceduraltexture.pyproject9
8 files changed, 350 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..b6dd47de8
--- /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() : ArrayBuffer {
+ 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
new file mode 100644
index 000000000..60bc9a3df
--- /dev/null
+++ b/examples/quick3d/proceduraltexture/doc/proceduraltexture-example.webp
Binary files differ
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..1511905cb
--- /dev/null
+++ b/examples/quick3d/proceduraltexture/gradienttexture.py
@@ -0,0 +1,106 @@
+# 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..971b0ecb4
--- /dev/null
+++ b/examples/quick3d/proceduraltexture/main.py
@@ -0,0 +1,27 @@
+# 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
+
+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"
+ ]
+}