[2] | 1 | /****************************************************************************
|
---|
| 2 | **
|
---|
[846] | 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
[561] | 4 | ** All rights reserved.
|
---|
| 5 | ** Contact: Nokia Corporation (qt-info@nokia.com)
|
---|
[2] | 6 | **
|
---|
| 7 | ** This file is part of the documentation of the Qt Toolkit.
|
---|
| 8 | **
|
---|
[846] | 9 | ** $QT_BEGIN_LICENSE:FDL$
|
---|
[2] | 10 | ** Commercial Usage
|
---|
| 11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
| 12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
[846] | 13 | ** Software or, alternatively, in accordance with the terms contained in a
|
---|
| 14 | ** written agreement between you and Nokia.
|
---|
[2] | 15 | **
|
---|
[846] | 16 | ** GNU Free Documentation License
|
---|
| 17 | ** Alternatively, this file may be used under the terms of the GNU Free
|
---|
| 18 | ** Documentation License version 1.3 as published by the Free Software
|
---|
| 19 | ** Foundation and appearing in the file included in the packaging of this
|
---|
| 20 | ** file.
|
---|
[2] | 21 | **
|
---|
[561] | 22 | ** If you have questions regarding the use of this file, please contact
|
---|
| 23 | ** Nokia at qt-info@nokia.com.
|
---|
[2] | 24 | ** $QT_END_LICENSE$
|
---|
| 25 | **
|
---|
| 26 | ****************************************************************************/
|
---|
| 27 |
|
---|
| 28 | /*!
|
---|
| 29 | \example opengl/overpainting
|
---|
| 30 | \title Overpainting Example
|
---|
| 31 |
|
---|
| 32 | The Overpainting example shows how QPainter can be used
|
---|
| 33 | to overpaint a scene rendered using OpenGL in a QGLWidget.
|
---|
| 34 |
|
---|
| 35 | \image overpainting-example.png
|
---|
| 36 |
|
---|
| 37 | QGLWidget provides a widget with integrated OpenGL graphics support
|
---|
| 38 | that enables 3D graphics to be displayed using normal OpenGL calls,
|
---|
| 39 | yet also behaves like any other standard Qt widget with support for
|
---|
| 40 | signals and slots, properties, and Qt's action system.
|
---|
| 41 |
|
---|
[561] | 42 | Usually, QGLWidget is subclassed to display a pure 3D scene. The
|
---|
[2] | 43 | developer reimplements \l{QGLWidget::initializeGL()}{initializeGL()}
|
---|
| 44 | to initialize any required resources, \l{QGLWidget::resizeGL()}{resizeGL()}
|
---|
| 45 | to set up the projection and viewport, and
|
---|
| 46 | \l{QGLWidget::paintGL()}{paintGL()} to perform the OpenGL calls needed
|
---|
| 47 | to render the scene. However, it is possible to subclass QGLWidget
|
---|
| 48 | differently to allow 2D graphics, drawn using QPainter, to be
|
---|
| 49 | painted over a scene rendered using OpenGL.
|
---|
| 50 |
|
---|
| 51 | In this example, we demonstrate how this is done by reusing the code
|
---|
| 52 | from the \l{Hello GL Example}{Hello GL} example to provide a 3D scene,
|
---|
| 53 | and painting over it with some translucent 2D graphics. Instead of
|
---|
| 54 | examining each class in detail, we only cover the parts of the
|
---|
| 55 | \c GLWidget class that enable overpainting, and provide more detailed
|
---|
| 56 | discussion in the final section of this document.
|
---|
| 57 |
|
---|
| 58 | \section1 GLWidget Class Definition
|
---|
| 59 |
|
---|
| 60 | The \c GLWidget class is a subclass of QGLWidget, based on the one used
|
---|
| 61 | in the \l{Hello GL Example}{Hello GL} example. Rather than describe the
|
---|
| 62 | class as a whole, we show the first few lines of the class and only
|
---|
| 63 | discuss the changes we have made to the rest of it:
|
---|
| 64 |
|
---|
| 65 | \snippet examples/opengl/overpainting/glwidget.h 0
|
---|
| 66 | \dots
|
---|
| 67 | \snippet examples/opengl/overpainting/glwidget.h 1
|
---|
| 68 | \dots
|
---|
| 69 | \snippet examples/opengl/overpainting/glwidget.h 4
|
---|
| 70 |
|
---|
| 71 | As usual, the widget uses \l{QGLWidget::initializeGL()}{initializeGL()}
|
---|
[561] | 72 | to set up geometry for our scene and perform OpenGL initialization tasks.
|
---|
[2] | 73 | The \l{QGLWidget::resizeGL()}{resizeGL()} function is used to ensure that
|
---|
| 74 | the 3D graphics in the scene are transformed correctly to the 2D viewport
|
---|
| 75 | displayed in the widget.
|
---|
| 76 |
|
---|
| 77 | Instead of implementing \l{QGLWidget::paintGL()}{paintGL()} to handle updates
|
---|
| 78 | to the widget, we implement a normal QWidget::paintEvent(). This
|
---|
| 79 | allows us to mix OpenGL calls and QPainter operations in a controlled way.
|
---|
| 80 |
|
---|
| 81 | In this example, we also implement QWidget::showEvent() to help with the
|
---|
| 82 | initialization of the 2D graphics used.
|
---|
| 83 |
|
---|
| 84 | The new private member functions and variables relate exclusively to the
|
---|
| 85 | 2D graphics and animation. The \c animate() slot is called periodically by the
|
---|
| 86 | \c animationTimer to update the widget; the \c createBubbles() function
|
---|
| 87 | initializes the \c bubbles list with instances of a helper class used to
|
---|
| 88 | draw the animation; the \c drawInstructions() function is responsible for
|
---|
[561] | 89 | a semi-transparent message that is also overpainted onto the OpenGL scene.
|
---|
[2] | 90 |
|
---|
| 91 | \section1 GLWidget Class Implementation
|
---|
| 92 |
|
---|
| 93 | Again, we only show the parts of the \c GLWidget implementation that are
|
---|
| 94 | relevant to this example. In the constructor, we initialize a QTimer to
|
---|
| 95 | control the animation:
|
---|
| 96 |
|
---|
| 97 | \snippet examples/opengl/overpainting/glwidget.cpp 0
|
---|
| 98 |
|
---|
| 99 | We turn off the widget's \l{QWidget::autoFillBackground}{autoFillBackground} property to
|
---|
| 100 | instruct OpenGL not to paint a background for the widget when
|
---|
| 101 | \l{QPainter::begin()}{QPainter::begin()} is called.
|
---|
| 102 |
|
---|
| 103 | As in the \l{Hello GL Example}{Hello GL} example, the destructor is responsible
|
---|
| 104 | for freeing any OpenGL-related resources:
|
---|
| 105 |
|
---|
| 106 | \snippet examples/opengl/overpainting/glwidget.cpp 1
|
---|
| 107 |
|
---|
[561] | 108 | The \c initializeGL() function is fairly minimal, only setting up the QtLogo
|
---|
| 109 | object used in the scene. See the \l{Hello GL Example}{Hello GL} example
|
---|
| 110 | for details of the QtLogo class.
|
---|
[2] | 111 |
|
---|
| 112 | \snippet examples/opengl/overpainting/glwidget.cpp 2
|
---|
| 113 |
|
---|
| 114 | To cooperate fully with QPainter, we defer matrix stack operations and attribute
|
---|
| 115 | initialization until the widget needs to be updated.
|
---|
| 116 |
|
---|
| 117 | In this example, we implement \l{QWidget::paintEvent()}{paintEvent()} rather
|
---|
| 118 | than \l{QGLWidget::paintGL()}{paintGL()} to render
|
---|
| 119 | our scene. When drawing on a QGLWidget, the paint engine used by QPainter
|
---|
| 120 | performs certain operations that change the states of the OpenGL
|
---|
| 121 | implementation's matrix and property stacks. Therefore, it is necessary to
|
---|
| 122 | make all the OpenGL calls to display the 3D graphics before we construct
|
---|
| 123 | a QPainter to draw the 2D overlay.
|
---|
| 124 |
|
---|
| 125 | We render a 3D scene by setting up model and projection transformations
|
---|
| 126 | and other attributes. We use an OpenGL stack operation to preserve the
|
---|
| 127 | original matrix state, allowing us to recover it later:
|
---|
| 128 |
|
---|
| 129 | \snippet examples/opengl/overpainting/glwidget.cpp 4
|
---|
| 130 |
|
---|
| 131 | We define a color to use for the widget's background, and set up various
|
---|
| 132 | attributes that define how the scene will be rendered.
|
---|
| 133 |
|
---|
| 134 | \snippet examples/opengl/overpainting/glwidget.cpp 6
|
---|
| 135 |
|
---|
| 136 | We call the \c setupViewport() private function to set up the
|
---|
| 137 | projection used for the scene. This is unnecessary in OpenGL
|
---|
| 138 | examples that implement the \l{QGLWidget::paintGL()}{paintGL()}
|
---|
| 139 | function because the matrix stacks are usually unmodified between
|
---|
| 140 | calls to \l{QGLWidget::resizeGL()}{resizeGL()} and
|
---|
| 141 | \l{QGLWidget::paintGL()}{paintGL()}.
|
---|
| 142 |
|
---|
| 143 | Since the widget's background is not drawn by the system or by Qt, we use
|
---|
| 144 | an OpenGL call to paint it before positioning the object defined earlier
|
---|
| 145 | in the scene:
|
---|
| 146 |
|
---|
| 147 | \snippet examples/opengl/overpainting/glwidget.cpp 7
|
---|
| 148 |
|
---|
[561] | 149 | Once the QtLogo object's draw method has been executed, the GL
|
---|
| 150 | states we changed and the matrix stack needs to be restored to its
|
---|
| 151 | original state at the start of this function before we can begin
|
---|
| 152 | overpainting:
|
---|
[2] | 153 |
|
---|
| 154 | \snippet examples/opengl/overpainting/glwidget.cpp 8
|
---|
| 155 |
|
---|
| 156 | With the 3D graphics done, we construct a QPainter for use on the widget
|
---|
| 157 | and simply overpaint the widget with 2D graphics; in this case, using a
|
---|
| 158 | helper class to draw a number of translucent bubbles onto the widget,
|
---|
| 159 | and calling \c drawInstructions() to overlay some instructions:
|
---|
| 160 |
|
---|
| 161 | \snippet examples/opengl/overpainting/glwidget.cpp 10
|
---|
| 162 |
|
---|
| 163 | When QPainter::end() is called, suitable OpenGL-specific calls are made to
|
---|
| 164 | write the scene, and its additional contents, onto the widget.
|
---|
| 165 |
|
---|
[561] | 166 | With \l{QGLWidget::paintGL()}{paintGL()} the
|
---|
| 167 | \l{QGLWidget::swapBuffers()}{swapBuffers()} call is done for us. But an explicit
|
---|
| 168 | call to swapBuffers() is still not required because in the
|
---|
| 169 | \l{QWidget::paintEvent()}{paintEvent()} method the QPainter on the OpenGL
|
---|
| 170 | widget takes care of this for us.
|
---|
| 171 |
|
---|
[2] | 172 | The implementation of the \l{QGLWidget::resizeGL()}{resizeGL()} function
|
---|
| 173 | sets up the dimensions of the viewport and defines a projection
|
---|
| 174 | transformation:
|
---|
| 175 |
|
---|
| 176 | \snippet examples/opengl/overpainting/glwidget.cpp 11
|
---|
| 177 |
|
---|
| 178 | Ideally, we want to arrange the 2D graphics to suit the widget's dimensions.
|
---|
| 179 | To achieve this, we implement the \l{QWidget::showEvent()}{showEvent()} handler,
|
---|
| 180 | creating new graphic elements (bubbles) if necessary at appropriate positions
|
---|
| 181 | in the widget.
|
---|
| 182 |
|
---|
| 183 | \snippet examples/opengl/overpainting/glwidget.cpp 12
|
---|
| 184 |
|
---|
| 185 | This function only has an effect if less than 20 bubbles have already been
|
---|
| 186 | created.
|
---|
| 187 |
|
---|
| 188 | The \c animate() slot is called every time the widget's \c animationTimer emits
|
---|
| 189 | the \l{QTimer::timeout()}{timeout()} signal. This keeps the bubbles moving
|
---|
| 190 | around.
|
---|
| 191 |
|
---|
| 192 | \snippet examples/opengl/overpainting/glwidget.cpp 13
|
---|
| 193 |
|
---|
| 194 | We simply iterate over the bubbles in the \c bubbles list, updating the
|
---|
| 195 | widget before and after each of them is moved.
|
---|
| 196 |
|
---|
| 197 | The \c setupViewport() function is called from \c paintEvent()
|
---|
| 198 | and \c resizeGL().
|
---|
| 199 |
|
---|
| 200 | \snippet examples/opengl/overpainting/glwidget.cpp 14
|
---|
| 201 |
|
---|
| 202 | The \c drawInstructions() function is used to prepare some basic
|
---|
| 203 | instructions that will be painted with the other 2D graphics over
|
---|
| 204 | the 3D scene.
|
---|
| 205 |
|
---|
| 206 | \snippet examples/opengl/overpainting/glwidget.cpp 15
|
---|
| 207 |
|
---|
| 208 | \section1 Summary
|
---|
| 209 |
|
---|
| 210 | When overpainting 2D content onto 3D content, we need to use a QPainter
|
---|
| 211 | \e and make OpenGL calls to achieve the desired effect. Since QPainter
|
---|
| 212 | itself uses OpenGL calls when used on a QGLWidget subclass, we need to
|
---|
| 213 | preserve the state of various OpenGL stacks when we perform our own
|
---|
| 214 | calls, using the following approach:
|
---|
| 215 |
|
---|
| 216 | \list
|
---|
| 217 | \o Reimplement QGLWidget::initializeGL(), but only perform minimal
|
---|
| 218 | initialization. QPainter will perform its own initialization
|
---|
| 219 | routines, modifying the matrix and property stacks, so it is better
|
---|
| 220 | to defer certain initialization tasks until just before you render
|
---|
| 221 | the 3D scene.
|
---|
| 222 | \o Reimplement QGLWidget::resizeGL() as in the pure 3D case.
|
---|
| 223 | \o Reimplement QWidget::paintEvent() to draw both 2D and 3D graphics.
|
---|
| 224 | \endlist
|
---|
| 225 |
|
---|
| 226 | The \l{QWidget::paintEvent()}{paintEvent()} implementation performs the
|
---|
| 227 | following tasks:
|
---|
| 228 |
|
---|
| 229 | \list
|
---|
| 230 | \o Push the current OpenGL modelview matrix onto a stack.
|
---|
| 231 | \o Perform initialization tasks usually done in the
|
---|
| 232 | \l{QGLWidget::initializeGL()}{initializeGL()} function.
|
---|
| 233 | \o Perform code that would normally be located in the widget's
|
---|
| 234 | \l{QGLWidget::resizeGL()}{resizeGL()} function to set the correct
|
---|
| 235 | perspective transformation and set up the viewport.
|
---|
| 236 | \o Render the scene using OpenGL calls.
|
---|
| 237 | \o Pop the OpenGL modelview matrix off the stack.
|
---|
| 238 | \o Construct a QPainter object.
|
---|
| 239 | \o Initialize it for use on the widget with the QPainter::begin() function.
|
---|
| 240 | \o Draw primitives using QPainter's member functions.
|
---|
| 241 | \o Call QPainter::end() to finish painting.
|
---|
| 242 | \endlist
|
---|
| 243 | */
|
---|