diff options
author | Friedemann Kleint <[email protected]> | 2022-09-15 13:21:53 +0200 |
---|---|---|
committer | Friedemann Kleint <[email protected]> | 2022-09-16 10:30:36 +0200 |
commit | cf32b66adbfb489cd6e5d5c0bf3f741b59ba204c (patch) | |
tree | 44be69c9487f5d4db1092d061a555bd6001c1ab4 /examples/quick/scenegraph/openglunderqml | |
parent | b20d6f6906f91f9df608d7800f4e27f7a7160abe (diff) |
Move examples around
Change the directory structure to closer match that of Qt.
Task-number: PYSIDE-841
Change-Id: I87aca346b6654aafe94dd1fb83c184c182ceb2e6
Reviewed-by: Qt CI Bot <[email protected]>
Reviewed-by: Cristian Maureira-Fredes <[email protected]>
Diffstat (limited to 'examples/quick/scenegraph/openglunderqml')
-rw-r--r-- | examples/quick/scenegraph/openglunderqml/doc/openglunderqml.rst | 21 | ||||
-rw-r--r-- | examples/quick/scenegraph/openglunderqml/doc/squircle.png | bin | 0 -> 37963 bytes | |||
-rw-r--r-- | examples/quick/scenegraph/openglunderqml/main.py | 27 | ||||
-rw-r--r-- | examples/quick/scenegraph/openglunderqml/main.qml | 39 | ||||
-rw-r--r-- | examples/quick/scenegraph/openglunderqml/openglunderqml.pyproject | 3 | ||||
-rw-r--r-- | examples/quick/scenegraph/openglunderqml/squircle.py | 79 | ||||
-rw-r--r-- | examples/quick/scenegraph/openglunderqml/squirclerenderer.py | 99 |
7 files changed, 268 insertions, 0 deletions
diff --git a/examples/quick/scenegraph/openglunderqml/doc/openglunderqml.rst b/examples/quick/scenegraph/openglunderqml/doc/openglunderqml.rst new file mode 100644 index 000000000..6a89a99d9 --- /dev/null +++ b/examples/quick/scenegraph/openglunderqml/doc/openglunderqml.rst @@ -0,0 +1,21 @@ +OpenGL under QML Squircle +========================= + +The OpenGL under QML example shows how an application can make use of the +QQuickWindow::beforeRendering() signal to draw custom OpenGL content under a Qt +Quick scene. This signal is emitted at the start of every frame, before the +scene graph starts its rendering, thus any OpenGL draw calls that are made as +a response to this signal, will stack under the Qt Quick items. + +As an alternative, applications that wish to render OpenGL content on top of +the Qt Quick scene, can do so by connecting to the +QQuickWindow::afterRendering() signal. + +In this example, we will also see how it is possible to have values that are +exposed to QML which affect the OpenGL rendering. We animate the threshold +value using a NumberAnimation in the QML file and this value is used by the +OpenGL shader program that draws the squircles. + +.. image:: squircle.png + :width: 400 + :alt: Squircle Screenshot diff --git a/examples/quick/scenegraph/openglunderqml/doc/squircle.png b/examples/quick/scenegraph/openglunderqml/doc/squircle.png Binary files differnew file mode 100644 index 000000000..c099a6b7e --- /dev/null +++ b/examples/quick/scenegraph/openglunderqml/doc/squircle.png diff --git a/examples/quick/scenegraph/openglunderqml/main.py b/examples/quick/scenegraph/openglunderqml/main.py new file mode 100644 index 000000000..a79d1bed8 --- /dev/null +++ b/examples/quick/scenegraph/openglunderqml/main.py @@ -0,0 +1,27 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import sys +from pathlib import Path + +from PySide6.QtCore import QUrl +from PySide6.QtGui import QGuiApplication +from PySide6.QtQuick import QQuickView, QQuickWindow, QSGRendererInterface + +from squircle import Squircle + +if __name__ == "__main__": + app = QGuiApplication(sys.argv) + + QQuickWindow.setGraphicsApi(QSGRendererInterface.OpenGL) + + view = QQuickView() + view.setResizeMode(QQuickView.SizeRootObjectToView) + qml_file = Path(__file__).parent / "main.qml" + view.setSource(QUrl.fromLocalFile(qml_file)) + + if view.status() == QQuickView.Error: + sys.exit(-1) + view.show() + + sys.exit(app.exec()) diff --git a/examples/quick/scenegraph/openglunderqml/main.qml b/examples/quick/scenegraph/openglunderqml/main.qml new file mode 100644 index 000000000..73bfa3262 --- /dev/null +++ b/examples/quick/scenegraph/openglunderqml/main.qml @@ -0,0 +1,39 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import OpenGLUnderQML + +Item { + + width: 320 + height: 480 + + Squircle { + SequentialAnimation on t { + NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad } + NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad } + loops: Animation.Infinite + running: true + } + } + Rectangle { + color: Qt.rgba(1, 1, 1, 0.7) + radius: 10 + border.width: 1 + border.color: "white" + anchors.fill: label + anchors.margins: -10 + } + + Text { + id: label + color: "black" + wrapMode: Text.WordWrap + text: "The background here is a squircle rendered with raw OpenGL using the 'beforeRender()' signal in QQuickWindow. This text label and its border is rendered using QML" + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.margins: 20 + } +} diff --git a/examples/quick/scenegraph/openglunderqml/openglunderqml.pyproject b/examples/quick/scenegraph/openglunderqml/openglunderqml.pyproject new file mode 100644 index 000000000..e7cfbc570 --- /dev/null +++ b/examples/quick/scenegraph/openglunderqml/openglunderqml.pyproject @@ -0,0 +1,3 @@ +{ + "files": [ "main.py", "main.qml", "squircle.py", "squirclerenderer.py"] +} diff --git a/examples/quick/scenegraph/openglunderqml/squircle.py b/examples/quick/scenegraph/openglunderqml/squircle.py new file mode 100644 index 000000000..d2900198b --- /dev/null +++ b/examples/quick/scenegraph/openglunderqml/squircle.py @@ -0,0 +1,79 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtCore import Property, QRunnable, Qt, Signal, Slot +from PySide6.QtQml import QmlElement +from PySide6.QtQuick import QQuickItem, QQuickWindow + +from squirclerenderer import SquircleRenderer + +# To be used on the @QmlElement decorator +# (QML_IMPORT_MINOR_VERSION is optional) +QML_IMPORT_NAME = "OpenGLUnderQML" +QML_IMPORT_MAJOR_VERSION = 1 + + +class CleanupJob(QRunnable): + def __init__(self, renderer): + super().__init__() + self._renderer = renderer + + def run(self): + del self._renderer + + +@QmlElement +class Squircle(QQuickItem): + + tChanged = Signal() + + def __init__(self, parent=None): + super().__init__(parent) + self._t = 0.0 + self._renderer = None + self.windowChanged.connect(self.handleWindowChanged) + + def t(self): + return self._t + + def setT(self, value): + if self._t == value: + return + self._t = value + self.tChanged.emit() + if self.window(): + self.window().update() + + @Slot(QQuickWindow) + def handleWindowChanged(self, win): + if win: + win.beforeSynchronizing.connect(self.sync, type=Qt.DirectConnection) + win.sceneGraphInvalidated.connect(self.cleanup, type=Qt.DirectConnection) + win.setColor(Qt.black) + self.sync() + + @Slot() + def cleanup(self): + del self._renderer + self._renderer = None + + @Slot() + def sync(self): + window = self.window() + if not self._renderer: + self._renderer = SquircleRenderer() + window.beforeRendering.connect(self._renderer.init, Qt.DirectConnection) + window.beforeRenderPassRecording.connect( + self._renderer.paint, Qt.DirectConnection + ) + self._renderer.setViewportSize(window.size() * window.devicePixelRatio()) + self._renderer.setT(self._t) + self._renderer.setWindow(window) + + def releaseResources(self): + self.window().scheduleRenderJob( + CleanupJob(self._renderer), QQuickWindow.BeforeSynchronizingStage + ) + self._renderer = None + + t = Property(float, t, setT, notify=tChanged) diff --git a/examples/quick/scenegraph/openglunderqml/squirclerenderer.py b/examples/quick/scenegraph/openglunderqml/squirclerenderer.py new file mode 100644 index 000000000..5d1759251 --- /dev/null +++ b/examples/quick/scenegraph/openglunderqml/squirclerenderer.py @@ -0,0 +1,99 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from textwrap import dedent + +import numpy as np +from OpenGL.GL import (GL_ARRAY_BUFFER, GL_BLEND, GL_DEPTH_TEST, GL_FLOAT, + GL_ONE, GL_SRC_ALPHA, GL_TRIANGLE_STRIP) +from PySide6.QtCore import QSize, Slot +from PySide6.QtGui import QOpenGLFunctions +from PySide6.QtOpenGL import (QOpenGLShader, QOpenGLShaderProgram, + QOpenGLVersionProfile) +from PySide6.QtQuick import QQuickWindow, QSGRendererInterface + +VERTEX_SHADER = dedent( + """\ + attribute highp vec4 vertices; + varying highp vec2 coords; + void main() { + gl_Position = vertices; + coords = vertices.xy; + } + """ +) +FRAGMENT_SHADER = dedent( + """\ + uniform lowp float t; + varying highp vec2 coords; + void main() { + lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.)); + i = smoothstep(t - 0.8, t + 0.8, i); + i = floor(i * 20.) / 20.; + gl_FragColor = vec4(coords * .5 + .5, i, i); + } + """ +) + + +class SquircleRenderer(QOpenGLFunctions): + def __init__(self): + QOpenGLFunctions.__init__(self) + self._viewport_size = QSize() + self._t = 0.0 + self._program = None + self._window = QQuickWindow() + + def setT(self, t): + self._t = t + + def setViewportSize(self, size): + self._viewport_size = size + + def setWindow(self, window): + self._window = window + + @Slot() + def init(self): + if not self._program: + rif = self._window.rendererInterface() + assert (rif.graphicsApi() == QSGRendererInterface.OpenGL) + self.initializeOpenGLFunctions() + self._program = QOpenGLShaderProgram() + self._program.addCacheableShaderFromSourceCode(QOpenGLShader.Vertex, VERTEX_SHADER) + self._program.addCacheableShaderFromSourceCode(QOpenGLShader.Fragment, FRAGMENT_SHADER) + self._program.bindAttributeLocation("vertices", 0) + self._program.link() + + @Slot() + def paint(self): + # Play nice with the RHI. Not strictly needed when the scenegraph uses + # OpenGL directly. + self._window.beginExternalCommands() + + self._program.bind() + + self._program.enableAttributeArray(0) + + values = np.array([-1, -1, 1, -1, -1, 1, 1, 1], dtype="single") + + # This example relies on (deprecated) client-side pointers for the vertex + # input. Therefore, we have to make sure no vertex buffer is bound. + self.glBindBuffer(GL_ARRAY_BUFFER, 0) + + self._program.setAttributeArray(0, GL_FLOAT, values, 2) + self._program.setUniformValue1f("t", self._t) + + self.glViewport(0, 0, self._viewport_size.width(), self._viewport_size.height()) + + self.glDisable(GL_DEPTH_TEST) + + self.glEnable(GL_BLEND) + self.glBlendFunc(GL_SRC_ALPHA, GL_ONE) + + self.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) + + self._program.disableAttributeArray(0) + self._program.release() + + self._window.endExternalCommands() |