I'm coding a rendering engine in C++ with OpenGL and GLUT and trying to integrate ImGUI into my engine, but I have a problem. it either renders the gui and only the background (no objects), or it only renders the objects and background (no GUI). This code:
glutDisplayFunc(renderScene);
glutIdleFunc(renderScene);
glutReshapeFunc(sizeChange);
glutSpecialUpFunc(releaseKey);
glutSpecialFunc(pressKey);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
ImGui::StyleColorsLight();
ImGui_ImplGLUT_Init();
ImGui_ImplGLUT_InstallFuncs();
ImGui_ImplOpenGL2_Init();
creates this:
and this code:
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
ImGui::StyleColorsLight();
ImGui_ImplGLUT_Init();
ImGui_ImplGLUT_InstallFuncs();
ImGui_ImplOpenGL2_Init();
glutDisplayFunc(renderScene);
glutIdleFunc(renderScene);
glutReshapeFunc(sizeChange);
glutSpecialUpFunc(releaseKey);
glutSpecialFunc(pressKey);
creates this:
The problem is that GLUT callback handlers in both of your examples are set both manually (glut...Func) and by ImGui via ImGui_ImplGLUT_InstallFuncs. The latter sets default ImGui handlers for many GLUT callbacks (see the source), in particular glutReshapeFunc is used to set current window resize callback to ImGui_ImplGLUT_ReshapeFunc (source), which sets internal ImGui display size according to given parameters.
When in the second example glutReshapeFunc(sizeChange) gets called, ImGui handler gets unset, so it doesn't get called during window resize (which, in particular, happens also before the first display according to the GLUT documentation), which leaves internal ImGui state not set properly. In the first example, the situation is reversed - your reshape handler sizeChange gets unset and replaced by ImGui one, so initialization and rendering of your cube doesn't go as intended because sizeChange never gets called.
To resolve this, you can omit ImGui_ImplGLUT_InstallFuncs and use your GLUT handlers while, apart from custom logic, calling default ImGui handlers from them if they are present and their functionality is needed (you can see what default handlers exist by inspecting the sources linked above). E.g. you can change sizeChange like this:
void sizeChange(int w, int h)
{
// Your logic
// ...
ImGui_ImplGLUT_ReshapeFunc(w, h);
}
If you want key presses and releases to be registered by ImGui as well as your own code, you may change releaseKey and pressKey in a similar way by adding, respectively, ImGui_ImplGLUT_SpecialUpFunc and ImGui_ImplGLUT_SpecialFunc calls to them.
Related
I have an application which renders a 3d object using OpenGL, allowing the user to rotate and zoom and inspect the object. Currently, this is driven directly by received mouse messages (it's a Windows MFC MDI application). When a mouse movement is received, the viewing matrix is updated, and the scene re-rendered into the back buffer, and then SwapBuffers is called. For a spinning view, I start a 20ms timer and render the scene on the timer, with small updates to the viewing matrix each frame. This is OK, but is not perfectly smooth. It sometimes pauses or skips frames, and is not linked to vsync. I would love to make it smoother and smarter with the rendering.
It's not like a game where it needs to be rendered every frame though. There are long periods where the object is not moved, and does not need to be re-rendered.
I have come across GLFW library and the glfwSwapInterval function. Is this a commonly used solution?
Should I create a separate thread for the render loop, rather than being message/timer driven?
Are there other solutions I should investigate?
Are there any good references for how to structure a suitable render loop? I'm OK with all the rendering code - just looking for a better structure around the rendering code.
So, I consider you are using GLFW for creating / operating your window.
If you don't have to update your window on each frame, suggest using glfwWaitEvents() or glfwWaitEventsTimeout(). The first one tells the system to put this process (not window) on sleep state, until any event happens (mouse press / resize event etc.). The second one is similar, but you can specify a timeout for the sleep state. The function will wait till any event happens OR till specified time runs out.
What's for the glfwSwapInterval(), this is probably not the solution you are looking for. This function sets the amount of frames that videocard has to skip (wait) when glfwSwapBuffers() is called.
If you, for example, use glfwSwapInterval(1) (assuming you have valid OpenGL context), this will sync your context to the framerate of your monitor (aka v-sync, but I'm not sure if it is valid to call it so).
If you use glfwSwapInterval(0), this will basicly unset your syncronisation with monitor, and videocard will swap buffers with glfwSwapBuffers() instanly, without waiting.
If you use glfwSwapInterval(2), this will double up the time that glfwSwapBuffers() waits after (or before?) flushing framebuffer to screen. So, if you have, for instance, 60 fps on your display, using glfwSwapInterval(2) will result in 30 fps in your program (assuming you use glfwSwapBuffers() to flush framebuffer).
The glfwSwapInterval(3) will give you 20 fps, glfwSwapInterval(4) - 15 fps and so on.
As for separate render thread, this is good if you want to divide your "thinking" and rendering processes, but it comes with its own advantages, disadvantages and difficulties. Tip: some window events can't be handled "properly" without having separate thread (See this question).
The usual render loop looks like this (as far as I've learned from learnopengl lessons):
// Setup process before...
while(!window_has_to_close) // <-- Run game loop until window is marked "has to
// close". In GLFW this is done using glfwWindowShouldClose()
// https://www.glfw.org/docs/latest/group__window.html#ga24e02fbfefbb81fc45320989f8140ab5
{
// Prepare for handling input events (e. g. callbacks in GLFW)
prepare();
// Handle events (if there are none, this is just skipped)
glfwPollEvents(); // <-- You can also use glfwWaitEvents()
// "Thinknig step" of your program
tick();
// Clear window framebuffer (better also put this in separate func)
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
// Render everything
render();
// Swap buffers (you can also put this in separate function)
glfwSwapBuffers(window); // <-- Flush framebuffer to screen
}
// Exiting operations after...
See this ("Ready your engines" part) for additional info. Wish you luck!
I'm writing some D3D11 apps and am using DirectXTK's CreateWICTextureFromFile to load a texture file into SRV. I wanted to show my rendering window only when I start to draw the scene (after initializing models, textures, shaders, constant buffers, etc.) so I've created the window early on but I omit the ShowWindow until later.
Unfortunately I get an error unless I show the window prior to creating the texture:
// ShowWindow(hwnd, SW_SHOW); // works
hr = DirectX::CreateWICTextureFromFile(device.Get(), L"../../Common/Resources/Textures/green_grid.png", nullptr, psTexture.GetAddressOf());
ShowWindow(hwnd, SW_SHOW); // fails
HResult error:
No such interface supported
Also it seems to work fine if I show the window at the end of initialization as long as I don't load any textures with this function.
Maybe I don't have a good understanding of how a window works with respect to the D3D API. Looking at CreateWICTextureFromFile's parameters, I only see a dependency on device and the SRV. I'm not sure why there's a dependency on the window visibility?
Before you call WICTextureLoader (which uses the Windows Imaging Component) you need to initialize COM as noted in the documentation.
In your main entry-point, add:
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
// error
The fact that ShowWindow happens to initalize COM is an interesting side-effect, but that's definitely not a function you are required to call to use my GitHub libraries.
I want to create the opengGL context using freeglut. I will first decide the which context to by checking the supporting version using glew and some other parameters. I know for glew to work, it needs a opengl context. So I first create a context using glutCreateWindow, then check the supported version and then set the version required using the glutInitContextVersion() and destroy the previous window using glutDestroyWindow and recreate the new window by using glutCreateWindow. I get this error Freeglut error: ERROR: No display callback registered for window 1 (I checked 1 is ID for my previous window which I destroyed) . Following is my code
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(900, 600);
int winID = glutCreateWindow("Rotating Cube"); //winID is 1 here
glewExperimental = GL_TRUE;
glewInit();
//I decide the context on some other parameters also except the supported version reported by glew.
//I have tested this with opengl 3.2 core profile as well.
//This is not working even if I forcefully set the opengl version to 2.0
if (glewIsSupported("GL_VERSION_3_1"))
{
glutInitContextVersion (3, 1);
glutInitContextFlags (GLUT_FORWARD_COMPATIBLE);
//glutInitContextVersion (3, 2);
//glutInitContextFlags (GLUT_CORE_PROFILE);
}
else if (glewIsSupported("GL_VERSION_2_0"))
{
glutInitContextVersion (2, 0);
}
glutDestroyWindow(winID);
winID = glutCreateWindow("Rotating Cube"); //winID is 2 here
glutSetWindow(winID);
glutDisplayFunc(RenderScene);
glutIdleFunc(RenderScene);
glutReshapeFunc(ChangeSize);
glutKeyboardFunc(ProcessNormalKeys);
glutSpecialFunc(ProcessSpecialKeys);
glutMainLoop();
I think I need to do this as a openGL context is always required for glew to work. I already tried setting the display function on first window as well (though I know I am going to destroy it) but that also didn't work. I am setting the current window to new window and then calling the glutMainLoop. So I think this should work
According to answer by rhashimoto, I tried to put the destroy command at different positions
if (g_winID>=0)
{
glutDestroyWindow(g_winID);
g_winID = -1;
}
I put the destroy command at begining of reshape callback
ERROR: No display callback registered for window 1
The I put the destroy command at beginning of display function
ERROR: Function <glutSwapBuffers> called with no current window defined.
If I put this at end of Display callback, it does not give error but the displayed scene is not correct. Somethings are missing from the scene
So is there some specific callback function I need to put this display command? I dont think there is any destroy callback I can set. yes there is glutCloseFunc but I think that is meant to be called when window is being destroyed that is when glutDestroyWindow has been called on window
I think you can argue that this is a FreeGLUT bug, either in the implementation or the documentation. It looks like glutDestroyWindow() needs to be called from a GLUT callback to work properly.
glutDestroyWindow() mainly puts the window on a list to be destroyed, as well as clearing all callbacks (except a destroy callback). This is probably why setting the display function didn't work for you - it was removed when you called glutDestroyWindow().
Windows are actually destroyed at the end of each main loop. So on the first time through the loop, your window still exists. The fact that it has no display callback makes GLUT unhappy.
The best workaround is probably to arrange to call glutDestroyWindow() only via one of the GLUT callbacks. I don't know if this will make the window briefly flash on the screen. My guess is it won't, but it might depend on the platform.
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()
I want to be able to render to an X Window given just its id.
In this case I have a window created in python by gtk.
I can get the window ID of a gtk.Drawable and pass it into my C python module, but can I then make OpenGL calls render to it?
I am aware of gtkglext, but would rather not use it if possible.
Update 1:
Ok, so (rather obviously now that I see it) You just do an XCreateWindow with a parent of the Window id that you get from gtk.window.xid, using the correct flags for an opengl window, and hey presto.
The only problem is I can't get it to work if there are not multiple widgets in the window, otherwise it seems that the xid represents a window that covers the entire toplevel window. Not sure how to rectify this.
Update 2:
It turns out that if you have a gl window that is the same size as the toplevel then the toplevel window will not get expose events until the gl window has its buffers swapped. You just have to keep swapping the buffers and things wil be fine.
Update 3:
To answer #babele's comment:
This page in the python gtk docs say how to make a gtk window from an existing xid. After that you just have to remeber to keep calling glXSwapBuffers for that window (if it is an opengl buffered window, otherwise it should just work when you use window_foreign_new).
So the process goes:
Create a gtk widget that will contain your OpenGL window (a DrawingArea is a good choice - you can't use eg a label as it won't have its own xid)
Get the widget's gtk.gdk.Window (docs)
Get the xid from the gtk.gdk.Window (call this window W1)
Pass it to your C/C++ code
Create the opengl capable window (W2) as a child of W1
Pass the xid of W2 back to python
Use window_foreign_new with W2's xid to create the new gtk.gdk.window object
Each time you call glXSwapBuffers on W2 gtk should then be able to react to expose events.
One bit that really threw me is that if W2 covers the whole of W1 then W1 won't receive events until W2's buffers get swapped. This is particularly confusing if W1 is a top-level window as it can be that nothing appears on your screen at all (the window is there but it looks like whatever is behind it until it gets painted to, which won't happen until it gets an expose event).
Also note that you'll have to manually manage the resizing of W2 by connecting to the gtk resize events. You can do this by connecting to this signal, then calling this function in the handler and passing the results to your c/c++ module where you can resize W2 appropriately. Its a good idea to request a minimum size.
You don't need to create a new window, you pass an existing window to glXMakeCurrent().
In your current setup you'd need to:
XGetWindowAttributes() to retrieve the visual the window was created with
glXCreateContext() using this visual
glXMakeCurrent() using this context and the window id
However, this is doing it backwards, forcing OpenGL to use the visual used for CreateWindow. This often works because the default visual has sane GLX properties, but a correct application will glXChooseVisual with desired GLX properties, then use that visual to create the window.