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 "glwindow.h"
52#include <QImage>
53#include <QOpenGLTexture>
54#include <QOpenGLShaderProgram>
55#include <QOpenGLBuffer>
56#include <QOpenGLContext>
57#include <QOpenGLVertexArrayObject>
58#include <QOpenGLExtraFunctions>
59#include <QPropertyAnimation>
60#include <QSequentialAnimationGroup>
61#include <QTimer>
62
63GLWindow::GLWindow()
64{
65 m_world.setToIdentity();
66 m_world.translate(0, 0, -1);
67 m_world.rotate(180, 1, 0, 0);
68
69 QSequentialAnimationGroup *animGroup = new QSequentialAnimationGroup(this);
70 animGroup->setLoopCount(-1);
71 QPropertyAnimation *zAnim0 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
72 zAnim0->setStartValue(1.5f);
73 zAnim0->setEndValue(10.0f);
74 zAnim0->setDuration(2000);
75 animGroup->addAnimation(zAnim0);
76 QPropertyAnimation *zAnim1 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
77 zAnim1->setStartValue(10.0f);
78 zAnim1->setEndValue(50.0f);
79 zAnim1->setDuration(4000);
80 zAnim1->setEasingCurve(QEasingCurve::OutElastic);
81 animGroup->addAnimation(zAnim1);
82 QPropertyAnimation *zAnim2 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
83 zAnim2->setStartValue(50.0f);
84 zAnim2->setEndValue(1.5f);
85 zAnim2->setDuration(2000);
86 animGroup->addAnimation(zAnim2);
87 animGroup->start();
88
89 QPropertyAnimation* rAnim = new QPropertyAnimation(this, QByteArrayLiteral("r"));
90 rAnim->setStartValue(0.0f);
91 rAnim->setEndValue(360.0f);
92 rAnim->setDuration(2000);
93 rAnim->setLoopCount(-1);
94 rAnim->start();
95
96 QTimer::singleShot(4000, this, &GLWindow::startSecondStage);
97}
98
99GLWindow::~GLWindow()
100{
101 makeCurrent();
102 delete m_texture;
103 delete m_program;
104 delete m_vbo;
105 delete m_vao;
106}
107
108void GLWindow::startSecondStage()
109{
110 QPropertyAnimation* r2Anim = new QPropertyAnimation(this, QByteArrayLiteral("r2"));
111 r2Anim->setStartValue(0.0f);
112 r2Anim->setEndValue(360.0f);
113 r2Anim->setDuration(20000);
114 r2Anim->setLoopCount(-1);
115 r2Anim->start();
116}
117
118void GLWindow::setZ(float v)
119{
120 m_eye.setZ(v);
121 m_uniformsDirty = true;
122 update();
123}
124
125void GLWindow::setR(float v)
126{
127 m_r = v;
128 m_uniformsDirty = true;
129 update();
130}
131
132void GLWindow::setR2(float v)
133{
134 m_r2 = v;
135 m_uniformsDirty = true;
136 update();
137}
138
139static const char *vertexShaderSource =
140 "layout(location = 0) in vec4 vertex;\n"
141 "layout(location = 1) in vec3 normal;\n"
142 "out vec3 vert;\n"
143 "out vec3 vertNormal;\n"
144 "out vec3 color;\n"
145 "uniform mat4 projMatrix;\n"
146 "uniform mat4 camMatrix;\n"
147 "uniform mat4 worldMatrix;\n"
148 "uniform mat4 myMatrix;\n"
149 "uniform sampler2D sampler;\n"
150 "void main() {\n"
151 " ivec2 pos = ivec2(gl_InstanceID % 32, gl_InstanceID / 32);\n"
152 " vec2 t = vec2(float(-16 + pos.x) * 0.8, float(-18 + pos.y) * 0.6);\n"
153 " float val = 2.0 * length(texelFetch(sampler, pos, 0).rgb);\n"
154 " mat4 wm = myMatrix * mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, val, 1) * worldMatrix;\n"
155 " color = texelFetch(sampler, pos, 0).rgb * vec3(0.4, 1.0, 0.0);\n"
156 " vert = vec3(wm * vertex);\n"
157 " vertNormal = mat3(transpose(inverse(wm))) * normal;\n"
158 " gl_Position = projMatrix * camMatrix * wm * vertex;\n"
159 "}\n";
160
161static const char *fragmentShaderSource =
162 "in highp vec3 vert;\n"
163 "in highp vec3 vertNormal;\n"
164 "in highp vec3 color;\n"
165 "out highp vec4 fragColor;\n"
166 "uniform highp vec3 lightPos;\n"
167 "void main() {\n"
168 " highp vec3 L = normalize(lightPos - vert);\n"
169 " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
170 " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
171 " fragColor = vec4(col, 1.0);\n"
172 "}\n";
173
174QByteArray versionedShaderCode(const char *src)
175{
176 QByteArray versionedSrc;
177
178 if (QOpenGLContext::currentContext()->isOpenGLES())
179 versionedSrc.append(QByteArrayLiteral("#version 300 es\n"));
180 else
181 versionedSrc.append(QByteArrayLiteral("#version 330\n"));
182
183 versionedSrc.append(src);
184 return versionedSrc;
185}
186
187void GLWindow::initializeGL()
188{
189 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
190
191 QImage img(":/qtlogo.png");
192 Q_ASSERT(!img.isNull());
193 delete m_texture;
194 m_texture = new QOpenGLTexture(img.scaled(32, 36).mirrored());
195
196 delete m_program;
197 m_program = new QOpenGLShaderProgram;
198 // Prepend the correct version directive to the sources. The rest is the
199 // same, thanks to the common GLSL syntax.
200 m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, versionedShaderCode(vertexShaderSource));
201 m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, versionedShaderCode(fragmentShaderSource));
202 m_program->link();
203
204 m_projMatrixLoc = m_program->uniformLocation("projMatrix");
205 m_camMatrixLoc = m_program->uniformLocation("camMatrix");
206 m_worldMatrixLoc = m_program->uniformLocation("worldMatrix");
207 m_myMatrixLoc = m_program->uniformLocation("myMatrix");
208 m_lightPosLoc = m_program->uniformLocation("lightPos");
209
210 // Create a VAO. Not strictly required for ES 3, but it is for plain OpenGL.
211 delete m_vao;
212 m_vao = new QOpenGLVertexArrayObject;
213 if (m_vao->create())
214 m_vao->bind();
215
216 m_program->bind();
217 delete m_vbo;
218 m_vbo = new QOpenGLBuffer;
219 m_vbo->create();
220 m_vbo->bind();
221 m_vbo->allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat));
222 f->glEnableVertexAttribArray(0);
223 f->glEnableVertexAttribArray(1);
224 f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
225 nullptr);
226 f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
227 reinterpret_cast<void *>(3 * sizeof(GLfloat)));
228 m_vbo->release();
229
230 f->glEnable(GL_DEPTH_TEST);
231 f->glEnable(GL_CULL_FACE);
232}
233
234void GLWindow::resizeGL(int w, int h)
235{
236 m_proj.setToIdentity();
237 m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
238 m_uniformsDirty = true;
239}
240
241void GLWindow::paintGL()
242{
243 // Now use QOpenGLExtraFunctions instead of QOpenGLFunctions as we want to
244 // do more than what GL(ES) 2.0 offers.
245 QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
246
247 f->glClearColor(0, 0, 0, 1);
248 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
249
250 m_program->bind();
251 m_texture->bind();
252
253 if (m_uniformsDirty) {
254 m_uniformsDirty = false;
255 QMatrix4x4 camera;
256 camera.lookAt(m_eye, m_eye + m_target, QVector3D(0, 1, 0));
257 m_program->setUniformValue(m_projMatrixLoc, m_proj);
258 m_program->setUniformValue(m_camMatrixLoc, camera);
259 QMatrix4x4 wm = m_world;
260 wm.rotate(m_r, 1, 1, 0);
261 m_program->setUniformValue(m_worldMatrixLoc, wm);
262 QMatrix4x4 mm;
263 mm.setToIdentity();
264 mm.rotate(-m_r2, 1, 0, 0);
265 m_program->setUniformValue(m_myMatrixLoc, mm);
266 m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
267 }
268
269 // Now call a function introduced in OpenGL 3.1 / OpenGL ES 3.0. We
270 // requested a 3.3 or ES 3.0 context, so we know this will work.
271 f->glDrawArraysInstanced(GL_TRIANGLES, 0, m_logo.vertexCount(), 32 * 36);
272}
273