diff options
author | Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io> | 2019-04-24 09:58:23 +0200 |
---|---|---|
committer | Cristián Maureira-Fredes <Cristian.Maureira-Fredes@qt.io> | 2021-09-21 20:40:30 +0200 |
commit | b31f0c37bdc78e939db2dfe3bd876eba47137a5c (patch) | |
tree | 9a634100de0cc9f8c53733757a1f352424092cbb /examples | |
parent | 00228b7605f63c58ab979362ecaa2bef96c7dc67 (diff) |
examples: Add OpenGL under Qml example
This is example is based on the Qt's scene graph example
that uses and OpenGL animation inside Qml code.
Fixes: PYSIDE-1034
Change-Id: I012818d81d757571a711fcea68df51fa566ae5f9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'examples')
-rw-r--r-- | examples/declarative/openglunderqml/doc/openglunderqml.rst | 21 | ||||
-rw-r--r-- | examples/declarative/openglunderqml/doc/squircle.png | bin | 0 -> 37963 bytes | |||
-rw-r--r-- | examples/declarative/openglunderqml/main.py | 66 | ||||
-rw-r--r-- | examples/declarative/openglunderqml/main.qml | 86 | ||||
-rw-r--r-- | examples/declarative/openglunderqml/openglunderqml.pyproject | 3 | ||||
-rw-r--r-- | examples/declarative/openglunderqml/squircle.py | 107 | ||||
-rw-r--r-- | examples/declarative/openglunderqml/squirclerenderer.py | 141 |
7 files changed, 424 insertions, 0 deletions
diff --git a/examples/declarative/openglunderqml/doc/openglunderqml.rst b/examples/declarative/openglunderqml/doc/openglunderqml.rst new file mode 100644 index 000000000..6a89a99d9 --- /dev/null +++ b/examples/declarative/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/declarative/openglunderqml/doc/squircle.png b/examples/declarative/openglunderqml/doc/squircle.png Binary files differnew file mode 100644 index 000000000..c099a6b7e --- /dev/null +++ b/examples/declarative/openglunderqml/doc/squircle.png diff --git a/examples/declarative/openglunderqml/main.py b/examples/declarative/openglunderqml/main.py new file mode 100644 index 000000000..26e059f93 --- /dev/null +++ b/examples/declarative/openglunderqml/main.py @@ -0,0 +1,66 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys +from pathlib import Path + +from PySide6.QtCore import QUrl +from PySide6.QtGui import QGuiApplication +from PySide6.QtQml import qmlRegisterType +from PySide6.QtQuick import QQuickView, QQuickWindow, QSGRendererInterface + +from squircle import Squircle + +if __name__ == "__main__": + app = QGuiApplication(sys.argv) + + QQuickWindow.setGraphicsApi(QSGRendererInterface.OpenGLRhi) + qmlRegisterType(Squircle, "OpenGLUnderQML", 1, 0, "Squircle") + + 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/declarative/openglunderqml/main.qml b/examples/declarative/openglunderqml/main.qml new file mode 100644 index 000000000..7edcf523b --- /dev/null +++ b/examples/declarative/openglunderqml/main.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +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/declarative/openglunderqml/openglunderqml.pyproject b/examples/declarative/openglunderqml/openglunderqml.pyproject new file mode 100644 index 000000000..e7cfbc570 --- /dev/null +++ b/examples/declarative/openglunderqml/openglunderqml.pyproject @@ -0,0 +1,3 @@ +{ + "files": [ "main.py", "main.qml", "squircle.py", "squirclerenderer.py"] +} diff --git a/examples/declarative/openglunderqml/squircle.py b/examples/declarative/openglunderqml/squircle.py new file mode 100644 index 000000000..8d2cbca84 --- /dev/null +++ b/examples/declarative/openglunderqml/squircle.py @@ -0,0 +1,107 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +from PySide6.QtCore import Property, QRunnable, Qt, Signal, Slot +from PySide6.QtQuick import QQuickItem, QQuickWindow + +from squirclerenderer import SquircleRenderer + + +class CleanupJob(QRunnable): + def __init__(self, renderer): + super().__init__() + self._renderer = renderer + + def run(self): + del self._renderer + + +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() + + def cleanup(self): + del self._renderer + self._renderer = None + + @Slot() + def sync(self): + if not self._renderer: + self._renderer = SquircleRenderer() + self.window().beforeRendering.connect(self._renderer.init, Qt.DirectConnection) + self.window().beforeRenderPassRecording.connect( + self._renderer.paint, Qt.DirectConnection + ) + self._renderer.setViewportSize(self.window().size() * self.window().devicePixelRatio()) + self._renderer.setT(self._t) + self._renderer.setWindow(self.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/declarative/openglunderqml/squirclerenderer.py b/examples/declarative/openglunderqml/squirclerenderer.py new file mode 100644 index 000000000..12cd93bb8 --- /dev/null +++ b/examples/declarative/openglunderqml/squirclerenderer.py @@ -0,0 +1,141 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +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() + self.profile = QOpenGLVersionProfile() + self.gl = None + + 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 + or rif.graphicsApi() == QSGRendererInterface.OpenGLRhy + ) + 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() |