65.9K
CodeProject 正在变化。 阅读更多。
Home

GLUI 窗口模板

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (32投票s)

2007年8月30日

CPOL

23分钟阅读

viewsIcon

427622

downloadIcon

13291

本文详细介绍了如何创建您的第一个 GLUI 窗口,并在其中添加一些基本控件,并为您的 OpenGL 应用程序提供了一个模板。

Screenshot - GLUI_Window_Template.jpg

目录

引言

本文详细介绍了如何创建您的第一个 GLUI 窗口,并在其中添加一些基本控件,并为您的 OpenGL 应用程序提供了一个模板。

本文可用于以下方面

  • 通过以下方式,学习如何以非常直接和简单的方式将 GUI 组件添加到您的 OpenGL 应用程序中
    • 文档
    • 交互式程序,向用户显示每个事件如何处理,并将这些事件分为 GLUTGLUI 事件。
    • 整洁且带注释的代码,反映了 GLUI 库的简洁性
  • 学习 GLUI 库中专门为图形操作创建的一些全新控件,例如旋转和平移控件。
  • 将代码用作 OpenGL 应用程序的模板。

请务必阅读 GLUT 窗口模板 文章,作为本文的先决条件。需要注意的一点是,GLUI 是一个 C++ 库,这意味着您的代码必须以 CPP 扩展名而不是 C 写入文件,否则链接器会报错。

什么是 GLUI?

GLUI 是一个 C++ GUI 库,完全基于 GLUT 构建,为 OpenGL 应用程序提供按钮、复选框、单选按钮、标签、面板、文本字段和微调器等 GUI 控件。它独立于窗口系统,依赖 GLUT 处理所有系统相关问题,例如窗口、键盘、操纵杆和鼠标管理。GLUI API 非常简单,允许我们使用一行代码创建新的 GLUI 窗口并在其中添加控件。为了增加其简洁性,GLUI 在控件放置到窗口上时会自动定位和调整控件大小。

GLUI 是什么样子?

这是取自 GLUI 手册GLUI 窗口示例,显示了不同的控件。此窗口完全使用 OpenGL 渲染。因此,它在 PC、Mac、SGI 和其他 UNIX 系统上看起来相同,无论使用 SGI 的 OpenGL 实现、Microsoft 的还是 Mesa 的。

GLUI_Window_Template/glui_window_template_1.jpg

除了上述控件,GLUI 在 2006 年 7 月发布的 2.35 新版本之后,还增加了一些额外的控件。您可以在下方看到新的滚动条、文本区域、文件浏览器、树、树面板和列表。

GLUI 3.2 new controls

为什么需要 GLUI 窗口模板?

当 OpenGL 应用程序变得越来越复杂时,我们需要一些比 GLUT 鼠标、键盘和/或弹出菜单更多的东西来与窗口上绘制的 OpenGL 对象进行交互。GLUI 通过允许我们添加 GUI 组件(例如按钮、复选框、单选按钮、微调器、列表框、列表、树、文件浏览器、文本字段、文本区域以及特殊控件:旋转和平移)来与 OpenGL 对象进行交互,从而为我们提供了更大的灵活性。

为了避免每次创建带 GUI 组件的 OpenGL 图形应用程序时都必须编写相同的代码,此程序代码可以用作模板,让您直接开始。

用法

运行程序

为了运行该程序,需要三个动态链接库 (DLL):*opengl32.dll*、*glu32.dll*、*glut32.dll*。*opengl32.dll* 和 *glu32.dll* 已随您的 Windows 操作系统提供,并位于 *system* 文件夹中。要运行可执行文件,您必须下载 *glut32.dll* 并将其复制到您的 *system* 文件夹中。您可以在随附的 zip 文件 *GLUI_Window_Template_dll.zip* 的 *Visual C++\dll* 下找到所有 DLL 文件。

使用 Microsoft Visual C++ 编译代码

要在 Windows 平台上使用 Microsoft Visual Studio 编写带有 GLUI 的 C++ OpenGL 应用程序,您需要以下文件

  • 头文件:*GL.h*、*GLU.h*、*GLUT.h* 和 *GLUI.h*
  • LIB 文件:*opengl32.lib*、*glu32.lib、glut32.lib* 和 *glui32.lib*

您可以在随附的 zip 文件 *GLUI_Window_Template_dll.zip* 的 *Visual C++\include\GL* 和 *Visual C++\lib* 下分别找到所有头文件和 lib 文件。

Microsoft Visual C++ 6

要在 Visual C++ 6.0 环境下使用代码,请执行以下步骤

  • 打开 Microsoft Visual C++ 6.0
  • 创建新的 Win32 控制台应用程序项目

    GLUI_Window_Template/glui_window_template_3.jpg
  • 将源文件 *GLUI_Window_Template.cpp* 复制到您的项目文件夹(在本例中为 *GLUI_Window_Template_src*)。您可能需要重命名文件以适应您的程序需求。
  • 使用菜单选项“项目”\“添加到项目”\“文件”将源文件添加到项目中。

    Add GLUI Source Code
  • 确保头文件在 *include* 文件夹中,lib 文件在 *lib* 文件夹中,DLL 在 *system* 文件夹中。
     
    文件 描述 源文件夹(附件) 目标文件夹
    *GL.H*、*GLU.H*、*GLUT.H*、*GLUI.H* 头文件 Visual C++/include/GL C:\Program Files\Microsoft Visual Studio\VC98\Include\GL
    *OPENGL32.LIB*、*GLU32.LIB*、*GLUT32.LIB*、*GLUI32.LIB* Lib 文件 Visual C++/lib C:\Program Files\Microsoft Visual Studio\VC98\Lib
    *OPENGL32.DLL*、*GLU32.DLL*、*GLUT32.DLL* DLL 文件 Visual C++/dll C:\WINDOWS\system32
  • 将代码链接到库。添加库的正确顺序是:GLUIGLUTGLUOpenGL

    Link to GLUI LIB

    为了避免在您创建的每个 Visual C++ 6.0 项目中设置链接设置,您可能希望在代码中包含以下代码段,这基本上会做与上述相同的事情。

    // Link the lib files to the program. This is not necessary
    // if you have added the lib names using Project/Settings/Link
    #pragma comment (lib, "glui32.lib")
    #pragma comment (lib, "glut32.lib")
    #pragma comment (lib, "glu32.lib")
    #pragma comment (lib, "opengl32.lib")
  • 为了避免在每次运行 OpenGL 窗口时都出现控制台窗口,您可能希望在代码中包含此指令。
     
    #pragma comment( linker,
        "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )

Using the Code

源代码旨在用作您的 OpenGL 应用程序的模板。要在新应用程序中使用它,您可以简单地重命名 CPP 文件并将其添加到您的 Visual Studio 项目中。

注意

请注意,GLUI 是一个 C++ 库,因此不适用于 C。因此,如果您在尝试构建 GLUI 程序时遇到链接错误,只需检查所有文件扩展名是否为 **CPP** 而不是 **C**。

代码解释

已知 GLUI 构建于 GLUT 之上,因此将 GLUI 组件与现有 GLUT 应用程序集成所需的更改非常少。因此,我们将从 GLUT 窗口模板 开始,并对其进行更改,直到我们创建自己的 GLUI 窗口模板。

1- 包含

首先,我们通过使用以下 include 指令包含 GLUI 的头文件来开始

#include <GL/glui.h>

无需包含 <GL/gl.h><GL/glu.h><GL/glut.h>,因为 <GL/glui.h> 已经包含它们。

2- GLUT 窗口

创建 GLUT 窗口后,我们需要确保跟踪窗口 ID,以便 GLUI 可以与它交互并向其发送重新显示事件。

main_window = glutCreateWindow (window_title);

3- 回调函数

GLUT“回调”函数照常注册,除了 Idle 函数。

// Set the callback functions
glutDisplayFunc (display);
glutReshapeFunc  (reshape);
glutMouseFunc (mouse);
glutMotionFunc (motion);
glutPassiveMotionFunc (pmotion);
glutKeyboardFunc (keyboard);
glutSpecialFunc (special);
glutEntryFunc (entry);

4- 空闲回调函数

