From db338bc9fca641e4c98f623ccc8d3ebc1955f354 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Morten=20S=C3=B8rvig?= <morten.sorvig@qt.io>
Date: Tue, 17 Sep 2024 13:42:16 +0200
Subject: [PATCH] Add hellogl

Simple OpenGL ES 3 example which also prints the OpenGL
version
---
 CMakeLists.txt         |   1 +
 deploy.sh              |   1 +
 hellogl/CMakeLists.txt |  51 +++++++++
 hellogl/glwindow.cpp   | 229 +++++++++++++++++++++++++++++++++++++++++
 hellogl/glwindow.h     |  61 +++++++++++
 hellogl/hellogles3.qrc |   5 +
 hellogl/logo.cpp       | 103 ++++++++++++++++++
 hellogl/logo.h         |  28 +++++
 hellogl/main.cpp       |  46 +++++++++
 hellogl/qtlogo.png     | Bin 0 -> 2318 bytes
 10 files changed, 525 insertions(+)
 create mode 100644 hellogl/CMakeLists.txt
 create mode 100644 hellogl/glwindow.cpp
 create mode 100644 hellogl/glwindow.h
 create mode 100644 hellogl/hellogles3.qrc
 create mode 100644 hellogl/logo.cpp
 create mode 100644 hellogl/logo.h
 create mode 100644 hellogl/main.cpp
 create mode 100644 hellogl/qtlogo.png

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 811d85c..7fce33b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,4 +3,5 @@ add_subdirectory(ordering-system)
 add_subdirectory(gallery_controls2)
 add_subdirectory(rasterwindow)
 add_subdirectory(principledmaterial)
+add_subdirectory(hellogl)
 # (slate is built separately)
\ No newline at end of file
diff --git a/deploy.sh b/deploy.sh
index cc56c84..a02531a 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -33,6 +33,7 @@ deploy "mandelbrot"
 deploy "multicanvas"
 deploy "rasterwindow"
 deploy "principledmaterial"
+deploy "hellogl"
 
 # slate
 rm -rf slate
