ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
60.82k stars 10.27k forks source link

OpenGL setup using GLUT #69

Closed UUSim closed 9 years ago

UUSim commented 10 years ago

First of all, thanks for this marvelous gui library! I've just started using it, and it looks quite promising.

I found it is easy to integrate in existing, however the example provided isn't completely explanatory.

In my case i'm using GLUT, and the issue is that rendering the ImGui overwrites anything that has already been drawn. The ImGui::Render call is made just before glutSwapBuffers(); and after the application's drawing calls. However, the background of the gui has the same color of the glClearColor, and is not transparent. If I move a gui window around, or fiddle with its size, the background sometimes becomes transparent (See the enclosed example images, nr1 is the expected result) I've copied the ImImpl_RenderDrawLists() function and most of the InitImGui() function of the opengl example.

My question is, how do I set up the ImGui rendering using GLUT such that it doesn't interfere with the items that have already been drawn? Thanks in advance!

UUSim commented 10 years ago

imgui example1 imgui example2

JarrettBillingsley commented 10 years ago

I doubt that GLUT is the culprit here. You'd have to show us your rendering code to see what's wrong with it.

JarrettBillingsley commented 10 years ago

Are you sure you didn't copy the glClear call along with the other stuff you copied from the example?

UUSim commented 10 years ago

Thanks a lot for your response JarrettBillingsley! I'm very sure that glClear is called exactly once, the code, and any calls to it occur & exist only once.

Here is all my OpenGL code combined:

int main(int argc, char** argv) {
    // ... app code
    glutInitDisplayMode(GLUT_RGB | GLUT_ALPHA | GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE);
    glutInitWindowSize(w,h);
    int screenWidth = glutGet(GLUT_SCREEN_WIDTH);
    glutInitWindowPosition(5 * screenWidth/ 12, 0);
    glutInit(&argc, argv);
    glutCreateWindow(title.c_str());

    // init opengl
    glEnable(GL_DEPTH_TEST);
    //setTransparencyEnabled(true); function's content in braces:
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }

    // set the background color
    // rgba=(0.25098, 0.501961, 0.12549, 1)
    double r,g,b,a; bgColor.getComponents_double(r,g,b,a);
    glClearColor(r,g,b,a);

    // Set up glut callbacks
    glutKeyboardFunc(keyboard);
    glutKeyboardUpFunc(keyboardUp);
    glutSpecialFunc(keyboardSpecialKeys);
    glutSpecialUpFunc(keyboardSpecialKeysUp);
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutMouseFunc(mouse);
    glutMotionFunc(mouseMotion_buttonPressed);
    glutPassiveMotionFunc(mouseMotion_noButtonPressed);

    // Set up ImGui
    ImGuiIO& guiIO = ImGui::GetIO();
    guiIO.DisplaySize.x = MA.resolution; // 1680.0f;
    guiIO.DisplaySize.y = MA.resolution; // 1050.0f;
    guiIO.DeltaTime = 1.0f / 60.0f;
    guiIO.IniFilename = "imgui.ini";
    guiIO.RenderDrawListsFn = ImImpl_RenderDrawLists;
    guiIO.KeyMap[0] = 9;    // tab
    guiIO.KeyMap[1] = GLUT_KEY_LEFT;    // Left
    guiIO.KeyMap[2] = GLUT_KEY_RIGHT;   // Right
    guiIO.KeyMap[3] = GLUT_KEY_UP;      // Up
    guiIO.KeyMap[4] = GLUT_KEY_DOWN;    // Down
    guiIO.KeyMap[5] = GLUT_KEY_HOME;    // Home
    guiIO.KeyMap[6] = GLUT_KEY_END;     // End
    guiIO.KeyMap[7] = 127;  // Delete
    guiIO.KeyMap[8] = 8;    // Backspace
    guiIO.KeyMap[9] = 13;   // Enter
    guiIO.KeyMap[10] = 27;  // Escape
    guiIO.KeyMap[11] = 1;   // ctrl-A
    guiIO.KeyMap[12] = 3;   // ctrl-C
    guiIO.KeyMap[13] = 22;  // ctrl-V
    guiIO.KeyMap[14] = 24;  // ctrl-X
    guiIO.KeyMap[15] = 25;  // ctrl-Y
    guiIO.KeyMap[16] = 26;  // ctrl-Z

    glGenTextures(1, &fontTex);
    glBindTexture(GL_TEXTURE_2D, fontTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    const void* png_data;
    unsigned int png_size;
    ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size);
    int tex_x, tex_y, tex_comp;
    void* tex_data = stbi_load_from_memory((const unsigned char*)png_data, (int)png_size, &tex_x, &tex_y, &tex_comp, 0);
    IM_ASSERT(tex_data != NULL);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_x, tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_data);
    stbi_image_free(tex_data);

    glutMainLoop();     // GLUT has its own main loop, which calls display();
}