空闲回调函数在达到空闲状态时被调用。空闲状态是指没有接收到系统事件进行回调处理的状态。glutIdleFunc 设置空闲回调函数,只要没有接收到事件,该函数就会持续调用。这意味着我们应该最小化空闲函数中的计算和渲染量,以获得更具交互性的应用程序。将 NULL 传递给 glutIdleFunc 会禁用空闲回调的生成。

在普通的 GLUT 应用程序中,idle 函数注册如下

glutIdleFunc (idle);

然而,当使用 GLUI 时,空闲回调函数是通过 GLUI 全局对象 GLUI_Master 注册的。这是因为 GLUI 大量使用空闲事件。从逻辑上讲,它这样做是为了不断刷新 GLUI 控件并监听用户与它们的交互。

GLUI_Master.set_glutIdleFunc (idle);

如果没有要使用的 Idle 回调,则传递 NULL

GLUI_Master.set_glutIdleFunc (NULL);

注释

  • 在我们的 GLUT idle 函数中,我们需要在发布重绘事件之前显式设置当前窗口。否则,重绘可能会意外地发送到 GLUI 窗口而不是 GLUT 窗口。
     
    void idle ( )
    {
        glutSetWindow(main_window);
        glutPostRedisplay();
    }
  • 众所周知,GLUI 即使在应用程序空闲时也会占用大量的 CPU 时间。原因是带有所有控件的 GLUI 窗口正在不断刷新。为了避免在运行 GLUI 窗口时 CPU 占用达到 100%,我们需要调用 Windows 系统函数 Sleep,这将有助于我们避免持续调用 idle 函数。

    即使没有指定 idle 函数,GLUI 应用程序也会消耗大部分 CPU 时间并导致您的系统运行缓慢。

    Task Manager CPU - GLUI Idle

    当我们按如下所示调用 Sleep 函数时,GLUI 应用程序的 CPU 使用率几乎降至 0%。

    Task Manager CPU Utilization - GLUI App with Sleep
     
    #include <windows.h>
    
    void idle ()
    {
        glutSetWindow (main_window);
        glutPostRedisplay ();
        Sleep (100);
    }

5- GLUI 窗口

GLUI 窗口的创建如下

//  pointer to a GLUI window
GLUI * glui_window;

//  Create GLUI window
glui_window = GLUI_Master.create_glui ("Options");

以下是 create_glui 函数的定义,如 GLUI 手册 中所述

GLUI *GLUI_Master_Object::create_glui(char *name, int flags=0, int x=-1,
    int y=-1);
Attribute 描述
名称 GLUI 窗口的名称
flags 初始化标志。当前版本未定义任何标志
x,y 窗口的初始位置。请注意,不能指定初始大小,因为 GLUI 会自动调整窗口大小以适应所有控件

请注意,flagsxy 是可选参数。如果未指定,将使用默认值。GLUI 尽可能为参数提供默认值。该函数返回一个指向新 GLUI 窗口的指针。

除了创建窗口的函数外,GLUI 还支持启用、禁用、隐藏、显示或关闭窗口的函数。以下是这些函数的原型和描述,如 GLUI 手册 中所示

名称 原型 描述
get_glut_window_id int GLUI::get_glut_window_id( void ); 返回 GLUI 窗口的标准 GLUT 窗口 ID
enable, disable void GLUI::enable( void );void GLUI::disable( void ); 启用或禁用(变灰)GLUI 窗口。当 GLUI 窗口被禁用时,没有控件处于活动状态
hide void GLUI::hide( void ); 隐藏 GLUI 窗口或子窗口。隐藏的窗口或子窗口无法接收任何用户输入
show void GLUI::show( void ); 取消隐藏之前隐藏的 GLUI 窗口或子窗口
close void GLUI::close( void ); 干净地销毁 GLUI 窗口或子窗口
close_all void GLUI_Master_Object::close_all( void ); 干净地销毁所有 GLUI 窗口和子窗口。此函数由以下示例代码调用:GLUI_Master.close_all();

6- 添加 GUI 控件

动态变量

GLUI 可以将动态变量与大多数类型的控件关联起来。动态变量只是 C 变量,当用户与 GLUI 控件交互时会自动更新。如果用户在不使用控件的 set 方法的情况下直接从代码更新动态变量,那么我们需要调用 sync_live () 函数以使控件与动态变量的值同步。

