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 <QOpenGLWindow>
52#include <QScreen>
53#include <QPainter>
54#include <QPainterPath>
55#include <QGuiApplication>
56#include <QMatrix4x4>
57#include <QStaticText>
58#include <QKeyEvent>
59
60#include "background_renderer.h"
61
62static QPainterPath painterPathForTriangle()
63{
64 static const QPointF bottomLeft(-1.0, -1.0);
65 static const QPointF top(0.0, 1.0);
66 static const QPointF bottomRight(1.0, -1.0);
67
68 QPainterPath path(bottomLeft);
69 path.lineTo(top);
70 path.lineTo(bottomRight);
71 path.closeSubpath();
72 return path;
73}
74
75class OpenGLWindow : public QOpenGLWindow
76{
77 Q_OBJECT
78
79public:
80 OpenGLWindow();
81
82protected:
83 void paintGL() override;
84 void resizeGL(int w, int h) override;
85 void keyPressEvent(QKeyEvent *e) override;
86
87private:
88 void setAnimating(bool enabled);
89
90 QMatrix4x4 m_window_normalised_matrix;
91 QMatrix4x4 m_window_painter_matrix;
92 QMatrix4x4 m_projection;
93 QMatrix4x4 m_view;
94 QMatrix4x4 m_model_triangle;
95 QMatrix4x4 m_model_text;
96
97 FragmentToy m_fragment_toy;
98 QStaticText m_text_layout;
99 bool m_animate;
100};
101
102// Use NoPartialUpdate. This means that all the rendering goes directly to
103// the window surface, no additional framebuffer object stands in the
104// middle. This is fine since we will clear the entire framebuffer on each
105// paint. Under the hood this means that the behavior is equivalent to the
106// manual makeCurrent - perform OpenGL calls - swapBuffers loop that is
107// typical in pure QWindow-based applications.
108OpenGLWindow::OpenGLWindow()
109 : QOpenGLWindow(QOpenGLWindow::NoPartialUpdate)
110 , m_fragment_toy("./background.frag")
111 , m_text_layout("The triangle and this text is rendered with QPainter")
112 , m_animate(true)
113{
114 setGeometry(300, 300, 500, 500);
115
116 m_view.lookAt(QVector3D(3,1,1),
117 QVector3D(0,0,0),
118 QVector3D(0,1,0));
119
120 setAnimating(m_animate);
121}
122
123void OpenGLWindow::paintGL()
124{
125 m_fragment_toy.draw(size());
126
127 QPainter p(this);
128 p.setWorldTransform(m_window_normalised_matrix.toTransform());
129
130 QMatrix4x4 mvp = m_projection * m_view * m_model_triangle;
131 p.setTransform(mvp.toTransform(), true);
132
133 p.fillPath(painterPathForTriangle(), QBrush(QGradient(QGradient::NightFade)));
134
135 QTransform text_transform = (m_window_painter_matrix * m_view * m_model_text).toTransform();
136 p.setTransform(text_transform, false);
137 p.setPen(QPen(Qt::black));
138 m_text_layout.prepare(text_transform);
139 qreal x = - (m_text_layout.size().width() / 2);
140 qreal y = 0;
141 p.drawStaticText(x, y, m_text_layout);
142
143 m_model_triangle.rotate(-1, 0, 1, 0);
144 m_model_text.rotate(1, 0, 1, 0);
145}
146
147void OpenGLWindow::resizeGL(int w, int h)
148{
149 m_window_normalised_matrix.setToIdentity();
150 m_window_normalised_matrix.translate(w / 2.0, h / 2.0);
151 m_window_normalised_matrix.scale(w / 2.0, -h / 2.0);
152
153 m_window_painter_matrix.setToIdentity();
154 m_window_painter_matrix.translate(w / 2.0, h / 2.0);
155
156 m_text_layout.setTextWidth(std::max(w * 0.2, 80.0));
157
158 m_projection.setToIdentity();
159 m_projection.perspective(45.f, qreal(w) / qreal(h), 0.1f, 100.f);
160}
161
162void OpenGLWindow::keyPressEvent(QKeyEvent *e)
163{
164 if (e->key() == Qt::Key_P) { // pause
165 m_animate = !m_animate;
166 setAnimating(m_animate);
167 }
168}
169
170void OpenGLWindow::setAnimating(bool enabled)
171{
172 if (enabled) {
173 // Animate continuously, throttled by the blocking swapBuffers() call the
174 // QOpenGLWindow internally executes after each paint. Once that is done
175 // (frameSwapped signal is emitted), we schedule a new update. This
176 // obviously assumes that the swap interval (see
177 // QSurfaceFormat::setSwapInterval()) is non-zero.
178 connect(this, &QOpenGLWindow::frameSwapped,
179 this, QOverload<>::of(&QPaintDeviceWindow::update));
180 update();
181 } else {
182 disconnect(this, &QOpenGLWindow::frameSwapped,
183 this, QOverload<>::of(&QPaintDeviceWindow::update));
184 }
185}
186
187int main(int argc, char **argv)
188{
189 QGuiApplication app(argc, argv);
190
191 OpenGLWindow window;
192 QSurfaceFormat fmt;
193 fmt.setDepthBufferSize(24);
194 fmt.setStencilBufferSize(8);
195 window.setFormat(fmt);
196 window.show();
197
198 return app.exec();
199}
200
201#include "main.moc"
202