void display() {
    glFlush();
    glClear(GL_COLOR_BUFFER_BIT);
    ImGui::NewFrame();

    // Draw application's things
    glViewport(0, 0, 800, 800);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-100, 100, -100, 100, -1, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    //e.g. some polygons
        glDepthFunc(GL_ALWAYS);
        glPolygonMode(GL_FRONT, GL_FILL);
        glColor4ub(polygon.color.r, polygon.color.g, polygon.color.b, polygon.color.a);

        glBegin(GL_POLYGON);
        for(vector<Point>::const_iterator it = polygon.pts.begin(); it != polygon.pts.end(); ++it) {
            glVertex2d(it->x, it->y);
        }
        glEnd();

    glViewport(0, 0, 800, 800);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 800, 0, 800, -1, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    //e.g. text "Simulation time: 164.3s"

        glDepthFunc(GL_ALWAYS);
        glColor4ub(text.color.r, text.color.g, text.color.b, text.color.a);
        glRasterPos2f(text.pos.x, text.pos.y);

        int n = (int)text.text.length();
        for(int i=0; i<n; ++i) {
            glutBitmapCharacter(GLUT_BITMAP_8_BY_13, text.text[i]);
        }

    // Draw ImGui (toggled using tab-key)
    if (guiDrawing) {
        static bool show_another_window = true;
        ImGui::Begin("Another Window", &show_another_window, ImVec2(200, 100));
        ImGui::Text("Hello");
        ImGui::End();

        static bool testOpen = true;
        ImGui::ShowTestWindow(&testOpen);

        ImGui::Render();
    }
    glutSwapBuffers();
}

// The rendering function is unchanged
static void ImImpl_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
{
    if (cmd_lists_count == 0)
        return;

    // We are using the OpenGL fixed pipeline to make the example code simpler to read!
    // A probable faster way to render would be to collate all vertices from all cmd_lists into a single vertex buffer.
    // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers.
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_SCISSOR_TEST);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    // Setup texture
    glBindTexture(GL_TEXTURE_2D, fontTex);
    glEnable(GL_TEXTURE_2D);

    // Setup orthographic projection matrix
    const float width = ImGui::GetIO().DisplaySize.x;
    const float height = ImGui::GetIO().DisplaySize.y;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0f, width, height, 0.0f, -1.0f, +1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // Render command lists
    for (int n = 0; n < cmd_lists_count; n++)
    {
        const ImDrawList* cmd_list = cmd_lists[n];
        const unsigned char* vtx_buffer = (const unsigned char*)cmd_list->vtx_buffer.begin();
        glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer));
        glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer + 8));
        glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (void*)(vtx_buffer + 16));

        int vtx_offset = 0;
        const ImDrawCmd* pcmd_end = cmd_list->commands.end();
        for (const ImDrawCmd* pcmd = cmd_list->commands.begin(); pcmd != pcmd_end; pcmd++)
        {
            glScissor((int)pcmd->clip_rect.x, (int)(height - pcmd->clip_rect.w), (int)(pcmd->clip_rect.z - pcmd->clip_rect.x), (int)(pcmd->clip_rect.w - pcmd->clip_rect.y));
            glDrawArrays(GL_TRIANGLES, vtx_offset, pcmd->vtx_count);
            vtx_offset += pcmd->vtx_count;
        }
    }
    glDisable(GL_SCISSOR_TEST);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

Sorry for the long post, this was the only way to provide all the details.

UUSim commented 10 years ago

So far I've not been able to get anywhere. I've noticed something strange, that i didn't notice before, which is that any text drawing calls of my main program are being displayed correctly.

