目录
OpenGL支持交互式输入设备的函数
在OpenGL程序中,交互设备输入由OpenGL Utility Toolkit(GLUT)中的子程序处理,ui那位这些子程序需要与一个窗口系统连接。对每一种设备指定一个程序(回调函数)来处理从该设备发生的输入。这些GLUT命令与其他的GLUT语句一起放置在main程序中。此外,来自基本库和GLU库的函数的组合也可以与GLUT的鼠标函数一起处理拾取输入。
GLU鼠标函数
用以下函数来指定一个鼠标指针在窗口之内并且一个鼠标按钮被按下或松开时调用的函数:
glutMouseFunc(mouseFcn);
这个名为mouseFcn的鼠标回调函数有四个参数:
void mouseFcn(GLint button,GLint action,GLint xMouse,GLint yMouse)
参数button的允许值为GLUT_LEFT_BUTTON、GLUT_MIDDLE_BUTTON和GLUT_RIGHR_BUTTON。
参数action指出哪种按钮行为来触发鼠标激活事件。允许值为GLUT_DOWN、GLUT_UP。取决于需要通过按下还是松开鼠标键来启动一个行为。
调用mouseFcn将返回鼠标在窗口中的位置坐标(xMouse,yMouse)。这是相对于窗口左上角的位置。xMouse是光标到窗口左边界的像素距离,yMouse是光标到窗口上边界的像素距离。
下面给出glutMouseFunc子程序的一个简单例子。在窗口中每次按下鼠标左键时,就在鼠标光标所在位置画一个尺寸为3的红点。
#include <GL/glut.h>
GLsizei winWidth = 400, winHeight = 300;
void Init()
{
glClearColor(0, 0, 1, 1);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 200, 0, 150);
}
void DisplayFcn()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
glPointSize(3);
}
void WinReshapFcn(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, GLdouble(newWidth), 0, GLdouble(newHeight));
winWidth = newWidth;
winHeight = newHeight;
}
void PlotPoint(GLint x, GLint y)
{
glBegin(GL_POINTS);
glVertex2f(x, y);
glEnd();
}
void MousePtPlot(GLint button, GLint action,
GLint xMouse, GLint yMouse)
{
if (button == GLUT_LEFT_BUTTON && action == GLUT_DOWN)
{
PlotPoint(xMouse, winHeight - yMouse);
}
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("MousePlotPoints");
Init();
glutDisplayFunc(DisplayFcn);
glutReshapeFunc(WinReshapFcn);
glutMouseFunc(MousePtPlot);
glutMainLoop();
return 0;
}
下一个例子用鼠标输入来选择直线段的端点位置。选中的直线段首尾相连,展示了交互构造一条折线的过程。
#include <GL/glut.h>
#include <stdlib.h>
GLsizei winWidth = 400, winHeight = 300;
GLint endPtCtr = 0;
class scrPt
{
public:
GLint x, y;
};
void Init()
{
glClearColor(0, 0, 1, 1);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 200, 0, 150);
}
void DisplayFcn()
{
glClear(GL_COLOR_BUFFER_BIT);
}
void WinReshapFcn(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, GLdouble(newWidth), 0, GLdouble(newHeight));
winWidth = newWidth;
winHeight = newHeight;
}
void DrawLineSegement(scrPt endPt1, scrPt endPt2)
{
glBegin(GL_LINES);
glVertex2f(endPt1.x, endPt1.y);
glVertex2f(endPt2.x, endPt2.y);
glEnd();
}
void PolyLine(GLint button, GLint action,
GLint xMouse, GLint yMouse)
{
static scrPt endPt1, endPt2;
if (endPtCtr == 0)
{
if (button == GLUT_LEFT_BUTTON && action == GLUT_DOWN)
{
endPt1.x = xMouse;
endPt1.y = winHeight - yMouse;
endPtCtr = 1;
}
else
{
if (button == GLUT_RIGHT_BUTTON)
{
exit (0);
}
}
}
else
{
if (button == GLUT_LEFT_BUTTON && action == GLUT_DOWN)
{
endPt2.x = xMouse;
endPt2.y = winHeight - yMouse;
DrawLineSegement(endPt1, endPt2);
endPt1 = endPt2;
}
else
{
if (button == GLUT_RIGHT_BUTTON)
{
exit(0);
}
}
}
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("Draw Interactive Polylines");
Init();
glutDisplayFunc(DisplayFcn);
glutReshapeFunc(WinReshapFcn);
glutMouseFunc(PolyLine);
glutMainLoop();
return 0;
}
可以使用的另一个GLUT鼠标子程序是glutMotionFuc(fcnDoSomething);
fcnDoSomething函数有两个参数:
void fcnDoSomething(GLint xMouse, GLint yMouse)
其中(xMouse,yMouse)是当鼠标被移动并且按钮被按下时,鼠标光标相对于窗口左上角的位置。
类似地,当鼠标在窗口内移动而鼠标键并未按下时,也可以执行一些动作:
glutPassiveMotionFunc(fcnDoSomething);
GLUT键盘函数
对于键盘输入,用以下函数指定一个当键盘上的一个键被按下时调用的函数:
glutKeyboardFunc(keyFcn);
被指定的函数有三个参数:
void keyFcn(GLubyte key, GLint xMouse, GLint yMouse)
参数key的取值是一个字符值或者对应的ASCII编码。返回的鼠标光标在窗口内的位置坐标(xMouse,yMouse)是相对于窗口左上角的。当一个指定的按钮被按下时,就可以用鼠标位置来启动某些行为。
下面的代码给出一个简单的用键盘输入的曲线绘制程序。
#include <GL/glut.h>
GLsizei winWidth = 400, winHeight = 300;
void Init()
{
glClearColor(0, 0, 1, 1);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 200, 0, 150);
}
void DisplayFcn()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
glPointSize(3);
}
void WinReshapFcn(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, GLdouble(newWidth), 0, GLdouble(newHeight));
winWidth = newWidth;
winHeight = newHeight;
}
void PlotPoint(GLint x, GLint y)
{
glBegin(GL_POINTS);
glVertex2f(x, y);
glEnd();
}
void CurveDrawing(GLubyte curvePlotKey, GLint xMouse, GLint yMouse)
{
GLint x = xMouse;
GLint y = winHeight - yMouse;
switch (curvePlotKey)
{
case 'c':
PlotPoint(x, y);
break;
default:
break;
}
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("MousePlotPoints");
Init();
glutDisplayFunc(DisplayFcn);
glutReshapeFunc(WinReshapFcn);
glutKeyboardFunc(CurveDrawing);
glutMainLoop();
return 0;
}
可以使用以下命令指定对于功能键、方向键及其他特殊键的处理函数:
glutSpecialFunc(specialKeyFcn);
被指定的函数也有三个参数:
void specialKeyFcn(GLint specialKey,GLint xMouse,GLint yMouse)
specialKey从GLUT_KEY_F1到GLUT_KEY_F12。方向键的符号常量类似GLUT_KEY_UP和、GLUT_KEY_RIGHT。其他特殊键(如翻页、首尾和插入键)用GLUT_KEY_PAGE_DOWN、GLUT_KEY_HOME等指定。"backspace"、“delete”和“escape”键通过glutKeyboardFunc用它们的ASII编码指定分别未8、127和27.
以下代码展示一个同时支持鼠标、键盘和功能键的交互程序。
#include <GL/glut.h>
#include <stdlib.h>
GLsizei winWidth = 400, winHeight = 300;
GLint edgeLength = 10;
void Init()
{
glClearColor(0, 0, 1, 1);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 200, 0, 150);
}
void DisplayFcn()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
}
void WinReshapFcn(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, GLdouble(newWidth), 0, GLdouble(newHeight));
winWidth = newWidth;
winHeight = newHeight;
}
void FillSquare(GLint button, GLint action,
GLint xMouse, GLint yMouse)
{
GLint x1, y1, x2, y2;
if (button == GLUT_LEFT_BUTTON && action == GLUT_DOWN)
{
x1 = xMouse;
y1 = winHeight - yMouse;
x2 = x1 + edgeLength;
y2 = y1 + edgeLength;
glRecti(x1, y1, x2, y2);
}
else
{
if (button == GLUT_RIGHT_BUTTON)
{
exit(0);
}
}
glFlush();
}
void EnlargeSqure(GLubyte sizeFactor, GLint xMouse, GLint yMouse)
{
switch (sizeFactor)
{
case '2':
edgeLength *= 2;
break;
case '3':
edgeLength *= 3;
break;
case '4':
edgeLength *= 4;
break;
default:
break;
}
}
void ReduceSqure(GLint reductionKey, GLint xMouse, GLint yMouse)
{
switch (reductionKey)
{
case GLUT_KEY_F2:
edgeLength /= 2;
break;
case GLUT_KEY_F3:
edgeLength /= 4;
break;
default:
break;
}
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("MousePlotPoints");
Init();
glutDisplayFunc(DisplayFcn);
glutReshapeFunc(WinReshapFcn);
glutMouseFunc(FillSquare);
glutKeyboardFunc(EnlargeSqure);
glutSpecialFunc(ReduceSqure);
glutMainLoop();
return 0;
}
OpenGL的菜单功能
GLUT还包括多种向程序中添加简单的弹出式菜单的函数。使用这些函数可以设置和访问多种菜单和子菜单。GLUT菜单命令和其他GLUT函数一起放置在main程序中。
创建GLUT菜单
以下语句创建一个弹出式菜单:
glutCreateMenu(menuFcn);
menuFcn是当一个菜单项被选中时调用的函数的名字。这个函数有一个整型参数对应于选中项的位置。
void menuFcn(GLint menuItemNumber)
一旦指定了菜单项被选中时调用的函数,列在菜单上的菜单项也必须被指定。使用一系列的语句来设定每个菜单项的名称和位置。通用形式如下:
glutAddMenuEntry(charString, menuItemNumber);
参数charString 设定显示在菜单上的文字;参数menuItemNumber设定该菜单项在菜单中的位置。
例如,下列语句创建一个具有两个菜单项的菜单:
glutCreateMenu(menuFcn);
glutAddMenuEntry("First Menu Item",1);
glutAddMenuEntry("First Menu Item",2);
然后,必须通过以下函数指定来选择菜单项的鼠标键:
glutAttachMenu(button);
以下示例程序展示了如何创建和使用GLUT菜单。
#include <GL/glut.h>
GLsizei winWidth = 400, winHeight = 400;
GLfloat red = 1.0, green = 1.0, blue = 1.0;
GLenum fillMode = GL_SMOOTH;
void Init()
{
glClearColor(0.6, 0.6, 0.6, 1);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 300, 0, 300);
}
void FillOption(GLint selectedOption)
{
switch (selectedOption)
{
case 1:
fillMode = GL_FLAT;
break;
case 2:
fillMode = GL_SMOOTH;
break;
}
glutPostRedisplay();
}
void DisplayTriangle()
{
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(fillMode);
glColor3f(red, green, blue);
glBegin(GL_TRIANGLES);
glVertex2i(280, 20);
glVertex2i(160, 280);
glColor3f(red, 0, 0);
glVertex2i(20, 100);
glEnd();
glFlush();
}
void ReshapFcn(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, GLdouble(newWidth), 0, GLdouble(newHeight));
DisplayTriangle();
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(200, 200);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("Menu Example");
Init();
glutDisplayFunc(DisplayTriangle);
glutCreateMenu(FillOption);
glutAddMenuEntry("Solid-Color Fill", 1);
glutAddMenuEntry("Color-Interpolation Fill", 2);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutReshapeFunc(ReshapFcn);
glutMainLoop();
return 0;
}
创建和管理多个GLUT菜单
当一个菜单被创建时,它被关联到当前的窗口。可以为一个窗口创建多个菜单,也可以为不同的窗口创建不同的菜单。每个菜单在创建时分配到一个整数标识符。菜单标识符按照菜单创建的顺序从1开始编号。glutCreateMenu子程序返回这个标识符。可以用以下语句记录新创建菜单的标识符:
menuID = glutCreateMenu(menuFcn);
最新创建的菜单成为当前窗口的当前菜单。可以用以下命令激活一个菜单成为当前窗口的当前菜单:
glutSetMenu(menuID);
menuID对应的菜单即成为当前菜单。当与此菜单关联的鼠标键在窗口内按下时,此菜单就会弹出。
以下命令用于清除一个菜单:
glutDestroyMenu(menuID);
如果menuID对应的菜单是窗口的当前菜单,那么即使存在其他的菜单,清除当前菜单后该窗口也没有当前菜单。
以下函数用于获得当前窗口的当前菜单的标识符:
currentMenuID = glutGetMenu();
如果当前窗口没有菜单,或者之前的当前菜单被glutDestroyMenu清除了,返回值为0。
创建GLUT子菜单
也可以将一个子菜单关联到一个菜单上。首先,用glutCreateMenu创建子菜单并且列出子菜单项;然后,将子菜单作为一个项添加到主菜单上。下列语句用于将一个子菜单添加到一个主菜单(或其他子菜单)的菜单项列表:
glutAddSubMenu函数也可以用来将子菜单添加到当前菜单。
下列例子展示了创建子菜单的过程。
#include <GL/glut.h>
GLsizei winWidth = 400, winHeight = 400;
GLfloat red = 1.0, green = 1.0, blue = 1.0;
GLenum renderingMode = GL_SMOOTH;
void Init()
{
glClearColor(0.6, 0.6, 0.6, 1);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 300, 0, 300);
}
void MainMenu(GLint renderingOption)
{
switch (renderingOption)
{
case 1:
renderingMode = GL_FLAT;
break;
case 2:
renderingMode = GL_SMOOTH;
break;
}
glutPostRedisplay();
}
void ColorSubMenu(GLint colorOption)
{
switch (colorOption)
{
case 1:
red = 0; green = 0; blue = 1;
break;
case 2:
red = 0; green = 1; blue = 0;
break;
case 3:
red = 1; green = 1; blue = 1;
break;
}
glutPostRedisplay();
}
void DisplayTriangle()
{
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(renderingMode);
glColor3f(red, green, blue);
glBegin(GL_TRIANGLES);
glVertex2i(280, 20);
glVertex2i(160, 280);
glColor3f(1, 0, 0);
glVertex2i(20, 100);
glEnd();
glFlush();
}
void ReshapFcn(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, GLdouble(newWidth), 0, GLdouble(newHeight));
DisplayTriangle();
glFlush();
}
int main(int argc, char* argv[])
{
GLint subMenu;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(200, 200);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("SubMenu Example");
Init();
glutDisplayFunc(DisplayTriangle);
subMenu = glutCreateMenu(ColorSubMenu);
glutAddMenuEntry("Blue", 1);
glutAddMenuEntry("Green", 2);
glutAddMenuEntry("White", 3);
glutCreateMenu(MainMenu);
glutAddMenuEntry("Solid-Color Fill", 1);
glutAddMenuEntry("Color-Interpolation Fill", 2);
glutAddSubMenu("Color", subMenu);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutReshapeFunc(ReshapFcn);
glutMainLoop();
return 0;
}
修改GLUT菜单
如果要改变用来选择菜单的鼠标键,首先要取消与当前鼠标键的关联,然后再关联一个新的键。以下函数用于取消关联:
glutDetachMenu(mouseButton);
参数mouseButton是之前关联到这个菜单的鼠标键(左中右)的GLUT符号常量。取消关联之后使用glutAttachMenu来将菜单与另一个按钮关联起来。
也可以改变一个已有菜单中的某些项。例如,可以用以下函数删除当前菜单中的一个项:
glutRemoveMenuItem(itemNumber);
其中参数itemNumber被赋值为欲删除菜单项的整数标识符。