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 demonstration applications 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 "squircle.h" |
52 | |
53 | #include <QtQuick/qquickwindow.h> |
54 | #include <QtGui/QOpenGLShaderProgram> |
55 | #include <QtGui/QOpenGLContext> |
56 | #include <QtCore/QRunnable> |
57 | |
58 | //! [7] |
59 | Squircle::Squircle() |
60 | : m_t(0) |
61 | , m_renderer(nullptr) |
62 | { |
63 | connect(sender: this, signal: &QQuickItem::windowChanged, receiver: this, slot: &Squircle::handleWindowChanged); |
64 | } |
65 | //! [7] |
66 | |
67 | //! [8] |
68 | void Squircle::setT(qreal t) |
69 | { |
70 | if (t == m_t) |
71 | return; |
72 | m_t = t; |
73 | emit tChanged(); |
74 | if (window()) |
75 | window()->update(); |
76 | } |
77 | //! [8] |
78 | |
79 | //! [1] |
80 | void Squircle::handleWindowChanged(QQuickWindow *win) |
81 | { |
82 | if (win) { |
83 | connect(sender: win, signal: &QQuickWindow::beforeSynchronizing, receiver: this, slot: &Squircle::sync, type: Qt::DirectConnection); |
84 | connect(sender: win, signal: &QQuickWindow::sceneGraphInvalidated, receiver: this, slot: &Squircle::cleanup, type: Qt::DirectConnection); |
85 | //! [1] |
86 | //! [3] |
87 | // Ensure we start with cleared to black. The squircle's blend mode relies on this. |
88 | win->setColor(Qt::black); |
89 | } |
90 | } |
91 | //! [3] |
92 | |
93 | //! [6] |
94 | void Squircle::cleanup() |
95 | { |
96 | delete m_renderer; |
97 | m_renderer = nullptr; |
98 | } |
99 | |
100 | class CleanupJob : public QRunnable |
101 | { |
102 | public: |
103 | CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { } |
104 | void run() override { delete m_renderer; } |
105 | private: |
106 | SquircleRenderer *m_renderer; |
107 | }; |
108 | |
109 | void Squircle::releaseResources() |
110 | { |
111 | window()->scheduleRenderJob(job: new CleanupJob(m_renderer), schedule: QQuickWindow::BeforeSynchronizingStage); |
112 | m_renderer = nullptr; |
113 | } |
114 | |
115 | SquircleRenderer::~SquircleRenderer() |
116 | { |
117 | delete m_program; |
118 | } |
119 | //! [6] |
120 | |
121 | //! [9] |
122 | void Squircle::sync() |
123 | { |
124 | if (!m_renderer) { |
125 | m_renderer = new SquircleRenderer(); |
126 | connect(sender: window(), signal: &QQuickWindow::beforeRendering, receiver: m_renderer, slot: &SquircleRenderer::init, type: Qt::DirectConnection); |
127 | connect(sender: window(), signal: &QQuickWindow::beforeRenderPassRecording, receiver: m_renderer, slot: &SquircleRenderer::paint, type: Qt::DirectConnection); |
128 | } |
129 | m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); |
130 | m_renderer->setT(m_t); |
131 | m_renderer->setWindow(window()); |
132 | } |
133 | //! [9] |
134 | |
135 | //! [4] |
136 | void SquircleRenderer::init() |
137 | { |
138 | if (!m_program) { |
139 | QSGRendererInterface *rif = m_window->rendererInterface(); |
140 | Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::OpenGL || rif->graphicsApi() == QSGRendererInterface::OpenGLRhi); |
141 | |
142 | initializeOpenGLFunctions(); |
143 | |
144 | m_program = new QOpenGLShaderProgram(); |
145 | m_program->addCacheableShaderFromSourceCode(type: QOpenGLShader::Vertex, |
146 | source: "attribute highp vec4 vertices;" |
147 | "varying highp vec2 coords;" |
148 | "void main() {" |
149 | " gl_Position = vertices;" |
150 | " coords = vertices.xy;" |
151 | "}" ); |
152 | m_program->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, |
153 | source: "uniform lowp float t;" |
154 | "varying highp vec2 coords;" |
155 | "void main() {" |
156 | " lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));" |
157 | " i = smoothstep(t - 0.8, t + 0.8, i);" |
158 | " i = floor(i * 20.) / 20.;" |
159 | " gl_FragColor = vec4(coords * .5 + .5, i, i);" |
160 | "}" ); |
161 | |
162 | m_program->bindAttributeLocation(name: "vertices" , location: 0); |
163 | m_program->link(); |
164 | |
165 | } |
166 | } |
167 | |
168 | //! [4] //! [5] |
169 | void SquircleRenderer::paint() |
170 | { |
171 | // Play nice with the RHI. Not strictly needed when the scenegraph uses |
172 | // OpenGL directly. |
173 | m_window->beginExternalCommands(); |
174 | |
175 | m_program->bind(); |
176 | |
177 | m_program->enableAttributeArray(location: 0); |
178 | |
179 | float values[] = { |
180 | -1, -1, |
181 | 1, -1, |
182 | -1, 1, |
183 | 1, 1 |
184 | }; |
185 | |
186 | // This example relies on (deprecated) client-side pointers for the vertex |
187 | // input. Therefore, we have to make sure no vertex buffer is bound. |
188 | glBindBuffer(GL_ARRAY_BUFFER, buffer: 0); |
189 | |
190 | m_program->setAttributeArray(location: 0, GL_FLOAT, values, tupleSize: 2); |
191 | m_program->setUniformValue(name: "t" , value: (float) m_t); |
192 | |
193 | glViewport(x: 0, y: 0, width: m_viewportSize.width(), height: m_viewportSize.height()); |
194 | |
195 | glDisable(GL_DEPTH_TEST); |
196 | |
197 | glEnable(GL_BLEND); |
198 | glBlendFunc(GL_SRC_ALPHA, GL_ONE); |
199 | |
200 | glDrawArrays(GL_TRIANGLE_STRIP, first: 0, count: 4); |
201 | |
202 | m_program->disableAttributeArray(location: 0); |
203 | m_program->release(); |
204 | |
205 | // Not strictly needed for this example, but generally useful for when |
206 | // mixing with raw OpenGL. |
207 | m_window->resetOpenGLState(); |
208 | |
209 | m_window->endExternalCommands(); |
210 | } |
211 | //! [5] |
212 | |