OpenGl 導入讀取多個3D模型 並且添加鼠標控制移動旋轉

  • 2019 年 10 月 6 日
  • 筆記

原文作者:aircraft

原文鏈接:https://www.cnblogs.com/DOMLX/p/11627508.html

 

 

前言:

 

  因為接下來的項目需求是要讀取多個3D模型,並且移動拼接,那麼我就先把基本的小demo給寫好當做前期測試。

 

  在我之前網上的博客都只有讀取移動旋轉單個3d模型的, 導致我根本查不到有關的資料,只能自己寫了。

 

  前人栽樹,後人乘涼。

  技術就是要分享出來,大家共同進步,我們走過的坑沒有必要讓後來人再走一次。

 

有了前面兩篇的分部實現,掌握這次的就不難了:

 

OpenGl讀取導入3D模型並且添加鼠標移動旋轉顯示

OpenGl 實現鼠標分別移動多個物體圖形 ———-移動一個物體另外一個物體不動–讀取多個3d模型操作的前期踏腳石

 

當然還是要回顧一下整個實現過程,和一些需求準備。

 

一.讀取3D模型

 

  在3d圖形處理中,一個模型(model)通常由一個或者多個Mesh(網格)組成,一個Mesh是可繪製的獨立實體。例如複雜的人物模型,可以分別劃分為頭部,四肢,服飾,武器等各個部分來建模,這些Mesh組合在一起最終形成人物模型。

Mesh由頂點、邊、面Faces組成的,它包含繪製所需的數據,例如頂點位置、紋理坐標、法向量,材質屬性等內容,它是OpenGL用來繪製的最小實體。Mesh的概念示意如下圖所示(來自:What is a mesh in OpenGL?):


Mesh

Mesh可以包含多個Face,一個Face是Mesh中一個可繪製的基本圖元,例如三角形,多邊形,點。要想模型更加逼真,一般需要增加更多圖元使Mesh更加精細,當然這也會受到硬件處理能力的限制,例如PC遊戲的處理能力要強於移動設備。由於多邊形都可以劃分為三角形,而三角形是圖形處理器中都支持的基本圖元,因此使用得較多的就是三角形網格來建模。例如下面的圖(來自:What is a mesh in OpenGL?)表達了使用越來越複雜的Mesh建模一隻兔子的過程:

Mesh2

 

 

 


隨着增加三角形個數,兔子模型變得越來越真實。

 

  讀取3d模型有很多種方法,但是最常用的無非就是調用別人寫好的庫,比如(openmesh),其次呢就是自己讀取解析3d模型文件裏面的一個個坐標數據,什麼v  vf  vn之類的。

在上面的鏈接中,我們使用的是openmesh庫來導入3d模型,並且添加光照,鼠標控制之類的。

如圖:

 

 

 

 

 

 

 

 

 

 

  那麼現在就是講一下第二種方法,就是直接解析讀取3d模型文件,提取裏面我們所需的數據。下面是一個obj的模型文件,我們可以使用記事本打開看看裏面是什麼:

