Where to use the makeCurrent() in QGLWidget? - c++

The qt's documentation said:
Your widget's OpenGL rendering context is made current when paintGL(),
resizeGL(), or initializeGL() is called. If you need to call the
standard OpenGL API functions from other places (e.g. in your widget's
constructor or in your own paint functions), you must call
makeCurrent() first.
for the following case:
paintGL()
{
drawSomething();
}
...
drawSomething()
{
glClearColor()...
//many other gl calls...
}
do I have to makeCurrent inside the drawSomething() function.
And if I only make QPainter call in stead of standard OpenGL API functions. Do I have to use makeCurrent?

do I have to makeCurrent inside the drawSomething() function.
If that function is called only from paintGL, no, as Qt will call paintGL with the context already current.
As the docs say, you'll need it whenever you need the GL context current in some other function.
// called from other code, context may not be current
void MyGLWidget::setBackgroundColor(const QColor &color) {
makeCurrent();
glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
}

As the documentation states:
[The] rendering context is made current when paintGL() ... is called
So there is no need to call makeCurrent in any method called from paintGL. When using QPainter, it is also not necessary (Btw.: QPainter does not necessarily use OpenGL).

Related

How to get resizeEvent-already-executed?

I have a QDialog-inherited class and one of its sub-window is initialized on resizeEvent like this.
void OpenGLWindow::resizeEvent(QResizeEvent *event) {
QWindow::resizeEvent(event);
// initialize on first call
if (m_context == nullptr)
initOpenGL();
resizeGL(width(), height());
}
Because OpenGL init needs width and height information, and need to adapt window size change, this init code can't move other place.
I should open this dialog and initialize some OpenGL draw objects.
m_Dialog->show();
m_Dialog->init(init_paramter); //I have to create opengl buffers of draw objects here
But unfortunately, resizeEvent() is called after init method was invoked.
e.g. current execution order is as following.
my_dialog_open_function() {
m_Dialog->show();
m_Dialog->init(init_paramter);
}
void OpenGLWindow::resizeEvent(QResizeEvent * event)
As OpenGL system was not initialized by resizeEvent Handler, my m_Dialog->init invoke crashes.
How can I get this execution flow?
m_Dialog->show();
void OpenGLWindow::resizeEvent(QResizeEvent * event)
m_Dialog->init(init_paramter);
Where is the proper place I can invoke my init function safely?
As #pptaszni mentioned in his comment, initOpenGL() doesn't look like it needs size information. So first I would reconsider the design.
As for the question:
The resize event is scheduled after the show and delivered when GUI events are processed. That's after your function returns.
You can simply delay the call by posting some event, which then will be delivered after the resize event. The easiest way is to use a single shot timer:
m_Dialog->show();
// Call me in 1ms
QTimer::singleShot(1, [=]() {
m_Dialog->init(init_paramter);
});
Note the delay of 1ms. If you call it with 0, Qt will optimize and call the handler immediately, so no gain there.

Moving from QGLWidget to QWindow

I have some code using Qt 4. I want to migrate it to Qt 5 and switch to QOpenGL stuff. I can't understand where the code from void paintGL() and void initializeGL() needs to go in a QWindow. Can anyone help me with an example?
I have created a simple example of using QWindow with OpenGL.
To simplify OpenGL development I have created an abstract class GLWindow, which contains the virtual functions initializeGL(), paintGL() and resizeGL(int w, int h). I believe the source code shows the relationship between the old style and the new style.
The example is available here:
https://github.com/mortennobel/QtOpenGLChapter/tree/master/OpenGL3xAlt
Qt has at least one example (Overpainting) of putting widgets over an OpenGL scene. By looking at that, it appears the easiest way would be to inherit from QGLWidget instead of QWidget and override the appropriate virtual functions for initialize and paint.
The standard example for using QWindow with OpenGL is hellowindow in qtbase/examples/opengl.
There is no direct replacement for initializeGL and paintGL. Instead you do something like this:
Have a QWindow with surface type OpenGLSurface.
Create a QOpenGLContext with a matching format.
When the window receives an expose event, start rendering (makeCurrent, your GL calls and finally swapBuffers).

Render loop function in QT to render opengl in Windows

