代码分析

      这是susu给我的一份关于glut的示例代码,里面涉及到的内容有:用glut来完成菜单管理,文本显示,显示列表,材质,光照,多窗口显示,鼠标事件处理,键盘事件处理,菜单事件处理,窗口创建,缩放,销毁,动画播放,定时器等功能,运行效果如图:

200782201.jpg

      我分成几个部分来对代码进行分析:

1,命令行参数检查

void  checkArgs( int  argc,  char   * argv[])
{
  
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,窗口的缩放

通过对窗口原点和大小的调整就可以实现窗口的缩放。

int  pos[MAXWIN][ 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];通过这样的按位或运算最终获得窗口的显示模式。

int  modes[MODES]  = { 0 };
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,菜单管理

menu1menu88int型变量用来保存创建的菜单项,并且menu2menu8都作为menu1的子菜单加入到menu1中。

ContractedBlock.gif ExpandedBlockStart.gif 创建菜单
None.gif
None.gif
void makeMenus(void)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//创建弹出菜单
InBlock.gif

ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* General control / debug */
InBlock.gif
InBlock.gif  menu2 
= glutCreateMenu(menuFunc);
InBlock.gif  glutAddMenuEntry(
"toggle auto demo mode (a)"312);
InBlock.gif  glutAddMenuEntry(
"toggle freezing in menus"300);
InBlock.gif  glutAddMenuEntry(
"toggle text per window (t)"301);
InBlock.gif  glutAddMenuEntry(
"toggle global timer"302);
InBlock.gif  glutAddMenuEntry(
"toggle global animation"303);
InBlock.gif  glutAddMenuEntry(
"toggle per window animation"304);
InBlock.gif  glutAddMenuEntry(
"toggle debug prints (D)"305);
InBlock.gif  glutAddMenuEntry(
"toggle shaded backdrop"307);
InBlock.gif  glutAddMenuEntry(
"toggle passive motion callback"308);
InBlock.gif  glutAddMenuEntry(
"increase line width (l)"310);
InBlock.gif  glutAddMenuEntry(
"decrease line width  (L)"311);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* Shapes */
InBlock.gif
InBlock.gif  menu3 
= glutCreateMenu(menuFunc);
InBlock.gif  glutAddMenuEntry(
"sphere"200);
InBlock.gif  glutAddMenuEntry(
"cube"201);
InBlock.gif  glutAddMenuEntry(
"cone"202);
InBlock.gif  glutAddMenuEntry(
"torus"203);
InBlock.gif  glutAddMenuEntry(
"dodecahedron"204);
InBlock.gif  glutAddMenuEntry(
"octahedron"205);
InBlock.gif  glutAddMenuEntry(
"tetrahedron"206);
InBlock.gif  glutAddMenuEntry(
"icosahedron"207);
InBlock.gif  glutAddMenuEntry(
"teapot"208);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* Open/close windows */
InBlock.gif
InBlock.gif  menu4 
= glutCreateMenu(menuFunc);
InBlock.gif  glutAddMenuEntry(
"open all windows"450);
InBlock.gif  glutAddMenuEntry(
"close all windows"451);
InBlock.gif  glutAddMenuEntry(
" "9999);
InBlock.gif  glutAddMenuEntry(
"create win 0"400);
InBlock.gif  glutAddMenuEntry(
"create win 1"401);
InBlock.gif  glutAddMenuEntry(
"create win 2"402);
InBlock.gif  glutAddMenuEntry(
"create win 3"403);
InBlock.gif  glutAddMenuEntry(
"create sub window"404);
InBlock.gif  glutAddMenuEntry(
"create color index win 6"406);
InBlock.gif  glutAddMenuEntry(
"create color index win 7"407);
InBlock.gif  glutAddMenuEntry(
" "9999);
InBlock.gif  glutAddMenuEntry(
"destroy win 0"410);
InBlock.gif  glutAddMenuEntry(
"destroy win 1"411);
InBlock.gif  glutAddMenuEntry(
"destroy win 2"412);
InBlock.gif  glutAddMenuEntry(
"destroy win 3"413);
InBlock.gif  glutAddMenuEntry(
"destroy sub window"414);
InBlock.gif  glutAddMenuEntry(
"destroy color index win 6"416);
InBlock.gif  glutAddMenuEntry(
"destroy color index win 7"417);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* Window manager stuff */
InBlock.gif
InBlock.gif  menu5 
= glutCreateMenu(menuFunc);
InBlock.gif  glutAddMenuEntry(
"move current win"430);
InBlock.gif  glutAddMenuEntry(
"resize current win"431);
InBlock.gif  glutAddMenuEntry(
"iconify current win"432);
InBlock.gif  glutAddMenuEntry(
"show current win"433);
InBlock.gif  glutAddMenuEntry(
"hide current win"434);
InBlock.gif  glutAddMenuEntry(
"push current win"435);
InBlock.gif  glutAddMenuEntry(
"pop current win"436);
InBlock.gif  glutAddMenuEntry(
" "9999);
InBlock.gif  glutAddMenuEntry(
"move win 1"420);
InBlock.gif  glutAddMenuEntry(
"resize win 1"421);
InBlock.gif  glutAddMenuEntry(
"iconify win 1"422);
InBlock.gif  glutAddMenuEntry(
"show win 1"423);
InBlock.gif  glutAddMenuEntry(
"hide win 1"424);
InBlock.gif  glutAddMenuEntry(
"push win 1"425);
InBlock.gif  glutAddMenuEntry(
"pop win 1"426);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* Gfx modes */
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif  createMenu6();        
/**//* build dynamically  */
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* Texty reports */
InBlock.gif
InBlock.gif  menu7 
= glutCreateMenu(menuFunc);
InBlock.gif  glutAddMenuEntry(
"report current win modes"700);
InBlock.gif  glutAddMenuEntry(
"report current device data"701);
InBlock.gif  glutAddMenuEntry(
"check OpenGL extensions"702);
InBlock.gif  glutAddMenuEntry(
"dump internal data (d)"703);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* Play with menus */
InBlock.gif
InBlock.gif  menu8 
= glutCreateMenu(menuFunc);
InBlock.gif  glutAddMenuEntry(
"toggle menus on left button"805);
InBlock.gif  glutAddMenuEntry(
"toggle menus on middle button"806);
InBlock.gif  glutAddMenuEntry(
"toggle menus on right button"807);
InBlock.gif  glutAddMenuEntry(
"---------------------------"9999);
InBlock.gif  glutAddMenuEntry(
"add plain items"800);
InBlock.gif  glutAddMenuEntry(
"add submenu items"801);
InBlock.gif  glutAddMenuEntry(
"change new entries to plain items"802);
InBlock.gif  glutAddMenuEntry(
"change new entries to submenus"803);
InBlock.gif  glutAddMenuEntry(
"remove all new items"804);
InBlock.gif  glutAddMenuEntry(
"---------------------------"9999);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//* Main menu */
InBlock.gif
InBlock.gif  menu1 
= glutCreateMenu(menuFunc);
InBlock.gif  glutAddSubMenu(
"control", menu2);
InBlock.gif  glutAddSubMenu(
"shapes", menu3);
InBlock.gif  glutAddSubMenu(
"windows", menu4);
InBlock.gif  glutAddSubMenu(
"window ops", menu5);
InBlock.gif  glutAddSubMenu(
"gfx modes", menu6);
InBlock.gif  glutAddSubMenu(
"reports", menu7);
InBlock.gif  glutAddSubMenu(
"menus", menu8);
InBlock.gif  glutAddMenuEntry(
"help (h)"101);
InBlock.gif  glutAddMenuEntry(
"quit (esc)"100);
ExpandedBlockEnd.gif}

