Jump to content
  • Advertisement
Sign in to follow this  
Mantear

OpenGL GUI suggestions

This topic is 4325 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Greetings! I've spent a fair amount of time off and on working on a GUI done in OpenGL. I'm doing this mainly as a learning experience, but as well as to have something useful for myself later. I've tried to abstract it away as much as possible from the rest of my rendering code to keep it somewhat portable. I'd like to give a run-down of what I've got working so far, what's on my near-term to-do list and how the GUI is construct and interacts with the rest of the program. I would like whatever feedback you guys have to offer. Features so far: ---------------- Menu Bar - Contains "Groups" that contain "Items". (A "Group" would be something like 'Edit', and an "Item" would be something like 'Copy") - Menu Items can have nested sub-Items. (sub-Items can also have nested sub-Items). Dialog Boxes - Everything not in the Menu Bar is in a Dialog Box and are called Widgets. - Upon construction, the following is defined: * Title * Dimentions (location defaults to center of the screen, but position is saved between open/close) * Default buttons (Any combination of Ok, Cancel, and Apply) - By default, Dialog Boxes are resizable and moveable, but that can be modified. Widgets - Widgets so far that are functional: * Buttons * Check Boxes * Radio Buttons (supports basic data types) * Text Boxes (supports all characters, int only, unsigned int only, or floats only) * Sliders (horizontal only) (supports int or float types) * Groups with support for nested Groups. To-Do List: ----------- - Verticle slider (only the drawing/picking code will be different from the horizontal one) - Spinners - Lists - Drop down lists - Tabs - Make GUI construction read in from a file (perhaps XML-ish?) GUI Construction: ----------------- - Dialog Boxes The Dialog Boxes are constructed first so that the Menu Bar Items have something to open. A source code snippet looks like this:
    int link_id;
    // ----- Global Illumination ----- (Start)
    BeginDialogBox("Global Illumination", 200, 200, DIALOG_OK | DIALOG_CANCEL | DIALOG_APPLY);
    {
        BeginCheckBox("Lighting Enabled");
        {
            SetWidgetLocation(2, 4);
            link_id = RegisterWidget("LightingEnabled");
        }
        EndWidget();

        BeginGroup("Light Color");
        {
            SetWidgetLocation(0, 30);

            BeginLabel("Red");
            {
                SetWidgetLocation(0, 0);
                link_id = RegisterWidget("GlobalLightRedLabel");
            }
            EndWidget();
            BeginHSlider(0.0f, 1.0f, 0.001f);
            {
                SetWidgetLocation(30, 0);
                link_id = RegisterWidget("GlobalLightRed");
            }
            EndWidget();

            BeginLabel("Green");
            {
                SetWidgetLocation(0, 30);
                link_id = RegisterWidget("GlobalLightGreenLabel");
            }
            EndWidget();
            BeginHSlider(0.0f, 1.0f, 0.001f);
            {
                SetWidgetLocation(30, 30);
                link_id = RegisterWidget("GlobalLightGreen");
            }
            EndWidget();

            BeginLabel("Blue");
            {
                SetWidgetLocation(0, 60);
                link_id = RegisterWidget("GlobalLightBlueLabel");
            }
            EndWidget();
            BeginHSlider(0.0f, 1.0f, 0.001f);
            {
                SetWidgetLocation(30, 60);
                link_id = RegisterWidget("GlobalLightBlue");
            }
            EndWidget();
        }
        EndWidget(); // Light Color
    }
    EndWidget(); // Global Illumination
    // ----- Global Illumination ----- (Stop)

    // ----- Light 1 ----- (Start)
    BeginDialogBox("Light 1", 200, 200, DIALOG_OK | DIALOG_CANCEL | DIALOG_APPLY);
    {
        BeginCheckBox("Light Enabled");
        {
            SetWidgetLocation(2, 4);
            link_id = RegisterWidget("Light1Enabled");
        }
        EndWidget();

        BeginRadioGroup("Light Type");
        {
            SetWidgetLocation(0, 20);
            AddRadioButton("Distant", eLightType_Distant);
            AddRadioButton("Point", eLightType_Point);
            AddRadioButton("Spot", eLightType_Spot);

            link_id = RegisterWidget("Light1Type");
        }
        EndWidget();

        BeginGroup("Light Color");
        {
            SetWidgetLocation(0, 100);

            BeginGroup("Ambient Color");
            {
                SetWidgetLocation(0, 0);

                BeginLabel("Red");
                {
                    SetWidgetLocation(0, 0);
                    link_id = RegisterWidget("Light1AmbientRedLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 0);
                    link_id = RegisterWidget("Light1AmbientRed");
                }
                EndWidget();

                BeginLabel("Green");
                {
                    SetWidgetLocation(0, 30);
                    link_id = RegisterWidget("Light1AmbientGreenLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 30);
                    link_id = RegisterWidget("Light1AmbientGreen");
                }
                EndWidget();

                BeginLabel("Blue");
                {
                    SetWidgetLocation(0, 60);
                    link_id = RegisterWidget("Light1AmbientBlueLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 60);
                    link_id = RegisterWidget("Light1AmbientBlue");
                }
                EndWidget();
            }
            EndWidget(); // Ambient Color

            BeginGroup("Diffuse Color");
            {
                SetWidgetLocation(0, 115);

                BeginLabel("Red");
                {
                    SetWidgetLocation(0, 0);
                    link_id = RegisterWidget("Light1DiffuseRedLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 0);
                    link_id = RegisterWidget("Light1DiffuseRed");
                }
                EndWidget();

                BeginLabel("Green");
                {
                    SetWidgetLocation(0, 30);
                    link_id = RegisterWidget("Light1DiffuseGreenLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 30);
                    link_id = RegisterWidget("Light1DiffuseGreen");
                }
                EndWidget();

                BeginLabel("Blue");
                {
                    SetWidgetLocation(0, 60);
                    link_id = RegisterWidget("Light1DiffuseBlueLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 60);
                    link_id = RegisterWidget("Light1DiffuseBlue");
                }
                EndWidget();
            }
            EndWidget(); // Diffuse Color

            BeginGroup("Specular Color");
            {
                SetWidgetLocation(0, 230);

                BeginLabel("Red");
                {
                    SetWidgetLocation(0, 0);
                    link_id = RegisterWidget("Light1SpecularRedLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 0);
                    link_id = RegisterWidget("Light1SpecularRed");
                }
                EndWidget();

                BeginLabel("Green");
                {
                    SetWidgetLocation(0, 30);
                    link_id = RegisterWidget("Light1SpecularGreenLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 30);
                    link_id = RegisterWidget("Light1SpecularGreen");
                }
                EndWidget();

                BeginLabel("Blue");
                {
                    SetWidgetLocation(0, 60);
                    link_id = RegisterWidget("Light1SpecularBlueLabel");
                }
                EndWidget();
                BeginHSlider(0.0f, 1.0f, 0.001f);
                {
                    SetWidgetLocation(30, 60);
                    link_id = RegisterWidget("Light1SpecularBlue");
                }
                EndWidget();
            }
            EndWidget(); // Specular Color
        }
        EndWidget(); // Light Color
    }
    EndWidget(); // Light 1
    // ----- Light 1 ----- (Stop)
This creates two Dialog Boxes for changing the lighting settings. There are several SetWidget*() functions not shown that can be used to modify the status of the current generic Widget. The calls to RegisterWidget() are used to interact with the rest of the program and explained later. - Menu Bar A source code snippet for the Menu Bar might look like this:
    BeginMenuGroup("Lights");
    {
        BeginMenuItem("Global Illumination");
        {
            LinkToDialogBox("Global Illumination");
        }
        EndWidget();

        BeginMenuItem("Light 1");
        {
            LinkToDialogBox("Light 1");
        }
        EndWidget();

        BeginMenuItem("Top Level Item");
        {
            BeginMenuItem("Sub-Item");
            {
            }
            EndWidget();
        }
        EndWidget();
    }
    EndWidget();
Here, the LinkToDialogBox() function makes it so when "Light 1" is selected, the "Light 1" Dialog Box opens. Interaction ----------- When the rest of the program wants to change a value in the GUI, the following code snippet would be used:
    m_pGUI->EngineUpdate("LightingEnabled", m_LightingEnabled);
     m_pGUI->EngineUpdate("Light1AmbientRed", m_Lights[0].m_Ambient[0]);
The GUI links the value passed into the EngineUpdate() function and updates the widget with the registered string name. When the rest of the program wants to get the values from the GUI, the following code snippet would be used:
    GUI_CheckBox* pCheckBox;
    GUI_RadioGroup* pRadioGroup;
    GUI_HSlider* pHSlider;

    // Get the first widget that has been updated.
    GUI_WidgetBase* pWidget = m_pGUI->GetGUIUpdated();

    // While there are widgets that have been updated...
    while (pWidget != NULL)
    {
        // ----- Global Illumination ---- (Start)
        if (pWidget->m_RegistrationName == "LightingEnabled")
        {
            pCheckBox = dynamic_cast<GUI_CheckBox*>(pWidget);
            m_LightingEnabled = pCheckBox->m_Value;
        }
        else if (pWidget->m_RegistrationName == "GlobalLightRed")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_GlobalAmbient[0] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "GlobalLightGreen")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_GlobalAmbient[1] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "GlobalLightBlue")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_GlobalAmbient[2] = pHSlider->m_Value.ValueFloat;
        }
        // ----- Global Illumination ----- (Stop)

        // ----- Light 1 ----- (Start)
        else if (pWidget->m_RegistrationName == "Light1Enabled")
        {
            pCheckBox = dynamic_cast<GUI_CheckBox*>(pWidget);
            m_Lights[0].m_Enabled = pCheckBox->m_Value;
        }
        else if (pWidget->m_RegistrationName == "Light1Type")
        {
            pRadioGroup = dynamic_cast<GUI_RadioGroup*>(pWidget);
            m_Lights[0].SetLightType(static_cast<eLightType>(pRadioGroup->m_Value));
        }
        // Ambient
        else if (pWidget->m_RegistrationName == "Light1AmbientRed")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Ambient[0] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "Light1AmbientGreen")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Ambient[1] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "Light1AmbientBlue")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Ambient[2] = pHSlider->m_Value.ValueFloat;
        }
        // Diffuse
        else if (pWidget->m_RegistrationName == "Light1DiffuseRed")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Diffuse[0] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "Light1DiffuseGreen")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Diffuse[1] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "Light1DiffuseBlue")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Diffuse[2] = pHSlider->m_Value.ValueFloat;
        }
        // Specular
        else if (pWidget->m_RegistrationName == "Light1SpecularRed")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Specular[0] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "Light1SpecularGreen")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Specular[1] = pHSlider->m_Value.ValueFloat;
        }
        else if (pWidget->m_RegistrationName == "Light1SpecularBlue")
        {
            pHSlider = dynamic_cast<GUI_HSlider*>(pWidget);
            m_Lights[0].m_Specular[2] = pHSlider->m_Value.ValueFloat;
        }
        // ----- Light 1 ----- (Stop)
        
        else if (pWidget->m_RegistrationName == "SomethingElse")
        {
        }
        else
        {
            MessageBoxA(NULL, "GUI Update Not Captured By Engine.", "INFO", MB_OK | MB_ICONINFORMATION);        
        }
        
        // Get the next updated widget.
        pWidget = m_pGUI->GetGUIUpdated();
    }
In this case, GetGUIUpdate only returns widgets that have had their values modified. Value modification in the GUI is record when either the Apply button or OK button on a Dialog Box has been pressed. The main feedback I'm looking for is how the GUI interacts with the rest of the program. The only part I really don't like is how the program gets values that have been updated in the GUI. It's a large if/else if list that'll only get larger. But since I've used std::strings to link things together, I'm not sure how else I should do it. Any other comments are suggestions are also welcomed. I could post screen shots if anyone is interested in what it actually looks like. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
Doesn't look bad, but I'd really look at handling the program interface another way. If you are using C++ and not just C, perhaps you could establish a base class for any editable value and use that as the core. Let the classes in the program contain the editable value, and pass the pointer of it too the GUI engine. Then when the GUI widget changes the value, the linked object gets notified and the program knows right away. Just a thought :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!