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