GLUI 窗口模板






4.94/5 (32投票s)
本文详细介绍了如何创建您的第一个 GLUI 窗口,并在其中添加一些基本控件,并为您的 OpenGL 应用程序提供了一个模板。
目录
引言
本文详细介绍了如何创建您的第一个 GLUI
窗口,并在其中添加一些基本控件,并为您的 OpenGL 应用程序提供了一个模板。
本文可用于以下方面
- 通过以下方式,学习如何以非常直接和简单的方式将 GUI 组件添加到您的 OpenGL 应用程序中
- 文档
- 交互式程序,向用户显示每个事件如何处理,并将这些事件分为
GLUT
和GLUI
事件。 - 整洁且带注释的代码,反映了
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
在 2006 年 7 月发布的 2.35 新版本之后,还增加了一些额外的控件。您可以在下方看到新的滚动条、文本区域、文件浏览器、树、树面板和列表。
为什么需要 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.cpp* 复制到您的项目文件夹(在本例中为 *GLUI_Window_Template_src*)。您可能需要重命名文件以适应您的程序需求。
- 使用菜单选项“项目”\“添加到项目”\“文件”将源文件添加到项目中。
- 确保头文件在 *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 - 将代码链接到库。添加库的正确顺序是:
GLUI
、GLUT
、GLU
、OpenGL
。
为了避免在您创建的每个 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 时间并导致您的系统运行缓慢。当我们按如下所示调用
Sleep
函数时,GLUI
应用程序的 CPU 使用率几乎降至 0%。
#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 会自动调整窗口大小以适应所有控件 |
请注意,flags
、x
和 y
是可选参数。如果未指定,将使用默认值。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
会自动将 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 |
可编辑的文本,并可选择解释为整数或浮点数。可以对整数和浮点数设置上限和下限 |
|
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_val get_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 |
|
设置按钮、复选框等的标签。 |
set_w , set_h |
|
设置控件的新最小宽度/高度。set_w() 对于增加可编辑文本控件或微调器中可编辑文本区域的大小特别有用。 |
get , set |
int GLUI_Control::get_int_val( void ); |
获取或设置控件的值。请参阅下面的各个控件描述,了解每个控件可以读取和设置的值。 |
disable , enable |
void GLUI_Control::enable( void ); |
禁用(变灰)或启用单个控件。禁用的控件无法激活或使用。禁用单选组会禁用其中的所有单选按钮,禁用面板会禁用其中的所有控件(包括其他面板)。启用行为类似。 |
set_alignment |
|
将控件的对齐方式设置为左对齐、右对齐或居中对齐。 |
添加控件
在描述了动态变量、回调、控件列表和常用函数之后,现在是时候看看如何添加控件以及它们如何与我们的 OpenGL 应用程序交互了。以下是每个控件的 GLUI
函数原型列表。有关以下每个函数的更多详细信息,请查阅 GLUI 手册。
Control | 添加控件 \ 添加控件到面板 \ 其他 |
面板 |
|
展开面板 |
|
Columns |
|
按钮 |
|
复选框 |
|
单选按钮 |
|
静态文本 |
|
可编辑文本框 |
|
微调器 |
|
分隔符 |
|
旋转控制 |
|
平移控制 |
|
列表框 |
|
添加对象属性
- 将“对象属性”面板添加到
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 ();
添加对象类型
![]() 展开面板 |
![]() 折叠的展开面板 |
- 将“对象类型”展开面板添加到
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
窗口。
// 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_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_XY
、GLUI_TRANSLATION_X
、GLUI_TRANSLATION_Y
或 GLUI_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 键限制为垂直旋转。
- 添加分隔符
// 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_INT
或GLUI_SPINNER_FLOAT
,并作为第三个参数传递给add_spinner_to_panel
方法。动态变量可以是float
或int
数据类型,具体取决于所选控件的数据类型。SCALE_SPINNER
是微调器控件的 ID,当用户操作微调器控件时,该 ID 将传递给glui_callback
函数。我们可以使用
set_float_limits
和set_int_limits
方法来设置可编辑文本框可以接受的整数或浮点值的上限和下限。为了让微调器中的值影响我们对象的缩放,我们需要使用 OpenGL
glScalef
函数在绘制对象之前对其应用缩放// Apply the scaling glScalef (scale, scale, scale);
添加退出按钮
最后,在添加完上述所有控件后,我们按如下方式添加“退出”按钮
// 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 文章撰写指南” 中指定的标准修改了文章
- 添加了更多控件
- 通过在命令行消息前放置
GLUT
或GLUI
来区分GLUI
和GLUT
事件 - 添加了参考文献
21/07/2005
- 原文