例如,假设我们有一个与动态变量关联的复选框,如下所示

int draw = 0;
glui_window->add_checkbox ( "Draw", &draw );

程序启动时,“绘制”复选框将未选中,因为 draw 动态变量的值为 0。如果 draw 的值为 1,则“绘制”复选框将显示为选中状态。假设程序加载后,用户单击复选框使其选中,如下所示

GLUI Check Box

发生这种情况时,GLUI 会自动将 draw 动态变量的值更新为 1

此外,假设在代码中的某个位置,程序员将 draw 的值更新回 0。这样做不会影响控件,因此控件和动态变量将不同步。

draw = 0;

为了避免这种情况,我们可以调用当前窗口的 sync_live() 方法来更新用户界面

glui_window->sync_live(); 

如果存在多个窗口,我们可以使用 GLUI 主对象的 sync_live_all() 函数来同步所有 GLUI 窗口中的动态变量和控件

GLUI_Master.sync_live_all();

否则,我们不应直接更改动态变量。我们可以通过直接使用控件的 set 方法来避免这种情况

draw_checkbox.set_int_val ( 0 );

回调

除了通过动态变量跟踪控件的值外,GLUI 还可以在控件值更改时生成回调。当控件首次创建时,用户可以选择指定当控件值更改时将调用的回调函数,以及一个整数 ID 来标识调用控件。这意味着多个控件可以调用相同的回调函数,并且仍然可以通过传递给函数的不同 ID 来识别。

glui_window->add_checkbox ( "Draw", &draw, ID,
    callback_func );

控件列表

以下是支持的控件类型、其 GLUI 名称、描述、访问器函数、动态变量和回调的列表,复制自 GLUI 手册

控件类型 类名 用于... 设置/获取 动态变量? 回调?
Panel GLUI_Panel 将控件分组到框中
-
-
N
Column GLUI_Column 将控件分组到列中
-
-
N
展开面板 GLUI_Rollout 将控件分组到可折叠框中
-
-
N
Button GLUI_Button 调用用户操作
-
-
Y
复选框 GLUI_Checkbox 处理布尔值
get_int_val
set_int_val
int
Y
单选组,
单选按钮
GLUI_RadioGroup, GLUI_RadioButton 处理互斥选项
get_int_val
set_int_val
int
Y
静态文本 GLUI_StaticText 纯文本标签
set_text
-
N
可编辑文本 GLUI_EditText 可编辑的文本,并可选择解释为整数或浮点数。可以对整数和浮点数设置上限和下限

get_int_val
set_int_val
get_float_val
set_float_val
get_text set_text

int
float
文本
Y
旋转 GLUI_Rotation 通过轨迹球输入旋转值
get_float_array_val
float [16]
Y
转换 GLUI_Translation 输入 X、Y 和 Z 值
get_x
get_y
get_z
float [1] OR [2]
Y
列表框 GLUI_Listbox 从项目列表中选择
get_int_val
int
Y
Spinner GLUI_Spinner 交互式操作数值。支持单击、按住和拖动。可以指定上限和下限
get_int_val
set_int_valget_float_val
set_float_val
int
float
Y
分隔符 GLUI_Separator 用简单的水平线分隔控件
-
-
N

常用函数

已知 GLUI 是通过面向对象 C++ 实现的,所有 GLUI 控件都继承自基类 GLUI_Control。有两个函数可以创建控件:add_control ()add_control_to_panel (),其中 control 被控件的类型替换。例如,我们可以使用以下两个函数中的任何一个来创建复选框

此函数在窗口的顶层添加一个复选框。

GLUI_Checkbox *GLUI::add_checkbox( char *name, int *live_var=NULL, int id=-1,
    GLUI_Update_CB callback=NULL);

此函数将复选框嵌套在面板中。

GLUI_Checkbox *GLUI::add_checkbox_to_panel( GLUI_Panel *panel, char *name,
    int *live_var=NULL, int id=-1, GLUI_Update_CB callback=NULL);

