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 <QMouseEvent> |
53 | #include <QOpenGLShaderProgram> |
54 | #include <QCoreApplication> |
55 | #include <math.h> |
56 | |
57 | bool GLWidget::m_transparent = false; |
58 | |
59 | GLWidget::GLWidget(QWidget *parent) |
60 | : QOpenGLWidget(parent) |
61 | { |
62 | m_core = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile; |
63 | // --transparent causes the clear color to be transparent. Therefore, on systems that |
64 | // support it, the widget will become transparent apart from the logo. |
65 | if (m_transparent) { |
66 | QSurfaceFormat fmt = format(); |
67 | fmt.setAlphaBufferSize(8); |
68 | setFormat(fmt); |
69 | } |
70 | } |
71 | |
72 | GLWidget::~GLWidget() |
73 | { |
74 | cleanup(); |
75 | } |
76 | |
77 | QSize GLWidget::minimumSizeHint() const |
78 | { |
79 | return QSize(50, 50); |
80 | } |
81 | |
82 | QSize GLWidget::sizeHint() const |
83 | { |
84 | return QSize(400, 400); |
85 | } |
86 | |
87 | static void qNormalizeAngle(int &angle) |
88 | { |
89 | while (angle < 0) |
90 | angle += 360 * 16; |
91 | while (angle > 360 * 16) |
92 | angle -= 360 * 16; |
93 | } |
94 | |
95 | void GLWidget::setXRotation(int angle) |
96 | { |
97 | qNormalizeAngle(angle); |
98 | if (angle != m_xRot) { |
99 | m_xRot = angle; |
100 | emit xRotationChanged(angle); |
101 | update(); |
102 | } |
103 | } |
104 | |
105 | void GLWidget::setYRotation(int angle) |
106 | { |
107 | qNormalizeAngle(angle); |
108 | if (angle != m_yRot) { |
109 | m_yRot = angle; |
110 | emit yRotationChanged(angle); |
111 | update(); |
112 | } |
113 | } |
114 | |
115 | void GLWidget::setZRotation(int angle) |
116 | { |
117 | qNormalizeAngle(angle); |
118 | if (angle != m_zRot) { |
119 | m_zRot = angle; |
120 | emit zRotationChanged(angle); |
121 | update(); |
122 | } |
123 | } |
124 | |
125 | void GLWidget::cleanup() |
126 | { |
127 | if (m_program == nullptr) |
128 | return; |
129 | makeCurrent(); |
130 | m_logoVbo.destroy(); |
131 | delete m_program; |
132 | m_program = nullptr; |
133 | doneCurrent(); |
134 | } |
135 | |
136 | static const char *vertexShaderSourceCore = |
137 | "#version 150\n" |
138 | "in vec4 vertex;\n" |
139 | "in vec3 normal;\n" |
140 | "out vec3 vert;\n" |
141 | "out vec3 vertNormal;\n" |
142 | "uniform mat4 projMatrix;\n" |
143 | "uniform mat4 mvMatrix;\n" |
144 | "uniform mat3 normalMatrix;\n" |
145 | "void main() {\n" |
146 | " vert = vertex.xyz;\n" |
147 | " vertNormal = normalMatrix * normal;\n" |
148 | " gl_Position = projMatrix * mvMatrix * vertex;\n" |
149 | "}\n" ; |
150 | |
151 | static const char *fragmentShaderSourceCore = |
152 | "#version 150\n" |
153 | "in highp vec3 vert;\n" |
154 | "in highp vec3 vertNormal;\n" |
155 | "out highp vec4 fragColor;\n" |
156 | "uniform highp vec3 lightPos;\n" |
157 | "void main() {\n" |
158 | " highp vec3 L = normalize(lightPos - vert);\n" |
159 | " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n" |
160 | " highp vec3 color = vec3(0.39, 1.0, 0.0);\n" |
161 | " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n" |
162 | " fragColor = vec4(col, 1.0);\n" |
163 | "}\n" ; |
164 | |
165 | static const char *vertexShaderSource = |
166 | "attribute vec4 vertex;\n" |
167 | "attribute vec3 normal;\n" |
168 | "varying vec3 vert;\n" |
169 | "varying vec3 vertNormal;\n" |
170 | "uniform mat4 projMatrix;\n" |
171 | "uniform mat4 mvMatrix;\n" |
172 | "uniform mat3 normalMatrix;\n" |
173 | "void main() {\n" |
174 | " vert = vertex.xyz;\n" |
175 | " vertNormal = normalMatrix * normal;\n" |
176 | " gl_Position = projMatrix * mvMatrix * vertex;\n" |
177 | "}\n" ; |
178 | |
179 | static const char *fragmentShaderSource = |
180 | "varying highp vec3 vert;\n" |
181 | "varying highp vec3 vertNormal;\n" |
182 | "uniform highp vec3 lightPos;\n" |
183 | "void main() {\n" |
184 | " highp vec3 L = normalize(lightPos - vert);\n" |
185 | " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n" |
186 | " highp vec3 color = vec3(0.39, 1.0, 0.0);\n" |
187 | " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n" |
188 | " gl_FragColor = vec4(col, 1.0);\n" |
189 | "}\n" ; |
190 | |
191 | void GLWidget::initializeGL() |
192 | { |
193 | // In this example the widget's corresponding top-level window can change |
194 | // several times during the widget's lifetime. Whenever this happens, the |
195 | // QOpenGLWidget's associated context is destroyed and a new one is created. |
196 | // Therefore we have to be prepared to clean up the resources on the |
197 | // aboutToBeDestroyed() signal, instead of the destructor. The emission of |
198 | // the signal will be followed by an invocation of initializeGL() where we |
199 | // can recreate all resources. |
200 | connect(sender: context(), signal: &QOpenGLContext::aboutToBeDestroyed, receiver: this, slot: &GLWidget::cleanup); |
201 | |
202 | initializeOpenGLFunctions(); |
203 | glClearColor(red: 0, green: 0, blue: 0, alpha: m_transparent ? 0 : 1); |
204 | |
205 | m_program = new QOpenGLShaderProgram; |
206 | m_program->addShaderFromSourceCode(type: QOpenGLShader::Vertex, source: m_core ? vertexShaderSourceCore : vertexShaderSource); |
207 | m_program->addShaderFromSourceCode(type: QOpenGLShader::Fragment, source: m_core ? fragmentShaderSourceCore : fragmentShaderSource); |
208 | m_program->bindAttributeLocation(name: "vertex" , location: 0); |
209 | m_program->bindAttributeLocation(name: "normal" , location: 1); |
210 | m_program->link(); |
211 | |
212 | m_program->bind(); |
213 | m_projMatrixLoc = m_program->uniformLocation(name: "projMatrix" ); |
214 | m_mvMatrixLoc = m_program->uniformLocation(name: "mvMatrix" ); |
215 | m_normalMatrixLoc = m_program->uniformLocation(name: "normalMatrix" ); |
216 | m_lightPosLoc = m_program->uniformLocation(name: "lightPos" ); |
217 | |
218 | // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x |
219 | // implementations this is optional and support may not be present |
220 | // at all. Nonetheless the below code works in all cases and makes |
221 | // sure there is a VAO when one is needed. |
222 | m_vao.create(); |
223 | QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); |
224 | |
225 | // Setup our vertex buffer object. |
226 | m_logoVbo.create(); |
227 | m_logoVbo.bind(); |
228 | m_logoVbo.allocate(data: m_logo.constData(), count: m_logo.count() * sizeof(GLfloat)); |
229 | |
230 | // Store the vertex attribute bindings for the program. |
231 | setupVertexAttribs(); |
232 | |
233 | // Our camera never changes in this example. |
234 | m_camera.setToIdentity(); |
235 | m_camera.translate(x: 0, y: 0, z: -1); |
236 | |
237 | // Light position is fixed. |
238 | m_program->setUniformValue(location: m_lightPosLoc, value: QVector3D(0, 0, 70)); |
239 | |
240 | m_program->release(); |
241 | } |
242 | |
243 | void GLWidget::setupVertexAttribs() |
244 | { |
245 | m_logoVbo.bind(); |
246 | QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); |
247 | f->glEnableVertexAttribArray(index: 0); |
248 | f->glEnableVertexAttribArray(index: 1); |
249 | f->glVertexAttribPointer(indx: 0, size: 3, GL_FLOAT, GL_FALSE, stride: 6 * sizeof(GLfloat), |
250 | ptr: nullptr); |
251 | f->glVertexAttribPointer(indx: 1, size: 3, GL_FLOAT, GL_FALSE, stride: 6 * sizeof(GLfloat), |
252 | ptr: reinterpret_cast<void *>(3 * sizeof(GLfloat))); |
253 | m_logoVbo.release(); |
254 | } |
255 | |
256 | void GLWidget::paintGL() |
257 | { |
258 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
259 | glEnable(GL_DEPTH_TEST); |
260 | glEnable(GL_CULL_FACE); |
261 | |
262 | m_world.setToIdentity(); |
263 | m_world.rotate(angle: 180.0f - (m_xRot / 16.0f), x: 1, y: 0, z: 0); |
264 | m_world.rotate(angle: m_yRot / 16.0f, x: 0, y: 1, z: 0); |
265 | m_world.rotate(angle: m_zRot / 16.0f, x: 0, y: 0, z: 1); |
266 | |
267 | QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); |
268 | m_program->bind(); |
269 | m_program->setUniformValue(location: m_projMatrixLoc, value: m_proj); |
270 | m_program->setUniformValue(location: m_mvMatrixLoc, value: m_camera * m_world); |
271 | QMatrix3x3 normalMatrix = m_world.normalMatrix(); |
272 | m_program->setUniformValue(location: m_normalMatrixLoc, value: normalMatrix); |
273 | |
274 | glDrawArrays(GL_TRIANGLES, first: 0, count: m_logo.vertexCount()); |
275 | |
276 | m_program->release(); |
277 | } |
278 | |
279 | void GLWidget::resizeGL(int w, int h) |
280 | { |
281 | m_proj.setToIdentity(); |
282 | m_proj.perspective(verticalAngle: 45.0f, aspectRatio: GLfloat(w) / h, nearPlane: 0.01f, farPlane: 100.0f); |
283 | } |
284 | |
285 | void GLWidget::mousePressEvent(QMouseEvent *event) |
286 | { |
287 | m_lastPos = event->pos(); |
288 | } |
289 | |
290 | void GLWidget::mouseMoveEvent(QMouseEvent *event) |
291 | { |
292 | int dx = event->x() - m_lastPos.x(); |
293 | int dy = event->y() - m_lastPos.y(); |
294 | |
295 | if (event->buttons() & Qt::LeftButton) { |
296 | setXRotation(m_xRot + 8 * dy); |
297 | setYRotation(m_yRot + 8 * dx); |
298 | } else if (event->buttons() & Qt::RightButton) { |
299 | setXRotation(m_xRot + 8 * dy); |
300 | setZRotation(m_zRot + 8 * dx); |
301 | } |
302 | m_lastPos = event->pos(); |
303 | } |
304 | |