1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 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 "window.h" |
52 | |
53 | #include <QOpenGLContext> |
54 | #include <QSGAbstractRenderer> |
55 | #include <QSGEngine> |
56 | #include <QSGSimpleTextureNode> |
57 | #include <QSGTransformNode> |
58 | #include <QScreen> |
59 | #include <QVariantAnimation> |
60 | #include <QOpenGLFunctions> |
61 | #include <QRandomGenerator> |
62 | |
63 | class Item { |
64 | public: |
65 | Item(QSGNode *parentNode, QSGTexture *texture, const QPointF &fromPos, const QPointF &toPos) { |
66 | textureNode = new QSGSimpleTextureNode; |
67 | textureNode->setRect(QRect(QPoint(), texture->textureSize())); |
68 | textureNode->setTexture(texture); |
69 | |
70 | transformNode = new QSGTransformNode; |
71 | transformNode->setFlag(QSGNode::OwnedByParent, false); |
72 | transformNode->appendChildNode(node: textureNode); |
73 | parentNode->appendChildNode(node: transformNode); |
74 | |
75 | int duration = QRandomGenerator::global()->generateDouble() * 400 + 800; |
76 | rotAnimation.setStartValue(QRandomGenerator::global()->generateDouble() * 720 - 180); |
77 | rotAnimation.setEndValue(QRandomGenerator::global()->generateDouble() * 720 - 180); |
78 | rotAnimation.setDuration(duration); |
79 | rotAnimation.start(); |
80 | |
81 | posAnimation.setStartValue(fromPos); |
82 | posAnimation.setEndValue(toPos); |
83 | posAnimation.setDuration(duration); |
84 | posAnimation.start(); |
85 | } |
86 | |
87 | ~Item() { |
88 | delete transformNode; |
89 | } |
90 | |
91 | bool isDone() const { return posAnimation.state() != QAbstractAnimation::Running; } |
92 | |
93 | void sync() { |
94 | QPointF currentPos = posAnimation.currentValue().toPointF(); |
95 | QPointF center = textureNode->rect().center(); |
96 | QMatrix4x4 m; |
97 | m.translate(x: currentPos.x(), y: currentPos.y()); |
98 | m.translate(x: center.x(), y: center.y()); |
99 | m.rotate(angle: rotAnimation.currentValue().toFloat(), x: 0, y: 0, z: 1); |
100 | m.translate(x: -center.x(), y: -center.y()); |
101 | transformNode->setMatrix(m); |
102 | } |
103 | |
104 | private: |
105 | QSGTransformNode *transformNode; |
106 | QSGSimpleTextureNode *textureNode; |
107 | QVariantAnimation posAnimation; |
108 | QVariantAnimation rotAnimation; |
109 | }; |
110 | |
111 | Window::Window() |
112 | : m_initialized(false) |
113 | , m_context(new QOpenGLContext) |
114 | , m_sgEngine(new QSGEngine) |
115 | , m_sgRootNode(new QSGRootNode) |
116 | { |
117 | setSurfaceType(QWindow::OpenGLSurface); |
118 | QRect g(0, 0, 640, 480); |
119 | g.moveCenter(p: screen()->geometry().center()); |
120 | setGeometry(g); |
121 | setTitle(QStringLiteral("Click me!" )); |
122 | |
123 | QSurfaceFormat format; |
124 | format.setDepthBufferSize(16); |
125 | setFormat(format); |
126 | m_context->setFormat(format); |
127 | m_context->create(); |
128 | |
129 | m_animationDriver.install(); |
130 | connect(sender: &m_animationDriver, signal: &QAnimationDriver::started, receiver: this, slot: &Window::update); |
131 | } |
132 | |
133 | Window::~Window() |
134 | { |
135 | } |
136 | |
137 | void Window::timerEvent(QTimerEvent *e) |
138 | { |
139 | if (e->timerId() == m_updateTimer.timerId()) { |
140 | m_updateTimer.stop(); |
141 | |
142 | if (!m_context->makeCurrent(surface: this)) |
143 | return; |
144 | |
145 | if (!m_initialized) |
146 | initialize(); |
147 | |
148 | sync(); |
149 | render(); |
150 | |
151 | if (m_animationDriver.isRunning()) { |
152 | m_animationDriver.advance(); |
153 | update(); |
154 | } |
155 | } else |
156 | QWindow::timerEvent(event: e); |
157 | } |
158 | |
159 | void Window::exposeEvent(QExposeEvent *) |
160 | { |
161 | if (isExposed()) |
162 | update(); |
163 | else |
164 | invalidate(); |
165 | } |
166 | |
167 | void Window::mousePressEvent(QMouseEvent *) |
168 | { |
169 | addItems(); |
170 | } |
171 | |
172 | void Window::keyPressEvent(QKeyEvent *) |
173 | { |
174 | addItems(); |
175 | } |
176 | |
177 | void Window::addItems() |
178 | { |
179 | if (!m_initialized) |
180 | return; |
181 | |
182 | QSGTexture *textures[] = { m_smileTexture.data(), m_qtTexture.data() }; |
183 | for (int i = 0; i < 50; ++i) { |
184 | QSGTexture *tex = textures[i%2]; |
185 | QPointF fromPos(-tex->textureSize().width(), QRandomGenerator::global()->generateDouble() * (height() - tex->textureSize().height())); |
186 | QPointF toPos(width(), QRandomGenerator::global()->generateDouble() * height() * 1.5 - height() * 0.25); |
187 | m_items.append(t: QSharedPointer<Item>::create(arguments: m_sgRootNode.data(), arguments&: tex, arguments&: fromPos, arguments&: toPos)); |
188 | } |
189 | update(); |
190 | } |
191 | |
192 | void Window::update() |
193 | { |
194 | if (!m_updateTimer.isActive()) |
195 | m_updateTimer.start(msec: 0, obj: this); |
196 | } |
197 | |
198 | void Window::sync() |
199 | { |
200 | QList<QSharedPointer<Item> > validItems; |
201 | for (QSharedPointer<Item> item : qAsConst(t&: m_items)) { |
202 | if (!item->isDone()) { |
203 | validItems.append(t: item); |
204 | item->sync(); |
205 | } |
206 | } |
207 | m_items.swap(other&: validItems); |
208 | } |
209 | |
210 | void Window::render() |
211 | { |
212 | m_sgRenderer->setDeviceRect(size()); |
213 | m_sgRenderer->setViewportRect(size()); |
214 | m_sgRenderer->setProjectionMatrixToRect(QRectF(QPointF(), size())); |
215 | m_sgRenderer->renderScene(); |
216 | |
217 | m_context->swapBuffers(surface: this); |
218 | } |
219 | |
220 | void Window::initialize() |
221 | { |
222 | m_sgEngine->initialize(context: m_context.data()); |
223 | m_sgRenderer.reset(other: m_sgEngine->createRenderer()); |
224 | m_sgRenderer->setRootNode(m_sgRootNode.data()); |
225 | m_sgRenderer->setClearColor(QColor(32, 32, 32)); |
226 | |
227 | // With QSGEngine::createTextureFromId |
228 | QOpenGLFunctions glFuncs(m_context.data()); |
229 | GLuint glTexture; |
230 | glFuncs.glGenTextures(n: 1, textures: &glTexture); |
231 | glFuncs.glBindTexture(GL_TEXTURE_2D, texture: glTexture); |
232 | QImage smile = QImage(":/scenegraph/sgengine/face-smile.png" ).scaled(w: 48, h: 48, aspectMode: Qt::KeepAspectRatio, mode: Qt::SmoothTransformation); |
233 | smile = smile.convertToFormat(f: QImage::Format_RGBA8888_Premultiplied); |
234 | Q_ASSERT(!smile.isNull()); |
235 | glFuncs.glTexImage2D(GL_TEXTURE_2D, level: 0, GL_RGBA, width: smile.width(), height: smile.height(), border: 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels: smile.constBits()); |
236 | m_smileTexture.reset(other: m_sgEngine->createTextureFromId(id: glTexture, size: smile.size(), options: QFlag(QSGEngine::TextureOwnsGLTexture | QSGEngine::TextureHasAlphaChannel))); |
237 | |
238 | // With QSGEngine::createTextureFromImage |
239 | QImage qtLogo = QImage(":/shared/images/qt-logo.png" ).scaled(w: 48, h: 48, aspectMode: Qt::KeepAspectRatio, mode: Qt::SmoothTransformation); |
240 | Q_ASSERT(!qtLogo.isNull()); |
241 | m_qtTexture.reset(other: m_sgEngine->createTextureFromImage(image: qtLogo)); |
242 | m_initialized = true; |
243 | } |
244 | |
245 | void Window::invalidate() |
246 | { |
247 | m_updateTimer.stop(); |
248 | m_items.clear(); |
249 | m_smileTexture.reset(); |
250 | m_qtTexture.reset(); |
251 | m_sgRenderer.reset(); |
252 | m_sgEngine->invalidate(); |
253 | m_initialized = false; |
254 | } |
255 | |