除了创建控件的常用方法外,还有一些可以在大多数控件上使用的常用函数,它们都继承自 GLUI_Control 基类。以下是这些方法的列表,复制自 GLUI 手册

名称 原型 描述
set_name

void GLUI_Control::set_name( char *name );

name - 控件的新标签

设置按钮、复选框等的标签。
set_w, set_h

void GLUI_Control::set_w( int new_size );
void GLUI_Control::set_h( int new_size );

new_size - 控件的新最小宽度或高度

设置控件的新最小宽度/高度。set_w() 对于增加可编辑文本控件或微调器中可编辑文本区域的大小特别有用。
get, set int GLUI_Control::get_int_val( void );
float GLUI_Control::get_float_val( void );
void GLUI_Control::get_float_array_val( float float_array_ptr );
char *GLUI_Control::get_text( void );
void GLUI_Control::set_int_val( int int_val );
void GLUI_Control::set_float_val( float float_val );
void GLUI_Control::set_float_array_val( float *float_array_val );
void GLUI_Control::set_text( char *text);
获取或设置控件的值。请参阅下面的各个控件描述,了解每个控件可以读取和设置的值。
disable, enable void GLUI_Control::enable( void );
void GLUI_Control::disable( void );
禁用(变灰)或启用单个控件。禁用的控件无法激活或使用。禁用单选组会禁用其中的所有单选按钮,禁用面板会禁用其中的所有控件(包括其他面板)。启用行为类似。
set_alignment

void GLUI_Control::set_alignment( int align );

align - 新对齐方式。可以是 GLUI_ALIGN_CENTERGLUI_ALIGN_RIGHTGLUI_ALIGN_LEFT 之一。

将控件的对齐方式设置为左对齐、右对齐或居中对齐。

添加控件

在描述了动态变量、回调、控件列表和常用函数之后,现在是时候看看如何添加控件以及它们如何与我们的 OpenGL 应用程序交互了。以下是每个控件的 GLUI 函数原型列表。有关以下每个函数的更多详细信息,请查阅 GLUI 手册

Control 添加控件 \ 添加控件到面板 \ 其他
面板
  • 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 );
展开面板
  • 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 );
按钮
  • 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);
复选框
  • 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);
单选按钮
  • 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 );
静态文本
  • GLUI_StaticText *GLUI::add_statictext( char *name );
  • GLUI_StaticText *GLUI::add_statictext_to_panel( GLUI_Panel *panel, char* name );
可编辑文本框
  • 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 );
微调器
  • 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 );
分隔符
  • void GLUI::add_separator( void );
  • void GLUI::add_separator_to_panel( GLUI_Panel *panel );
旋转控制
  • 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 );
平移控制
  • 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 );
列表框
  • 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 );
添加对象属性

