summaryrefslogtreecommitdiffstats
path: root/tests/manual/examples/opengl/qopenglwindow
diff options
context:
space:
mode:
Diffstat (limited to 'tests/manual/examples/opengl/qopenglwindow')
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt49
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/background.frag30
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/background_renderer.cpp167
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/background_renderer.h46
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/main.cpp154
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/qopenglwindow.pro16
-rw-r--r--tests/manual/examples/opengl/qopenglwindow/shaders.qrc5
7 files changed, 467 insertions, 0 deletions
diff --git a/tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt b/tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt
new file mode 100644
index 0000000000..be3cb45152
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwindow/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(qopenglwindow LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/opengl/qopenglwindow")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
+
+qt_standard_project_setup()
+
+qt_add_executable(qopenglwindow
+ background_renderer.cpp background_renderer.h
+ main.cpp
+)
+
+set_target_properties(qopenglwindow PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(qopenglwindow PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+)
+
+# Resources:
+set(shaders_resource_files
+ "background.frag"
+)
+
+qt_add_resources(qopenglwindow "shaders"
+ PREFIX
+ "/"
+ FILES
+ ${shaders_resource_files}
+)
+
+install(TARGETS qopenglwindow
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/tests/manual/examples/opengl/qopenglwindow/background.frag b/tests/manual/examples/opengl/qopenglwindow/background.frag
new file mode 100644
index 0000000000..4af85aae8e
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwindow/background.frag
@@ -0,0 +1,30 @@
+#define M_PI 3.14159265358979323846
+#define SPEED 10000.0
+
+uniform int currentTime;
+uniform highp vec2 windowSize;
+
+highp float noise(highp vec2 co)
+{
+ return 0.5 * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
+}
+
+highp float curvSpeed()
+{
+ return (mod(float(currentTime), SPEED) / SPEED) * (2.0 * M_PI);
+}
+
+highp float curv(int curvCount)
+{
+ highp float curv_y = 0.1 *(cos((gl_FragCoord.x / windowSize.x) * (float(curvCount * 2) * M_PI) - curvSpeed())) + 0.5;
+ highp float frag_y = gl_FragCoord.y / windowSize.y;
+ return 1.0 - abs(curv_y - frag_y);
+}
+
+void main()
+{
+ highp float coordNoise = noise(gl_FragCoord.xy);
+ highp float proximity = smoothstep(0.85, 1.0, (curv(6) + 1.0) * (coordNoise ));
+ highp vec3 color = vec3(coordNoise) * proximity;
+ gl_FragColor = vec4(color, 1.0);
+}
diff --git a/tests/manual/examples/opengl/qopenglwindow/background_renderer.cpp b/tests/manual/examples/opengl/qopenglwindow/background_renderer.cpp
new file mode 100644
index 0000000000..5939894dfe
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwindow/background_renderer.cpp
@@ -0,0 +1,167 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "background_renderer.h"
+
+#include <qmath.h>
+#include <QFileInfo>
+#include <QTime>
+
+#include <QOpenGLShaderProgram>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+
+#include <math.h>
+
+static const char vertex_shader[] =
+ "attribute highp vec3 vertexCoord;"
+ "void main() {"
+ " gl_Position = vec4(vertexCoord,1.0);"
+ "}";
+
+static const char fragment_shader[] =
+ "void main() {"
+ " gl_FragColor = vec4(0.0,1.0,0.0,1.0);"
+ "}";
+
+static const float vertices[] = { -1, -1, 0,
+ -1, 1, 0,
+ 1, -1, 0,
+ 1, 1, 0 };
+
+FragmentToy::FragmentToy(const QString &fragmentSource, QObject *parent)
+ : QObject(parent)
+ , m_recompile_shaders(true)
+{
+ if (QFile::exists(fragmentSource)) {
+ QFileInfo info(fragmentSource);
+ m_fragment_file_last_modified = info.lastModified();
+ m_fragment_file = fragmentSource;
+#if QT_CONFIG(filesystemwatcher)
+ m_watcher.addPath(info.canonicalPath());
+ QObject::connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FragmentToy::fileChanged);
+#endif
+ }
+}
+
+FragmentToy::~FragmentToy()
+ = default;
+
+void FragmentToy::draw(const QSize &windowSize)
+{
+ if (!m_program)
+ initializeOpenGLFunctions();
+
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (!m_vao.isCreated())
+ m_vao.create();
+
+ QOpenGLVertexArrayObject::Binder binder(&m_vao);
+
+ if (!m_vertex_buffer.isCreated()) {
+ m_vertex_buffer.create();
+ m_vertex_buffer.bind();
+ m_vertex_buffer.allocate(vertices, sizeof(vertices));
+ m_vertex_buffer.release();
+ }
+
+ if (!m_program) {
+ m_program.reset(new QOpenGLShaderProgram);
+ m_program->create();
+ m_vertex_shader.reset(new QOpenGLShader(QOpenGLShader::Vertex));
+ if (!m_vertex_shader->compileSourceCode(vertex_shader)) {
+ qWarning() << "Failed to compile the vertex shader:" << m_vertex_shader->log();
+ }
+ if (!m_program->addShader(m_vertex_shader.get())) {
+ qWarning() << "Failed to add vertex shader to program:" << m_program->log();
+ }
+ }
+
+ if (!m_fragment_shader && m_recompile_shaders) {
+ QByteArray data;
+ if (m_fragment_file.size()) {
+ QFile file(m_fragment_file);
+ if (file.open(QIODevice::ReadOnly)) {
+ data = file.readAll();
+ } else {
+ qWarning() << "Failed to load input file, falling back to default";
+ data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
+ }
+ } else {
+ QFile qrcFile(":/background.frag");
+ if (qrcFile.open(QIODevice::ReadOnly))
+ data = qrcFile.readAll();
+ else
+ data = QByteArray::fromRawData(fragment_shader, sizeof(fragment_shader));
+ }
+ if (data.size()) {
+ m_fragment_shader.reset(new QOpenGLShader(QOpenGLShader::Fragment));
+ if (!m_fragment_shader->compileSourceCode(data)) {
+ qWarning() << "Failed to compile fragment shader:" << m_fragment_shader->log();
+ m_fragment_shader.reset(nullptr);
+ }
+ } else {
+ qWarning() << "Unknown error, no fragment shader";
+ }
+
+ if (m_fragment_shader) {
+ if (!m_program->addShader(m_fragment_shader.get())) {
+ qWarning() << "Failed to add fragment shader to program:" << m_program->log();
+ }
+ }
+ }
+
+ if (m_recompile_shaders) {
+ m_recompile_shaders = false;
+
+ if (m_program->link()) {
+ m_vertex_coord_pos = m_program->attributeLocation("vertexCoord");
+ } else {
+ qWarning() << "Failed to link shader program" << m_program->log();
+ }
+
+ }
+
+ if (!m_program->isLinked())
+ return;
+
+ m_program->bind();
+
+ m_vertex_buffer.bind();
+ m_program->setAttributeBuffer("vertexCoord", GL_FLOAT, 0, 3, 0);
+ m_program->enableAttributeArray("vertexCoord");
+ m_vertex_buffer.release();
+
+ m_program->setUniformValue("currentTime", (uint) QDateTime::currentMSecsSinceEpoch());
+ m_program->setUniformValue("windowSize", windowSize);
+
+ QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ m_program->release();
+}
+
+void FragmentToy::fileChanged(const QString &path)
+{
+ Q_UNUSED(path);
+ if (QFile::exists(m_fragment_file)) {
+ QFileInfo fragment_source(m_fragment_file);
+ if (fragment_source.lastModified() > m_fragment_file_last_modified) {
+ m_fragment_file_last_modified = fragment_source.lastModified();
+ m_recompile_shaders = true;
+ if (m_program) {
+ m_program->removeShader(m_fragment_shader.get());
+ m_fragment_shader.reset(nullptr);
+ }
+ }
+ } else {
+ m_recompile_shaders = true;
+ if (m_program) {
+ m_program->removeShader(m_fragment_shader.get());
+ m_fragment_shader.reset(nullptr);
+ }
+ }
+}
diff --git a/tests/manual/examples/opengl/qopenglwindow/background_renderer.h b/tests/manual/examples/opengl/qopenglwindow/background_renderer.h
new file mode 100644
index 0000000000..76c0bec6a8
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwindow/background_renderer.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FRAGMENT_TOY_H
+#define FRAGMENT_TOY_H
+
+#include <QObject>
+#include <QFile>
+#include <QDateTime>
+#if QT_CONFIG(filesystemwatcher)
+#include <QFileSystemWatcher>
+#endif
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLFunctions>
+
+#include <memory>
+
+class FragmentToy : public QObject, protected QOpenGLFunctions
+{
+ Q_OBJECT
+public:
+ explicit FragmentToy(const QString &fragmentSource, QObject *parent = nullptr);
+ ~FragmentToy();
+
+ void draw(const QSize &windowSize);
+
+private:
+ void fileChanged(const QString &path);
+ bool m_recompile_shaders;
+#if QT_CONFIG(filesystemwatcher)
+ QFileSystemWatcher m_watcher;
+#endif
+ QString m_fragment_file;
+ QDateTime m_fragment_file_last_modified;
+
+ std::unique_ptr<QOpenGLShaderProgram> m_program;
+ std::unique_ptr<QOpenGLShader> m_vertex_shader;
+ std::unique_ptr<QOpenGLShader> m_fragment_shader;
+ QOpenGLVertexArrayObject m_vao;
+ QOpenGLBuffer m_vertex_buffer;
+ GLuint m_vertex_coord_pos;
+};
+
+#endif //FRAGMENT_TOY_H
diff --git a/tests/manual/examples/opengl/qopenglwindow/main.cpp b/tests/manual/examples/opengl/qopenglwindow/main.cpp
new file mode 100644
index 0000000000..44810a4ecf
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwindow/main.cpp
@@ -0,0 +1,154 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QOpenGLWindow>
+#include <QScreen>
+#include <QPainter>
+#include <QPainterPath>
+#include <QGuiApplication>
+#include <QMatrix4x4>
+#include <QStaticText>
+#include <QKeyEvent>
+
+#include "background_renderer.h"
+
+static QPainterPath painterPathForTriangle()
+{
+ static const QPointF bottomLeft(-1.0, -1.0);
+ static const QPointF top(0.0, 1.0);
+ static const QPointF bottomRight(1.0, -1.0);
+
+ QPainterPath path(bottomLeft);
+ path.lineTo(top);
+ path.lineTo(bottomRight);
+ path.closeSubpath();
+ return path;
+}
+
+class OpenGLWindow : public QOpenGLWindow
+{
+ Q_OBJECT
+
+public:
+ OpenGLWindow();
+
+protected:
+ void paintGL() override;
+ void resizeGL(int w, int h) override;
+ void keyPressEvent(QKeyEvent *e) override;
+
+private:
+ void setAnimating(bool enabled);
+
+ QMatrix4x4 m_window_normalised_matrix;
+ QMatrix4x4 m_window_painter_matrix;
+ QMatrix4x4 m_projection;
+ QMatrix4x4 m_view;
+ QMatrix4x4 m_model_triangle;
+ QMatrix4x4 m_model_text;
+
+ FragmentToy m_fragment_toy;
+ QStaticText m_text_layout;
+ bool m_animate;
+};
+
+// Use NoPartialUpdate. This means that all the rendering goes directly to
+// the window surface, no additional framebuffer object stands in the
+// middle. This is fine since we will clear the entire framebuffer on each
+// paint. Under the hood this means that the behavior is equivalent to the
+// manual makeCurrent - perform OpenGL calls - swapBuffers loop that is
+// typical in pure QWindow-based applications.
+OpenGLWindow::OpenGLWindow()
+ : QOpenGLWindow(QOpenGLWindow::NoPartialUpdate)
+ , m_fragment_toy("./background.frag")
+ , m_text_layout("The triangle and this text is rendered with QPainter")
+ , m_animate(true)
+{
+ setGeometry(300, 300, 500, 500);
+
+ m_view.lookAt(QVector3D(3,1,1),
+ QVector3D(0,0,0),
+ QVector3D(0,1,0));
+
+ setAnimating(m_animate);
+}
+
+void OpenGLWindow::paintGL()
+{
+ m_fragment_toy.draw(size());
+
+ QPainter p(this);
+ p.setWorldTransform(m_window_normalised_matrix.toTransform());
+
+ QMatrix4x4 mvp = m_projection * m_view * m_model_triangle;
+ p.setTransform(mvp.toTransform(), true);
+
+ p.fillPath(painterPathForTriangle(), QBrush(QGradient(QGradient::NightFade)));
+
+ QTransform text_transform = (m_window_painter_matrix * m_view * m_model_text).toTransform();
+ p.setTransform(text_transform, false);
+ p.setPen(QPen(Qt::black));
+ m_text_layout.prepare(text_transform);
+ qreal x = - (m_text_layout.size().width() / 2);
+ qreal y = 0;
+ p.drawStaticText(x, y, m_text_layout);
+
+ m_model_triangle.rotate(-1, 0, 1, 0);
+ m_model_text.rotate(1, 0, 1, 0);
+}
+
+void OpenGLWindow::resizeGL(int w, int h)
+{
+ m_window_normalised_matrix.setToIdentity();
+ m_window_normalised_matrix.translate(w / 2.0, h / 2.0);
+ m_window_normalised_matrix.scale(w / 2.0, -h / 2.0);
+
+ m_window_painter_matrix.setToIdentity();
+ m_window_painter_matrix.translate(w / 2.0, h / 2.0);
+
+ m_text_layout.setTextWidth(std::max(w * 0.2, 80.0));
+
+ m_projection.setToIdentity();
+ m_projection.perspective(45.f, qreal(w) / qreal(h), 0.1f, 100.f);
+}
+
+void OpenGLWindow::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_P) { // pause
+ m_animate = !m_animate;
+ setAnimating(m_animate);
+ }
+}
+
+void OpenGLWindow::setAnimating(bool enabled)
+{
+ if (enabled) {
+ // Animate continuously, throttled by the blocking swapBuffers() call the
+ // QOpenGLWindow internally executes after each paint. Once that is done
+ // (frameSwapped signal is emitted), we schedule a new update. This
+ // obviously assumes that the swap interval (see
+ // QSurfaceFormat::setSwapInterval()) is non-zero.
+ connect(this, &QOpenGLWindow::frameSwapped,
+ this, QOverload<>::of(&QPaintDeviceWindow::update));
+ update();
+ } else {
+ disconnect(this, &QOpenGLWindow::frameSwapped,
+ this, QOverload<>::of(&QPaintDeviceWindow::update));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ OpenGLWindow window;
+ QSurfaceFormat fmt;
+ fmt.setDepthBufferSize(24);
+ fmt.setStencilBufferSize(8);
+ window.setFormat(fmt);
+ window.show();
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/examples/opengl/qopenglwindow/qopenglwindow.pro b/tests/manual/examples/opengl/qopenglwindow/qopenglwindow.pro
new file mode 100644
index 0000000000..e7073a324b
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwindow/qopenglwindow.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+TARGET = qopenglwindow
+INCLUDEPATH += .
+QT += opengl
+
+RESOURCES += shaders.qrc
+
+SOURCES += \
+ main.cpp \
+ background_renderer.cpp
+
+HEADERS += \
+ background_renderer.h
+
+target.path = $$[QT_INSTALL_EXAMPLES]/opengl/qopenglwindow
+INSTALLS += target
diff --git a/tests/manual/examples/opengl/qopenglwindow/shaders.qrc b/tests/manual/examples/opengl/qopenglwindow/shaders.qrc
new file mode 100644
index 0000000000..64eefe2c70
--- /dev/null
+++ b/tests/manual/examples/opengl/qopenglwindow/shaders.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>background.frag</file>
+</qresource>
+</RCC>