Since QGLWidget cannot be used for OpenGL / ES, i am left with QWidget in Qt for widgets to render opengl. Reference
if i have the following renderView function to render in the current window, i am looking for "that" function that will be called repeatedly by qt for QWidget to refresh screen so that i can call renderView in that function by overloading it.
any idea how to do this or common work around?
Original problem: I do not want to call renderView in a timer of 1 MilliSec or even 16.66 Milli sec as per the screen refresh rate.
class MyWindow
{
void renderView()
{
if(DeviceContext && RenderingContext)
{
wglMakeCurrent(m_hdc, m_hrc); //Set current context
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// glViewPort.... and render using shaders
SwapBuffers(m_hdc); // Double buffering thats why
}
}
};
class MyWidget : public QWidget, public MyWindow
{
// What is the function called repeatedly to refresh the screen
// so that i would ask that function to call renderView
};
Thanks in advance!
Since QGLWidget cannot be used for OpenGL / ES
Right, we need to clear up some confusion before going further down the road...
It can be. This widget was invented to extend the usual QWidget interface with OpenGL desktop and embedded support. Please refer to the following example to see yourself it is possible to use this widget for OpenGL ES functionality:
OpenGL Hello GL ES Example
Now, we can come to the point to answer your question. What you should probably reimplement is the following protected method.
void QGLWidget::paintGL() [virtual protected]
This virtual function is called whenever the widget needs to be painted. Reimplement it in a subclass.
While we are at discussing it, please also check the protected initialization method:
void QGLWidget::initializeGL() [virtual protected]
This virtual function is called once before the first call to paintGL() or resizeGL(), and then once whenever the widget has been assigned a new QGLContext. Reimplement it in a subclass.
This function should set up any required OpenGL context rendering flags, defining display lists, etc.
In fact, you could even deal with overlays with this interface as per the following protected methods:
void QGLWidget::paintOverlayGL() [virtual protected]
This virtual function is used in the same manner as paintGL() except that it operates on the widget's overlay context instead of the widget's main context. This means that paintOverlayGL() is called whenever the widget's overlay needs to be painted. Reimplement it in a subclass.
and
void QGLWidget::initializeOverlayGL() [virtual protected]
This virtual function is used in the same manner as initializeGL() except that it operates on the widget's overlay context instead of the widget's main context. This means that initializeOverlayGL() is called once before the first call to paintOverlayGL() or resizeOverlayGL(). Reimplement it in a subclass.
This function should set up any required OpenGL context rendering flags, defining display lists, etc. for the overlay context.

Trouble initializing shaders before resize on wxGLCanvas

wxGLCanvas seems to hate OpenGL shaders, by not providing an initialization callback function like the one in Qt; Making me unable to create the shaders even after creating the context myself in the constructor, like so:
m_context = new wxGLContext(this);
Also tried:
SetCurrent(*m_current);
Still no luck and GLEW keeps throwing "Missing GL Version" which indicates that the context has not been created (from Google).
I have tried:
Using WX_EVT() for show and activate events, still no luck.
Initializing shaders in the resize event, still no luck.
Is there any way to make wxGLCanvas call my initialization function before anything else?
There is no specific method to initialize OpenGL that gets called before everything else, but after the window was shown, in wxWidgets. You can roll your own with a member variable that indicates whether OpenGL has been initialized, and doing your initialization in the Paint event handler if the variable is false.
In my experience it is safest to issue all OpenGL commands only in the Paint event handler, so in your Size event handler, you should save the new viewport size and update the projection matrix in your Paint handler (or just query the viewport size using wxGLCanvas' GetClientRect() method).
In the examples in http://wiki.wxwidgets.org/WxGLCanvas it is suggested that you do the initialization on the paint event (EVT_PAINT(BasicGLPane::render)), as said by Kristian Duske in the comments above.
Instead you can initialize OpenGL in any other place, you just have to show the wxwidget window or frame first (e.g. frame->Show() ).
I do this:
initialize wxGLCanvas (in my case with a wxFrame frame as parent)
canvas = new Canvas( (wxFrame*) frame, args);
show window
frame->Show()
Now call your custom OpenGL initialization method in the canvas class (only once):
set context
wxGLCanvas::SetCurrent(*m_context);
initialize glew:
glewExperimental = GL_TRUE;
GLenum err = glewInit();
After this I compile the shaders etc.
In the paint event then only do the drawing: glDrawArrays() and SwapBuffers()

How to have dynamic scene update in libqglviewer

I'm using libqglviewer for a project, I read input from a motion capture device through USB and display this as a human in the viewer. I draw the opengl things in the draw() method of the viewer, it works fine. However, when the motion controllers change, I actually get new position values and i draw these in the viewer, BUT i dont see this update until i click on the viewer screen. Is it possible to update the frames in the viewer by itself?
It looks like you just need to post an updateGL right after you get the new position values.
QGLWidget::updateGL()
void QGLWidget::updateGL () [virtual slot]
Updates the widget by calling glDraw().
For painting in 2D the function is called update.
Also, don't call it from inside your draw method (see updateGL in the libQGLViewer documentation).
This note comes from QWidget::paintEvent():
Note: Generally, you should refrain from calling update() or repaint() inside a paintEvent(). For example, calling update() or repaint() on children inside a paintevent() results in undefined behavior; the child may or may not get a paint event.
The same probably applies for QGLViewer.
You can also use repaint, but is isn't recommended (see QWidget::repaint()).