Object Properties GLUI Panel

  • 将“对象属性”面板添加到 GLUI 窗口。 “面板”用于将控件分组。面板可以嵌套,因此可以说 add_panel_to_panel ()
     
    //  Add the 'Object Properties' Panel to the GLUI window
    GLUI_Panel *op_panel = glui_window->add_panel (
        "Object Properties");
  • 将“绘制”复选框添加到面板中。
     
    //  Draw Check Box Live Variable
    int draw = 1;
    
    // Add the Draw Check box to the 'Object Properties' Panel
    glui_window->add_checkbox_to_panel (op_panel, "Draw", &draw );

    当“绘制”复选框被选中或取消选中时,它会影响对象是否显示在窗口上。这是通过将以下代码段放在显示函数中来完成的

    if (draw)
    {
        //  Draw Object...
    }
  • 添加“线框”复选框。
     
    //  Wireframe checkbox Live Variable
    int wireframe = 1;
    
    //  Add the Wireframe checkbox to the 'Object Properties' Panel
    glui_window->add_checkbox_to_panel (op_panel, "Wireframe",
        &wireframe );

    当“线框”复选框被选中或取消选中时,它会影响对象是以实体还是线框显示。这是通过将以下代码段放在显示函数中来完成的

    if (wireframe)
    {
        drawWireObject ();
    }
    else
    {
        drawSolidObject ();
    }
  • 添加一个“分隔符”
     
    //  Add a separator
    glui_window->add_separator_to_panel (op_panel);
  • 添加“颜色”列表框。add_listbox_to_panel 接受以下参数:要添加列表框的面板、列表框的名称、将使用选定项目的 ID 更新的动态变量、将传递给回调函数的列表框控件 ID 以及回调函数。创建列表框后,通过调用 add_item 函数向其添加项目,该函数接受项目 ID 和项目名称作为参数。创建列表框后,我们通过调用 set_int_val () 函数设置列表框中的默认项目。
     
    //  Id of the selected item in the list box (List Box Live Variable)
    int listbox_item_id = 12;
    
    //  Add the Color listbox to the 'Object Properties' Panel
    GLUI_Listbox *color_listbox = glui_window->add_listbox_to_panel (
        op_panel, "Color", &listbox_item_id, COLOR_LISTBOX, glui_callback);
    
    //  Add the items to the listbox
    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");
    
    //  Select the White Color by default
    color_listbox->set_int_val (12);

    每当用户从列表框中选择颜色时,glui_callback 函数都会被调用,并且 COLOR_LISTBOX ID 会被传递给它。根据所选项目,颜色数组将填充适当的 RGB 组件。例如,当用户选择灰色时,会在 glui_callback 函数中执行以下代码

    switch (control_id)
    {
        //  Color Listbox item changed
        case COLOR_LISTBOX:
    
            switch (listbox_item_id)
            {
                //  Select grey color
                case 5:
                    color[0] = 128/255.0;
                    color[1] = 128/255.0;
                    color[2] = 128/255.0;
                    break;
            }
        break;
    }

    GLUI 回调之后,GLUI 会自动发布一个重绘事件,并调用 display 函数。为了反映对象颜色的变化,以下代码片段在绘制对象之前放置在显示函数中

    //  Set the color
    glColor3fv (color);
    
    //  Draw object
    drawObject ();
添加对象类型
Object Type GLUI Rollout Panel
展开面板
Object Type GLUI Rollout Panel Collapsed
折叠的展开面板
  • 将“对象类型”展开面板添加到 GLUI 窗口。展开面板是可折叠的面板。
     
    //  Add the 'Object Type' Rollout Panel to the GLUI window
    GLUI_Rollout *ot_rollout = glui_window->add_rollout (
        "Object Type");
  • 向“对象类型”面板添加一个单选组。单选组是一组相关的单选按钮。
     
    //  Id of the selected radio button
    int radiogroup_item_id = 0;
    
    //  Create radio button group
    GLUI_RadioGroup *ot_group = glui_window->add_radiogroup_to_panel (ot_panel,
        &radiogroup_item_id, OBJECTYPE_RADIOGROUP, glui_callback);
  • 将表示对象类型的单选按钮添加到组中。单选按钮用于处理互斥选项,如果未添加到单选组中则没有意义。单选按钮按添加到组中的顺序分配一个数字,从零开始。当前选定的按钮可以通过 GLUI_RadioGroup::get_int_val() 确定,或者通过 GLUI_RadioGroup::set_int_val() 设置。
     
    //  Add the radio buttons to the radio group
    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" );

    根据选定的单选按钮和 wireframe 复选框状态,我们需要显示正确的 GLUT 几何形状。

    //  The different GLUT shapes
    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
    };
    //  Get the id of the selected object
    int selected_object = (radiogroup_item_id * 2) + 1 - wireframe;
    
    switch (selected_object)
    {
        //  draw glut wire cube
        case GLUT_WIRE_CUBE:
    
            glutWireCube (0.5);
    
            break;
        //  draw glut solid cube
        case GLUT_SOLID_CUBE:
    
            glutSolidCube (0.5);
    
            break;
        //  draw glut wire sphere
        case GLUT_WIRE_SPHERE:
    
            glutWireSphere (0.5, 50, 50);
    
            break;
        //  draw glut solid sphere
        case GLUT_SOLID_SPHERE:
    
            glutSolidSphere (0.5, 50, 50);
    
            break;
        //  and so on...
    }

    例如,假设 wireframe 复选框被选中,并且 Teapot 单选按钮被单击。那么,wireframe 动态变量将是 1,因为复选框被选中。radiogroup_item_id 的值将是 8,这是 Teapot 单选按钮的 ID。因此,所选对象将计算如下

    selected_object = (8 * 2) + 1 - 1 = 16.

    其中 16 等同于 GLUT_WIRE_TEAPOT,因此将执行以下代码段来绘制 GLUT 线框茶壶。

    glutWireTeapot (0.5);

    有关 GLUT 几何形状的更多信息,您可以查阅 GLUT 几何形状 文章。

