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 "renderwindow.h"
52#include <QTimer>
53#include <QMatrix4x4>
54#include <QOpenGLContext>
55#include <QtOpenGL/QOpenGLShaderProgram>
56#include <QOpenGLFunctions>
57
58RenderWindow::RenderWindow(const QSurfaceFormat &format)
59 : m_context(nullptr),
60 m_initialized(false),
61 m_forceGLSL110(false),
62 m_angle(0.0f)
63{
64 setSurfaceType(QWindow::OpenGLSurface);
65 setFormat(format);
66 m_context = new QOpenGLContext(this);
67 m_context->setFormat(requestedFormat());
68 if (!m_context->create()) {
69 delete m_context;
70 m_context = nullptr;
71 }
72}
73
74void RenderWindow::exposeEvent(QExposeEvent *)
75{
76 if (isExposed())
77 render();
78}
79
80// ES needs the precision qualifiers.
81// On desktop GL QOpenGLShaderProgram inserts dummy defines for highp/mediump/lowp.
82static const char *vertexShaderSource110 =
83 "attribute highp vec4 posAttr;\n"
84 "attribute lowp vec4 colAttr;\n"
85 "varying lowp vec4 col;\n"
86 "uniform highp mat4 matrix;\n"
87 "void main() {\n"
88 " col = colAttr;\n"
89 " gl_Position = matrix * posAttr;\n"
90 "}\n";
91
92static const char *fragmentShaderSource110 =
93 "varying lowp vec4 col;\n"
94 "void main() {\n"
95 " gl_FragColor = col;\n"
96 "}\n";
97
98static const char *vertexShaderSource =
99 "#version 150\n"
100 "in vec4 posAttr;\n"
101 "in vec4 colAttr;\n"
102 "out vec4 col;\n"
103 "uniform mat4 matrix;\n"
104 "void main() {\n"
105 " col = colAttr;\n"
106 " gl_Position = matrix * posAttr;\n"
107 "}\n";
108
109static const char *fragmentShaderSource =
110 "#version 150\n"
111 "in vec4 col;\n"
112 "out vec4 fragColor;\n"
113 "void main() {\n"
114 " fragColor = col;\n"
115 "}\n";
116
117static GLfloat vertices[] = {
118 0.0f, 0.707f,
119 -0.5f, -0.5f,
120 0.5f, -0.5f
121};
122
123static GLfloat colors[] = {
124 1.0f, 0.0f, 0.0f,
125 0.0f, 1.0f, 0.0f,
126 0.0f, 0.0f, 1.0f
127};
128
129void RenderWindow::init()
130{
131 m_program = new QOpenGLShaderProgram(this);
132
133 QSurfaceFormat format = m_context->format();
134 bool useNewStyleShader = format.profile() == QSurfaceFormat::CoreProfile;
135 // Try to handle 3.0 & 3.1 that do not have the core/compatibility profile concept 3.2+ has.
136 // This may still fail since version 150 (3.2) is specified in the sources but it's worth a try.
137 if (format.renderableType() == QSurfaceFormat::OpenGL && format.majorVersion() == 3 && format.minorVersion() <= 1)
138 useNewStyleShader = !format.testOption(QSurfaceFormat::DeprecatedFunctions);
139 if (m_forceGLSL110)
140 useNewStyleShader = false;
141
142 const char *vsrc = useNewStyleShader ? vertexShaderSource : vertexShaderSource110;
143 const char *fsrc = useNewStyleShader ? fragmentShaderSource : fragmentShaderSource110;
144 qDebug("Using version %s shader", useNewStyleShader ? "150" : "110");
145
146 if (!m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc)) {
147 emit error(m_program->log());
148 return;
149 }
150 if (!m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc)) {
151 emit error(m_program->log());
152 return;
153 }
154 if (!m_program->link()) {
155 emit error(m_program->log());
156 return;
157 }
158
159 m_posAttr = m_program->attributeLocation("posAttr");
160 m_colAttr = m_program->attributeLocation("colAttr");
161 m_matrixUniform = m_program->uniformLocation("matrix");
162
163 m_vbo.create();
164 m_vbo.bind();
165 m_vbo.allocate(vertices, sizeof(vertices) + sizeof(colors));
166 m_vbo.write(sizeof(vertices), colors, sizeof(colors));
167 m_vbo.release();
168
169 QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
170 if (m_vao.isCreated()) // have VAO support, use it
171 setupVertexAttribs();
172}
173
174void RenderWindow::setupVertexAttribs()
175{
176 m_vbo.bind();
177 m_program->setAttributeBuffer(m_posAttr, GL_FLOAT, 0, 2);
178 m_program->setAttributeBuffer(m_colAttr, GL_FLOAT, sizeof(vertices), 3);
179 m_program->enableAttributeArray(m_posAttr);
180 m_program->enableAttributeArray(m_colAttr);
181 m_vbo.release();
182}
183
184bool RenderWindow::event(QEvent *ev)
185{
186 if (ev->type() == QEvent::UpdateRequest)
187 render();
188 return QWindow::event(ev);
189}
190
191void RenderWindow::render()
192{
193 if (!m_context->makeCurrent(this)) {
194 emit error(tr("makeCurrent() failed"));
195 return;
196 }
197
198 QOpenGLFunctions *f = m_context->functions();
199 if (!m_initialized) {
200 m_initialized = true;
201 f->glEnable(GL_DEPTH_TEST);
202 f->glClearColor(0, 0, 0, 1);
203 init();
204 emit ready();
205 }
206
207 if (!m_vbo.isCreated()) // init() failed, don't bother with trying to render
208 return;
209
210 const qreal retinaScale = devicePixelRatio();
211 f->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
212 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
213
214 m_program->bind();
215 QMatrix4x4 matrix;
216 matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
217 matrix.translate(0.0f, 0.0f, -2.0f);
218 matrix.rotate(m_angle, 0.0f, 1.0f, 0.0f);
219 m_program->setUniformValue(m_matrixUniform, matrix);
220
221 if (m_vao.isCreated())
222 m_vao.bind();
223 else // no VAO support, set the vertex attribute arrays now
224 setupVertexAttribs();
225
226 f->glDrawArrays(GL_TRIANGLES, 0, 3);
227
228 m_vao.release();
229 m_program->release();
230
231 // swapInterval is 1 by default which means that swapBuffers() will (hopefully) block
232 // and wait for vsync.
233 m_context->swapBuffers(this);
234
235 m_angle += 1.0f;
236
237 requestUpdate();
238}
239