I separate 2D and 3D rendering from my CAD-like application for the same reasons as you, although in my case I am not a widget, but this should not change. Here's how to approach the problem:
- When your widget changes display it on a
QGLFramebufferObject , do it using FBO as a QPaintDevice for QPainter in QGLWidget::paintEvent(..) and calling myWidget->render( myQPainter, ...) . Repeat this for any number of widgets that you have, but only on the same FBO - do not create FBOs for each of them ... Remember to clear it first as a "normal" framebuffer. - When your current OpenGL background changes, render it to another
QGLFramebufferObject using standard OpenGL calls in the same way. - Create a passage through the vertex shader (the "camera" will be just a cube of one) and a very simple fragment shader that can stack two textures on top of each other.
- At the end of
QGLWidget::paintEvent(..) activate your shader program, bind the framebuffers as textures for it ( myFBO->texture() gets a handle) and draw a square of one. Since your camera is a unit square, and the viewport size determines the size of the FBO, it perfectly fills the viewport pixel.
However, the easy part ... The hard part is the interaction of the widget. Since you essentially represent a “proxy”, you will have to pass the interaction between the “real” and “proxy” widgets, while preserving the invisible “real” widget. Here's how I would start:
- Some operating systems are a little strange in visualizing widgets without even displaying them, so you may need to show and then hide the widget after creating the instance - due to the smart drawing queue in Qt, this is unlikely to really appear on the screen.
- Catch all mouse events in the viewport, work out a “proxy” widget over which the cursor ended (if any), and then move it to get the relative position for the “real” hidden widget - this value will depend on what the parent object has view of the "real" widget. Then pass the event to the “real” widget before redrawing the widget framebuffer.
I must point out that I also had to create a “tagging” system to draw the redraw well. You do not want each widget event to trigger a FBO widget redraw because there could be many simultaneous events (not just thinking about the mouse), but you only need one redraw. Therefore, I created a system in which, if something in the application can visually visualize something in the viewport, then the flag will be visible as "dirty". Then configure a QTimer for any number of frames that you are aiming for (in my situation, the scene can become very difficult, so I also calculated how long the frame takes, and then used this value + 10% as a timer delay for the next check, so thus, the system is not bombarded when rendering becomes lagging). And then check the dirty status: if it's dirty, redraw; otherwise no. I found life easier with two dirty flags, one for 3D material and one for 2D, but if you need to maintain a constant drawing speed for an OpenGL drawing, maybe there is no need for two.
I imagine what I did, it was not the easiest way to do this, but it provides many options for customization and profiling - which makes life easier in the long run. All the answers are certainly not in this post, but hopefully it will help you in choosing a strategy.
cmannett85
source share