添加变换

GLUI Transformatoin Panel

  • 将转换面板添加到 GLUI 窗口。
     
    // Add the 'Transformation' Panel to the GLUI window
    GLUI_Panel *transformation_panel = glui_window->add_panel (
        "Transformation");
  • 向“转换”面板添加一个面板。此面板没有标签,用于将平移控件组合在一起。
    //  Create transformation panel 1 that will contain the Translation
    //  controls
    GLUI_Panel *transformation_panel1 = glui_window->add_panel_to_panel (
        transformation_panel, "");
  • 将 XY 平移控件添加到上面的转换面板 1。平移控件允许用户更改窗口上绘制的对象的 X、Y 或 Z 位置。可以通过按住 SHIFT 进行快速移动(快 100 倍)或按住 CTRL 进行慢速移动(慢 100 倍)来改变平移的变化率。下面显示了四种类型的平移控件。使用 XY 控件时,可以通过按住 ALT 并单击水平或垂直箭头将移动限制在单个轴(X 或 Y)。
GLUI Translation Control XY
GLUI Translation Control X
GLUI Translation Control Y
GLUI Translation Control Z
GLUI_TRANSLATION_XY
GLUI_TRANSLATION_X
GLUI_TRANSLATION_Y
GLUI_TRANSLATION_Z

为了添加 XY 平移控件,我们执行以下代码段

//  Translation XY Live Variable
float translate_xy[2] = {0, 0};

//  Add the xy translation control
GLUI_Translation *translation_xy = glui_window->add_translation_to_panel(
    transformation_panel1, "Translation XY",
    GLUI_TRANSLATION_XY, translate_xy,
    TRANSLATION_XY, glui_callback );
//  Set the translation speed
translation_xy->set_speed( 0.005 );

add_translation_to_panel 的第一个参数是要添加平移控件的面板。第二个参数是平移控件的名称。第三个参数表示平移的类型,可以是 GLUI_TRANSLATION_XYGLUI_TRANSLATION_XGLUI_TRANSLATION_YGLUI_TRANSLATION_Z 中的任何一个。第四个参数是用于跟踪正在更改的 x、y 或 z 坐标的动态变量。第五个参数表示控件的 ID,因为它会传递给回调函数,回调函数在第六个参数中指定。