A topic on the OpenGL discussion boards suggests that cases like this are due to an incorrect usage of GL_MODELVIEW / GL_PROJECTION. I could however not solve the issue by fiddling around with these.

ocornut commented 9 years ago

Have you been able to solve the problem? It's hard to tell what's going wrong from the code snippet. Your snippet doesn't show your code setting its own projection and model matrix.

What you can do is comment chunks of the ImImpl_RenderDrawLists() until you find the specific bit of OpenGL state that the function changes and makes your application break.

UUSim commented 9 years ago

Unfortunately, I have not. Sorry for the missing models & projections, I've added them to the snippet. My application is 2D, but uses world coordinates, and screen coordinates. Therefore, the 1st series sets the world coordinates (+ draws world things), and the 2nd the screen coordinates (and draws overlay text). After this, ImGui is rendered.

void display() {
    glFlush();
    glClear(GL_COLOR_BUFFER_BIT);
    ImGui::NewFrame();

    // Draw application's things
    glViewport(0, 0, 800, 800);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-100, 100, -100, 100, -1, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    //e.g. some polygons
        glDepthFunc(GL_ALWAYS);
        glPolygonMode(GL_FRONT, GL_FILL);
        glColor4ub(polygon.color.r, polygon.color.g, polygon.color.b, polygon.color.a);

        glBegin(GL_POLYGON);
        for(vector<Point>::const_iterator it = polygon.pts.begin(); it != polygon.pts.end(); ++it) {
            glVertex2d(it->x, it->y);
        }
        glEnd();

    glViewport(0, 0, 800, 800);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 800, 0, 800, -1, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    //e.g. text "Simulation time: 164.3s"

        glDepthFunc(GL_ALWAYS);
        glColor4ub(text.color.r, text.color.g, text.color.b, text.color.a);
        glRasterPos2f(text.pos.x, text.pos.y);

        int n = (int)text.text.length();
        for(int i=0; i<n; ++i) {
            glutBitmapCharacter(GLUT_BITMAP_8_BY_13, text.text[i]);
        }

    // Draw ImGui (toggled using tab-key)
    if (guiDrawing) {
        static bool show_another_window = true;
        ImGui::Begin("Another Window", &show_another_window, ImVec2(200, 100));
        ImGui::Text("Hello");
        ImGui::End();

        static bool testOpen = true;
        ImGui::ShowTestWindow(&testOpen);

        ImGui::Render();
    }
    glutSwapBuffers();
}
ocornut commented 9 years ago

Can you try commenting out chunks of your ImImpl_RenderDrawLists() function to find which render state change is affecting your application?

UUSim commented 9 years ago

It is fixed!

glPushMatrix();
glPushAttrib(GL_ALL_ATTRIB_BITS);
// Drawing calls to ImGui, e.g.:
if (ImGui::InputFloat("Timestep", &guiTimeStep, 0.001f, 0.005f, 4)) {
    simulator->setTimeStep(guiTimeStep);
}
ImGui::End();
static bool testOpen = true;
ImGui::ShowTestWindow(&testOpen);
ImGui::Render();
glPopAttrib();
glPopMatrix();

did the trick, as per: https://stackoverflow.com/questions/13408777/nifty-gui-control-prevents-the-rest-of-my-application-from-rendering?rq=1

I tried various options of commenting out openGL calls, but to no avail. Apparently, the rendering function makes changes in the openGL environment that cannot be restored be resetting proper glOrtho, glMatrixMode, and glLoadIdentity.

So maybe it is a good idea to incorporate the following code in the OpenGL example:

glPushMatrix();
glPushAttrib(GL_ALL_ATTRIB_BITS);
// normal render code
glPopAttrib();
glPopMatrix();

Thanks for your support guys

ocornut commented 9 years ago

Need to fix the sample.

ocornut commented 9 years ago

I have committed code to push/pop the 2 matrices it modifies as well as the attribs I think it modifies (but not all attrib). Can you confirm it works with that? if you remove your own Push/Pop? https://github.com/ocornut/imgui/commit/36e52167da399a2f811f72adc7bea5141e6bcb3f

UUSim commented 9 years ago

Just tried it, and I can confirm, it works like a charm!