这是susu给我的一份关于glut的示例代码,里面涉及到的内容有:用glut来完成菜单管理,文本显示,显示列表,材质,光照,多窗口显示,鼠标事件处理,键盘事件处理,菜单事件处理,窗口创建,缩放,销毁,动画播放,定时器等功能,运行效果如图:
我分成几个部分来对代码进行分析:
1,命令行参数检查
{
int argp;
GLboolean quit = GL_FALSE;
GLboolean error = GL_FALSE;
#define AA argv[argp]
argp = 1 ;
while (argp < argc)
{
if (match(AA, " -help " ))
{
commandLineHelp();
quit = GL_TRUE;
}
else if (match(AA, " -version " ))
{
printf(VERSIONLONG " \n " );
quit = GL_TRUE;
}
else if (match(AA, " -auto " ))
{ // 自动运行
demoMode = GL_TRUE;
}
else if (match(AA, " -scale " ))
{ // 放缩
argp ++ ;
scaleFactor = atof(argv[argp]); // 设置缩放因子
}
else
{ // 出错处理
fprintf(stderr, " Unknown arg: %s\n " , AA);
error = GL_TRUE;
quit = GL_TRUE;
}
argp ++ ;
}
GLboolean match( char * arg, char * t)
{
if (strstr(t, arg))
return GL_TRUE;
else
return GL_FALSE;
}
if (error)
{
commandLineHelp();
exit( 1 );
}
if (quit)
exit( 0 );
}
这里match函数考虑到-help可能被输入为-h等形式,因此用的是strstr而不是strcmp来进行字符串的匹配。
2,窗口的缩放
通过对窗口原点和大小的调整就可以实现窗口的缩放。
{ // 各个窗口的左上角坐标
{ 50 , 150 }, /* win 0 */
{ 450 , 150 }, /* win 1 */
{ 50 , 600 }, /* win 2 */
{ 450 , 600 }, /* win 3 */
{ 10 , 10 }, /* subwin 4 (relative to parent win 0) */
{ 300 , 400 }, /* help win 5 */
{ 850 , 150 }, /* cmap win 6 */
{ 850 , 600 }, /* cmap win 7 */
{ 250 , 450 } /* text win 8 */
};
int size[MAXWIN][ 2 ] =
{ // 各个窗口大小(宽度,高度)
{ 350 , 350 }, /* win 0 */
{ 350 , 350 }, /* win 1 */
{ 350 , 350 }, /* win 2 */
{ 350 , 350 }, /* win 3 */
{ 200 , 200 }, /* subwin 4 */
{ 700 , 300 }, /* help win 5 */
{ 350 , 350 }, /* cmap win 6 */
{ 350 , 350 }, /* cmap win 7 */
{ 800 , 450 } /* text win 8 */
};
void scaleWindows( float scale)
{ // 放缩初始窗口大小和位置
int i;
for (i = 0 ; i < MAXWIN; i ++ )
{
pos[i][ 0 ] = pos[i][ 0 ] * scale; // x坐标
pos[i][ 1 ] = pos[i][ 1 ] * scale; // y坐标
size[i][ 0 ] = size[i][ 0 ] * scale; // 宽度
size[i][ 1 ] = size[i][ 1 ] * scale; // 高度
}
}
3,设置显示模式、
Int型的数组modes用来记录各个模式位的值(0或者1),从而表明窗口是否支持这种模式。displayMode |= glutMode[i];通过这样的按位或运算最终获得窗口的显示模式。
modes[RGBA] = 1 ;
modes[DOUBLEBUFFER] = 1 ;
modes[DEPTH] = 1 ;
setInitDisplayMode()
void setInitDisplayMode( void )
{ // 设置初始显示模式
int i;
displayMode = 0 ;
for (i = 0 ; i < MODES; i ++ ) {
if (modes[i]) {
/* printf("Requesting %s \n", modeNames[i]); */
displayMode |= glutMode[i]; // 进行按位或运行,
}
}
glutInitDisplayMode(displayMode);
createMenu6();
if ( ! glutGet(GLUT_DISPLAY_MODE_POSSIBLE))
warning( " This display mode not supported\n " );
}
4,菜单管理
menu1到menu8这8个int型变量用来保存创建的菜单项,并且menu2到menu8都作为menu1的子菜单加入到menu1中。
void makeMenus(void)
{//创建弹出菜单
/**//* General control / debug */
menu2 = glutCreateMenu(menuFunc);
glutAddMenuEntry("toggle auto demo mode (a)", 312);
glutAddMenuEntry("toggle freezing in menus", 300);
glutAddMenuEntry("toggle text per window (t)", 301);
glutAddMenuEntry("toggle global timer", 302);
glutAddMenuEntry("toggle global animation", 303);
glutAddMenuEntry("toggle per window animation", 304);
glutAddMenuEntry("toggle debug prints (D)", 305);
glutAddMenuEntry("toggle shaded backdrop", 307);
glutAddMenuEntry("toggle passive motion callback", 308);
glutAddMenuEntry("increase line width (l)", 310);
glutAddMenuEntry("decrease line width (L)", 311);
/**//* Shapes */
menu3 = glutCreateMenu(menuFunc);
glutAddMenuEntry("sphere", 200);
glutAddMenuEntry("cube", 201);
glutAddMenuEntry("cone", 202);
glutAddMenuEntry("torus", 203);
glutAddMenuEntry("dodecahedron", 204);
glutAddMenuEntry("octahedron", 205);
glutAddMenuEntry("tetrahedron", 206);
glutAddMenuEntry("icosahedron", 207);
glutAddMenuEntry("teapot", 208);
/**//* Open/close windows */
menu4 = glutCreateMenu(menuFunc);
glutAddMenuEntry("open all windows", 450);
glutAddMenuEntry("close all windows", 451);
glutAddMenuEntry(" ", 9999);
glutAddMenuEntry("create win 0", 400);
glutAddMenuEntry("create win 1", 401);
glutAddMenuEntry("create win 2", 402);
glutAddMenuEntry("create win 3", 403);
glutAddMenuEntry("create sub window", 404);
glutAddMenuEntry("create color index win 6", 406);
glutAddMenuEntry("create color index win 7", 407);
glutAddMenuEntry(" ", 9999);
glutAddMenuEntry("destroy win 0", 410);
glutAddMenuEntry("destroy win 1", 411);
glutAddMenuEntry("destroy win 2", 412);
glutAddMenuEntry("destroy win 3", 413);
glutAddMenuEntry("destroy sub window", 414);
glutAddMenuEntry("destroy color index win 6", 416);
glutAddMenuEntry("destroy color index win 7", 417);
/**//* Window manager stuff */
menu5 = glutCreateMenu(menuFunc);
glutAddMenuEntry("move current win", 430);
glutAddMenuEntry("resize current win", 431);
glutAddMenuEntry("iconify current win", 432);
glutAddMenuEntry("show current win", 433);
glutAddMenuEntry("hide current win", 434);
glutAddMenuEntry("push current win", 435);
glutAddMenuEntry("pop current win", 436);
glutAddMenuEntry(" ", 9999);
glutAddMenuEntry("move win 1", 420);
glutAddMenuEntry("resize win 1", 421);
glutAddMenuEntry("iconify win 1", 422);
glutAddMenuEntry("show win 1", 423);
glutAddMenuEntry("hide win 1", 424);
glutAddMenuEntry("push win 1", 425);
glutAddMenuEntry("pop win 1", 426);
/**//* Gfx modes */
createMenu6(); /**//* build dynamically */
/**//* Texty reports */
menu7 = glutCreateMenu(menuFunc);
glutAddMenuEntry("report current win modes", 700);
glutAddMenuEntry("report current device data", 701);
glutAddMenuEntry("check OpenGL extensions", 702);
glutAddMenuEntry("dump internal data (d)", 703);
/**//* Play with menus */
menu8 = glutCreateMenu(menuFunc);
glutAddMenuEntry("toggle menus on left button", 805);
glutAddMenuEntry("toggle menus on middle button", 806);
glutAddMenuEntry("toggle menus on right button", 807);
glutAddMenuEntry("---------------------------", 9999);
glutAddMenuEntry("add plain items", 800);
glutAddMenuEntry("add submenu items", 801);
glutAddMenuEntry("change new entries to plain items", 802);
glutAddMenuEntry("change new entries to submenus", 803);
glutAddMenuEntry("remove all new items", 804);
glutAddMenuEntry("---------------------------", 9999);
/**//* Main menu */
menu1 = glutCreateMenu(menuFunc);
glutAddSubMenu("control", menu2);
glutAddSubMenu("shapes", menu3);
glutAddSubMenu("windows", menu4);
glutAddSubMenu("window ops", menu5);
glutAddSubMenu("gfx modes", menu6);
glutAddSubMenu("reports", menu7);
glutAddSubMenu("menus", menu8);
glutAddMenuEntry("help (h)", 101);
glutAddMenuEntry("quit (esc)", 100);
}
5,创建窗口
本文中创建的窗口有4种类型,第1种是普通的RGB窗口,用来显示要绘制的图形,第2种是第1种窗口的子窗口(类似于“画中画”的效果),第3种是文本窗口和帮助窗口,第4种是颜色索引窗口。设置好窗口的显示模式,并根据保存的窗口大小和位置创建完窗口后,就可以对窗口进行OpenGL绘制的初始化工作,这是在gfxInit函数中完成的,最后就是为窗口加上各种事件处理函数。
void makeWindow(int index)
{//建立窗口
char str[99];
if (winId[index] != 0)
{//已经存在的窗口
/**//* warning("Attempt to create window which is already
created"); */
return;
}
switch (index)
{
//普通的RGB窗口
case 0: /**//* ordinary RGB windows */
case 1:
case 2:
case 3:
setInitDisplayMode();
glutInitWindowPosition(pos[index][0], pos[index][1]);//窗口左上角位置
glutInitWindowSize(size[index][0], size[index][1]);//窗口大小
winId[index] = glutCreateWindow(" ");//保存窗口标识符
PR("Window %d id = %d \n", index, winId[index]);
gfxInit(index);//初始化openGL
addCallbacks();
sprintf(str, "window %d (RGB)", index);
glutSetWindowTitle(str);
sprintf(str, "icon %d", index);
glutSetIconTitle(str);
glutSetMenu(menu1);
glutAttachMenu(GLUT_RIGHT_BUTTON);
break;
case 4: /**//* subwindow 子窗口*/
setInitDisplayMode();
winId[index] = glutCreateSubWindow(winId[0], pos[index][0], pos[index]
[1], size[index][0], size[index][1]);
PR("Window %d id = %d \n", index, winId[index]);
gfxInit(index);
glutDisplayFunc(drawScene);
glutVisibilityFunc(visible);
glutReshapeFunc(reshapeFunc);
break;
case 5: /**//* help window 帮助窗口*/
case 8: /**//* text window 文本窗口*/
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition(pos[index][0], pos[index][1]);
glutInitWindowSize(size[index][0], size[index][1]);
winId[index] = glutCreateWindow(" ");
PR("Window %d id = %d \n", index, winId[index]);
/**//* addCallbacks(); */
glutKeyboardFunc(keyFunc);
glutSpecialFunc(specialFunc);
glClearColor(0.15, 0.15, 0.15, 1.0);
glColor3f(1, 1, 1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, 300, 0, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (index == 5) {
glutDisplayFunc(updateHelp);
glutSetWindowTitle("help (RGB) win 5");
glutSetIconTitle("help");
} else {
glutDisplayFunc(updateText);
glutSetWindowTitle("text (RGB) win 8");
glutSetIconTitle("text");
}
glutSetMenu(menu1);
glutAttachMenu(GLUT_RIGHT_BUTTON);
break;
case 6: /**//* color index window 颜色索引窗口*/
case 7: /**//* color index window */
glutInitDisplayMode(GLUT_DOUBLE | GLUT_INDEX | GLUT_DEPTH);
glutInitWindowPosition(pos[index][0], pos[index][1]);
glutInitWindowSize(size[index][0], size[index][1]);
winId[index] = glutCreateWindow(" ");
PR("Window %d id = %d \n", index, winId[index]);
gfxInit(index);
addCallbacks();
sprintf(str, "window %d (color index)", index);
glutSetWindowTitle(str);
sprintf(str, "icon %d", index);
glutSetIconTitle(str);
glutSetMenu(menu1);
glutAttachMenu(GLUT_RIGHT_BUTTON);
break;
}
}
为每个窗口初始化OpenGL时,首先通过redefineShapes为窗口建立其显示列表,然后渲染其背景矩阵,接着进行投影变换和视图变换,为了简单起见,作者采用了默认的白色光源来进行材质和光源位置的设置,最后就是启用光照并设置窗口的背景颜色。
void gfxInit(int index)
{//为每个窗口初始化OpenGL
GLfloat grey10[] =
{0.10, 0.10, 0.10, 1.0};
GLfloat grey20[] =
{0.2, 0.2, 0.2, 1.0};
GLfloat black[] =
{0.0, 0.0, 0.0, 0.0};
//光源位置
GLfloat diffuse0[] =
{1.0, 0.0, 0.0, 1.0};
GLfloat diffuse1[] =
{0.0, 1.0, 0.0, 1.0};
GLfloat diffuse2[] =
{1.0, 1.0, 0.0, 1.0};
GLfloat diffuse3[] =
{0.0, 1.0, 1.0, 1.0};
GLfloat diffuse4[] =
{1.0, 0.0, 1.0, 1.0};
#define XX 3
#define YY 3
#define ZZ -2.5
//背景的包围矩阵
float vertex[][3] =
{
{-XX, -YY, ZZ},
{+XX, -YY, ZZ},
{+XX, +YY, ZZ},
{-XX, +YY, ZZ}
};
/* warning: This func mixes RGBA and CMAP calls in an ugly
fashion */
redefineShapes(currentShape); /* set up display lists 建立显示列表 */
glutSetWindow(winId[index]); //设置为当前窗口
/* hack - redefineShapes
changes glut win */
/* Shaded backdrop square (RGB or CMAP) */
//渲染背景矩阵
glNewList(100, GL_COMPILE);
glPushAttrib(GL_LIGHTING);
glDisable(GL_LIGHTING);
glBegin(GL_POLYGON);
glColor4fv(black);
glIndexi(0);
glVertex3fv(vertex[0]);
glColor4fv(grey10);
glIndexi(3);
glVertex3fv(vertex[1]);
glColor4fv(grey20);
glIndexi(4);
glVertex3fv(vertex[2]);
glColor4fv(grey10);
glIndexi(7);
glVertex3fv(vertex[3]);
glEnd();
glPopAttrib();
glIndexi(9);
glEndList();
/* Set proj+view */
//投影变换和视图变换
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(40.0, 1.0, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.);//gluLookAt前三个参数表示了观察点的位置,中间三个参数表示了观察目标的位置,最后三个参数代表从(0,0,0)到 (x,y,z)的直线,它表示了观察者认为的“上”方向
glTranslatef(0.0, 0.0, -1.0);//移入屏幕一个单位
if (index == 6 || index == 7)//颜色索引模式
goto colorindex;
/* Set basic material, lighting for RGB windows */
//材质和光源位置的设置(默认的白色光源)
if (index == 0)
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse0);
else if (index == 1)
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse1);
else if (index == 2)
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse2);
else if (index == 3)
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse3);
else if (index == 4)
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse4);
//启用光源
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
if (index == 4)
glClearColor(0.15, 0.15, 0.15, 1);//设置子窗口的背景颜色
else
glClearColor(0.1, 0.1, 0.1, 1.0);//其他窗口的背景颜色
return;
/* Set GL basics for CMAP windows 6,7 */
//颜色索引模式
colorindex:
glEnable(GL_DEPTH_TEST);
if (glutGet(GLUT_WINDOW_COLORMAP_SIZE) < 16)
warning("Color map size too small for color index window");
/* Try to reuse an existing color map */
if ((index == 6) && (winId[7] != 0))
{
glutCopyColormap(winId[7]);
} else if ((index == 7) && (winId[6] != 0)) {
glutCopyColormap(winId[6]);
} else {
glutSetColor(8, 0.1, 0.1, 0.1);
glutSetColor(9, 1.0, 0.5, 0.0);
glutSetColor(10, 1.0, 0.6, 0.8);
}
glClearIndex(8);
glIndexi(index + 3);
}
void addCallbacks(void)
{//为窗口增加回调函数
glutDisplayFunc(drawScene);
glutVisibilityFunc(visible);
glutReshapeFunc(reshapeFunc);
glutKeyboardFunc(keyFunc);
glutSpecialFunc(specialFunc);
glutMouseFunc(mouseFunc);
glutMotionFunc(motionFunc);
glutEntryFunc(entryFunc);
/* Callbacks for exotic input devices. Must get my dials &
buttons back. */
glutSpaceballMotionFunc(spaceballMotionCB);
glutSpaceballRotateFunc(spaceballRotateCB);
glutSpaceballButtonFunc(spaceballButtonCB);
glutButtonBoxFunc(buttonBoxCB);
glutDialsFunc(dialsCB);
glutTabletMotionFunc(tabletMotionCB);
glutTabletButtonFunc(tabletButtonCB);
}
6,动画效果
我在MFC中是通过设置一个定时器,并且在定时方法中修改旋转角度来刷新屏幕的,从而实现动画旋转的效果,在这里作者把这部分代码放到窗口的空闲事件处理函数中进行。每次执行时角度都进行了变换,并且通知窗口强制其重绘。
void idleFunc( void )
{
int i;
if ( ! leftDown && ! middleDown) // 旋转角度加1
angle += 1 ;
angle = angle % 360 ;
for (i = 0 ; i < MAXWIN; i ++ )
{
if (winId[i] && winVis[i] && ! winFreeze[i])
{
glutSetWindow(winId[i]);
glutPostRedisplay(); // 强制重画
}
}
}
7,自动演示
这里采用了一个小的技巧来实现多个窗口连续创建的自动演示功能。通过执行当前的动作后,为下一个应该接着发生的动作设置一个定时器,从而实现动作之间的接连发送效果。
void autoDemo(int value)
{
static int index = 0;
static int count = 0;
static int restartValue = -2;
if (value == -999)
value = restartValue;
restartValue = value;
#define AUTODELAY2 (unsigned int) (AUTODELAY*0.66)
/* fprintf(stderr, "autoDemo: value %d \n", value); */
if (!demoMode)
return;
if (menu_state == GLUT_MENU_IN_USE)
{
glutTimerFunc(AUTODELAY / 2, autoDemo, value);
return;
}
switch (value)
{
/* Entry point; kill off existing windows. */
case -2:
killAllWindows();
glutTimerFunc(AUTODELAY / 2, autoDemo, 1);
break;
/* Start making windows */
case -1:
makeWindow(0);
glutTimerFunc(AUTODELAY, autoDemo, 0); /* skip case 0
first time */
break;
/* Change shape & backdrop */
case 0:
currentShape = (currentShape + 1) % 9;
redefineShapes(currentShape);
count += 1;
if (count % 2)
backdrop = !backdrop;
glutTimerFunc(AUTODELAY, autoDemo, 1);
break;
/* Keep making windows */
case 1:
makeWindow(1);
glutTimerFunc(AUTODELAY, autoDemo, 2);
break;
case 2:
makeWindow(2);
glutTimerFunc(AUTODELAY, autoDemo, 3);
break;
case 3:
makeWindow(3);
glutTimerFunc(AUTODELAY, autoDemo, 4);
break;
case 4:
makeWindow(4);
glutTimerFunc(AUTODELAY, autoDemo, 5);
break;
case 5:
makeWindow(5);
glutTimerFunc(AUTODELAY * 2, autoDemo, 51);
break;
case 51:
makeWindow(6);
glutTimerFunc(AUTODELAY * 2, autoDemo, 52);
break;
case 52:
makeWindow(7);
glutTimerFunc(AUTODELAY * 2, autoDemo, 53);
break;
/* Kill last 3 windows, leave 4 up. */
case 53:
killWindow(7);
glutTimerFunc(AUTODELAY, autoDemo, 54);
break;
case 54:
killWindow(6);
glutTimerFunc(AUTODELAY, autoDemo, 6);
break;
case 6:
killWindow(5);
glutTimerFunc(AUTODELAY, autoDemo, 7);
break;
case 7:
killWindow(4);
glutTimerFunc(AUTODELAY, autoDemo, 700);
break;
/* Change shape again */
case 700:
currentShape = (currentShape + 1) % 9;
redefineShapes(currentShape);
glutTimerFunc(AUTODELAY, autoDemo, 701);
break;
/* Cycle 4 main windows through various window ops. */
case 701:
positionWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 701 : 702);
break;
case 702:
reshapeWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 702 : 703);
break;
case 703:
iconifyWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 703 : 704);
break;
case 704:
showWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 704 : 705);
break;
case 705:
hideWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 705 : 706);
break;
case 706:
showWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 706 : 707);
break;
case 707:
pushWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 707 : 708);
break;
case 708:
popWindow(index);
index = (index + 1) % 4;
glutTimerFunc(AUTODELAY2, autoDemo, index > 0 ? 708 : 8);
break;
/* Kill all windows */
case 8:
killWindow(3);
glutTimerFunc(AUTODELAY, autoDemo, 9);
break;
case 9:
killWindow(2);
glutTimerFunc(AUTODELAY, autoDemo, 10);
break;
case 10:
killWindow(1);
glutTimerFunc(AUTODELAY, autoDemo, 11);
break;
case 11:
killWindow(0);
glutTimerFunc(AUTODELAY, autoDemo, -1); /* back to start */
break;
}
}
8,图形绘制代码部分
所有窗口都使用drawScene来绘制其图形,只是各个窗口调用的显示列表不同而已,这通过各个窗口的标识符来进行区别,并且通过调用trackBall(APPLY, 0, 0, 0, 0);来绘制鼠标左键控制的结果(旋转后的角度或者平移后的距离),如果窗口还有文本要显示,则调用showText来显示文本信息。
void drawScene(void)
{
int winIndex;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
winIndex = idToIndex(glutGetWindow());
/* printf("drawScene for index %d, id %d\n", winIndex,
glutGetWindow()); */
glPushMatrix();
glLineWidth(lineWidth);
if (backdrop)
glCallList(100);
/* Left button spinning */
trackBall(APPLY, 0, 0, 0, 0);//鼠标左键控制结果的绘制
/* Apply continuous spinning */
glRotatef(angle, 0, 1, 0);//绕y轴旋转
glCallList(winIndex + 1);
glPopMatrix();
if (text[winIndex])
showText();//显示文本
glutSwapBuffers();
}
/* showText - Render some text in the current GLUT window */
void showText(void)
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, 100, 0, 100);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glColor3f(1.0, 1.0, 1.0);
glIndexi(7);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glLineWidth(lineWidth);
textString(1, 1, "GLUT_BITMAP_8_BY_13", GLUT_BITMAP_8_BY_13);
textString(1, 5, "GLUT_BITMAP_9_BY_15", GLUT_BITMAP_9_BY_15);
textString(1, 10, "GLUT_BITMAP_TIMES_ROMAN_10", GLUT_BITMAP_TIMES_ROMAN_10);
textString(1, 15, "GLUT_BITMAP_TIMES_ROMAN_24", GLUT_BITMAP_TIMES_ROMAN_24);
strokeString(1, 25, "GLUT_STROKE_ROMAN", GLUT_STROKE_ROMAN);
strokeString(1, 35, "GLUT_STROKE_MONO_ROMAN", GLUT_STROKE_MONO_ROMAN);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
9.文本显示
这里有两种文本的显示方式,第一种采用位图字体来显示文本,第二种采用Stroke 字体来显示文本。
{ // 显示文本,x,y是起始坐标,msg:要显示的文本,font:显示的字体
glRasterPos2f(x, y); // 定位位图字体
while ( * msg)
{
glutBitmapCharacter(font, * msg);
msg ++ ;
}
}
/* strokeString - Stroke font string */
void strokeString( int x, int y, char * msg, void * font)
{
glPushMatrix();
glTranslatef(x, y, 0 );
glScalef(. 04 , . 04 , . 04 );
while ( * msg)
{
glutStrokeCharacter(font, * msg);
msg ++ ;
}
glPopMatrix();
}
10,显示列表
显示列表由于是已经编译好的代码段,因此可以加快程序的速度。这里每种要绘制的图形(如球,茶壶等)都有实体和虚体两种模式可以选择。
void redefineShapes(int shape)
{
int i;
//根据窗口号来绘制两种不同类型的图形
#define C3 \
switch(i) \
{ \
case 0: \
case 3: \
C1; \
break; \
\
case 1: \
case 2: \
case 4: \
case 6: \
case 7: \
C2; \
break; \
} \
currentShape = shape
for (i = 0; i < MAXWIN; i++)
{//为每个要创建的窗口建立显示列表
if (winId[i])
{
glutSetWindow(winId[i]);
if (glIsList(i + 1))
glDeleteLists(i + 1, 1);//若已经存在显示列表,就删除掉
glNewList(i + 1, GL_COMPILE);//为当前窗口新建显示列表
switch (shape)
{
#undef C1
#define C1 glutSolidSphere(1.5, 10, 10)
#undef C2
#define C2 glutWireSphere(1.5, 10, 10)
case 0:
C3;
break;
#undef C1
#define C1 glutSolidCube(2)
#undef C2
#define C2 glutWireCube(2)
case 1:
C3;
break;
#undef C1
#define C1 glutSolidCone(1.5, 1.75, 10, 10);
#undef C2
#define C2 glutWireCone(1.5, 1.75, 10, 10);
case 2:
C3;
break;
#undef C1
#define C1 glutSolidTorus(0.5, 1.1, 10, 10)
#undef C2
#define C2 glutWireTorus(0.5, 1.1, 10, 10)
case 3:
C3;
break;
#undef C1
#define C1 glScalef(.8, .8, .8);glutSolidDodecahedron()
#undef C2
#define C2 glScalef(.8, .8, .8);glutWireDodecahedron()
case 4:
C3;
break;
#undef C1
#define C1 glScalef(1.5, 1.5, 1.5);glutSolidOctahedron()
#undef C2
#define C2 glScalef(1.5, 1.5, 1.5);glutWireOctahedron()
case 5:
C3;
break;
#undef C1
#define C1 glScalef(1.8, 1.8, 1.8);glutSolidTetrahedron()
#undef C2
#define C2 glScalef(1.8, 1.8, 1.8);glutWireTetrahedron()
case 6:
C3;
break;
#undef C1
#define C1 glScalef(1.5, 1.5, 1.5);glutSolidIcosahedron()
#undef C2
#define C2 glScalef(1.5, 1.5, 1.5);glutWireIcosahedron()
case 7:
C3;
break;
#undef C1
#define C1 glutSolidTeapot(1.5);
#undef C2
#define C2 glutWireTeapot(1.5);
case 8:
C3;
break;
}
glEndList();
}
}
}
11,鼠标事件处理
这里鼠标有三种控制方式,1)左键进行旋转。2)中键进行xy平面的平移。3)左键+中键进行关于Z轴的平移(产生缩放的效果)。鼠标的事件模式这里有RESET,MOUSEBUTTON,APPLY, MOUSEMOTION四种,其中RESET是用来对清空以往的操作,让图形回到原点处。MOUSEBUTTON是鼠标按下激发的,用来记录下鼠标的位置,MOUSEMOTION是鼠标按下后并移动鼠标时,用来计算旋转的角度或者平移的距离,而最终这些变换后产生的效果的绘制是APPLY发生的,在这里进行了实际的旋转和平移动作。
void trackBall(int mode, int button, int state, int x, int y)
{//鼠标控制
static int startMX = 0, startMY = 0; /* initial mouse pos */
static int deltaMX = 0, deltaMY = 0; /* initial mouse pos */
static float steadyXangle = 0.0, steadyYangle = 0.0;//绕x,y轴旋转的角度
static float varXangle = 0.0, varYangle = 0.0;
static float steadyX = 0.0, steadyY = 0.0, steadyZ = 0.0;//;沿坐标轴缩放的比例
static float varX = 0.0, varY = 0.0, varZ = 0.0;
switch (mode)
{
case RESET://重置
steadyXangle = steadyYangle = steadyX = steadyY = steadyZ = 0.0;
break;
case MOUSEBUTTON:
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN && !middleDown)
{//左键按下
STARTROTATE(x, y);//开始旋转,记录起始点坐标
leftDown = GL_TRUE;
}
else if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN &&
middleDown)
{//原来中键已按下且未放开,这时按下左键,则停止移动,开始缩放
STOPPAN(x, y);
STARTZOOM(x, y);
leftDown = GL_TRUE;
}
else if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN &&
!leftDown)
{//中键按下,开始移动
STARTPAN(x, y);
middleDown = GL_TRUE;
}
else if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN &&
leftDown)
{//原来左键已按下且未放开,这时按下中键,则停止旋转,开始缩放
STOPROTATE(x, y);
STARTZOOM(x, y);
middleDown = GL_TRUE;
}
else if (state == GLUT_UP && button == GLUT_LEFT_BUTTON && !middleDown)
{//左键放开且中键未按下,停止旋转
STOPROTATE(x, y);
leftDown = GL_FALSE;
}
else if (state == GLUT_UP && button == GLUT_LEFT_BUTTON && middleDown)
{//原来左中键已按下且未放开,这时放开左键,则停止缩放,开始旋转
STOPZOOM(x, y);
STARTROTATE(x, y);
leftDown = GL_FALSE;
}
else if (state == GLUT_UP && button == GLUT_MIDDLE_BUTTON && !leftDown)
{
STOPPAN(x, y);
middleDown = GL_FALSE;
}
else if (state == GLUT_UP && button == GLUT_MIDDLE_BUTTON && leftDown)
{
STOPZOOM(x, y);
STARTROTATE(x, y);
middleDown = GL_FALSE;
}
break;
case APPLY://结果的绘制
if (leftDown && !middleDown)
{//旋转
glTranslatef(steadyX, steadyY, steadyZ);
glRotatef(varXangle, 0, 1, 0);//绕y轴旋转
glRotatef(varYangle, 1, 0, 0);//绕x轴旋转
}
/* Middle button pan */
else if (middleDown && !leftDown)
{//平移
glTranslatef(varX, varY, steadyZ);//在xy平面内平移
glRotatef(steadyXangle, 0, 1, 0);
glRotatef(steadyYangle, 1, 0, 0);
}
/* Left + middle zoom. */
else if (leftDown && middleDown)
{//缩放(其实不能说缩放,只是让z轴的距离变化,镜头推近推远产生缩放的感觉)
glTranslatef(steadyX, steadyY, varZ);
glRotatef(steadyXangle, 0, 1, 0);
glRotatef(steadyYangle, 1, 0, 0);
}
/* Nothing down. */
else
{
glTranslatef(steadyX, steadyY, steadyZ);
glRotatef(steadyXangle, 0, 1, 0);
glRotatef(steadyYangle, 1, 0, 0);
}
break;
case MOUSEMOTION:
deltaMX = x - startMX;//计算x的偏移量
deltaMY = startMY - y;//y的偏移量
if (leftDown && !middleDown)
{//旋转
varXangle = fixAngle(steadyXangle + deltaMX);//计算旋转后对于y轴的角度
varYangle = fixAngle(steadyYangle + deltaMY);//计算旋转后对于x轴的角度
}
else if (middleDown && !leftDown)
{//移动
varX = steadyX + deltaMX / 100.0;//平移后相对于x轴的距离
varY = steadyY + deltaMY / 100.0;//平移后相对于y轴的距离
}
else if (leftDown && middleDown)
{//缩放
varZ = steadyZ - deltaMY / 50.0;//相对于z轴的缩放比例
}
break;
}
}
最后,代码中还加入了对空间球,图形板,拨号按键盒的事件处理支持。