# Blender3D v249 OBJ File: untitled.blend  # www.blender3d.org  mtllib cube.mtl  v 1.000000 -1.000000 -1.000000  v 1.000000 -1.000000 1.000000  v -1.000000 -1.000000 1.000000  v -1.000000 -1.000000 -1.000000  v 1.000000 1.000000 -1.000000  v 0.999999 1.000000 1.000001  v -1.000000 1.000000 1.000000  v -1.000000 1.000000 -1.000000  vt 0.748573 0.750412  vt 0.749279 0.501284  vt 0.999110 0.501077  vt 0.999455 0.750380  vt 0.250471 0.500702  vt 0.249682 0.749677  vt 0.001085 0.750380  vt 0.001517 0.499994  vt 0.499422 0.500239  vt 0.500149 0.750166  vt 0.748355 0.998230  vt 0.500193 0.998728  vt 0.498993 0.250415  vt 0.748953 0.250920  vn 0.000000 0.000000 -1.000000  vn -1.000000 -0.000000 -0.000000  vn -0.000000 -0.000000 1.000000  vn -0.000001 0.000000 1.000000  vn 1.000000 -0.000000 0.000000  vn 1.000000 0.000000 0.000001  vn 0.000000 1.000000 -0.000000  vn -0.000000 -1.000000 0.000000  usemtl Material_ray.png  s off  f 5/1/1 1/2/1 4/3/1  f 5/1/1 4/3/1 8/4/1  f 3/5/2 7/6/2 8/7/2  f 3/5/2 8/7/2 4/8/2  f 2/9/3 6/10/3 3/5/3  f 6/10/4 7/6/4 3/5/4  f 1/2/5 5/1/5 2/9/5  f 5/1/6 6/10/6 2/9/6  f 5/1/7 8/11/7 6/10/7  f 8/11/7 7/12/7 6/10/7  f 1/2/8 2/9/8 3/13/8  f 1/2/8 3/13/8 4/14/8

對這個文本格式做一個簡要說明:

  1.     以#開始的行為注釋行
  2.     usemtl和mtllib表示的材質相關數據,解析材質數據稍微繁瑣,本節我們只是為了說明加載模型的原理,不做討論。
  3.     o 引入一個新的object
  4.     v 表示頂點位置
  5.     vt 表示頂點紋理坐標
  6.     vn 表示頂點法向量
  7.     f 表示一個面,面使用1/2/8這樣格式,表示頂點位置/紋理坐標/法向量的索引,這裡索引的是前面用v,vt,vn定義的數據 注意這裡Obj的索引是從1開始的,而不是0

那麼我們只要拿到這些數據,按照opengl的繪製的規則,不就可以把他們都繪製出來了嗎?

提取數據代碼:

struct POINT3 {      double X;      double Y;      double Z;  };  struct WenLi {      double TU;      double TV;  };  struct FaXiangLiang {      double NX;      double NY;      double NZ;  };  struct Mian {      int V[3];      int T[3];      int N[3];  };  class PIC  {  public:      vector<POINT3> V;//V:代表頂點。格式為V X Y Z,V後面的X Y Z表示三個頂點坐標。浮點型      vector<WenLi>  VT;//表示紋理坐標。格式為VT TU TV。浮點型      vector<FaXiangLiang> VN;//VN:法向量。每個三角形的三個頂點都要指定一個法向量。格式為VN NX NY NZ。浮點型      vector<Mian> F;//F:面。面後面跟着的整型值分別是屬於這個面的頂點、紋理坐標、法向量的索引。                     //面的格式為:f Vertex1/Texture1/Normal1 Vertex2/Texture2/Normal2 Vertex3/Texture3/Normal3  };  //GLfloat m_fAngle = 0.0f;;        // Rotation angle of the cube  PIC m_pic1;  PIC m_pic2;    void ReadPIC2()  {      ifstream ifs("cow.obj");//cube bunny Eight      string s;      Mian *f;      POINT3 *v;      FaXiangLiang *vn;      WenLi    *vt;      while (getline(ifs, s))      {          if (s.length() < 2)continue;          if (s[0] == 'v') {              if (s[1] == 't') {//vt 0.581151 0.979929 紋理                  istringstream in(s);                  vt = new WenLi();                  string head;                  in >> head >> vt->TU >> vt->TV;                  m_pic2.VT.push_back(*vt);              }              else if (s[1] == 'n') {//vn 0.637005 -0.0421857 0.769705 法向量                  istringstream in(s);                  vn = new FaXiangLiang();                  string head;                  in >> head >> vn->NX >> vn->NY >> vn->NZ;                  m_pic2.VN.push_back(*vn);              }              else {//v -53.0413 158.84 -135.806 點                  istringstream in(s);                  v = new POINT3();                  string head;                  in >> head >> v->X >> v->Y >> v->Z;                  m_pic2.V.push_back(*v);              }          }          else if (s[0] == 'f') {//f 2443//2656 2442//2656 2444//2656 面              for (int k = s.size() - 1; k >= 0; k--) {                  if (s[k] == '/')s[k] = ' ';              }              istringstream in(s);              f = new Mian();              string head;              in >> head;              int i = 0;              while (i < 3)              {                  if (m_pic2.V.size() != 0)                  {                      in >> f->V[i];                      f->V[i] -= 1;                  }                  if (m_pic2.VT.size() != 0)                  {                      in >> f->T[i];                      f->T[i] -= 1;                  }                  if (m_pic2.VN.size() != 0)                  {                      in >> f->N[i];                      f->N[i] -= 1;                  }                  i++;              }              m_pic2.F.push_back(*f);          }      }  }

 

那麼接下來直接將其繪製出來即可:

for (int i = 0; i < m_pic1.F.size(); i++)      {          glBegin(GL_TRIANGLES);                            // 繪製三角形          if (m_pic1.VT.size() != 0)glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[0]].TU, m_pic1.VT[m_pic1.F[i].T[0]].TV);  //紋理              if (m_pic1.VN.size() != 0)glNormal3f(m_pic1.VN[m_pic1.F[i].N[0]].NX, m_pic1.VN[m_pic1.F[i].N[0]].NY, m_pic1.VN[m_pic1.F[i].N[0]].NZ);//法向量          glVertex3f(m_pic1.V[m_pic1.F[i].V[0]].X / YU, m_pic1.V[m_pic1.F[i].V[0]].Y / YU, m_pic1.V[m_pic1.F[i].V[0]].Z / YU);        // 上頂點            if (m_pic1.VT.size() != 0)glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[1]].TU, m_pic1.VT[m_pic1.F[i].T[1]].TV);  //紋理          if (m_pic1.VN.size() != 0)glNormal3f(m_pic1.VN[m_pic1.F[i].N[1]].NX, m_pic1.VN[m_pic1.F[i].N[1]].NY, m_pic1.VN[m_pic1.F[i].N[1]].NZ);//法向量          glVertex3f(m_pic1.V[m_pic1.F[i].V[1]].X / YU, m_pic1.V[m_pic1.F[i].V[1]].Y / YU, m_pic1.V[m_pic1.F[i].V[1]].Z / YU);        // 左下            if (m_pic1.VT.size() != 0)glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[2]].TU, m_pic1.VT[m_pic1.F[i].T[2]].TV);  //紋理          if (m_pic1.VN.size() != 0)glNormal3f(m_pic1.VN[m_pic1.F[i].N[2]].NX, m_pic1.VN[m_pic1.F[i].N[2]].NY, m_pic1.VN[m_pic1.F[i].N[2]].NZ);//法向量          glVertex3f(m_pic1.V[m_pic1.F[i].V[2]].X / YU, m_pic1.V[m_pic1.F[i].V[2]].Y / YU, m_pic1.V[m_pic1.F[i].V[2]].Z / YU);        // 右下          glEnd();                                        // 三角形繪製結束          }

 

那麼讀取多個3d模型,就是把函數和繪製多調用幾次不就行了嗎?

 

二.鼠標控制相關

  1.鼠標控制函數準備

 

  我們需要對鼠標信息的獲取,那麼必然需要一個鼠標事件的響應函數來控制,很好opengl已經有內部的鼠標控制函數了,我們直接拿來使用就行了。

 

 

 

glutMouseFunc( (void*)Func(int button, int state, int x, int y) );
glutMouseFunc這個是調用鼠標函數的入口,func是我們給鼠標處理函數的命名, 三個參數分別是鼠標響應的事件類型,比如左鍵點擊,右鍵點擊之類,x,y則是當前鼠標在窗口的位置坐標。

下面這個是處理鼠標移動時候的調用函數
glutMotionFunc(&func(int x,inty)); // 鼠標移動的時候的函數 x,y當前鼠標坐標

反正調用起來非常的簡單只要自己寫好一個鼠標點擊類事件處理函數和一個鼠標移動事件處理函數,然後傳入進去就行了,調用函數放在main函數里。反正後面代碼有。

 

 

// 鼠標運動時  void onMouseMove(int x, int y) {      //當鼠標狀態為按下時進入後續判斷      if (mousetate) {          if (choose != 0) {              //x對應y是因為對應的是法向量              if (choose == 1) {                  movX1 = (x - x1) / 2 * 5;                  glutPostRedisplay();                  movY1 = -((y - Y1) / 2 * 5);                  glutPostRedisplay();                  std::cout << " 移動 x1 = " << x << " y1 = " << y << std::endl;              }              if (choose == 2) {                  movX2 = (x - x2) / 2 * 5;                  glutPostRedisplay();                  movY2 = -((y - y2) / 2 * 5);                  glutPostRedisplay();                  std::cout << " 移動 x2 = " << x << " y2 = " << y << std::endl;              }              if (choose == 3) {                  //x對應y是因為對應的是法向量                  yRotate1 += x - Oldx1;                  glutPostRedisplay();//標記當前窗口需要重新繪製                  Oldx1 = x;                  xRotate1 += y - Oldy1;                  glutPostRedisplay();                  Oldy1 = y;                  std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;              }              if (choose == 4) {                  //x對應y是因為對應的是法向量                  yRotate2 += x - Oldx2;                  glutPostRedisplay();//標記當前窗口需要重新繪製                  Oldx2 = x;                  xRotate2 += y - Oldy2;                  glutPostRedisplay();                  Oldy2 = y;                  std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;              }          }          else std::cout << "not choose" << std::endl;      }  }

2.一些鼠標的響應事件

if(state == GLUT_DOWN) //相當於“如果某個鼠標鍵被按下”
if(state == GLUT_UP) //相當於“如果某個鼠標鍵被放開”
if(button == GLUT_LEFT_BUTTON) //相當於“如果鼠標左鍵被按下或者被放開”
if(button == GLUT_RIGHT_BUTTON) //相當於“如果鼠標右鍵被按下或被放開”
if(button == GLUT_MIDDLE_BUTTON) //相當於“如果鼠標中鍵被按下或者被放開”

還有鼠標的滾輪事件

GLUT_WHEEL_UP  

GLUT_WHEEL_DOWN

這兩個可能有時候會遇到自己gult庫沒有定義,那麼就是版本比較老的緣故,不想麻煩下新版本或者下了新版本還是沒有解決的話就直接像這樣定義在文件頭部:

#define  GLUT_WHEEL_UP 3           //定義滾輪操作
#define  GLUT_WHEEL_DOWN 4

 

三.實現過程

  我們將讀取的兩個3d模型繪製,並且分開顯示(光照還沒有搞好QAQ。。。。)

 

 

 

繪製代碼:

void myDisplay()  {      //要清除之前的深度緩存      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);      glLoadIdentity();      //與顯示相關的函數      /*      glRotatef(xRotate, 1.0f, 0.0f, 0.0f); // 讓物體旋轉的函數 第一個參數是角度大小,後面的參數是旋轉的法向量      glRotatef(yRotate, 0.0f, 1.0f, 0.0f);      //glTranslatef(0.0f, 0.0f, ty);      glTranslatef(tx, ty, 0);        //移動      glScalef(scale, scale, scale); // 縮放 x,y,z分別乘以scale      */                         //每次display都要使用glcalllist回調函數顯示想顯示的頂點列表      //glScalef(scale, scale, scale);      glScalef(0.001, 0.001, 0.001);        //glTranslatef(400, 0, 0);      //glPushMatrix();      //glTranslatef(-6, 0, 0);      glPushMatrix();      glColor3f(0, 0, 1);        //glRotatef(50,1, 0, 0);      glTranslatef(movX1, movY1, 0);      glRotatef(xRotate1, 1.0f, 0.0f, 0.0f);      glRotatef(yRotate1, 0.0f, 1.0f, 0.0f);      //用鍵盤的insert按鍵控制顯示的模式  網格,面,網格面 分別顯示      for (int i = 0; i < m_pic1.F.size(); i++)      {          glBegin(GL_TRIANGLES);                            // 繪製三角形          if (m_pic1.VT.size() != 0)glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[0]].TU, m_pic1.VT[m_pic1.F[i].T[0]].TV);  //紋理              if (m_pic1.VN.size() != 0)glNormal3f(m_pic1.VN[m_pic1.F[i].N[0]].NX, m_pic1.VN[m_pic1.F[i].N[0]].NY, m_pic1.VN[m_pic1.F[i].N[0]].NZ);//法向量          glVertex3f(m_pic1.V[m_pic1.F[i].V[0]].X / YU, m_pic1.V[m_pic1.F[i].V[0]].Y / YU, m_pic1.V[m_pic1.F[i].V[0]].Z / YU);        // 上頂點            if (m_pic1.VT.size() != 0)glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[1]].TU, m_pic1.VT[m_pic1.F[i].T[1]].TV);  //紋理          if (m_pic1.VN.size() != 0)glNormal3f(m_pic1.VN[m_pic1.F[i].N[1]].NX, m_pic1.VN[m_pic1.F[i].N[1]].NY, m_pic1.VN[m_pic1.F[i].N[1]].NZ);//法向量          glVertex3f(m_pic1.V[m_pic1.F[i].V[1]].X / YU, m_pic1.V[m_pic1.F[i].V[1]].Y / YU, m_pic1.V[m_pic1.F[i].V[1]].Z / YU);        // 左下            if (m_pic1.VT.size() != 0)glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[2]].TU, m_pic1.VT[m_pic1.F[i].T[2]].TV);  //紋理          if (m_pic1.VN.size() != 0)glNormal3f(m_pic1.VN[m_pic1.F[i].N[2]].NX, m_pic1.VN[m_pic1.F[i].N[2]].NY, m_pic1.VN[m_pic1.F[i].N[2]].NZ);//法向量          glVertex3f(m_pic1.V[m_pic1.F[i].V[2]].X / YU, m_pic1.V[m_pic1.F[i].V[2]].Y / YU, m_pic1.V[m_pic1.F[i].V[2]].Z / YU);        // 右下          glEnd();                                        // 三角形繪製結束          }      glPopMatrix();        glPushMatrix();      glColor3f(0, 1, 0);      glTranslatef(movX2, movY2, 0);      glRotatef(xRotate2, 1.0f, 0.0f, 0.0f);      glRotatef(yRotate2, 0.0f, 1.0f, 0.0f);        for (int i = 0; i < m_pic2.F.size(); i++)      {          glBegin(GL_TRIANGLES);                            // 繪製三角形          if (m_pic2.VT.size() != 0)glTexCoord2f(m_pic2.VT[m_pic2.F[i].T[0]].TU, m_pic2.VT[m_pic2.F[i].T[0]].TV);  //紋理              if (m_pic2.VN.size() != 0)glNormal3f(m_pic2.VN[m_pic2.F[i].N[0]].NX, m_pic2.VN[m_pic2.F[i].N[0]].NY, m_pic2.VN[m_pic2.F[i].N[0]].NZ);//法向量          glVertex3f(m_pic2.V[m_pic2.F[i].V[0]].X / YU, m_pic2.V[m_pic2.F[i].V[0]].Y / YU, m_pic2.V[m_pic2.F[i].V[0]].Z / YU);        // 上頂點            if (m_pic2.VT.size() != 0)glTexCoord2f(m_pic2.VT[m_pic2.F[i].T[1]].TU, m_pic2.VT[m_pic2.F[i].T[1]].TV);  //紋理          if (m_pic2.VN.size() != 0)glNormal3f(m_pic2.VN[m_pic2.F[i].N[1]].NX, m_pic2.VN[m_pic2.F[i].N[1]].NY, m_pic2.VN[m_pic2.F[i].N[1]].NZ);//法向量          glVertex3f(m_pic2.V[m_pic2.F[i].V[1]].X / YU, m_pic2.V[m_pic2.F[i].V[1]].Y / YU, m_pic2.V[m_pic2.F[i].V[1]].Z / YU);        // 左下            if (m_pic2.VT.size() != 0)glTexCoord2f(m_pic2.VT[m_pic2.F[i].T[2]].TU, m_pic2.VT[m_pic2.F[i].T[2]].TV);  //紋理          if (m_pic2.VN.size() != 0)glNormal3f(m_pic2.VN[m_pic2.F[i].N[2]].NX, m_pic2.VN[m_pic2.F[i].N[2]].NY, m_pic2.VN[m_pic2.F[i].N[2]].NZ);//法向量          glVertex3f(m_pic2.V[m_pic2.F[i].V[2]].X / YU, m_pic2.V[m_pic2.F[i].V[2]].Y / YU, m_pic2.V[m_pic2.F[i].V[2]].Z / YU);        // 右下          glEnd();                                        // 三角形繪製結束          }      glPopMatrix();      glutSwapBuffers(); //這是Opengl中用於實現雙緩存技術的一個重要函數  }

 

  其次我們鼠標要點擊選取一個物體,當我們鼠標按住移動時,物體跟隨我們的鼠標移動。按住鼠標點擊選取的範圍可以是這個物體中心為定點坐標,以邊長為d的一個矩形區域,當鼠標點擊在這個區域時,我們則判定選取了這個物體。

  當兩個物體重疊時,我們優先選取畫出的第一個物體進行移動。

 

  那麼問題就來了,選取了物體後,如何實現物體跟隨我們鼠標移動呢?

  非常簡單,水平方向上,只要在鼠標移動時將移動後的坐標減去移動前的坐標然後除以實際移動的差值(自己誤差測試) ,就得到了移動的法向量。movX1 = (x – x1)  / 2 * 5;

  垂直方向上,同理可得movY1 = -((y – Y1) / 2 * 5);  為什麼這裡多個負號,是因為向下移動是負數,向上是正數。

  然後將移動後改變的移動法向量,讓程序調用窗口重新繪製一次即可。如果出現閃爍問題,可以使用雙緩衝。

 最後實現右鍵按住移動鼠標,被選中的物體會進行旋轉。

  這個我們就比較簡單了,我們只需要將移動後的坐標減去移動前的坐標這個差值作為一個法向量,然後傳給旋轉繪製的變量,後面調用重繪即可實現。

 

鼠標部分控制代碼:

 

// 鼠標交互  void myMouse(int button, int state, int x, int y)  {    //鼠標左鍵按下或者鬆開      if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {          mousetate = 1;          if (!choose) {              if (x<(x1 + chooseWidth) && x>(x1 - chooseWidth)) {                  if (y<(Y1 + chooseHeight) && y>(Y1 - chooseHeight)) {                      x1 = 400;                      Y1 = 400;                      movX1 = (x - x1) * 2 / 5;                      movY1 = -((y - Y1) * 2 / 5);                      choose = 1;                      std::cout << "選擇圖形一  x = " << x << " y = " << y << std::endl;                  }              }              else if (x<(x2 + chooseWidth) && x>(x2 - chooseWidth)) {                  if (y<(y2 + chooseHeight) && y>(y2 - chooseHeight)) {                      x2 = 400;                      y2 = 400;                      movX2 = (x - x2) * 2 / 5;                      movY2 = -((y - y2) * 2 / 5);                      choose = 2;                  }              }            }          std::cout << "x = " << x << " y = " << y << std::endl;          std::cout << "x1 = " << x1 << " y1 = " << Y1 << std::endl;          std::cout << "x2 = " << x2 << " y2 = " << y2 << std::endl;      }      if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {          if (choose == 1) {              x1 += (movX1 * 2 / 5);              Y1 += -(movY1 * 2 / 5);          }          if (choose == 2) {              x2 += (movX2 * 2 / 5);              y2 += -(movY2 * 2 / 5);          }          mousetate = 0;          choose = 0;          std::cout << "x = " << x1 << " y = " << Y1 << std::endl;      }        //右鍵按下控制旋轉      if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {          mousetate = 1;          if (!choose) {              if (x<(x1 + chooseWidth) && x>(x1 - chooseWidth)) {                  if (y<(Y1 + chooseHeight) && y>(Y1 - chooseHeight)) {                      Oldx1 = x;                      Oldy1 = y;                      choose = 3;                      std::cout << "右擊選擇圖形一  x = " << x << " y = " << y << std::endl;                  }              }              else if (x<(x2 + chooseWidth) && x>(x2 - chooseWidth)) {                  if (y<(y2 + chooseHeight) && y>(y2 - chooseHeight)) {                      Oldx2 = x;                      Oldy2 = y;                      choose = 4;                  }              }            }      }      if (button == GLUT_RIGHT_BUTTON && state == GLUT_UP) {          mousetate = 0;          choose = 0;      }        //滾輪事件      //scale 增加就是放大 減小就是縮小      //currentfile 對不同的模型用不用的scale      if (state == GLUT_UP && button == GLUT_WHEEL_UP) {          //cout << "hello" << endl;          //滑輪向上滑,則scale減小          if (currentfile == 1)              scale += 0.0005;          if (currentfile == 2)              scale += 0.001;          if (currentfile == 5) {              scale += 0.1;          }          else              scale += 0.001;      }      if (state == GLUT_UP && button == GLUT_WHEEL_DOWN) {          //cout << "good" << endl;          //滑輪向下滑,則scale增加          if (currentfile == 1)              scale -= 0.0005;          if (currentfile == 2)              scale -= 0.001;          if (currentfile == 5) {              scale -= 0.1;          }          else              scale -= 0.001;      }      glutPostRedisplay();//促使主程序儘快的重繪窗口  }  // 鼠標運動時  void onMouseMove2(int x, int y) {      //當鼠標狀態為按下時進入後續判斷      std::cout << " 移動 x = " << x << " y = " << y << std::endl;  }  // 鼠標運動時  void onMouseMove(int x, int y) {      //當鼠標狀態為按下時進入後續判斷      if (mousetate) {          if (choose != 0) {              //x對應y是因為對應的是法向量              if (choose == 1) {                  movX1 = (x - x1) / 2 * 5;                  glutPostRedisplay();                  movY1 = -((y - Y1) / 2 * 5);                  glutPostRedisplay();                  std::cout << " 移動 x1 = " << x << " y1 = " << y << std::endl;              }              if (choose == 2) {                  movX2 = (x - x2) / 2 * 5;                  glutPostRedisplay();                  movY2 = -((y - y2) / 2 * 5);                  glutPostRedisplay();                  std::cout << " 移動 x2 = " << x << " y2 = " << y << std::endl;              }              if (choose == 3) {                  //x對應y是因為對應的是法向量                  yRotate1 += x - Oldx1;                  glutPostRedisplay();//標記當前窗口需要重新繪製                  Oldx1 = x;                  xRotate1 += y - Oldy1;                  glutPostRedisplay();                  Oldy1 = y;                  std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;              }              if (choose == 4) {                  //x對應y是因為對應的是法向量                  yRotate2 += x - Oldx2;                  glutPostRedisplay();//標記當前窗口需要重新繪製                  Oldx2 = x;                  xRotate2 += y - Oldy2;                  glutPostRedisplay();                  Oldy2 = y;                  std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;              }          }          else std::cout << "not choose" << std::endl;      }  }

 

 

這些基本就是這個博客講的所有內容了,讓我們最後看看成果。

移動旋轉前:

 

 

 

移動旋轉後: