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