diff options
Diffstat (limited to 'tests/manual/qopenglwidget/dockedopenglwidget')
12 files changed, 527 insertions, 0 deletions
diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/CMakeLists.txt b/tests/manual/qopenglwidget/dockedopenglwidget/CMakeLists.txt new file mode 100644 index 0000000000..e34342fc37 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +set(CMAKE_AUTOUIC ON) + +qt_internal_add_manual_test(dockedopenglwidget + GUI + SOURCES + main.cpp + geometryengine.cpp geometryengine.h + mainwidget.cpp mainwidget.h + mainwindow.cpp mainwindow.h + mainwindow.ui + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::OpenGL + Qt::OpenGLWidgets + Qt::Widgets + Qt::WidgetsPrivate +) + +qt_add_resources(dockedopenglwidget "dockedopenglwidget" + PREFIX + "/" + FILES + vshader.glsl + fshader.glsl + cube.png +) diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/cube.png b/tests/manual/qopenglwidget/dockedopenglwidget/cube.png Binary files differnew file mode 100644 index 0000000000..42c8c51b3a --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/cube.png diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl b/tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl new file mode 100644 index 0000000000..ddc6247574 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/fshader.glsl @@ -0,0 +1,10 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +uniform sampler2D texture; +varying vec2 v_texcoord; + +void main() +{ + gl_FragColor = texture2D(texture, v_texcoord); +} + diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp new file mode 100644 index 0000000000..d7b9b8d05a --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.cpp @@ -0,0 +1,126 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "geometryengine.h" + +#include <QVector2D> +#include <QVector3D> + +struct VertexData +{ + QVector3D position; + QVector2D texCoord; +}; + +GeometryEngine::GeometryEngine() + : indexBuf(QOpenGLBuffer::IndexBuffer) +{ + initializeOpenGLFunctions(); + + // Generate 2 VBOs + arrayBuf.create(); + indexBuf.create(); + + // Initializes cube geometry and transfers it to VBOs + initCubeGeometry(); +} + +GeometryEngine::~GeometryEngine() +{ + arrayBuf.destroy(); + indexBuf.destroy(); +} + +void GeometryEngine::initCubeGeometry() +{ + // For cube we would need only 8 vertices but we have to + // duplicate vertex for each face because texture coordinate + // is different. + VertexData vertices[] = { + // Vertex data for face 0 + {QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.0f, 0.0f)}, // v0 + {QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.0f)}, // v1 + {QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.0f, 0.5f)}, // v2 + {QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v3 + + // Vertex data for face 1 + {QVector3D( 1.0f, -1.0f, 1.0f), QVector2D( 0.0f, 0.5f)}, // v4 + {QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.5f)}, // v5 + {QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.0f, 1.0f)}, // v6 + {QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v7 + + // Vertex data for face 2 + {QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v8 + {QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(1.0f, 0.5f)}, // v9 + {QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)}, // v10 + {QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(1.0f, 1.0f)}, // v11 + + // Vertex data for face 3 + {QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v12 + {QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(1.0f, 0.0f)}, // v13 + {QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v14 + {QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(1.0f, 0.5f)}, // v15 + + // Vertex data for face 4 + {QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.0f)}, // v16 + {QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v17 + {QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v18 + {QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v19 + + // Vertex data for face 5 + {QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v20 + {QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v21 + {QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v22 + {QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)} // v23 + }; + + // Indices for drawing cube faces using triangle strips. + // Triangle strips can be connected by duplicating indices + // between the strips. If connecting strips have opposite + // vertex order then last index of the first strip and first + // index of the second strip needs to be duplicated. If + // connecting strips have same vertex order then only last + // index of the first strip needs to be duplicated. + GLushort indices[] = { + 0, 1, 2, 3, 3, // Face 0 - triangle strip ( v0, v1, v2, v3) + 4, 4, 5, 6, 7, 7, // Face 1 - triangle strip ( v4, v5, v6, v7) + 8, 8, 9, 10, 11, 11, // Face 2 - triangle strip ( v8, v9, v10, v11) + 12, 12, 13, 14, 15, 15, // Face 3 - triangle strip (v12, v13, v14, v15) + 16, 16, 17, 18, 19, 19, // Face 4 - triangle strip (v16, v17, v18, v19) + 20, 20, 21, 22, 23 // Face 5 - triangle strip (v20, v21, v22, v23) + }; + + // Transfer vertex data to VBO 0 + arrayBuf.bind(); + arrayBuf.allocate(vertices, 24 * sizeof(VertexData)); + + // Transfer index data to VBO 1 + indexBuf.bind(); + indexBuf.allocate(indices, 34 * sizeof(GLushort)); +} + +void GeometryEngine::drawCubeGeometry(QOpenGLShaderProgram *program) +{ + // Tell OpenGL which VBOs to use + arrayBuf.bind(); + indexBuf.bind(); + + // Offset for position + quintptr offset = 0; + + // Tell OpenGL programmable pipeline how to locate vertex position data + int vertexLocation = program->attributeLocation("a_position"); + program->enableAttributeArray(vertexLocation); + program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData)); + + // Offset for texture coordinate + offset += sizeof(QVector3D); + + // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data + int texcoordLocation = program->attributeLocation("a_texcoord"); + program->enableAttributeArray(texcoordLocation); + program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData)); + + // Draw cube geometry using indices from VBO 1 + glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, nullptr); +} diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h new file mode 100644 index 0000000000..646d6570c8 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/geometryengine.h @@ -0,0 +1,26 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef GEOMETRYENGINE_H +#define GEOMETRYENGINE_H + +#include <QOpenGLFunctions> +#include <QOpenGLShaderProgram> +#include <QOpenGLBuffer> + +class GeometryEngine : protected QOpenGLFunctions +{ +public: + GeometryEngine(); + virtual ~GeometryEngine(); + + void drawCubeGeometry(QOpenGLShaderProgram *program); + +private: + void initCubeGeometry(); + + QOpenGLBuffer arrayBuf; + QOpenGLBuffer indexBuf; +}; + +#endif // GEOMETRYENGINE_H diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/main.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/main.cpp new file mode 100644 index 0000000000..e21a976ca7 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" +#include <QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp new file mode 100644 index 0000000000..b9dc4c5fe2 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.cpp @@ -0,0 +1,161 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwidget.h" +#include <QMouseEvent> +#include <cmath> + +MainWidget::~MainWidget() +{ + cleanup(); +} + +void MainWidget::cleanup() +{ + makeCurrent(); + delete texture; + texture = nullptr; + delete geometries; + geometries = nullptr; + delete program; + program = nullptr; + doneCurrent(); + + QObject::disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MainWidget::cleanup); +} + +void MainWidget::mousePressEvent(QMouseEvent *e) +{ + // Save mouse press position + mousePressPosition = QVector2D(e->position()); +} + +void MainWidget::mouseReleaseEvent(QMouseEvent *e) +{ + // Mouse release position - mouse press position + QVector2D diff = QVector2D(e->position()) - mousePressPosition; + + // Rotation axis is perpendicular to the mouse position difference + // vector + QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized(); + + // Accelerate angular speed relative to the length of the mouse sweep + qreal acc = diff.length() / 100.0; + + // Calculate new rotation axis as weighted sum + rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized(); + + // Increase angular speed + angularSpeed += acc; +} + +void MainWidget::timerEvent(QTimerEvent *) +{ + // Decrease angular speed (friction) + angularSpeed *= 0.99; + + // Stop rotation when speed goes below threshold + if (angularSpeed < 0.01) { + angularSpeed = 0.0; + } else { + // Update rotation + rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation; + + // Request an update + update(); + } +} + +void MainWidget::initializeGL() +{ + initializeOpenGLFunctions(); + + glClearColor(0, 0, 0, 1); + + initShaders(); + initTextures(); + + glEnable(GL_DEPTH_TEST); + + glEnable(GL_CULL_FACE); + + geometries = new GeometryEngine; + + // Use QBasicTimer because its faster than QTimer + timer.start(12, this); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MainWidget::cleanup); +} + +void MainWidget::initShaders() +{ + program = new QOpenGLShaderProgram; + // Compile vertex shader + if (!program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl")) + close(); + + // Compile fragment shader + if (!program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fshader.glsl")) + close(); + + // Link shader pipeline + if (!program->link()) + close(); + + // Bind shader pipeline for use + if (!program->bind()) + close(); +} + +void MainWidget::initTextures() +{ + // Load cube.png image + texture = new QOpenGLTexture(QImage(":/cube.png").mirrored()); + + // Set nearest filtering mode for texture minification + texture->setMinificationFilter(QOpenGLTexture::Nearest); + + // Set bilinear filtering mode for texture magnification + texture->setMagnificationFilter(QOpenGLTexture::Linear); + + // Wrap texture coordinates by repeating + // f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2) + texture->setWrapMode(QOpenGLTexture::Repeat); +} + +void MainWidget::resizeGL(int w, int h) +{ + // Calculate aspect ratio + qreal aspect = qreal(w) / qreal(h ? h : 1); + + // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees + const qreal zNear = 3.0, zFar = 7.0, fov = 45.0; + + // Reset projection + projection.setToIdentity(); + + // Set perspective projection + projection.perspective(fov, aspect, zNear, zFar); +} + +void MainWidget::paintGL() +{ + // Clear color and depth buffer + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + texture->bind(); + + // Calculate model view transformation + QMatrix4x4 matrix; + matrix.translate(0.0, 0.0, -5.0); + matrix.rotate(rotation); + + // Set modelview-projection matrix + program->setUniformValue("mvp_matrix", projection * matrix); + + // Use texture unit 0 which contains cube.png + program->setUniformValue("texture", 0); + + // Draw cube geometry + geometries->drawCubeGeometry(program); +} diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h new file mode 100644 index 0000000000..b21131d485 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwidget.h @@ -0,0 +1,57 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include "geometryengine.h" + +#include <QOpenGLWidget> +#include <QOpenGLFunctions> +#include <QMatrix4x4> +#include <QQuaternion> +#include <QVector2D> +#include <QBasicTimer> +#include <QOpenGLShaderProgram> +#include <QOpenGLTexture> + +class GeometryEngine; + +class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT + +public: + using QOpenGLWidget::QOpenGLWidget; + ~MainWidget(); + +protected: + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void timerEvent(QTimerEvent *e) override; + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void initShaders(); + void initTextures(); + +private slots: + void cleanup(); + +private: + QBasicTimer timer; + QOpenGLShaderProgram *program = nullptr; + GeometryEngine *geometries = nullptr; + QOpenGLTexture *texture = nullptr; + + QMatrix4x4 projection; + + QVector2D mousePressPosition; + QVector3D rotationAxis; + qreal angularSpeed = 0; + QQuaternion rotation; +}; + +#endif // MAINWIDGET_H diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp new file mode 100644 index 0000000000..9a530bc4f7 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "mainwidget.h" +#include <QDockWidget> + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + MainWidget *w1 = new MainWidget(); + centralWidget()->layout()->addWidget(w1); + + MainWidget *w2 = new MainWidget(); + QDockWidget *dock = new QDockWidget("OpenGL Dock", this); + w2->setFixedSize(300, 300); + dock->setWidget(w2); + dock->setFixedSize(300, 300); + + addDockWidget(Qt::RightDockWidgetArea, dock); + dock->setFloating(false); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h new file mode 100644 index 0000000000..bea5cc41cc --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.h @@ -0,0 +1,24 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; +#endif // MAINWINDOW_H diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.ui b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.ui new file mode 100644 index 0000000000..f013b8ec25 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/mainwindow.ui @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QHBoxLayout" name="horizontalLayout"/> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>20</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl b/tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl new file mode 100644 index 0000000000..10f3646aa6 --- /dev/null +++ b/tests/manual/qopenglwidget/dockedopenglwidget/vshader.glsl @@ -0,0 +1,14 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +uniform mat4 mvp_matrix; + +attribute vec4 a_position; +attribute vec2 a_texcoord; + +varying vec2 v_texcoord; + +void main() +{ + gl_Position = mvp_matrix * a_position; + v_texcoord = a_texcoord; +} |