5,创建窗口

本文中创建的窗口有4种类型,第1种是普通的RGB窗口,用来显示要绘制的图形,第2种是第1种窗口的子窗口(类似于画中画的效果),第3种是文本窗口和帮助窗口,第4种是颜色索引窗口。设置好窗口的显示模式,并根据保存的窗口大小和位置创建完窗口后,就可以对窗口进行OpenGL绘制的初始化工作,这是在gfxInit函数中完成的,最后就是为窗口加上各种事件处理函数。

ContractedBlock.gif ExpandedBlockStart.gif 创建窗口
None.gifvoid makeWindow(int index)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//建立窗口
InBlock.gif
  char str[99];
InBlock.gif
InBlock.gif  
if (winId[index] != 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{//已经存在的窗口
ExpandedSubBlockStart.gifContractedSubBlock.gif
    /**//* warning("Attempt to create window which is already
ExpandedSubBlockEnd.gif       created");  
*/

InBlock.gif    
return;
ExpandedSubBlockEnd.gif  }

InBlock.gif  
switch (index) 
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif
//普通的RGB窗口
ExpandedSubBlockStart.gifContractedSubBlock.gif
  case 0:              /**//* ordinary RGB windows  */
InBlock.gif  
case 1:
InBlock.gif  
case 2:
InBlock.gif  
case 3:
InBlock.gif
InBlock.gif    setInitDisplayMode();
InBlock.gif    glutInitWindowPosition(pos[index][
0], pos[index][1]);//窗口左上角位置
InBlock.gif
    glutInitWindowSize(size[index][0], size[index][1]);//窗口大小
InBlock.gif
    winId[index] = glutCreateWindow(" ");//保存窗口标识符
InBlock.gif
    PR("Window %d id = %d \n", index, winId[index]);
InBlock.gif    gfxInit(index);
//初始化openGL
InBlock.gif

InBlock.gif    addCallbacks();
InBlock.gif
InBlock.gif    sprintf(str, 
"window %d (RGB)", index);
InBlock.gif    glutSetWindowTitle(str);
InBlock.gif    sprintf(str, 
"icon %d", index);
InBlock.gif    glutSetIconTitle(str);
InBlock.gif    glutSetMenu(menu1);
InBlock.gif    glutAttachMenu(GLUT_RIGHT_BUTTON);
InBlock.gif    
break;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif  
case 4:              /**//* subwindow  子窗口*/
InBlock.gif
InBlock.gif    setInitDisplayMode();
InBlock.gif    winId[index] 
= glutCreateSubWindow(winId[0], pos[index][0], pos[index]
InBlock.gif      [
1], size[index][0], size[index][1]);
InBlock.gif    PR(
"Window %d id = %d \n", index, winId[index]);
InBlock.gif    gfxInit(index);
InBlock.gif    glutDisplayFunc(drawScene);
InBlock.gif    glutVisibilityFunc(visible);
InBlock.gif    glutReshapeFunc(reshapeFunc);
InBlock.gif
InBlock.gif    
break;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif  
case 5:              /**//* help window  帮助窗口*/
ExpandedSubBlockStart.gifContractedSubBlock.gif  
case 8:              /**//* text window  文本窗口*/
InBlock.gif    glutInitDisplayMode(GLUT_DOUBLE 
| GLUT_RGB | GLUT_DEPTH);
InBlock.gif    glutInitWindowPosition(pos[index][
0], pos[index][1]);
InBlock.gif    glutInitWindowSize(size[index][
0], size[index][1]);
InBlock.gif    winId[index] 
= glutCreateWindow(" ");
InBlock.gif    PR(
"Window %d id = %d \n", index, winId[index]);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//* addCallbacks();  */
InBlock.gif    glutKeyboardFunc(keyFunc);
InBlock.gif    glutSpecialFunc(specialFunc);
InBlock.gif
InBlock.gif    glClearColor(
0.150.150.151.0);
InBlock.gif    glColor3f(
111);
InBlock.gif    glMatrixMode(GL_PROJECTION);
InBlock.gif    glLoadIdentity();
InBlock.gif    gluOrtho2D(
03000100);
InBlock.gif    glMatrixMode(GL_MODELVIEW);
InBlock.gif    glLoadIdentity();
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
if (index == 5dot.gif{
InBlock.gif      glutDisplayFunc(updateHelp);
InBlock.gif      glutSetWindowTitle(
"help (RGB) win 5");
InBlock.gif      glutSetIconTitle(
"help");
ExpandedSubBlockStart.gifContractedSubBlock.gif    }
 else dot.gif{
InBlock.gif      glutDisplayFunc(updateText);
InBlock.gif      glutSetWindowTitle(
"text (RGB) win 8");
InBlock.gif      glutSetIconTitle(
"text");
ExpandedSubBlockEnd.gif    }

InBlock.gif    glutSetMenu(menu1);
InBlock.gif    glutAttachMenu(GLUT_RIGHT_BUTTON);
InBlock.gif    
break;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif  
case 6:              /**//* color index window  颜色索引窗口*/
ExpandedSubBlockStart.gifContractedSubBlock.gif  
case 7:              /**//* color index window  */
InBlock.gif
InBlock.gif    glutInitDisplayMode(GLUT_DOUBLE 
| GLUT_INDEX | GLUT_DEPTH);
InBlock.gif    glutInitWindowPosition(pos[index][
0], pos[index][1]);
InBlock.gif    glutInitWindowSize(size[index][
0], size[index][1]);
InBlock.gif    winId[index] 
= glutCreateWindow(" ");
InBlock.gif    PR(
"Window %d id = %d \n", index, winId[index]);
InBlock.gif
InBlock.gif    gfxInit(index);
InBlock.gif
InBlock.gif    addCallbacks();
InBlock.gif
InBlock.gif    sprintf(str, 
"window %d (color index)", index);
InBlock.gif    glutSetWindowTitle(str);
InBlock.gif    sprintf(str, 
"icon %d", index);
InBlock.gif    glutSetIconTitle(str);
InBlock.gif    glutSetMenu(menu1);
InBlock.gif    glutAttachMenu(GLUT_RIGHT_BUTTON);
InBlock.gif    
break;
InBlock.gif
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

为每个窗口初始化OpenGL时,首先通过redefineShapes为窗口建立其显示列表,然后渲染其背景矩阵,接着进行投影变换和视图变换,为了简单起见,作者采用了默认的白色光源来进行材质和光源位置的设置,最后就是启用光照并设置窗口的背景颜色。

ContractedBlock.gif ExpandedBlockStart.gif OpenGL初始化
void gfxInit(int index)
{
//为每个窗口初始化OpenGL

  GLfloat grey10[] 
=
  {
0.100.100.101.0};
  GLfloat grey20[] 
=
  {
0.20.20.21.0};

  GLfloat black[] 
=
  {
0.00.00.00.0};

  
//光源位置
  GLfloat diffuse0[] =
  {
1.00.00.01.0};
  GLfloat diffuse1[] 
=
  {
0.01.00.01.0};
  GLfloat diffuse2[] 
=
  {
1.01.00.01.0};
  GLfloat diffuse3[] 
=
  {
0.01.01.01.0};
  GLfloat diffuse4[] 
=
  {
1.00.01.01.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.01.01.020.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(
0.00.05.00.00.00.00.01.00.);//gluLookAt前三个参数表示了观察点的位置,中间三个参数表示了观察目标的位置,最后三个参数代表从(0,0,0)到 (x,y,z)的直线,它表示了观察者认为的“上”方向
  glTranslatef(0.00.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.150.150.151);//设置子窗口的背景颜色
  else
    glClearColor(
0.10.10.11.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(
80.10.10.1);
    glutSetColor(
91.00.50.0);
    glutSetColor(
101.00.60.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中是通过设置一个定时器,并且在定时方法中修改旋转角度来刷新屏幕的,从而实现动画旋转的效果,在这里作者把这部分代码放到窗口的空闲事件处理函数中进行。每次执行时角度都进行了变换,并且通知窗口强制其重绘。

/*  idleFunc - GLUT idle func callback - animates windows  */
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,自动演示

这里采用了一个小的技巧来实现多个窗口连续创建的自动演示功能。通过执行当前的动作后,为下一个应该接着发生的动作设置一个定时器,从而实现动作之间的接连发送效果。

ContractedBlock.gif ExpandedBlockStart.gif 旋转动画
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来显示文本信息。

ContractedBlock.gif ExpandedBlockStart.gif 图形绘制代码
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, 
0000);//鼠标左键控制结果的绘制 
  /* Apply continuous spinning  */
  glRotatef(angle, 
010);//绕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(
01000100);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glColor3f(
1.01.01.0);
  glIndexi(
7);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_LIGHTING);
  glLineWidth(lineWidth);
  textString(
11"GLUT_BITMAP_8_BY_13", GLUT_BITMAP_8_BY_13);
  textString(
15"GLUT_BITMAP_9_BY_15", GLUT_BITMAP_9_BY_15);
  textString(
110"GLUT_BITMAP_TIMES_ROMAN_10", GLUT_BITMAP_TIMES_ROMAN_10);
  textString(
115"GLUT_BITMAP_TIMES_ROMAN_24", GLUT_BITMAP_TIMES_ROMAN_24);

  strokeString(
125"GLUT_STROKE_ROMAN", GLUT_STROKE_ROMAN);
  strokeString(
135"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 字体来显示文本。

void  textString( int  x,  int  y,  char   * msg,  void   * font)
{
// 显示文本,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,显示列表

显示列表由于是已经编译好的代码段,因此可以加快程序的速度。这里每种要绘制的图形(如球,茶壶等)都有实体和虚体两种模式可以选择。

ContractedBlock.gif ExpandedBlockStart.gif 显示列表
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 
+ 11);//若已经存在显示列表,就删除掉
      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轴的平移(产生缩放的效果)。鼠标的事件模式这里有RESETMOUSEBUTTONAPPLY MOUSEMOTION四种,其中RESET是用来对清空以往的操作,让图形回到原点处。MOUSEBUTTON是鼠标按下激发的,用来记录下鼠标的位置,MOUSEMOTION是鼠标按下后并移动鼠标时,用来计算旋转的角度或者平移的距离,而最终这些变换后产生的效果的绘制是APPLY发生的,在这里进行了实际的旋转和平移动作。

ContractedBlock.gif ExpandedBlockStart.gif 鼠标控制
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, 
010);//绕y轴旋转
      glRotatef(varYangle, 100);//绕x轴旋转
    }
    
/* Middle button pan  */
    
else if (middleDown && !leftDown) 
    {
//平移
      glTranslatef(varX, varY, steadyZ);//在xy平面内平移
      glRotatef(steadyXangle, 010);
      glRotatef(steadyYangle, 
100);
    }
    
/* Left + middle zoom.  */
    
else if (leftDown && middleDown) 
    {
//缩放(其实不能说缩放,只是让z轴的距离变化,镜头推近推远产生缩放的感觉)
      glTranslatef(steadyX, steadyY, varZ);
      glRotatef(steadyXangle, 
010);
      glRotatef(steadyYangle, 
100);
    }
    
/* Nothing down.  */
    
else 
    {
      glTranslatef(steadyX, steadyY, steadyZ);
      glRotatef(steadyXangle, 
010);
      glRotatef(steadyYangle, 
100);
    }
    
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;
  }

}

最后,代码中还加入了对空间球,图形板,拨号按键盒的事件处理支持。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值