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
63class Item {
64public:
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
104private:
105 QSGTransformNode *transformNode;
106 QSGSimpleTextureNode *textureNode;
107 QVariantAnimation posAnimation;
108 QVariantAnimation rotAnimation;
109};
110
111Window::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
133Window::~Window()
134{
135}
136
137void 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
159void Window::exposeEvent(QExposeEvent *)
160{
161 if (isExposed())
162 update();
163 else
164 invalidate();
165}
166
167void Window::mousePressEvent(QMouseEvent *)
168{
169 addItems();
170}
171
172void Window::keyPressEvent(QKeyEvent *)
173{
174 addItems();
175}
176
177void 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
192void Window::update()
193{
194 if (!m_updateTimer.isActive())
195 m_updateTimer.start(msec: 0, obj: this);
196}
197
198void 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
210void 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
220void 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 = 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
245void 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

source code of qtdeclarative/examples/quick/scenegraph/sgengine/window.cpp