diff --git a/CMakeLists.txt b/CMakeLists.txt index 811d85c4663f49e7dcfb75e27e3be5f82e5e160d..7fce33b39c73d1728e4a97f72ebfd0584c331199 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 cc56c84351228b1d280a54d3ea28efcbc90f2acd..a02531a107259f65d2e14c5d8a0571451a4b6e83 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 0000000000000000000000000000000000000000..7a9829fe85b03dc4555e34a46e4ed67eb3f7cea8 --- /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 0000000000000000000000000000000000000000..8f19bd38993ac690e83077904e5fe378ee3e99bd --- /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 0000000000000000000000000000000000000000..95c6a9b568f9107dea2179a6a4128193a5638d41 --- /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 0000000000000000000000000000000000000000..f3a09780843359aad7acc9257cdd3875016f9b01 --- /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 0000000000000000000000000000000000000000..b924ba043d06b360a5da38ce6415e69e26461349 --- /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 0000000000000000000000000000000000000000..0eed3f6978dc4a44ad0ac1bb8cb842d8385ae271 --- /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 0000000000000000000000000000000000000000..b75702e9622fe35f535eed572a8d6f0647577ab5 --- /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 Binary files /dev/null and b/hellogl/qtlogo.png differ