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 | |
59 | GLWidget::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 | |
132 | GLWidget::~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 | |
141 | void GLWidget::paintEvent(QPaintEvent *) |
142 | { |
143 | draw(); |
144 | } |
145 | |
146 | void 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 | |
235 | void GLWidget::mousePressEvent(QMouseEvent *e) |
236 | { |
237 | anchor = e->pos(); |
238 | } |
239 | |
240 | void 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 | |
254 | void GLWidget::wheelEvent(QWheelEvent *e) |
255 | { |
256 | e->angleDelta().y() > 0 ? scale += scale*0.1f : scale -= scale*0.1f; |
257 | draw(); |
258 | } |
259 | |
260 | void GLWidget::mouseDoubleClickEvent(QMouseEvent *) |
261 | { |
262 | anim->start(); |
263 | } |
264 | |
265 | void GLWidget::animate(qreal val) |
266 | { |
267 | rot_y = val * 180; |
268 | draw(); |
269 | } |
270 | |
271 | void GLWidget::animFinished() |
272 | { |
273 | if (anim->direction() == QTimeLine::Forward) |
274 | anim->setDirection(QTimeLine::Backward); |
275 | else |
276 | anim->setDirection(QTimeLine::Forward); |
277 | } |
278 | |
279 | void GLWidget::saveGLState() |
280 | { |
281 | glPushAttrib(GL_ALL_ATTRIB_BITS); |
282 | glMatrixMode(GL_PROJECTION); |
283 | glPushMatrix(); |
284 | glMatrixMode(GL_MODELVIEW); |
285 | glPushMatrix(); |
286 | } |
287 | |
288 | void GLWidget::restoreGLState() |
289 | { |
290 | glMatrixMode(GL_PROJECTION); |
291 | glPopMatrix(); |
292 | glMatrixMode(GL_MODELVIEW); |
293 | glPopMatrix(); |
294 | glPopAttrib(); |
295 | } |
296 | |
297 | void 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 | |