diff --git a/hellogl/CMakeLists.txt b/hellogl/CMakeLists.txt
new file mode 100644
index 0000000..7a9829f
--- /dev/null
+++ b/hellogl/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(hellogl LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
+
+qt_standard_project_setup()
+
+qt_add_executable(hellogl
+    logo.cpp logo.h
+    glwindow.cpp glwindow.h
+    main.cpp
+)
+
+set_target_properties(hellogl PROPERTIES
+    WIN32_EXECUTABLE TRUE
+    MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(hellogl PRIVATE
+    Qt6::Core
+    Qt6::Gui
+    Qt6::OpenGL
+)
+
+# Resources:
+set(hellogl_resource_files
+    "qtlogo.png"
+)
+
+qt_add_resources(hellogl "hellogl"
+    PREFIX
+        "/"
+    FILES
+        ${hellogl_resource_files}
+)
+
+install(TARGETS hellogl
+    BUNDLE  DESTINATION .
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_app_script(
+    TARGET hellogl
+    OUTPUT_SCRIPT deploy_script
+    NO_UNSUPPORTED_PLATFORM_ERROR
+)
+install(SCRIPT ${deploy_script})
diff --git a/hellogl/glwindow.cpp b/hellogl/glwindow.cpp
new file mode 100644
index 0000000..8f19bd3
--- /dev/null
+++ b/hellogl/glwindow.cpp
@@ -0,0 +1,229 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "glwindow.h"
+#include <QImage>
+#include <QOpenGLTexture>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLBuffer>
+#include <QOpenGLContext>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLExtraFunctions>
+#include <QPropertyAnimation>
+#include <QSequentialAnimationGroup>
+#include <QTimer>
+#include <QPainter>
+
+GLWindow::GLWindow()
+{
+    m_world.setToIdentity();
+    m_world.translate(0, 0, -1);
+    m_world.rotate(180, 1, 0, 0);
+
+    QSequentialAnimationGroup *animGroup = new QSequentialAnimationGroup(this);
+    animGroup->setLoopCount(-1);
+    QPropertyAnimation *zAnim0 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
+    zAnim0->setStartValue(1.5f);
+    zAnim0->setEndValue(10.0f);
+    zAnim0->setDuration(2000);
+    animGroup->addAnimation(zAnim0);
+    QPropertyAnimation *zAnim1 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
+    zAnim1->setStartValue(10.0f);
+    zAnim1->setEndValue(50.0f);
+    zAnim1->setDuration(4000);
+    zAnim1->setEasingCurve(QEasingCurve::OutElastic);
+    animGroup->addAnimation(zAnim1);
+    QPropertyAnimation *zAnim2 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
+    zAnim2->setStartValue(50.0f);
+    zAnim2->setEndValue(1.5f);
+    zAnim2->setDuration(2000);
+    animGroup->addAnimation(zAnim2);
+    animGroup->start();
+
+    QPropertyAnimation* rAnim = new QPropertyAnimation(this, QByteArrayLiteral("r"));
+    rAnim->setStartValue(0.0f);
+    rAnim->setEndValue(360.0f);
+    rAnim->setDuration(2000);
+    rAnim->setLoopCount(-1);
+    rAnim->start();
+
+    QTimer::singleShot(4000, this, &GLWindow::startSecondStage);
+}
+
+GLWindow::~GLWindow()
+{
+    makeCurrent();
+    delete m_texture;
+    delete m_program;
+    delete m_vbo;
+    delete m_vao;
+}
+
+void GLWindow::startSecondStage()
+{
+    QPropertyAnimation* r2Anim = new QPropertyAnimation(this, QByteArrayLiteral("r2"));
+    r2Anim->setStartValue(0.0f);
+    r2Anim->setEndValue(360.0f);
+    r2Anim->setDuration(20000);
+    r2Anim->setLoopCount(-1);
+    r2Anim->start();
+}
+
+void GLWindow::setZ(float v)
+{
+    m_eye.setZ(v);
+    m_uniformsDirty = true;
+    update();
+}
+
+void GLWindow::setR(float v)
+{
+    m_r = v;
+    m_uniformsDirty = true;
+    update();
+}
+
+void GLWindow::setR2(float v)
+{
+    m_r2 = v;
+    m_uniformsDirty = true;
+    update();
+}
+
+static const char *vertexShaderSource =
+    "layout(location = 0) in vec4 vertex;\n"
+    "layout(location = 1) in vec3 normal;\n"
+    "out vec3 vert;\n"
+    "out vec3 vertNormal;\n"
+    "out vec3 color;\n"
+    "uniform mat4 projMatrix;\n"
+    "uniform mat4 camMatrix;\n"
+    "uniform mat4 worldMatrix;\n"
+    "uniform mat4 myMatrix;\n"
+    "uniform sampler2D sampler;\n"
+    "void main() {\n"
+    "   ivec2 pos = ivec2(gl_InstanceID % 32, gl_InstanceID / 32);\n"
+    "   vec2 t = vec2(float(-16 + pos.x) * 0.8, float(-18 + pos.y) * 0.6);\n"
+    "   float val = 2.0 * length(texelFetch(sampler, pos, 0).rgb);\n"
+    "   mat4 wm = myMatrix * mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, val, 1) * worldMatrix;\n"
+    "   color = texelFetch(sampler, pos, 0).rgb * vec3(0.4, 1.0, 0.0);\n"
+    "   vert = vec3(wm * vertex);\n"
+    "   vertNormal = mat3(transpose(inverse(wm))) * normal;\n"
+    "   gl_Position = projMatrix * camMatrix * wm * vertex;\n"
+    "}\n";
+
+static const char *fragmentShaderSource =
+    "in highp vec3 vert;\n"
+    "in highp vec3 vertNormal;\n"
+    "in highp vec3 color;\n"
+    "out highp vec4 fragColor;\n"
+    "uniform highp vec3 lightPos;\n"
+    "void main() {\n"
+    "   highp vec3 L = normalize(lightPos - vert);\n"
+    "   highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
+    "   highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
+    "   fragColor = vec4(col, 1.0);\n"
+    "}\n";
+
+QByteArray versionedShaderCode(const char *src)
+{
+    QByteArray versionedSrc;
+
+    if (QOpenGLContext::currentContext()->isOpenGLES())
+        versionedSrc.append(QByteArrayLiteral("#version 300 es\n"));
+    else
+        versionedSrc.append(QByteArrayLiteral("#version 330\n"));
+
+    versionedSrc.append(src);
+    return versionedSrc;
+}
+
+void GLWindow::initializeGL()
+{
+    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+    
+    QSurfaceFormat format = QOpenGLContext::currentContext()->format();
+    qDebug() << QString("OpenGL Version:%1.%2").arg(format.majorVersion()).arg(format.minorVersion());
+
+    QImage img(":/qtlogo.png");
+    Q_ASSERT(!img.isNull());
+    delete m_texture;
+    m_texture = new QOpenGLTexture(img.scaled(32, 36).mirrored());
+
+    delete m_program;
+    m_program = new QOpenGLShaderProgram;
+    // Prepend the correct version directive to the sources. The rest is the
+    // same, thanks to the common GLSL syntax.
+    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, versionedShaderCode(vertexShaderSource));
+    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, versionedShaderCode(fragmentShaderSource));
+    m_program->link();
+
+    m_projMatrixLoc = m_program->uniformLocation("projMatrix");
+    m_camMatrixLoc = m_program->uniformLocation("camMatrix");
+    m_worldMatrixLoc = m_program->uniformLocation("worldMatrix");
+    m_myMatrixLoc = m_program->uniformLocation("myMatrix");
+    m_lightPosLoc = m_program->uniformLocation("lightPos");
+
+    // Create a VAO. Not strictly required for ES 3, but it is for plain OpenGL.
+    delete m_vao;
+    m_vao = new QOpenGLVertexArrayObject;
+    if (m_vao->create())
+        m_vao->bind();
+
+    m_program->bind();
+    delete m_vbo;
+    m_vbo = new QOpenGLBuffer;
+    m_vbo->create();
+    m_vbo->bind();
+    m_vbo->allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat));
+    f->glEnableVertexAttribArray(0);
+    f->glEnableVertexAttribArray(1);
+    f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
+                             nullptr);
+    f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
+                             reinterpret_cast<void *>(3 * sizeof(GLfloat)));
+    m_vbo->release();
+
+    f->glEnable(GL_DEPTH_TEST);
+    f->glEnable(GL_CULL_FACE);
+}
+
+void GLWindow::resizeGL(int w, int h)
+{
+    m_proj.setToIdentity();
+    m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
+    m_uniformsDirty = true;
+}
+
+void GLWindow::paintGL()
+{
+    // Now use QOpenGLExtraFunctions instead of QOpenGLFunctions as we want to
+    // do more than what GL(ES) 2.0 offers.
+    QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
+
+    f->glClearColor(0, 0, 0, 1);
+    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    m_program->bind();
+    m_texture->bind();
+
+    if (m_uniformsDirty) {
+        m_uniformsDirty = false;
+        QMatrix4x4 camera;
+        camera.lookAt(m_eye, m_eye + m_target, QVector3D(0, 1, 0));
+        m_program->setUniformValue(m_projMatrixLoc, m_proj);
+        m_program->setUniformValue(m_camMatrixLoc, camera);
+        QMatrix4x4 wm = m_world;
+        wm.rotate(m_r, 1, 1, 0);
+        m_program->setUniformValue(m_worldMatrixLoc, wm);
+        QMatrix4x4 mm;
+        mm.setToIdentity();
+        mm.rotate(-m_r2, 1, 0, 0);
+        m_program->setUniformValue(m_myMatrixLoc, mm);
+        m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
+    }
+
+    // Now call a function introduced in OpenGL 3.1 / OpenGL ES 3.0. We
+    // requested a 3.3 or ES 3.0 context, so we know this will work.
+    f->glDrawArraysInstanced(GL_TRIANGLES, 0, m_logo.vertexCount(), 32 * 36);
+}
diff --git a/hellogl/glwindow.h b/hellogl/glwindow.h
new file mode 100644
index 0000000..95c6a9b
--- /dev/null
+++ b/hellogl/glwindow.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef GLWIDGET_H
+#define GLWIDGET_H
+
+#include <QOpenGLWindow>
+#include <QMatrix4x4>
+#include <QVector3D>
+#include "logo.h"
+
+QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
+QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
+QT_FORWARD_DECLARE_CLASS(QOpenGLBuffer)
+QT_FORWARD_DECLARE_CLASS(QOpenGLVertexArrayObject)
+
+class GLWindow : public QOpenGLWindow
+{
+    Q_OBJECT
+    Q_PROPERTY(float z READ z WRITE setZ)
+    Q_PROPERTY(float r READ r WRITE setR)
+    Q_PROPERTY(float r2 READ r2 WRITE setR2)
+
+public:
+    GLWindow();
+    ~GLWindow();
+
+    void initializeGL();
+    void resizeGL(int w, int h);
+    void paintGL();
+
+    float z() const { return m_eye.z(); }
+    void setZ(float v);
+
+    float r() const { return m_r; }
+    void setR(float v);
+    float r2() const { return m_r2; }
+    void setR2(float v);
+private slots:
+    void startSecondStage();
+private:
+    QOpenGLTexture *m_texture = nullptr;
+    QOpenGLShaderProgram *m_program = nullptr;
+    QOpenGLBuffer *m_vbo = nullptr;
+    QOpenGLVertexArrayObject *m_vao = nullptr;
+    Logo m_logo;
+    int m_projMatrixLoc = 0;
+    int m_camMatrixLoc = 0;
+    int m_worldMatrixLoc = 0;
+    int m_myMatrixLoc = 0;
+    int m_lightPosLoc = 0;
+    QMatrix4x4 m_proj;
+    QMatrix4x4 m_world;
+    QVector3D m_eye;
+    QVector3D m_target = {0, 0, -1};
+    bool m_uniformsDirty = true;
+    float m_r = 0;
+    float m_r2 = 0;
+};
+
+#endif
diff --git a/hellogl/hellogles3.qrc b/hellogl/hellogles3.qrc
new file mode 100644
index 0000000..f3a0978
--- /dev/null
+++ b/hellogl/hellogles3.qrc
@@ -0,0 +1,5 @@
+<RCC>
+  <qresource>
+    <file>qtlogo.png</file>
+  </qresource>
+</RCC>
diff --git a/hellogl/logo.cpp b/hellogl/logo.cpp
new file mode 100644
index 0000000..b924ba0
--- /dev/null
+++ b/hellogl/logo.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "logo.h"
+#include <qmath.h>
+
+Logo::Logo()
+{
+    m_data.resize(2500 * 6);
+
+    const GLfloat x1 = +0.06f;
+    const GLfloat y1 = -0.14f;
+    const GLfloat x2 = +0.14f;
+    const GLfloat y2 = -0.06f;
+    const GLfloat x3 = +0.08f;
+    const GLfloat y3 = +0.00f;
+    const GLfloat x4 = +0.30f;
+    const GLfloat y4 = +0.22f;
+
+    quad(x1, y1, x2, y2, y2, x2, y1, x1);
+    quad(x3, y3, x4, y4, y4, x4, y3, x3);
+
+    extrude(x1, y1, x2, y2);
+    extrude(x2, y2, y2, x2);
+    extrude(y2, x2, y1, x1);
+    extrude(y1, x1, x1, y1);
+    extrude(x3, y3, x4, y4);
+    extrude(x4, y4, y4, x4);
+    extrude(y4, x4, y3, x3);
+
+    const int NumSectors = 100;
+
+    for (int i = 0; i < NumSectors; ++i) {
+        GLfloat angle = (i * 2 * M_PI) / NumSectors;
+        GLfloat angleSin = qSin(angle);
+        GLfloat angleCos = qCos(angle);
+        const GLfloat x5 = 0.30f * angleSin;
+        const GLfloat y5 = 0.30f * angleCos;
+        const GLfloat x6 = 0.20f * angleSin;
+        const GLfloat y6 = 0.20f * angleCos;
+
+        angle = ((i + 1) * 2 * M_PI) / NumSectors;
+        angleSin = qSin(angle);
+        angleCos = qCos(angle);
+        const GLfloat x7 = 0.20f * angleSin;
+        const GLfloat y7 = 0.20f * angleCos;
+        const GLfloat x8 = 0.30f * angleSin;
+        const GLfloat y8 = 0.30f * angleCos;
+
+        quad(x5, y5, x6, y6, x7, y7, x8, y8);
+
+        extrude(x6, y6, x7, y7);
+        extrude(x8, y8, x5, y5);
+    }
+}
+
+void Logo::add(const QVector3D &v, const QVector3D &n)
+{
+    GLfloat *p = m_data.data() + m_count;
+    *p++ = v.x();
+    *p++ = v.y();
+    *p++ = v.z();
+    *p++ = n.x();
+    *p++ = n.y();
+    *p++ = n.z();
+    m_count += 6;
+}
+
+void Logo::quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4)
+{
+    QVector3D n = QVector3D::normal(QVector3D(x4 - x1, y4 - y1, 0.0f), QVector3D(x2 - x1, y2 - y1, 0.0f));
+
+    add(QVector3D(x1, y1, -0.05f), n);
+    add(QVector3D(x4, y4, -0.05f), n);
+    add(QVector3D(x2, y2, -0.05f), n);
+
+    add(QVector3D(x3, y3, -0.05f), n);
+    add(QVector3D(x2, y2, -0.05f), n);
+    add(QVector3D(x4, y4, -0.05f), n);
+
+    n = QVector3D::normal(QVector3D(x1 - x4, y1 - y4, 0.0f), QVector3D(x2 - x4, y2 - y4, 0.0f));
+
+    add(QVector3D(x4, y4, 0.05f), n);
+    add(QVector3D(x1, y1, 0.05f), n);
+    add(QVector3D(x2, y2, 0.05f), n);
+
+    add(QVector3D(x2, y2, 0.05f), n);
+    add(QVector3D(x3, y3, 0.05f), n);
+    add(QVector3D(x4, y4, 0.05f), n);
+}
+
+void Logo::extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
+{
+    QVector3D n = QVector3D::normal(QVector3D(0.0f, 0.0f, -0.1f), QVector3D(x2 - x1, y2 - y1, 0.0f));
+
+    add(QVector3D(x1, y1, +0.05f), n);
+    add(QVector3D(x1, y1, -0.05f), n);
+    add(QVector3D(x2, y2, +0.05f), n);
+
+    add(QVector3D(x2, y2, -0.05f), n);
+    add(QVector3D(x2, y2, +0.05f), n);
+    add(QVector3D(x1, y1, -0.05f), n);
+}
diff --git a/hellogl/logo.h b/hellogl/logo.h
new file mode 100644
index 0000000..0eed3f6
--- /dev/null
+++ b/hellogl/logo.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef LOGO_H
+#define LOGO_H
+
+#include <qopengl.h>
+#include <QList>
+#include <QVector3D>
+
+class Logo
+{
+public:
+    Logo();
+    const GLfloat *constData() const { return m_data.constData(); }
+    int count() const { return m_count; }
+    int vertexCount() const { return m_count / 6; }
+
+private:
+    void quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4);
+    void extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
+    void add(const QVector3D &v, const QVector3D &n);
+
+    QList<GLfloat> m_data;
+    int m_count = 0;
+};
+
+#endif // LOGO_H
diff --git a/hellogl/main.cpp b/hellogl/main.cpp
new file mode 100644
index 0000000..b75702e
--- /dev/null
+++ b/hellogl/main.cpp
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QSurfaceFormat>
+#include <QOpenGLContext>
+
+#include "glwindow.h"
+
+// This example demonstrates easy, cross-platform usage of OpenGL ES 3.0 functions via
+// QOpenGLExtraFunctions in an application that works identically on desktop platforms
+// with OpenGL 3.3 and mobile/embedded devices with OpenGL ES 3.0.
+
+// The code is always the same, with the exception of two places: (1) the OpenGL context
+// creation has to have a sufficiently high version number for the features that are in
+// use, and (2) the shader code's version directive is different.
+
+int main(int argc, char *argv[])
+{
+    QGuiApplication app(argc, argv);
+
+    QSurfaceFormat fmt;
+    fmt.setDepthBufferSize(24);
+
+    /*
+        Use the default WebGL version. Should be
+        OpenGL ES 3.0 / WebGL 2 on recent Qt versions.
+
+    // Request OpenGL 3.3 core or OpenGL ES 3.0.
+    if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
+        qDebug("Requesting 3.3 core context");
+        fmt.setVersion(3, 3);
+        fmt.setProfile(QSurfaceFormat::CoreProfile);
+    } else {
+        qDebug("Requesting 3.0 context");
+        fmt.setVersion(3, 0);
+    }
+*/
+
+    QSurfaceFormat::setDefaultFormat(fmt);
+
+    GLWindow glWindow;
+    glWindow.show();
+
+    return app.exec();
+}
diff --git a/hellogl/qtlogo.png b/hellogl/qtlogo.png
new file mode 100644
index 0000000000000000000000000000000000000000..9cb2e01d3895ed7d1a81e91ed5393b474d041671
GIT binary patch
literal 2318
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
E0+|Y_$p8QV

literal 0
HcmV?d00001

-- 
GitLab