Contents
This article describes in detail how to create your first GLUI
window with some basic controls inside it, and provides you with a template for your OpenGL applications.
This article can be used in the following ways:
- Learn how to add GUI components to your OpenGL application in a very straight-forward and simple manner through:
- Documentation
- Interactive program that displays to the user how every event is handled and classifies these events into
GLUT
and GLUI
events. - Neat and commented code that reflects the simplicity of the
GLUI
library
- Learn some totally new controls in the
GLUI
library created specifically for graphical manipulation, such as the rotation and translation controls. - Use the code as a template for your OpenGL applications.
Make sure you read the GLUT Window Template article as a prerequisite to this article. One important thing to note is that GLUI
is a C++ library, which means that your code must be written in files with CPP extension rather than C, otherwise the linker will complain.
GLUI
is a C++ GUI library, built entirely on top of GLUT
, to provide GUI controls such as buttons, checkboxes, radio buttons, labels, panels, text fields and spinners to OpenGL applications. It is window-system independent, relying on GLUT
to handle all system-dependent issues, such as window, keyboard, joystick and mouse management. The GLUI
API is very simple, allowing us to create new GLUI
windows and add controls inside them with a single line of code. To add to its simplicity, GLUI
automatically positions and sizes controls when placed on their window.
Here's an example GLUI
window taken from the GLUI Manual, showing the different controls. This window is rendered entirely in OpenGL. It therefore looks the same on PCs, Macs, SGIs, and other UNIXes, using either SGI's implementation of OpenGL, Microsoft's, or Mesa's.
In addition to the above controls, GLUI
has seen some additional controls after its new 2.35 version release in July 2006. You can see below the new scroll bar, text area, file browser, tree, tree panel and list.
When OpenGL applications get more complex, we need something more than a GLUT
mouse, keyboard, and/or popup menus to interact with our OpenGL objects drawn on the window. GLUI
gives us more flexibility by allowing us to add GUI components to interact with our OpenGL objects, such as buttons, check boxes, radio buttons, spinners, list boxes, lists, trees, file browsers, text fields, text areas, and the special controls: rotation and translation.
To avoid having to write the same code every time you want to create an OpenGL graphical application with GUI components, this program code can be used as a template to get you directly started.
In order to run the program, three dynamic link libraries (DLLs) are required: opengl32.dll, glu32.dll, glut32.dll. The opengl32.dll and glu32.dll already come with your Windows OS and are found in the system folder. To run the executable, you have to download the glut32.dll and copy it into your system folder. You can find all the DLL files in the attached zip file GLUI_Window_Template_dll.zip under Visual C++\dll.
In order to write a C++ OpenGL application with GLUI
using Microsoft Visual Studio on a Windows platform, you need the following files:
- Header Files: GL.h, GLU.h, GLUT.h and GLUI.h
- LIB Files: opengl32.lib, glu32.lib, glut32.lib and glui32.lib
You can find all the header files and lib files in the attached zip file GLUI_Window_Template_dll.zip under Visual C++\include\GL and Visual C++\lib respectively.
To use the code under a Visual C++ 6.0 environment, perform the following steps:
- Open Microsoft Visual C++ 6.0
- Create a new Win32 Console Application Project
- Copy the source file GLUI_Window_Template.cpp to your project folder (GLUI_Window_Template_src in our case). You may want to rename the file name to fit your program's needs.
- Add the source file to the project using the menu option Project\Add To Project\Files.
- Make sure that the header files are in the include folder, lib files are in the lib folder and DLLs are in the system folder.
Files | Description | Source Folder (Attached) | Target Folder |
GL.H, GLU.H, GLUT.H, GLUI.H | Header Files | Visual C++/include/GL | C:\Program Files\Microsoft Visual Studio\VC98\Include\GL |
OPENGL32.LIB, GLU32.LIB, GLUT32.LIB, GLUI32.LIB | Lib Files | Visual C++/lib | C:\Program Files\Microsoft Visual Studio\VC98\Lib |
OPENGL32.DLL, GLU32.DLL, GLUT32.DLL | DLL Files | Visual C++/dll | C:\WINDOWS\system32 |
- Link the code to the libraries. The proper order in which to add libraries is:
GLUI
, GLUT
, GLU
, OpenGL
.
To avoid having to set the link settings in every Visual C++ 6.0 project you create, you may want to include the following code segment in your code, which would basically do the same thing as above.
#pragma comment (lib, "glui32.lib")
#pragma comment (lib, "glut32.lib")
#pragma comment (lib, "glu32.lib")
#pragma comment (lib, "opengl32.lib")
- To avoid getting the console window whenever you want to run your OpenGL window, you may want to include this directive in your code.
#pragma comment( linker,
"/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
The source code is intended to be used as a template for your OpenGL applications. To use it in your new application, you can simply rename the CPP file and add it to your Visual Studio project.
Please note that GLUI
is a C++ library, and thus doesn't work with C. So if you're obtaining linkage errors while trying to build a GLUI
program, just check that all file extensions are CPP and not C.
Knowing that GLUI
is built on top of GLUT
, it requires very little change to integrate GLUI
components with an existing GLUT
application. Thus, we will start with the GLUT Window Template and apply changes to it until we create our GLUI
Window Template.
First, we start by including the header file for GLUI
using the following include directive:
#include <GL/glui.h>
There is no need to include <GL/gl.h>
, <GL/glu.h>
, or <GL/glut.h>
since <GL/glui.h>
already includes them.
After creating our GLUT
window, we need to make sure that we keep track of the window id so that GLUI
can interact with it and send it redisplay events.
main_window = glutCreateWindow (window_title);
GLUT
'callback' functions are registered as usual, except for the Idle
function.
glutDisplayFunc (display);
glutReshapeFunc (reshape);
glutMouseFunc (mouse);
glutMotionFunc (motion);
glutPassiveMotionFunc (pmotion);
glutKeyboardFunc (keyboard);
glutSpecialFunc (special);
glutEntryFunc (entry);
An idle
callback function is called whenever an idle
state is reached. An idle
state is a state where no system events are received for processing as callbacks. The glutIdleFunc
sets the idle
callback function, which is continuously called as long as there are no events received. This means that we should minimize the amount of computation and rendering in the idle
function to have a more interactive application. Passing NULL
to glutIdleFunc
disables the generation of the idle
callback.
In a normal GLUT
application, the idle
function is registered as follows:
glutIdleFunc (idle);
However, when GLUI
is used, the idle
callback function is registered through the GLUI
global object, GLUI_Master
. The reason for this is that GLUI
makes extensive use of idle
events. Logically, it does so in order to keep refreshing the GLUI
controls and to listen to user's interactions with them.
GLUI_Master.set_glutIdleFunc (idle);
If there is no Idle
callback to be used, NULL
is passed:
GLUI_Master.set_glutIdleFunc (NULL);
- In our
GLUT
idle
function, we need to explicitly set the current window before posting a redisplay event. Otherwise, the redisplay may be accidentally sent to the GLUI
window rather than the GLUT
window.
void idle ( )
{
glutSetWindow(main_window);
glutPostRedisplay();
}
- It is well known that
GLUI
takes an inordinate amount of the CPU time even when the application is idle. The reason for this is that the GLUI
window with all the controls inside it are being refreshed continuously. In order to avoid having a 100% CPU usage whenever we run a GLUI
window, we need to call the Windows system function, Sleep
, which would help us avoid continuously calling the idle
function.
Even if no idle
function is specified, the GLUI
application would consume most of the CPU time and cause your system to run slowly.
When we call the Sleep
function as shown below, the CPU usage by the GLUI
application reduces to almost 0%.
#include <windows.h>
void idle ()
{
glutSetWindow (main_window);
glutPostRedisplay ();
Sleep (100);
}
The GLUI
window is created as follows:
GLUI * glui_window;
glui_window = GLUI_Master.create_glui ("Options");
Here is the create_glui
function definition, as described in the GLUI manual:
GLUI *GLUI_Master_Object::create_glui(char *name, int flags=0, int x=-1,
int y=-1);
Attribute | Description |
name | Name of new GLUI window |
flags | Initialization flags. No flags are defined in the current version |
x ,y | Initial location of window. Note that no initial size can be specified, because GLUI automatically resizes Windows to fit all controls |
Note that flags
, x
, and y
are optional arguments. If they are not specified, default values will be used. GLUI
provides default values for arguments whenever possible. The function returns a pointer to a new GLUI
window.
In addition to the function that creates the window, GLUI
also supports functions to enable, disable, hide, show, or close a window. Below is the prototype and description of each of these functions, as shown in the GLUI manual:
Name | Prototype | Description |
get_glut_window_id | int GLUI::get_glut_window_id( void ); | Returns the standard GLUT window ID of a GLUI window |
enable , disable | void GLUI::enable( void ); void GLUI::disable( void ); | Enables or disables (grays out) a GLUI window. No controls are active when a GLUI window is disabled |
hide | void GLUI::hide( void ); | Hides a GLUI window or subwindow. A hidden window or subwindow cannot receive any user input |
show | void GLUI::show( void ); | Unhides a previously-hidden GLUI window or subwindow |
close | void GLUI::close( void ); | Cleanly destroys a GLUI window or subwindow |
close_all | void GLUI_Master_Object::close_all( void ); | Cleanly destroys all GLUI windows and subwindows. This function is called by the following example code: GLUI_Master.close_all(); |
GLUI
can associate live variables with most types of controls. Live variables are simply C variables that are automatically updated when the user interacts with a GLUI
control. If the user directly updates a live variable from the code without using the set
method of the control, then we need to call the sync_live ()
function in order to synchronize the control with the value of the live variable.
For example, let's say we have a checkbox associated with a live variable as follows:
int draw = 0;
glui_window->add_checkbox ( "Draw", &draw );
When the program starts, the Draw
checkbox will be un-checked since the value of the draw
live variable is 0
. If the value of draw
was 1
, then the Draw
checkbox would have appeared as checked. Let's say that after the program loads, the user clicks on the checkbox to make it checked as follows:
When that happens, GLUI
automatically updates the value of the draw
live variable to 1
.
Also, let's assume that somewhere in the code, the programmer updates the value of the draw
value back to 0
. Doing this will not affect the control, and thus the control and the live variable will become un-synchronized.
draw = 0;
In order to avoid this, we can call the sync_live()
method of the current window to update the user interface:
glui_window->sync_live();
In case there are multiple windows, we can use the sync_live_all()
function of the GLUI
Master Object to synchronize live variables and controls in all GLUI
windows:
GLUI_Master.sync_live_all();
Otherwise, we should not apply any direct changes to live variables. We can avoid that by directly using the set
method(s) of the control:
draw_checkbox.set_int_val ( 0 );
In addition to having live variables to track the values of controls, GLUI
can also generate callbacks whenever the value of a control changes. When a control is first created, the user may optionally specify the callback function that will be called whenever the control's value changes, along with an integer ID to identify the calling control. This would mean that multiple controls may call the same callback function, and still be identified through their different ID
s passed to the function.
glui_window->add_checkbox ( "Draw", &draw, ID,
callback_func );
Here is the list of supported control types, their GLUI
name, description, accessor functions, live variables and callbacks, as copied from the GLUI Manual:
Control Type | Class Name | Used for... | Set /Get values | Live var? | Callback? |
Panel | GLUI_Panel | grouping controls into boxes | -
| -
| N
|
Column | GLUI_Column | grouping controls into columns | -
| -
| N
|
Rollout | GLUI_Rollout | grouping controls into collapsible boxes | -
| -
| N
|
Button | GLUI_Button | invoking user actions | -
| -
| Y
|
Checkbox | GLUI_Checkbox | handling booleans | get_int_val
set_int_val
| int
| Y
|
Radio Group,
Radio Button | GLUI_RadioGroup , GLUI_RadioButton | handling mutually-exclusive options | get_int_val
set_int_val
| int
| Y
|
Static Text | GLUI_StaticText | plain text labels | set_text
| -
| N
|
Editable Text | GLUI_EditText | text that can be edited and optionally interpreted as integers or floats. Upper and lower bounds can be placed on integers and floats | get_int_val
set_int_val
get_float_val
set_float_val
get_text set_text
| int
float
text
| Y
|
Rotation | GLUI_Rotation | Inputting rotation values via an arcball | get_float_array_val
| float [16]
| Y
|
Translation | GLUI_Translation | Inputting X, Y, and Z values | get_x
get_y
get_z
| float [1] OR [2]
| Y
|
Listbox | GLUI_Listbox | Choosing from a list of items | get_int_val
| int
| Y
|
Spinner | GLUI_Spinner | interactively manipulating numeric values. Supports single clicks, click-hold, and click-drag. Upper and lower bounds can be specified | get_int_val
set_int_val get_float_val
set_float_val
| int
float
| Y
|
Separator | GLUI_Separator | separating controls with simple horizontal lines | -
| -
| N
|
Knowing that GLUI
is implemented through Object Oriented C++, all the GLUI
controls inherit from the base class GLUI_Control
. There are two functions to create a control: add_control ()
and add_control_to_panel ()
, where control
is replaced by the type of the control. For example, we can create a checkbox using any of the following two functions:
This function adds a checkbox at the top level of the window.
GLUI_Checkbox *GLUI::add_checkbox( char *name, int *live_var=NULL, int id=-1,
GLUI_Update_CB callback=NULL);
This function nests the checkbox within a panel.
GLUI_Checkbox *GLUI::add_checkbox_to_panel( GLUI_Panel *panel, char *name,
int *live_var=NULL, int id=-1, GLUI_Update_CB callback=NULL);
In addition to the common method for creating controls, there are common functions that can be used on most of the controls and are all inherited from the GLUI_Control
base class. Here is the list of those methods, copied from the GLUI manual:
Name | Prototype | Description |
set_name | void GLUI_Control::set_name( char *name );
name - New label for control
| Sets the label on a button, checkbox, etc. |
set_w , set_h |
void GLUI_Control::set_w( int new_size );<br />
void GLUI_Control::set_h( int new_size );
new_size - New minimum width or height for control
| Sets new minimum width/height for a control. set_w() is especially useful to increase the size of the editable text area in an editable text control or spinner. |
get , set |
int GLUI_Control::get_int_val( void );<br />
float GLUI_Control::get_float_val( void );<br />
void GLUI_Control::get_float_array_val( float float_array_ptr );<br />
char *GLUI_Control::get_text( void );<br />
void GLUI_Control::set_int_val( int int_val );<br />
void GLUI_Control::set_float_val( float float_val );<br />
void GLUI_Control::set_float_array_val( float *float_array_val );<br />
void GLUI_Control::set_text( char *text); | Gets or sets the value of a control. Refer to the individual control descriptions below to see which values can be read and set for each control. |
disable , enable |
void GLUI_Control::enable( void );<br />
void GLUI_Control::disable( void ); | Disables (grays out) or enables an individual control. A disabled control cannot be activated or used. Disabling a radio group disables all radio buttons within it, and disabling a panel disables all controls within it (including other panels). Enabling behaves similarly. |
set_alignment | void GLUI_Control::set_alignment( int align );
align - New alignment. May be one of GLUI_ALIGN_CENTER , GLUI_ALIGN_RIGHT , or GLUI_ALIGN_LEFT .
| Sets the alignment of a control to left-aligned, right-aligned, or centered. |
After describing live variables, callbacks, list of controls, and common functions, now it's time to see how controls are added and how they would interact with our OpenGL application. Below is the list of GLUI
function prototypes for every control. For more details about each of the following functions, please check the GLUI manual.
Control | add_control \ add_control_to_panel \ Other |
Panels |
GLUI_Panel *GLUI::add_panel( char *name, int type = GLUI_PANEL_EMBOSSED ); GLUI_Panel *GLUI::add_panel_to_panel( GLUI_Panel *panel, char *name, int type = GLUI_PANEL_EMBOSSED );
|
Rollouts |
GLUI_Rollout *GLUI::add_rollout( char *name, int open = true ); GLUI_Rollout *GLUI::add_rollout_to_panel( GLUI_Panel *panel, char *name, int open = true );
|
Columns |
void GLUI::add_column( int draw_bar = true ); void GLUI::add_column_to_panel( GLUI_Panel *panel, int draw_bar = true );
|
Buttons |
GLUI_Button *GLUI::add_button( char *name, int id=-1, GLUI_Update_CB callback=NULL); GLUI_Button *GLUI::add_button_to_panel( GLUI_Panel *panel, char *name, int id=-1, GLUI_Update_CB callback=NULL);
|
Checkboxes |
GLUI_Checkbox *GLUI::add_checkbox( char *name, int *live_var=NULL, int id=-1, GLUI_Update_CB callback=NULL); GLUI_Checkbox *GLUI::add_checkbox( char *name, int *live_var=NULL, int id=-1, GLUI_Update_CB callback=NULL);
|
Radio Buttons |
GLUI_RadioGroup *GLUI::add_radiogroup( int *live_var=NULL, int user_id=-1, GLUI_Update_CB callback=NULL); GLUI_RadioGroup *GLUI::add_radiogroup_to_panel( GLUI_Panel *panel, int *live_var=NULL, int user_id=-1, GLUI_Update_CB callback = NULL ); GLUI_RadioButton *GLUI::add_radiobutton_to_group( GLUI_RadioGroup *group, char* name );
|
Static Text |
GLUI_StaticText *GLUI::add_statictext( char *name ); GLUI_StaticText *GLUI::add_statictext_to_panel( GLUI_Panel *panel, char* name );
|
Editable Text Boxes |
GLUI_EditText *GLUI::add_edittext( char *name, int data_type=GLUI_EDITTEXT_TEXT, void *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); GLUI_EditText *GLUI::add_edittext_to_panel( GLUI_Panel *panel, char *name, int data_type=GLUI_EDITTEXT_TEXT, void *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); void GLUI_EditText::set_int_limits( int low, int high, int limit_type = GLUI_LIMIT_CLAMP ); void GLUI_EditText::set_float_limits( float low, float high, int limit_type = GLUI_LIMIT_CLAMP );
|
Spinners |
GLUI_Spinner *GLUI::add_spinner( char *name, int data_type=GLUI_SPINNER_INT, void *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); GLUI_Spinner *GLUI::add_spinner_to_panel( GLUI_Panel *panel, char *name, int data_type=GLUI_SPINNER_INT, void *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); void GLUI_Spinner::set_int_limits( int low, int high, int limit_type = GLUI_LIMIT_CLAMP ); void GLUI_Spinner::set_float_limits( float low, float high, int limit_type = GLUI_LIMIT_CLAMP ); void GLUI_Spinner::set_speed( float speed );
|
Separators |
void GLUI::add_separator( void ); void GLUI::add_separator_to_panel( GLUI_Panel *panel );
|
Rotation Controls |
GLUI_Rotation *GLUI::add_rotation( char *name, float *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); GLUI_Rotation *GLUI::add_rotation_to_panel( GLUI_Panel *panel, char *name, float *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); void GLUI_Rotation::get_float_array_val( float *array_ptr ); void GLUI_Rotation::set_float_array_val( float *array_ptr ); void GLUI_Rotation::set_spin( float damping_factor ); void GLUI_Rotation::reset( void );
|
Translation Controls |
GLUI_Translation *GLUI::add_translation( char *name, int trans_type, float *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); GLUI_Translation *GLUI::add_translation_to_panel( GLUI_Panel *panel, char *name, int trans_type, float *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); float GLUI_Translation::get_x( void ); float GLUI_Translation::get_y( void ); float GLUI_Translation::get_z( void ); void GLUI_Translation::set_x( float x ); void GLUI_Translation::set_y( float y ); void GLUI_Translation::set_z( float z ); void GLUI_Translation::set_speed( float speed_factor );
|
Listboxes |
GLUI_Listbox *GLUI::add_listbox( char *name, void *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); GLUI_Listbox *GLUI::add_listbox_to_panel( GLUI_Panel *panel, char *name, void *live_var=NULL, int id=-1, GLUI_Update_CB callback = NULL ); int GLUI_Listbox::add_item( int id, char *text ); int GLUI_Listbox::delete_item( int id ); int GLUI_Listbox::delete_item( char *text );
|
Adding Object Properties
- Add the 'Object Properties' Panel to the
GLUI
window. A "panel" is used to group controls together. Panels can be nested, and thus one can say add_panel_to_panel ()
.
GLUI_Panel *op_panel = glui_window->add_panel (
"Object Properties");
- Add the
Draw
checkbox to the panel.
int draw = 1;
glui_window->add_checkbox_to_panel (op_panel, "Draw", &draw );
When the Draw
checkbox is checked or unchecked, it affects whether the object is displayed on the window. This is done by placing the following code segment in the display function:
if (draw)
{
}
- Add the
Wireframe
checkbox.
int wireframe = 1;
glui_window->add_checkbox_to_panel (op_panel, "Wireframe",
&wireframe );
When the Wireframe
checkbox is checked or unchecked, it affects whether the object is displayed in solid or wireframe. This is done by placing the following code segment in the display function:
if (wireframe)
{
drawWireObject ();
}
else
{
drawSolidObject ();
}
- Add a "separator":
glui_window->add_separator_to_panel (op_panel);
- Add the
Color
listbox. The add_listbox_to_panel
takes the following arguments: the panel to which the listbox is to be added, the name of the listbox, the live variable which will be updated with the id of the selected item, the listbox control id that will be passed to the callback function, and the callback function. After creating the listbox, an item is added to it by calling the add_item
function, which takes the item id and item name as arguments. After creating the listbox, we set the default item in the listbox by calling the set_int_val ()
function.
int listbox_item_id = 12;
GLUI_Listbox *color_listbox = glui_window->add_listbox_to_panel (
op_panel, "Color", &listbox_item_id, COLOR_LISTBOX, glui_callback);
color_listbox->add_item (1, "Black");
color_listbox->add_item (2, "Blue");
color_listbox->add_item (3, "Cyan");
color_listbox->add_item (4, "Dark Grey");
color_listbox->add_item (5, "Grey");
color_listbox->add_item (6, "Green");
color_listbox->add_item (7, "Light Grey");
color_listbox->add_item (8, "Magenta");
color_listbox->add_item (9, "Orange");
color_listbox->add_item (10, "Pink");
color_listbox->add_item (11, "Red");
color_listbox->add_item (12, "White");
color_listbox->add_item (13, "Yellow");
color_listbox->set_int_val (12);
Whenever a user selects a color from the listbox, the glui_callback
function is called and the COLOR_LISTBOX
id is passed to it. Based on the item selected, the color array will be filled with the appropriate RGB components. For example, when the user selects the Grey color, the following code is executed in the glui_callback
function:
switch (control_id)
{
case COLOR_LISTBOX:
switch (listbox_item_id)
{
case 5:
color[0] = 128/255.0;
color[1] = 128/255.0;
color[2] = 128/255.0;
break;
}
break;
}
After a GLUI
callback, GLUI
automatically posts a redisplay event, and the display
function is called. In order to reflect the changes in the color of the object, the following code fragment is placed in the display function before the object is drawn:
glColor3fv (color);
drawObject ();
Adding Object Type
Rollout Panel
|
Collapsed Rollout Panel
|
- Add the 'Object Type' rollout to the
GLUI
window. A rollout is a collapsible panel.
GLUI_Rollout *ot_rollout = glui_window->add_rollout (
"Object Type");
- Add a radio group to the 'Object Type' panel. A radio group is a collection of related radio buttons.
int radiogroup_item_id = 0;
GLUI_RadioGroup *ot_group = glui_window->add_radiogroup_to_panel (ot_panel,
&radiogroup_item_id, OBJECTYPE_RADIOGROUP, glui_callback);
- Add the radio buttons representing object types to the group. Radio buttons are used to handle mutually exclusive options, and have no meaning if they are not added to a radio group. Radio buttons are assigned a number in the order in which they are added to the group, beginning with zero. The currently selected button can be determined with
GLUI_RadioGroup::get_int_val()
, or set with GLUI_RadioGroup::set_int_val()
.
glui_window->add_radiobutton_to_group( ot_group, "Cube" );
glui_window->add_radiobutton_to_group( ot_group, "Sphere" );
glui_window->add_radiobutton_to_group( ot_group, "Cone" );
glui_window->add_radiobutton_to_group( ot_group, "Torus" );
glui_window->add_radiobutton_to_group( ot_group, "Dodecahedron" );
glui_window->add_radiobutton_to_group( ot_group, "Octahedron" );
glui_window->add_radiobutton_to_group( ot_group, "Tetrahedron" );
glui_window->add_radiobutton_to_group( ot_group, "Icosahedron" );
glui_window->add_radiobutton_to_group( ot_group, "Teapot" );
Based on the radio button selected and the wireframe
checkbox state, we need to display the right GLUT
geometric shape.
enum GLUT_SHAPES
{
GLUT_WIRE_CUBE = 0,
GLUT_SOLID_CUBE,
GLUT_WIRE_SPHERE,
GLUT_SOLID_SPHERE,
GLUT_WIRE_CONE,
GLUT_SOLID_CONE,
GLUT_WIRE_TORUS,
GLUT_SOLID_TORUS,
GLUT_WIRE_DODECAHEDRON,
GLUT_SOLID_DODECAHEDRON,
GLUT_WIRE_OCTAHEDRON,
GLUT_SOLID_OCTAHEDRON,
GLUT_WIRE_TETRAHEDRON,
GLUT_SOLID_TETRAHEDRON,
GLUT_WIRE_ICOSAHEDRON,
GLUT_SOLID_ICOSAHEDRON,
GLUT_WIRE_TEAPOT,
GLUT_SOLID_TEAPOT
};
int selected_object = (radiogroup_item_id * 2) + 1 - wireframe;
switch (selected_object)
{
case GLUT_WIRE_CUBE:
glutWireCube (0.5);
break;
case GLUT_SOLID_CUBE:
glutSolidCube (0.5);
break;
case GLUT_WIRE_SPHERE:
glutWireSphere (0.5, 50, 50);
break;
case GLUT_SOLID_SPHERE:
glutSolidSphere (0.5, 50, 50);
break;
}
For example, let's say that the wireframe
checkbox is checked and the Teapot
radio button is clicked. Then, the wireframe
live variable would be 1
since the checkbox is checked. The radiogroup_item_id
value would be 8
, which is the id of the Teapot
radio button. Thus, the selected object would be calculated as follows:
selected_object = (8 * 2) + 1 - 1 = 16.
where 16
is the same as GLUT_WIRE_TEAPOT
, and thus the following code segment would be executed to draw the GLUT
wire teapot.
glutWireTeapot (0.5);
For more information about the GLUT
geometric shapes, you may check the GLUT Geometric Shapes article.
Adding Transformation
- Add the transformation panel to the
GLUI
window.
GLUI_Panel *transformation_panel = glui_window->add_panel (
"Transformation");
- Add a panel to the 'Transformation' Panel. This panel is without a label and is used to hold together the translation controls.
GLUI_Panel *transformation_panel1 = glui_window->add_panel_to_panel (
transformation_panel, "");
- Add the XY Translation control to transformation panel 1 above. The translation control allows the user to change the X, Y, or Z position of objects drawn on the window. The rate of change of translation can be varied by holding down SHIFT for fast movement (100 times faster), or CTRL for slow movement (100 times slower). The four types of translation controls are shown below. When using the XY control, movement can be restricted to a single axis (either X or Y) by holding down ALT and clicking on either the horizontal or the vertical arrows.
|
|
|
|
GLUI_TRANSLATION_XY
| GLUI_TRANSLATION_X
| GLUI_TRANSLATION_Y
| GLUI_TRANSLATION_Z
|
In order to add the XY translation control, we execute the following code segment:
float translate_xy[2] = {0, 0};
GLUI_Translation *translation_xy = glui_window->add_translation_to_panel(
transformation_panel1, "Translation XY",
GLUI_TRANSLATION_XY, translate_xy,
TRANSLATION_XY, glui_callback );
translation_xy->set_speed( 0.005 );
The first parameter of the add_translation_to_panel
is the panel to which we want to add the translation control. The second parameter is the name of the translation control. The third parameter represents the type of the translation, which could be any of GLUI_TRANSLATION_XY
, GLUI_TRANSLATION_X
, GLUI_TRANSLATION_Y
, or GLUI_TRANSLATION_Z
. The fourth parameter is the live variable used to track the x, y, or z coordinates being changed. The fifth parameter represents the ID of the control as it gets passed to the callback
function, which is specified in the sixth parameter.
The set_speed
method allows us to adjust the speed of the translation control in response to user mouse movements.
- Add an invisible column to transformation panel 1 in order to place the XY and Z translation controls horizontally.
glui_window->add_column_to_panel (transformation_panel1, false);
- Add the Z "translation control" to transformation panel 1.
float translate_z = 0;
GLUI_Translation *translation_z = glui_window->add_translation_to_panel (
transformation_panel1, "Translation Z",
GLUI_TRANSLATION_Z, &translate_z,
TRANSLATION_Z, glui_callback );
translation_z->set_speed( 0.005 );
In order to have our objects translated whenever the user moves with the translation control, we need to apply the translation to the matrix on top of the model stack before we display our object. This is done using the glTranslate
OpenGL function as follows:
glTranslatef (translate_xy[0], translate_xy[1], -translate_z);
- Create transformation panel 2 and add to the main 'Transformation' Panel.
GLUI_Panel *transformation_panel2 = glui_window->add_panel_to_panel (
transformation_panel, "");
- Add the rotation control to the transformation panel 2.
float rotation_matrix[16] = { 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 };
glui_window->add_rotation_to_panel (transformation_panel2,
"Rotation", rotation_matrix, ROTATION, glui_callback);
The ROTATION
is a constant representing the id of the rotation control, which will be sent to the callback function whenever the user rotates the archball. The rotation_matrix
is the live variable for the rotation control and is automatically updated whenever the archball is rotated. The rotation_matrix
represents the matrix that will cause rotation when multiplied to the current matrix at the top of our model view stack. To cause our object to rotate, we must multiply the rotation_matrix
by the current matrix before our object gets displayed. This is done as follows:
glMultMatrixf (rotation_matrix);
The rotation control can be constrained to horizontal-only movement by holding the CTRL key, or to vertical rotation by holding the ALT key.
- Add a separator:
glui_window->add_separator_to_panel (transformation_panel2);
- Add a
spinner
to allow for scaling our object. The spinner is added to transformation panel 2, and it is named Scale
.
GLUI_Spinner *spinner = glui_window->add_spinner_to_panel (
transformation_panel2, "Scale", GLUI_SPINNER_FLOAT,
&scale, SCALE_SPINNER, glui_callback);
spinner->set_float_limits ( -4.0, 4.0 );
The spinner is an integer or floating point editable textbox with two attached arrows, which increases or decreases the current value of the control. The rate at which the spinner changes can be controlled using the set_speed
function. Also, one can hold SHIFT while initially clicking an arrow to increase the step amount by a factor of 100, or CONTROL to decrease the step amount to 1/100th its usual value. The data type of the spinner can be either GLUI_SPINNER_INT
or GLUI_SPINNER_FLOAT
, and it is passed as the third parameter to the add_spinner_to_panel
method. The live variable could be a float
or int
data type, based on the selected data type of the control. The SCALE_SPINNER
is the ID of the spinner control, which would be passed to the glui_callback
function whenever the user manipulates the spinner control.
We can use the set_float_limits
and set_int_limits
methods to set the upper and lower limits on the integer or float values that an editable text box can accept.
In order for our value in the spinner to affect the scaling of our object, we need to use the OpenGL glScalef
function to apply the scaling on the object before it gets drawn:
glScalef (scale, scale, scale);
Adding Quit Button
Finally, after adding all of the above controls, we add the Quit
button as follows:
glui_window->add_button ("Quit", QUIT_BUTTON, glui_callback);
When the Quit
button is clicked, the glui_callback
function is executed with QUIT_BUTTON
ID as its parameter and causes the program to exit:
case QUIT_BUTTON:
printf ("Quit Button clicked... Exit!\n");
exit (1);
break;
After creating the GLUI
window and adding controls to it, we need to associate it with the "main graphics window" as follows:
glui_window->set_main_gfx_window (main_window);
When a control in the GLUI
window changes value, a redisplay request will be sent to this main graphics window.
Invoke the standard GLUT
main "event loop":
glutMainLoop();
I think this article can significantly help in creating an OpenGL application with graphical user controls, and at the same time, the template can be used to save lots of copy and paste from old projects or the Internet.
In case you find this template useful or have suggestions, please let me know.
25/09/2007
30/08/2007
- Deleted and re-posted at author's request
16/07/2007
- Modified article to meet criteria specified in "A Guide To Writing Articles For Code Project" by Marc Clifton
- Added more controls
- Notes difference between
GLUI
and GLUT
events by placing GLUT
or GLUI
in front of a message at the command prompt - Added references
21/07/2005