| 1 | /**************************************************************************** | 
| 2 |  ** | 
| 3 |  ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 |  ** Contact: https://www.qt.io/licensing/ | 
| 5 |  ** | 
| 6 |  ** This file is part of the examples of the Qt Toolkit. | 
| 7 |  ** | 
| 8 |  ** $QT_BEGIN_LICENSE:BSD$ | 
| 9 |  ** Commercial License Usage | 
| 10 |  ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 |  ** accordance with the commercial license agreement provided with the | 
| 12 |  ** Software or, alternatively, in accordance with the terms contained in | 
| 13 |  ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 |  ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 |  ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 |  ** | 
| 17 |  ** BSD License Usage | 
| 18 |  ** Alternatively, you may use this file under the terms of the BSD license | 
| 19 |  ** as follows: | 
| 20 |  ** | 
| 21 |  ** "Redistribution and use in source and binary forms, with or without | 
| 22 |  ** modification, are permitted provided that the following conditions are | 
| 23 |  ** met: | 
| 24 |  **   * Redistributions of source code must retain the above copyright | 
| 25 |  **     notice, this list of conditions and the following disclaimer. | 
| 26 |  **   * Redistributions in binary form must reproduce the above copyright | 
| 27 |  **     notice, this list of conditions and the following disclaimer in | 
| 28 |  **     the documentation and/or other materials provided with the | 
| 29 |  **     distribution. | 
| 30 |  **   * Neither the name of The Qt Company Ltd nor the names of its | 
| 31 |  **     contributors may be used to endorse or promote products derived | 
| 32 |  **     from this software without specific prior written permission. | 
| 33 |  ** | 
| 34 |  ** | 
| 35 |  ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| 36 |  ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| 37 |  ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
| 38 |  ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
| 39 |  ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
| 40 |  ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
| 41 |  ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| 42 |  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 43 |  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 44 |  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| 45 |  ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | 
| 46 |  ** | 
| 47 |  ** $QT_END_LICENSE$ | 
| 48 |  ** | 
| 49 |  ****************************************************************************/ | 
| 50 |  | 
| 51 | #include "glwidget.h" | 
| 52 | #include <QPainter> | 
| 53 | #include <QPaintEngine> | 
| 54 | #include <QOpenGLShaderProgram> | 
| 55 | #include <QOpenGLTexture> | 
| 56 | #include <QRandomGenerator> | 
| 57 | #include <QCoreApplication> | 
| 58 | #include <qmath.h> | 
| 59 |  | 
| 60 | #include "mainwindow.h" | 
| 61 | #include "bubble.h" | 
| 62 |  | 
| 63 | const int bubbleNum = 8; | 
| 64 |  | 
| 65 | #ifndef GL_SRGB8_ALPHA8 | 
| 66 | #define GL_SRGB8_ALPHA8 0x8C43 | 
| 67 | #endif | 
| 68 |  | 
| 69 | GLWidget::GLWidget(MainWindow *mw, bool button, const QColor &background) | 
| 70 |     : m_mainWindow(mw), | 
| 71 |       m_hasButton(button), | 
| 72 |       m_background(background) | 
| 73 | { | 
| 74 |     setMinimumSize(minw: 300, minh: 250); | 
| 75 |     if (QCoreApplication::arguments().contains(QStringLiteral("--srgb" ))) | 
| 76 |         setTextureFormat(GL_SRGB8_ALPHA8); | 
| 77 | } | 
| 78 |  | 
| 79 | GLWidget::~GLWidget() | 
| 80 | { | 
| 81 |     qDeleteAll(c: m_bubbles); | 
| 82 |  | 
| 83 |     // And now release all OpenGL resources. | 
| 84 |     makeCurrent(); | 
| 85 |     delete m_texture; | 
| 86 |     delete m_program1; | 
| 87 |     delete m_program2; | 
| 88 |     delete m_vshader1; | 
| 89 |     delete m_fshader1; | 
| 90 |     delete m_vshader2; | 
| 91 |     delete m_fshader2; | 
| 92 |     m_vbo1.destroy(); | 
| 93 |     m_vbo2.destroy(); | 
| 94 |     doneCurrent(); | 
| 95 | } | 
| 96 |  | 
| 97 | void GLWidget::setScaling(int scale) | 
| 98 | { | 
| 99 |     if (scale > 30) | 
| 100 |         m_fScale = 1 + qreal(scale - 30) / 30 * 0.25; | 
| 101 |     else if (scale < 30) | 
| 102 |         m_fScale =  1 - (qreal(30 - scale) / 30 * 0.25); | 
| 103 |     else | 
| 104 |         m_fScale = 1; | 
| 105 | } | 
| 106 |  | 
| 107 | void GLWidget::setLogo() | 
| 108 | { | 
| 109 |     m_qtLogo = true; | 
| 110 | } | 
| 111 |  | 
| 112 | void GLWidget::setTexture() | 
| 113 | { | 
| 114 |     m_qtLogo = false; | 
| 115 | } | 
| 116 |  | 
| 117 | void GLWidget::setShowBubbles(bool bubbles) | 
| 118 | { | 
| 119 |     m_showBubbles = bubbles; | 
| 120 | } | 
| 121 |  | 
| 122 | void GLWidget::paintQtLogo() | 
| 123 | { | 
| 124 |     m_program1->enableAttributeArray(location: m_vertexAttr1); | 
| 125 |     m_program1->enableAttributeArray(location: m_normalAttr1); | 
| 126 |  | 
| 127 |     m_vbo1.bind(); | 
| 128 |     // The data in the buffer is placed like this: | 
| 129 |     // vertex1.x, vertex1.y, vertex1.z, normal1.x, normal1.y, normal1.z, vertex2.x, ... | 
| 130 |     m_program1->setAttributeBuffer(location: m_vertexAttr1, GL_FLOAT, offset: 0, tupleSize: 3, stride: 6 * sizeof(GLfloat)); | 
| 131 |     m_program1->setAttributeBuffer(location: m_normalAttr1, GL_FLOAT, offset: 3 * sizeof(GLfloat), tupleSize: 3, stride: 6 * sizeof(GLfloat)); | 
| 132 |     m_vbo1.release(); | 
| 133 |  | 
| 134 |     glDrawArrays(GL_TRIANGLES, first: 0, count: m_vertices.size()); | 
| 135 |  | 
| 136 |     m_program1->disableAttributeArray(location: m_normalAttr1); | 
| 137 |     m_program1->disableAttributeArray(location: m_vertexAttr1); | 
| 138 | } | 
| 139 |  | 
| 140 | void GLWidget::paintTexturedCube() | 
| 141 | { | 
| 142 |     m_texture->bind(); | 
| 143 |  | 
| 144 |     if (!m_vbo2.isCreated()) { | 
| 145 |         static GLfloat afVertices[] = { | 
| 146 |             -0.5, 0.5, 0.5, 0.5,-0.5,0.5,-0.5,-0.5,0.5, | 
| 147 |             0.5, -0.5, 0.5, -0.5,0.5,0.5,0.5,0.5,0.5, | 
| 148 |             -0.5, -0.5, -0.5, 0.5,-0.5,-0.5,-0.5,0.5,-0.5, | 
| 149 |             0.5, 0.5, -0.5, -0.5,0.5,-0.5,0.5,-0.5,-0.5, | 
| 150 |  | 
| 151 |             0.5, -0.5, -0.5, 0.5,-0.5,0.5,0.5,0.5,-0.5, | 
| 152 |             0.5, 0.5, 0.5, 0.5,0.5,-0.5,0.5,-0.5,0.5, | 
| 153 |             -0.5, 0.5, -0.5, -0.5,-0.5,0.5,-0.5,-0.5,-0.5, | 
| 154 |             -0.5, -0.5, 0.5, -0.5,0.5,-0.5,-0.5,0.5,0.5, | 
| 155 |  | 
| 156 |             0.5, 0.5,  -0.5, -0.5, 0.5,  0.5,  -0.5,  0.5,  -0.5, | 
| 157 |             -0.5,  0.5,  0.5,  0.5,  0.5,  -0.5, 0.5, 0.5,  0.5, | 
| 158 |             -0.5,  -0.5, -0.5, -0.5, -0.5, 0.5,  0.5, -0.5, -0.5, | 
| 159 |             0.5, -0.5, 0.5,  0.5,  -0.5, -0.5, -0.5,  -0.5, 0.5 | 
| 160 |         }; | 
| 161 |  | 
| 162 |         static GLfloat afTexCoord[] = { | 
| 163 |             0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f, | 
| 164 |             1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f, | 
| 165 |             1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f, | 
| 166 |             0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f, | 
| 167 |  | 
| 168 |             1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f, | 
| 169 |             0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f, | 
| 170 |             0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f, | 
| 171 |             1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f, | 
| 172 |  | 
| 173 |             0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f, | 
| 174 |             1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f, | 
| 175 |             1.0f,0.0f, 1.0f,1.0f, 0.0f,0.0f, | 
| 176 |             0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f | 
| 177 |         }; | 
| 178 |  | 
| 179 |         GLfloat afNormals[] = { | 
| 180 |  | 
| 181 |             0,0,-1, 0,0,-1, 0,0,-1, | 
| 182 |             0,0,-1, 0,0,-1, 0,0,-1, | 
| 183 |             0,0,1, 0,0,1, 0,0,1, | 
| 184 |             0,0,1, 0,0,1, 0,0,1, | 
| 185 |  | 
| 186 |             -1,0,0, -1,0,0, -1,0,0, | 
| 187 |             -1,0,0, -1,0,0, -1,0,0, | 
| 188 |             1,0,0, 1,0,0, 1,0,0, | 
| 189 |             1,0,0, 1,0,0, 1,0,0, | 
| 190 |  | 
| 191 |             0,-1,0, 0,-1,0, 0,-1,0, | 
| 192 |             0,-1,0, 0,-1,0, 0,-1,0, | 
| 193 |             0,1,0, 0,1,0, 0,1,0, | 
| 194 |             0,1,0, 0,1,0, 0,1,0 | 
| 195 |         }; | 
| 196 |  | 
| 197 |         m_vbo2.create(); | 
| 198 |         m_vbo2.bind(); | 
| 199 |         m_vbo2.allocate(count: 36 * 8 * sizeof(GLfloat)); | 
| 200 |         m_vbo2.write(offset: 0, data: afVertices, count: sizeof(afVertices)); | 
| 201 |         m_vbo2.write(offset: sizeof(afVertices), data: afTexCoord, count: sizeof(afTexCoord)); | 
| 202 |         m_vbo2.write(offset: sizeof(afVertices) + sizeof(afTexCoord), data: afNormals, count: sizeof(afNormals)); | 
| 203 |         m_vbo2.release(); | 
| 204 |     } | 
| 205 |  | 
| 206 |     m_program2->setUniformValue(location: m_textureUniform2, value: 0); // use texture unit 0 | 
| 207 |  | 
| 208 |     m_program2->enableAttributeArray(location: m_vertexAttr2); | 
| 209 |     m_program2->enableAttributeArray(location: m_normalAttr2); | 
| 210 |     m_program2->enableAttributeArray(location: m_texCoordAttr2); | 
| 211 |  | 
| 212 |     m_vbo2.bind(); | 
| 213 |     // In the buffer we first have 36 vertices (3 floats for each), then 36 texture | 
| 214 |     // coordinates (2 floats for each), then 36 normals (3 floats for each). | 
| 215 |     m_program2->setAttributeBuffer(location: m_vertexAttr2, GL_FLOAT, offset: 0, tupleSize: 3); | 
| 216 |     m_program2->setAttributeBuffer(location: m_texCoordAttr2, GL_FLOAT, offset: 36 * 3 * sizeof(GLfloat), tupleSize: 2); | 
| 217 |     m_program2->setAttributeBuffer(location: m_normalAttr2, GL_FLOAT, offset: 36 * 5 * sizeof(GLfloat), tupleSize: 3); | 
| 218 |     m_vbo2.release(); | 
| 219 |  | 
| 220 |     glDrawArrays(GL_TRIANGLES, first: 0, count: 36); | 
| 221 |  | 
| 222 |     m_program2->disableAttributeArray(location: m_vertexAttr2); | 
| 223 |     m_program2->disableAttributeArray(location: m_normalAttr2); | 
| 224 |     m_program2->disableAttributeArray(location: m_texCoordAttr2); | 
| 225 | } | 
| 226 |  | 
| 227 | void GLWidget::initializeGL() | 
| 228 | { | 
| 229 |     initializeOpenGLFunctions(); | 
| 230 |  | 
| 231 |     m_texture = new QOpenGLTexture(QImage(":/qt.png" )); | 
| 232 |  | 
| 233 |     m_vshader1 = new QOpenGLShader(QOpenGLShader::Vertex); | 
| 234 |     const char *vsrc1 = | 
| 235 |         "attribute highp vec4 vertex;\n"  | 
| 236 |         "attribute mediump vec3 normal;\n"  | 
| 237 |         "uniform mediump mat4 matrix;\n"  | 
| 238 |         "varying mediump vec4 color;\n"  | 
| 239 |         "void main(void)\n"  | 
| 240 |         "{\n"  | 
| 241 |         "    vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"  | 
| 242 |         "    float angle = max(dot(normal, toLight), 0.0);\n"  | 
| 243 |         "    vec3 col = vec3(0.40, 1.0, 0.0);\n"  | 
| 244 |         "    color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"  | 
| 245 |         "    color = clamp(color, 0.0, 1.0);\n"  | 
| 246 |         "    gl_Position = matrix * vertex;\n"  | 
| 247 |         "}\n" ; | 
| 248 |     m_vshader1->compileSourceCode(source: vsrc1); | 
| 249 |  | 
| 250 |     m_fshader1 = new QOpenGLShader(QOpenGLShader::Fragment); | 
| 251 |     const char *fsrc1 = | 
| 252 |         "varying mediump vec4 color;\n"  | 
| 253 |         "void main(void)\n"  | 
| 254 |         "{\n"  | 
| 255 |         "    gl_FragColor = color;\n"  | 
| 256 |         "}\n" ; | 
| 257 |     m_fshader1->compileSourceCode(source: fsrc1); | 
| 258 |  | 
| 259 |     m_program1 = new QOpenGLShaderProgram; | 
| 260 |     m_program1->addShader(shader: m_vshader1); | 
| 261 |     m_program1->addShader(shader: m_fshader1); | 
| 262 |     m_program1->link(); | 
| 263 |  | 
| 264 |     m_vertexAttr1 = m_program1->attributeLocation(name: "vertex" ); | 
| 265 |     m_normalAttr1 = m_program1->attributeLocation(name: "normal" ); | 
| 266 |     m_matrixUniform1 = m_program1->uniformLocation(name: "matrix" ); | 
| 267 |  | 
| 268 |     m_vshader2 = new QOpenGLShader(QOpenGLShader::Vertex); | 
| 269 |     const char *vsrc2 = | 
| 270 |         "attribute highp vec4 vertex;\n"  | 
| 271 |         "attribute highp vec4 texCoord;\n"  | 
| 272 |         "attribute mediump vec3 normal;\n"  | 
| 273 |         "uniform mediump mat4 matrix;\n"  | 
| 274 |         "varying highp vec4 texc;\n"  | 
| 275 |         "varying mediump float angle;\n"  | 
| 276 |         "void main(void)\n"  | 
| 277 |         "{\n"  | 
| 278 |         "    vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"  | 
| 279 |         "    angle = max(dot(normal, toLight), 0.0);\n"  | 
| 280 |         "    gl_Position = matrix * vertex;\n"  | 
| 281 |         "    texc = texCoord;\n"  | 
| 282 |         "}\n" ; | 
| 283 |     m_vshader2->compileSourceCode(source: vsrc2); | 
| 284 |  | 
| 285 |     m_fshader2 = new QOpenGLShader(QOpenGLShader::Fragment); | 
| 286 |     const char *fsrc2 = | 
| 287 |         "varying highp vec4 texc;\n"  | 
| 288 |         "uniform sampler2D tex;\n"  | 
| 289 |         "varying mediump float angle;\n"  | 
| 290 |         "void main(void)\n"  | 
| 291 |         "{\n"  | 
| 292 |         "    highp vec3 color = texture2D(tex, texc.st).rgb;\n"  | 
| 293 |         "    color = color * 0.2 + color * 0.8 * angle;\n"  | 
| 294 |         "    gl_FragColor = vec4(clamp(color, 0.0, 1.0), 1.0);\n"  | 
| 295 |         "}\n" ; | 
| 296 |     m_fshader2->compileSourceCode(source: fsrc2); | 
| 297 |  | 
| 298 |     m_program2 = new QOpenGLShaderProgram; | 
| 299 |     m_program2->addShader(shader: m_vshader2); | 
| 300 |     m_program2->addShader(shader: m_fshader2); | 
| 301 |     m_program2->link(); | 
| 302 |  | 
| 303 |     m_vertexAttr2 = m_program2->attributeLocation(name: "vertex" ); | 
| 304 |     m_normalAttr2 = m_program2->attributeLocation(name: "normal" ); | 
| 305 |     m_texCoordAttr2 = m_program2->attributeLocation(name: "texCoord" ); | 
| 306 |     m_matrixUniform2 = m_program2->uniformLocation(name: "matrix" ); | 
| 307 |     m_textureUniform2 = m_program2->uniformLocation(name: "tex" ); | 
| 308 |  | 
| 309 |     m_fAngle = 0; | 
| 310 |     m_fScale = 1; | 
| 311 |  | 
| 312 |     createGeometry(); | 
| 313 |  | 
| 314 |     // Use a vertex buffer object. Client-side pointers are old-school and should be avoided. | 
| 315 |     m_vbo1.create(); | 
| 316 |     m_vbo1.bind(); | 
| 317 |     // For the cube all the data belonging to the texture coordinates and | 
| 318 |     // normals is placed separately, after the vertices. Here, for the Qt logo, | 
| 319 |     // let's do something different and potentially more efficient: create a | 
| 320 |     // properly interleaved data set. | 
| 321 |     const int vertexCount = m_vertices.count(); | 
| 322 |     QVector<GLfloat> buf; | 
| 323 |     buf.resize(asize: vertexCount * 3 * 2); | 
| 324 |     GLfloat *p = buf.data(); | 
| 325 |     for (int i = 0; i < vertexCount; ++i) { | 
| 326 |         *p++ = m_vertices[i].x(); | 
| 327 |         *p++ = m_vertices[i].y(); | 
| 328 |         *p++ = m_vertices[i].z(); | 
| 329 |         *p++ = m_normals[i].x(); | 
| 330 |         *p++ = m_normals[i].y(); | 
| 331 |         *p++ = m_normals[i].z(); | 
| 332 |     } | 
| 333 |     m_vbo1.allocate(data: buf.constData(), count: buf.count() * sizeof(GLfloat)); | 
| 334 |     m_vbo1.release(); | 
| 335 |  | 
| 336 |     createBubbles(number: bubbleNum - m_bubbles.count()); | 
| 337 | } | 
| 338 |  | 
| 339 | void GLWidget::paintGL() | 
| 340 | { | 
| 341 |     createBubbles(number: bubbleNum - m_bubbles.count()); | 
| 342 |  | 
| 343 |     QPainter painter; | 
| 344 |     painter.begin(this); | 
| 345 |  | 
| 346 |     painter.beginNativePainting(); | 
| 347 |  | 
| 348 |     glClearColor(red: m_background.redF(), green: m_background.greenF(), blue: m_background.blueF(), alpha: m_transparent ? 0.0f : 1.0f); | 
| 349 |     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
| 350 |  | 
| 351 |     glFrontFace(GL_CW); | 
| 352 |     glCullFace(GL_FRONT); | 
| 353 |     glEnable(GL_CULL_FACE); | 
| 354 |     glEnable(GL_DEPTH_TEST); | 
| 355 |  | 
| 356 |     QMatrix4x4 modelview; | 
| 357 |     modelview.rotate(angle: m_fAngle, x: 0.0f, y: 1.0f, z: 0.0f); | 
| 358 |     modelview.rotate(angle: m_fAngle, x: 1.0f, y: 0.0f, z: 0.0f); | 
| 359 |     modelview.rotate(angle: m_fAngle, x: 0.0f, y: 0.0f, z: 1.0f); | 
| 360 |     modelview.scale(factor: m_fScale); | 
| 361 |     modelview.translate(x: 0.0f, y: -0.2f, z: 0.0f); | 
| 362 |  | 
| 363 |     if (m_qtLogo) { | 
| 364 |         m_program1->bind(); | 
| 365 |         m_program1->setUniformValue(location: m_matrixUniform1, value: modelview); | 
| 366 |         paintQtLogo(); | 
| 367 |         m_program1->release(); | 
| 368 |     } else { | 
| 369 |         m_program2->bind(); | 
| 370 |         m_program2->setUniformValue(location: m_matrixUniform2, value: modelview); | 
| 371 |         paintTexturedCube(); | 
| 372 |         m_program2->release(); | 
| 373 |     } | 
| 374 |  | 
| 375 |     glDisable(GL_DEPTH_TEST); | 
| 376 |     glDisable(GL_CULL_FACE); | 
| 377 |  | 
| 378 |     painter.endNativePainting(); | 
| 379 |  | 
| 380 |     if (m_showBubbles) { | 
| 381 |         for (Bubble *bubble : qAsConst(t&: m_bubbles)) | 
| 382 |             bubble->drawBubble(painter: &painter); | 
| 383 |     } | 
| 384 |  | 
| 385 |     if (const int elapsed = m_time.elapsed()) { | 
| 386 |         QString framesPerSecond; | 
| 387 |         framesPerSecond.setNum(m_frames /(elapsed / 1000.0), f: 'f', prec: 2); | 
| 388 |         painter.setPen(m_transparent ? Qt::black : Qt::white); | 
| 389 |         painter.drawText(x: 20, y: 40, s: framesPerSecond + " paintGL calls / s" ); | 
| 390 |     } | 
| 391 |  | 
| 392 |     painter.end(); | 
| 393 |  | 
| 394 |     for (Bubble *bubble : qAsConst(t&: m_bubbles)) | 
| 395 |         bubble->move(bbox: rect()); | 
| 396 |  | 
| 397 |     if (!(m_frames % 100)) { | 
| 398 |         m_time.start(); | 
| 399 |         m_frames = 0; | 
| 400 |     } | 
| 401 |     m_fAngle += 1.0f; | 
| 402 |     ++m_frames; | 
| 403 |  | 
| 404 |     // When requested, follow the ideal way to animate: Rely on | 
| 405 |     // blocking swap and just schedule updates continuously. | 
| 406 |     if (!m_mainWindow->timerEnabled()) | 
| 407 |         update(); | 
| 408 | } | 
| 409 |  | 
| 410 | void GLWidget::createBubbles(int number) | 
| 411 | { | 
| 412 |     for (int i = 0; i < number; ++i) { | 
| 413 |         QPointF position(width()*(0.1 + QRandomGenerator::global()->bounded(highest: 0.8)), | 
| 414 |                          height()*(0.1 + QRandomGenerator::global()->bounded(highest: 0.8))); | 
| 415 |         qreal radius = qMin(a: width(), b: height())*(0.0175 + QRandomGenerator::global()->bounded(highest: 0.0875)); | 
| 416 |         QPointF velocity(width()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(highest: 1.0)), | 
| 417 |                          height()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(highest: 1.0))); | 
| 418 |  | 
| 419 |         m_bubbles.append(t: new Bubble(position, radius, velocity)); | 
| 420 |     } | 
| 421 | } | 
| 422 |  | 
| 423 | void GLWidget::createGeometry() | 
| 424 | { | 
| 425 |     m_vertices.clear(); | 
| 426 |     m_normals.clear(); | 
| 427 |  | 
| 428 |     qreal x1 = +0.06f; | 
| 429 |     qreal y1 = -0.14f; | 
| 430 |     qreal x2 = +0.14f; | 
| 431 |     qreal y2 = -0.06f; | 
| 432 |     qreal x3 = +0.08f; | 
| 433 |     qreal y3 = +0.00f; | 
| 434 |     qreal x4 = +0.30f; | 
| 435 |     qreal y4 = +0.22f; | 
| 436 |  | 
| 437 |     quad(x1, y1, x2, y2, x3: y2, y3: x2, x4: y1, y4: x1); | 
| 438 |     quad(x1: x3, y1: y3, x2: x4, y2: y4, x3: y4, y3: x4, x4: y3, y4: x3); | 
| 439 |  | 
| 440 |     extrude(x1, y1, x2, y2); | 
| 441 |     extrude(x1: x2, y1: y2, x2: y2, y2: x2); | 
| 442 |     extrude(x1: y2, y1: x2, x2: y1, y2: x1); | 
| 443 |     extrude(x1: y1, y1: x1, x2: x1, y2: y1); | 
| 444 |     extrude(x1: x3, y1: y3, x2: x4, y2: y4); | 
| 445 |     extrude(x1: x4, y1: y4, x2: y4, y2: x4); | 
| 446 |     extrude(x1: y4, y1: x4, x2: y3, y2: x3); | 
| 447 |  | 
| 448 |     const int NumSectors = 100; | 
| 449 |     const qreal sectorAngle = 2 * qreal(M_PI) / NumSectors; | 
| 450 |  | 
| 451 |     for (int i = 0; i < NumSectors; ++i) { | 
| 452 |         qreal angle = i * sectorAngle; | 
| 453 |         qreal x5 = 0.30 * sin(x: angle); | 
| 454 |         qreal y5 = 0.30 * cos(x: angle); | 
| 455 |         qreal x6 = 0.20 * sin(x: angle); | 
| 456 |         qreal y6 = 0.20 * cos(x: angle); | 
| 457 |  | 
| 458 |         angle += sectorAngle; | 
| 459 |         qreal x7 = 0.20 * sin(x: angle); | 
| 460 |         qreal y7 = 0.20 * cos(x: angle); | 
| 461 |         qreal x8 = 0.30 * sin(x: angle); | 
| 462 |         qreal y8 = 0.30 * cos(x: angle); | 
| 463 |  | 
| 464 |         quad(x1: x5, y1: y5, x2: x6, y2: y6, x3: x7, y3: y7, x4: x8, y4: y8); | 
| 465 |  | 
| 466 |         extrude(x1: x6, y1: y6, x2: x7, y2: y7); | 
| 467 |         extrude(x1: x8, y1: y8, x2: x5, y2: y5); | 
| 468 |     } | 
| 469 |  | 
| 470 |     for (int i = 0;i < m_vertices.size();i++) | 
| 471 |         m_vertices[i] *= 2.0f; | 
| 472 | } | 
| 473 |  | 
| 474 | void GLWidget::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4) | 
| 475 | { | 
| 476 |     m_vertices << QVector3D(x1, y1, -0.05f); | 
| 477 |     m_vertices << QVector3D(x2, y2, -0.05f); | 
| 478 |     m_vertices << QVector3D(x4, y4, -0.05f); | 
| 479 |  | 
| 480 |     m_vertices << QVector3D(x3, y3, -0.05f); | 
| 481 |     m_vertices << QVector3D(x4, y4, -0.05f); | 
| 482 |     m_vertices << QVector3D(x2, y2, -0.05f); | 
| 483 |  | 
| 484 |     QVector3D n = QVector3D::normal | 
| 485 |         (v1: QVector3D(x2 - x1, y2 - y1, 0.0f), v2: QVector3D(x4 - x1, y4 - y1, 0.0f)); | 
| 486 |  | 
| 487 |     m_normals << n; | 
| 488 |     m_normals << n; | 
| 489 |     m_normals << n; | 
| 490 |  | 
| 491 |     m_normals << n; | 
| 492 |     m_normals << n; | 
| 493 |     m_normals << n; | 
| 494 |  | 
| 495 |     m_vertices << QVector3D(x4, y4, 0.05f); | 
| 496 |     m_vertices << QVector3D(x2, y2, 0.05f); | 
| 497 |     m_vertices << QVector3D(x1, y1, 0.05f); | 
| 498 |  | 
| 499 |     m_vertices << QVector3D(x2, y2, 0.05f); | 
| 500 |     m_vertices << QVector3D(x4, y4, 0.05f); | 
| 501 |     m_vertices << QVector3D(x3, y3, 0.05f); | 
| 502 |  | 
| 503 |     n = QVector3D::normal | 
| 504 |         (v1: QVector3D(x2 - x4, y2 - y4, 0.0f), v2: QVector3D(x1 - x4, y1 - y4, 0.0f)); | 
| 505 |  | 
| 506 |     m_normals << n; | 
| 507 |     m_normals << n; | 
| 508 |     m_normals << n; | 
| 509 |  | 
| 510 |     m_normals << n; | 
| 511 |     m_normals << n; | 
| 512 |     m_normals << n; | 
| 513 | } | 
| 514 |  | 
| 515 | void GLWidget::extrude(qreal x1, qreal y1, qreal x2, qreal y2) | 
| 516 | { | 
| 517 |     m_vertices << QVector3D(x1, y1, +0.05f); | 
| 518 |     m_vertices << QVector3D(x2, y2, +0.05f); | 
| 519 |     m_vertices << QVector3D(x1, y1, -0.05f); | 
| 520 |  | 
| 521 |     m_vertices << QVector3D(x2, y2, -0.05f); | 
| 522 |     m_vertices << QVector3D(x1, y1, -0.05f); | 
| 523 |     m_vertices << QVector3D(x2, y2, +0.05f); | 
| 524 |  | 
| 525 |     QVector3D n = QVector3D::normal | 
| 526 |         (v1: QVector3D(x2 - x1, y2 - y1, 0.0f), v2: QVector3D(0.0f, 0.0f, -0.1f)); | 
| 527 |  | 
| 528 |     m_normals << n; | 
| 529 |     m_normals << n; | 
| 530 |     m_normals << n; | 
| 531 |  | 
| 532 |     m_normals << n; | 
| 533 |     m_normals << n; | 
| 534 |     m_normals << n; | 
| 535 | } | 
| 536 |  | 
| 537 | void GLWidget::setTransparent(bool transparent) | 
| 538 | { | 
| 539 |     setAttribute(Qt::WA_AlwaysStackOnTop, on: transparent); | 
| 540 |     m_transparent = transparent; | 
| 541 |     // Call update() on the top-level window after toggling AlwayStackOnTop to make sure | 
| 542 |     // the entire backingstore is updated accordingly. | 
| 543 |     window()->update(); | 
| 544 | } | 
| 545 |  | 
| 546 | void GLWidget::resizeGL(int, int) | 
| 547 | { | 
| 548 |     if (m_hasButton) { | 
| 549 |         if (!m_btn) { | 
| 550 |             m_btn = new QPushButton("A widget on top.\nPress for more widgets." , this); | 
| 551 |             connect(sender: m_btn, signal: &QPushButton::clicked, receiver: this, slot: &GLWidget::handleButtonPress); | 
| 552 |         } | 
| 553 |         m_btn->move(ax: 20, ay: 80); | 
| 554 |     } | 
| 555 | } | 
| 556 |  | 
| 557 | void GLWidget::handleButtonPress() | 
| 558 | { | 
| 559 |     m_mainWindow->addNew(); | 
| 560 | } | 
| 561 |  |