set_speed 方法允许我们根据用户鼠标移动调整平移控件的速度。

  • 在转换面板 1 中添加一个不可见的列,以便水平放置 XY 和 Z 转换控件。
     
    //  Add column, but don't draw it
    glui_window->add_column_to_panel (transformation_panel1, false);
  • 将 *Z* “平移控件”添加到转换面板 1。
     
    //  Translation Z Live Variable
    float translate_z = 0;
    
    //  Add the z translation control
    GLUI_Translation *translation_z = glui_window->add_translation_to_panel (
        transformation_panel1, "Translation Z",
        GLUI_TRANSLATION_Z, &translate_z,
        TRANSLATION_Z, glui_callback );
    //  Set the translation speed
    translation_z->set_speed( 0.005 );

    为了让我们的对象在用户使用平移控件移动时进行平移,我们需要在显示对象之前将平移应用到模型堆栈顶部的矩阵上。这是通过使用 glTranslate OpenGL 函数完成的,如下所示

    //  Apply the translation
    glTranslatef (translate_xy[0], translate_xy[1], -translate_z);
  • 创建转换面板 2 并添加到主“转换”面板。
     
    //  Create transformation panel 2 that will contain the rotation and
    //  spinner controls
    GLUI_Panel *transformation_panel2 = glui_window->add_panel_to_panel (
        transformation_panel, "");
  • 将旋转控件添加到转换面板 2。
     
    //  Rotation Matrix Live Variable Array (Initialize as Identity Matrix)
    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 };
    //  Add the rotation control
    glui_window->add_rotation_to_panel (transformation_panel2,
        "Rotation", rotation_matrix, ROTATION, glui_callback);

    ROTATION 是一个常量,表示旋转控件的 ID,当用户旋转轨迹球时,该 ID 将发送给回调函数。rotation_matrix 是旋转控件的动态变量,当轨迹球旋转时会自动更新。rotation_matrix 表示当乘以我们模型视图堆栈顶部的当前矩阵时,将导致旋转的矩阵。要使我们的对象旋转,我们必须在显示对象之前将 rotation_matrix 乘以当前矩阵。这可以通过以下方式完成

    //  Apply the rotation matrix
    glMultMatrixf (rotation_matrix);

    旋转控制可以通过按住 CTRL 键限制为仅水平移动,或者通过按住 ALT 键限制为垂直旋转。

    GLUI Rotation Control
  • 添加分隔符
     
    //  Add separator
    glui_window->add_separator_to_panel (transformation_panel2);
  • 添加一个微调器以允许缩放我们的对象。微调器添加到转换面板 2,并命名为“缩放”。
     
    //  Add the scale spinner
    GLUI_Spinner *spinner = glui_window->add_spinner_to_panel (
        transformation_panel2, "Scale", GLUI_SPINNER_FLOAT,
        &scale, SCALE_SPINNER, glui_callback);
    //  Set the limits for the spinner
    spinner->set_float_limits ( -4.0, 4.0 );

    微调器是一个整数或浮点型可编辑文本框,带有两个附加箭头,用于增加或减少控件的当前值。微调器改变的速度可以使用 set_speed 函数控制。此外,可以在最初点击箭头时按住 SHIFT 键,以将步长增加 100 倍,或者按住 CONTROL 键,以将步长减小到通常值的 1/100。微调器的数据类型可以是 GLUI_SPINNER_INTGLUI_SPINNER_FLOAT,并作为第三个参数传递给 add_spinner_to_panel 方法。动态变量可以是 floatint 数据类型,具体取决于所选控件的数据类型。SCALE_SPINNER 是微调器控件的 ID,当用户操作微调器控件时,该 ID 将传递给 glui_callback 函数。

    GLUI Spinner

    我们可以使用 set_float_limitsset_int_limits 方法来设置可编辑文本框可以接受的整数或浮点值的上限和下限。

    为了让微调器中的值影响我们对象的缩放,我们需要使用 OpenGL glScalef 函数在绘制对象之前对其应用缩放

    //  Apply the scaling
    glScalef (scale, scale, scale);
添加退出按钮

GLUI Quit Button

最后,在添加完上述所有控件后,我们按如下方式添加“退出”按钮

//  Add the Quit Button
glui_window->add_button ("Quit", QUIT_BUTTON, glui_callback);

当单击“退出”按钮时,glui_callback 函数将以 QUIT_BUTTON ID 作为其参数执行,并导致程序退出

//  Quit Button clicked
case QUIT_BUTTON:
    printf ("Quit Button clicked... Exit!\n");

    exit (1);

break;

7- 合并 GLUT 和 GLUI

创建 GLUI 窗口并向其添加控件后,我们需要将其与“主图形窗口”关联,如下所示

glui_window->set_main_gfx_window (main_window);

GLUI 窗口中的控件值改变时,会向此主图形窗口发送重绘请求。

8- 主事件循环

调用标准 GLUT“事件循环”

glutMainLoop();

结论

我认为这篇文章可以极大地帮助创建带有图形用户控件的 OpenGL 应用程序,同时,该模板可以用来节省从旧项目或互联网上复制粘贴大量代码。

如果您觉得此模板有用或有任何建议,请告诉我。

参考文献

修订历史

25/09/2007

  • 添加了目录

30/08/2007

  • 应作者要求删除并重新发布

16/07/2007

  • 根据 Marc Clifton 在 “Code Project 文章撰写指南” 中指定的标准修改了文章
  • 添加了更多控件
  • 通过在命令行消息前放置 GLUTGLUI 来区分 GLUIGLUT 事件
  • 添加了参考文献

21/07/2005

  • 原文
© . All rights reserved.