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 <QtGui/QImage>
52#include <qmath.h>
53#include "glwidget.h"
54
55#ifndef GL_MULTISAMPLE
56#define GL_MULTISAMPLE 0x809D
57#endif
58
59GLWidget::GLWidget(QWidget *parent)
60 : QGLWidget(QGLFormat(QGL::SampleBuffers|QGL::AlphaChannel), parent)
61{
62 setWindowTitle(tr(s: "OpenGL framebuffer objects"));
63 makeCurrent();
64
65 if (QGLFramebufferObject::hasOpenGLFramebufferBlit()) {
66 QGLFramebufferObjectFormat format;
67 format.setSamples(4);
68 format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
69
70 render_fbo = new QGLFramebufferObject(512, 512, format);
71 texture_fbo = new QGLFramebufferObject(512, 512);
72 } else {
73 render_fbo = new QGLFramebufferObject(1024, 1024);
74 texture_fbo = render_fbo;
75 }
76
77 rot_x = rot_y = rot_z = 0.0f;
78 scale = 0.1f;
79 anim = new QTimeLine(750, this);
80 anim->setUpdateInterval(20);
81 connect(asender: anim, SIGNAL(valueChanged(qreal)), SLOT(animate(qreal)));
82 connect(asender: anim, SIGNAL(finished()), SLOT(animFinished()));
83
84 svg_renderer = new QSvgRenderer(QLatin1String(":/res/bubbles.svg"), this);
85 connect(sender: svg_renderer, SIGNAL(repaintNeeded()), receiver: this, SLOT(draw()));
86
87 logo = QImage(":/res/designer.png");
88 logo = logo.convertToFormat(f: QImage::Format_ARGB32);
89
90 tile_list = glGenLists(range: 1);
91 glNewList(list: tile_list, GL_COMPILE);
92 glBegin(GL_QUADS);
93 {
94 glTexCoord2f(s: 0.0f, t: 0.0f); glVertex3f(x: -1.0f, y: -1.0f, z: 1.0f);
95 glTexCoord2f(s: 1.0f, t: 0.0f); glVertex3f( x: 1.0f, y: -1.0f, z: 1.0f);
96 glTexCoord2f(s: 1.0f, t: 1.0f); glVertex3f( x: 1.0f, y: 1.0f, z: 1.0f);
97 glTexCoord2f(s: 0.0f, t: 1.0f); glVertex3f(x: -1.0f, y: 1.0f, z: 1.0f);
98
99 glTexCoord2f(s: 1.0f, t: 0.0f); glVertex3f(x: -1.0f, y: -1.0f, z: -1.0f);
100 glTexCoord2f(s: 1.0f, t: 1.0f); glVertex3f(x: -1.0f, y: 1.0f, z: -1.0f);
101 glTexCoord2f(s: 0.0f, t: 1.0f); glVertex3f( x: 1.0f, y: 1.0f, z: -1.0f);
102 glTexCoord2f(s: 0.0f, t: 0.0f); glVertex3f( x: 1.0f, y: -1.0f, z: -1.0f);
103
104 glTexCoord2f(s: 0.0f, t: 1.0f); glVertex3f(x: -1.0f, y: 1.0f, z: -1.0f);
105 glTexCoord2f(s: 0.0f, t: 0.0f); glVertex3f(x: -1.0f, y: 1.0f, z: 1.0f);
106 glTexCoord2f(s: 1.0f, t: 0.0f); glVertex3f( x: 1.0f, y: 1.0f, z: 1.0f);
107 glTexCoord2f(s: 1.0f, t: 1.0f); glVertex3f( x: 1.0f, y: 1.0f, z: -1.0f);
108
109 glTexCoord2f(s: 1.0f, t: 1.0f); glVertex3f(x: -1.0f, y: -1.0f, z: -1.0f);
110 glTexCoord2f(s: 0.0f, t: 1.0f); glVertex3f( x: 1.0f, y: -1.0f, z: -1.0f);
111 glTexCoord2f(s: 0.0f, t: 0.0f); glVertex3f( x: 1.0f, y: -1.0f, z: 1.0f);
112 glTexCoord2f(s: 1.0f, t: 0.0f); glVertex3f(x: -1.0f, y: -1.0f, z: 1.0f);
113
114 glTexCoord2f(s: 1.0f, t: 0.0f); glVertex3f( x: 1.0f, y: -1.0f, z: -1.0f);
115 glTexCoord2f(s: 1.0f, t: 1.0f); glVertex3f( x: 1.0f, y: 1.0f, z: -1.0f);
116 glTexCoord2f(s: 0.0f, t: 1.0f); glVertex3f( x: 1.0f, y: 1.0f, z: 1.0f);
117 glTexCoord2f(s: 0.0f, t: 0.0f); glVertex3f( x: 1.0f, y: -1.0f, z: 1.0f);
118
119 glTexCoord2f(s: 0.0f, t: 0.0f); glVertex3f(x: -1.0f, y: -1.0f, z: -1.0f);
120 glTexCoord2f(s: 1.0f, t: 0.0f); glVertex3f(x: -1.0f, y: -1.0f, z: 1.0f);
121 glTexCoord2f(s: 1.0f, t: 1.0f); glVertex3f(x: -1.0f, y: 1.0f, z: 1.0f);
122 glTexCoord2f(s: 0.0f, t: 1.0f); glVertex3f(x: -1.0f, y: 1.0f, z: -1.0f);
123 }
124 glEnd();
125 glEndList();
126
127 wave = new GLfloat[logo.width()*logo.height()];
128 memset(s: wave, c: 0, n: logo.width()*logo.height());
129 startTimer(interval: 30); // wave timer
130}
131
132GLWidget::~GLWidget()
133{
134 delete[] wave;
135 glDeleteLists(list: tile_list, range: 1);
136 delete texture_fbo;
137 if (render_fbo != texture_fbo)
138 delete render_fbo;
139}
140
141void GLWidget::paintEvent(QPaintEvent *)
142{
143 draw();
144}
145
146void GLWidget::draw()
147{
148 QPainter p(this); // used for text overlay
149
150 // save the GL state set for QPainter
151 saveGLState();
152
153 // render the 'bubbles.svg' file into our framebuffer object
154 QPainter fbo_painter(render_fbo);
155 svg_renderer->render(p: &fbo_painter);
156 fbo_painter.end();
157
158 if (render_fbo != texture_fbo) {
159 QRect rect(0, 0, render_fbo->width(), render_fbo->height());
160 QGLFramebufferObject::blitFramebuffer(target: texture_fbo, targetRect: rect,
161 source: render_fbo, sourceRect: rect);
162 }
163
164 // draw into the GL widget
165 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
166 glMatrixMode(GL_PROJECTION);
167 glLoadIdentity();
168 glFrustum(left: -1, right: 1, bottom: -1, top: 1, near_val: 10, far_val: 100);
169 glTranslatef(x: 0.0f, y: 0.0f, z: -15.0f);
170 glMatrixMode(GL_MODELVIEW);
171 glLoadIdentity();
172 glViewport(x: 0, y: 0, width: width(), height: height());
173 glEnable(GL_BLEND);
174 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
175
176 glBindTexture(GL_TEXTURE_2D, texture: texture_fbo->texture());
177 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
178 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
179 glEnable(GL_TEXTURE_2D);
180 glEnable(GL_MULTISAMPLE);
181 glEnable(GL_CULL_FACE);
182
183 // draw background
184 glPushMatrix();
185 glScalef(x: 1.7f, y: 1.7f, z: 1.7f);
186 glColor4f(red: 1.0f, green: 1.0f, blue: 1.0f, alpha: 1.0f);
187 glCallList(list: tile_list);
188 glPopMatrix();
189
190 const int w = logo.width();
191 const int h = logo.height();
192
193 glRotatef(angle: rot_x, x: 1.0f, y: 0.0f, z: 0.0f);
194 glRotatef(angle: rot_y, x: 0.0f, y: 1.0f, z: 0.0f);
195 glRotatef(angle: rot_z, x: 0.0f, y: 0.0f, z: 1.0f);
196 glScalef(x: scale/w, y: scale/w, z: scale/w);
197
198 glDepthFunc(GL_LESS);
199 glEnable(GL_DEPTH_TEST);
200 // draw the Qt icon
201 glTranslatef(x: -w+1, y: -h+1, z: 0.0f);
202 for (int y=h-1; y>=0; --y) {
203 uint *p = (uint*) logo.scanLine(y);
204 uint *end = p + w;
205 int x = 0;
206 while (p < end) {
207 glColor4ub(red: qRed(rgb: *p), green: qGreen(rgb: *p), blue: qBlue(rgb: *p), alpha: uchar(qAlpha(rgb: *p)*.9));
208 glTranslatef(x: 0.0f, y: 0.0f, z: wave[y*w+x]);
209 if (qAlpha(rgb: *p) > 128)
210 glCallList(list: tile_list);
211 glTranslatef(x: 0.0f, y: 0.0f, z: -wave[y*w+x]);
212 glTranslatef(x: 2.0f, y: 0.0f, z: 0.0f);
213 ++x;
214 ++p;
215 }
216 glTranslatef(x: -w*2.0f, y: 2.0f, z: 0.0f);
217 }
218
219 // restore the GL state that QPainter expects
220 restoreGLState();
221
222 // draw the overlayed text using QPainter
223 p.setPen(QColor(197, 197, 197, 157));
224 p.setBrush(QColor(197, 197, 197, 127));
225 p.drawRect(r: QRect(0, 0, width(), 50));
226 p.setPen(Qt::black);
227 p.setBrush(Qt::NoBrush);
228 const QString str1(tr(s: "A simple OpenGL framebuffer object example."));
229 const QString str2(tr(s: "Use the mouse wheel to zoom, press buttons and move mouse to rotate, double-click to flip."));
230 QFontMetrics fm(p.font());
231 p.drawText(x: width()/2 - fm.horizontalAdvance(str1)/2, y: 20, s: str1);
232 p.drawText(x: width()/2 - fm.horizontalAdvance(str2)/2, y: 20 + fm.lineSpacing(), s: str2);
233}
234
235void GLWidget::mousePressEvent(QMouseEvent *e)
236{
237 anchor = e->pos();
238}
239
240void GLWidget::mouseMoveEvent(QMouseEvent *e)
241{
242 QPoint diff = e->pos() - anchor;
243 if (e->buttons() & Qt::LeftButton) {
244 rot_x += diff.y()/5.0f;
245 rot_y += diff.x()/5.0f;
246 } else if (e->buttons() & Qt::RightButton) {
247 rot_z += diff.x()/5.0f;
248 }
249
250 anchor = e->pos();
251 draw();
252}
253
254void GLWidget::wheelEvent(QWheelEvent *e)
255{
256 e->angleDelta().y() > 0 ? scale += scale*0.1f : scale -= scale*0.1f;
257 draw();
258}
259
260void GLWidget::mouseDoubleClickEvent(QMouseEvent *)
261{
262 anim->start();
263}
264
265void GLWidget::animate(qreal val)
266{
267 rot_y = val * 180;
268 draw();
269}
270
271void GLWidget::animFinished()
272{
273 if (anim->direction() == QTimeLine::Forward)
274 anim->setDirection(QTimeLine::Backward);
275 else
276 anim->setDirection(QTimeLine::Forward);
277}
278
279void GLWidget::saveGLState()
280{
281 glPushAttrib(GL_ALL_ATTRIB_BITS);
282 glMatrixMode(GL_PROJECTION);
283 glPushMatrix();
284 glMatrixMode(GL_MODELVIEW);
285 glPushMatrix();
286}
287
288void GLWidget::restoreGLState()
289{
290 glMatrixMode(GL_PROJECTION);
291 glPopMatrix();
292 glMatrixMode(GL_MODELVIEW);
293 glPopMatrix();
294 glPopAttrib();
295}
296
297void GLWidget::timerEvent(QTimerEvent *)
298{
299 if (QApplication::mouseButtons() != 0)
300 return;
301
302 static bool scale_in = true;
303
304 if (scale_in && scale > 35.0f)
305 scale_in = false;
306 else if (!scale_in && scale < .5f)
307 scale_in = true;
308
309 scale *= scale_in ? 1.01f : 0.99f;
310 rot_z += 0.3f;
311 rot_x += 0.1f;
312
313 static float wt = 0.0;
314 wt += 0.1f;
315
316 const int width = logo.width();
317 const int dx = width >> 1, dy = dx; // disturbance point
318 const float W = .3f;
319 const float v = -4; // wave speed
320 const int AMP = 5;
321
322 for (int i = 0; i < width; ++i) {
323 for (int j = 0; j < width; ++j) {
324 const float s = hypot(x: j - dx, y: i - dy);
325 const double raw = AMP * sin(x: 2 * M_PI * W * (wt + s / v));
326 if (s != 0)
327 wave[i * width + j] = raw / (0.2 * (s + 2));
328 else
329 wave[i * width + j] = raw;
330 }
331 }
332}
333

source code of qtsvg/examples/svg/opengl/framebufferobject/glwidget.cpp