ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • 三次元オブジェクトをマウス回転(glut使用)

    入門者がOpenGLを使う時、コードと出力結果だけでは何がどうなったのかわかりにくい事があります。

    せめて回転ぐらいはしたいので、マウスの左ドラッグで描画対象を回転できるシンプルなコードを作成しました。

    (例のごとく同種のものはいくらでもあると思います)

     

    使い方:

    マウス押下時に、dragstartを呼び出して回転開始を宣言します。

    その後、マウスが動くたびにdragtoを呼び出して、回転状態を更新します。

    最後、マウスボタンが離された時にdragendを呼び出して、回転の終了を宣言します。

     

    球と直線の交差に関する式は

    The Textbook of RayTracing @TDU

    を参考にしました。

     

    呼出元(main)
    #pragma once
    #include <windows.h>
    #include <GL/gl.h>
    #include <GL/freeglut.h>
    #include "nurotate.h"
    #include "cube.h"
    int width, height;
    //回転オブジェクト定義
    nu::mrotate camr;
    void disp(void) {
      glClear(GL_COLOR_BUFFER_BIT);
      glViewport(0, 0, width, height);
    
      glPushMatrix();
    
      //原点を視線方向に0.5ずらす
      glTranslated(0, 0, -0.5);
    
      //回転行列を適用
      double mat[16];
      glMultMatrixd(camr.getmatrix(mat));
    
      //一辺0.7のキューブを描画
      cube(0.7);
    
      glPopMatrix();
    
      glFlush();
    }
    void reshape(int w, int h) {
      width = w; height = h;
    
      //クライアント領域のサイズを指定
      //Win32APIの場合はGetClientRectで求めたrightとbottomだがglutではwとh
      camr.setWindowSize(width, height);
    
      //クライアント領域のどこの範囲に描画しているかを指定。
      //glViewportで一画面丸々描画しているので、クライアント領域の(0,0)-(width,height)に書いていることになる
      camr.setRenderRect(0, 0, width, height);
      disp();
    }
    //マウスクリック時のイベント
    void mouse(int button, int state, int x, int y) {
      if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        camr.dragstart(x, y);//最初の回転軸を決定
      }
      else if (state == GLUT_UP) {
        camr.dragend();//ドラッグ終了
      }
      glutPostRedisplay();
    }
    //ドラッグ時のイベント
    void motion(int x, int y) {
      camr.dragto(x, y);//移動中の回転軸を更新
      disp();
    }
    //エントリポイント
    int main(int argc, char ** argv) {
      glutInit(&argc, argv);
      glutInitWindowPosition(100, 50);
      glutInitWindowSize(400, 300);
      glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    
      glutCreateWindow("MouseRotateSample");
      glutDisplayFunc(disp);
      glutReshapeFunc(reshape);
      glutMouseFunc(mouse);
      glutMotionFunc(motion);
      glutMainLoop();
      return 0;
    }
    

    呼出元のコードは見ての通りglutを使用していますが、freeglutを使っています。

    また回転行列はglMultMatrixdで掛け合わせていますのでglRotate*等は使っていません。

     

    ヘッダファイル(nurotate.h)
    #pragma once
    #include <cmath>
    namespace nu {
    
      ////////////////////////////////////
      //! @brief マウス回転用クラス
      class mrotate
      {
        double m_cam_matrix[16]; //!< @brief 現在の行列
        double m_tmp_matrix[16]; //!< @brief マウス回転中に使用する一時的な行列
    
    
        double m_vstart[3]; //!< @brief 回転スタート時の、「球の中心→"クリック位置のレイと球の交点"」 のベクトル
        double m_vend[3]; //!< @brief マウスが移動中の、〃
        double m_raxis[3]; //vstartとvendの外積から算出した、回転中の回転軸(ベクトル)
    
        //viewportで指定した表示範囲をウィンドウ座標系で指定
        int m_rectLTx, m_rectLTy;
        int m_rectRBx, m_rectRBy;
    
        //ウィンドウのサイズ
        int windowSizeX, windowSizeY;
    
        //マウスボタンが押されている間はtrueになり、trueの間は回転処理を行う
        bool m_fmousepush;
      public:
        mrotate();
        ~mrotate();
    
        //! @brief クライアント領域のサイズを指定する
        //! @param [in] width ウィンドウのクライアント領域の幅(ピクセル)
        //! @param [in] height ウィンドウのクライアント領域の高さ(ピクセル)
        void setWindowSize(const int width, const int height) {
          windowSizeX = width;
          windowSizeY = height;
        }
    
        //! @brief クライアント領域内にOpenGLで描画する範囲を矩形で指定。glviewportと同じだがこちらはクライアント座標系で矩形の四隅の座標を指定する
        void setRenderRect(int LeftTopX, int LeftTopY, int RightBottomX, int RightBottomY) {
          m_rectLTx = LeftTopX;
          m_rectLTy = LeftTopY;
          m_rectRBx = RightBottomX;
          m_rectRBy = RightBottomY;
        }
    
        //! @brief ドラッグ開始時に呼び出す
        //! @param [in] x クライアント座標系のマウスのx座標
        //! @param [in] y クライアント座標系のマウスのy座標
        //! @return なし
        void dragstart(int curx, int cury);
    
    
        //! @brief ドラッグ中に呼び出す
        //! @param [in] curx クライアント座標系のマウスのx座標
        //! @param [in] cury クライアント座標系のマウスのy座標
        //! @return なし
        void dragto(int curx, int cury);
    
    
        //! @brief ドラッグ終了時に呼び出す
        //! @return なし
        void dragend();
    
        //! @brief 現在の回転行列を取得する
        const double* getmatrix(double* const dst)const;
      };
      ////////////////////////////////////
    
      /////////////////////////////////////////////   
      //以下ベクトルと行列演算用の汎用的な関数////////
    
      //三次元ベクトルの長さを求める
      inline double length3(const double* const vec3) {
        return std::sqrt(vec3[0] * vec3[0] + vec3[1] * vec3[1] + vec3[2] * vec3[2]);
      }
    
      //三次元ベクトルの外積を求める
      inline double inner3(const double * const vec1, const double * const vec2) {
        return ((vec1[0])*(vec2[0]) + (vec1[1])*(vec2[1]) + (vec1[2])*(vec2[2]));
      }
    
      inline void outer3(double * const dvec, double const * const svec1, double const * const svec2) {
    
        const double& x1 = svec1[0];
        const double& y1 = svec1[1];
        const double& z1 = svec1[2];
        const double& x2 = svec2[0];
        const double& y2 = svec2[1];
        const double& z2 = svec2[2];
    
        dvec[0] = static_cast<double>(y1 * z2 - z1 * y2);
        dvec[1] = static_cast<double>(z1 * x2 - x1 * z2);
        dvec[2] = static_cast<double>(x1 * y2 - y1 * x2);
      }
      inline double vangle3(double const * const vec1, double const * const vec2) {
        return acos(inner3(vec1, vec2) / (length3(vec1) * length3(vec2)));
    
      }
      inline void GetRotateMatrix(double* const m, const double radian, const double * const axis) {
        double s = sin(radian);
        double c = cos(radian);
        double len = length3(axis);
        double x = axis[0] / len;
        double y = axis[1] / len;
        double z = axis[2] / len;
    
        m[0] = x * x*(1 - c) + c;  m[4] = x * y*(1 - c) - z * s;  m[8] = x * z*(1 - c) + y * s;  m[12] = 0;
        m[1] = x * y*(1 - c) + z * s;  m[5] = y * y*(1 - c) + c;  m[9] = y * z*(1 - c) - x * s;  m[13] = 0;
        m[2] = x * z*(1 - c) - y * s;  m[6] = y * z*(1 - c) + x * s;  m[10] = z * z*(1 - c) + c;  m[14] = 0;
        m[3] = 0;              m[7] = 0;              m[11] = 0;              m[15] = 1;
      }
    
    
      inline bool normalize3(double * const pV)
      {
        double len;
        double& x = pV[0];
        double& y = pV[1];
        double& z = pV[2];
    
        len = sqrt(x * x + y * y + z * z);
    
        if (len < 1e-6) return false;
    
        len = 1.0 / len;
        x *= len;
        y *= len;
        z *= len;
    
        return true;
      }
    
      inline double* CopyMat44(double* const dst16, const double* const src16) {
        for (int i = 0; i < 16; i++) {
          dst16[i] = src16[i];
        }
        return dst16;
      }
    
      inline double* MultMatrix(double* const dst, const double* const m1, const double* const m2) {
        for (int i = 0; i < 16; i++)
          dst[i] = 0.0;
    
        double d1, d2;
        for (size_t j = 0; j < 4; j++)
          for (size_t k = 0; k < 4; k++)
            for (size_t c = 0; c < 4; c++) {
              d1 = m1[4 * j + c];
    
              d2 = m2[4 * c + k];
    
              dst[4 * j + k] += d1 * d2;
    
            }
        return dst;
      }
    
      inline double* MultMatrix(double* dst, const double* const src) {
        double m1[16];
        for (int i = 0; i < 16; i++) {
          m1[i] = dst[i];
        }
        MultMatrix(dst, m1, src);
        return dst;
      }
      inline void loadIdentity16(double * const mat) {
        for (int i = 0; i < 16; i++) {
          mat[i] = 0;
        }
        mat[0] = 1;
        mat[5] = 1;
        mat[10] = 1;
        mat[15] = 1;
      }
    }
    

    前半はクラスの定義です。後半はベクトルや行列に関する演算用の汎用的な関数です。

     

    ヘッダファイル(nurotate.cpp)
    #include "nurotate.h"
    #include <cstdio>
    namespace nu {
    
      mrotate::mrotate() {
        loadIdentity16(m_cam_matrix);
        loadIdentity16(m_tmp_matrix);
    
        m_fmousepush = false;
      }
    
      mrotate::~mrotate()
      {
      }
    
      const double* mrotate::getmatrix(double* const dst)const {
        CopyMat44(dst, m_cam_matrix);
        MultMatrix(dst, m_tmp_matrix);
    
        return dst;
      }
    
    
      void mrotate::dragstart(int curx, int cury) {
    
        //本来はマウスが押されている間に呼び出されることはない
        //もしそういうことが起こったら何もせずに抜ける
        if (m_fmousepush == true)
          return;
    
        //ドラッグ中であることを示す
        m_fmousepush = true;
    
        printf(" dragstart\n");
    
        const long cx = (m_rectRBx - m_rectLTx) / 2;//開始点を左上と考えた時の、画面中央の座標
        const long cy = (m_rectRBy - m_rectLTy) / 2;//
    
        const long x = curx - m_rectLTx - cx;//cx,cyを0と考えた時のマウスの座標(左下原点)
        const long y = (windowSizeY - cury) - m_rectLTy - cy;
    
    
        //球の半径
        double r = std::fmin((m_rectRBx - m_rectLTx), (m_rectRBy - m_rectLTy));
    
        //レイの方程式 p = s + td
        double s[3] = { (double)x,(double)y,-1000 };
        double d[3] = { 0,0,1 };
    
        double A = std::pow(length3(d), 2);
        double B = 2 * inner3(s, d);
        double C = std::pow(length3(s), 2) - r * r;
        double t1 = (-B + std::sqrt(B*B - 4 * A * C)) / (2 * A);
        double t2 = (-B - std::sqrt(B*B - 4 * A * C)) / (2 * A);
    
        //交点
        double p[3] = {
          s[0] + t1 * d[0],
          s[1] + t1 * d[1],
          s[2] + t1 * d[2]
        };
        normalize3(p);
    
        //中心→交点のベクトル
        m_vstart[0] = p[0];
        m_vstart[1] = p[1];
        m_vstart[2] = p[2];
    
      }
      void mrotate::dragto(int curx, int cury) {
    
        //マウスドラッグ中でないなら何もしない
        if (m_fmousepush == false)
          return;
    
        printf(" dragend\n");
    
        const long cx = (m_rectRBx - m_rectLTx) / 2;//開始点を左上と考えた時の、画面中央の座標
        const long cy = (m_rectRBy - m_rectLTy) / 2;//
    
        const long x = curx - m_rectLTx - cx;//cx,cyを0と考えた時のマウスの座標(左下原点)
        const long y = (windowSizeY - cury) - m_rectLTy - cy;
    
    
        //球の半径
        double r = std::fmin((m_rectRBx - m_rectLTx), (m_rectRBy - m_rectLTy));
    
        //レイの方程式 p = s + td
        double s[3] = { (double)x,(double)y,-1000 };
        double d[3] = { 0,0,1 };
    
        double A = std::pow(length3(d), 2);
        double B = 2 * inner3(s, d);
        double C = std::pow(length3(s), 2) - r * r;
        double t1 = (-B + std::sqrt(B*B - 4 * A * C)) / (2 * A);
        double t2 = (-B - std::sqrt(B*B - 4 * A * C)) / (2 * A);
    
        //交点
        double p[3] = {
          s[0] + t1 * d[0],
          s[1] + t1 * d[1],
          s[2] + t1 * d[2]
        };
        normalize3(p);
    
        //中心→交点のベクトル
        m_vend[0] = p[0];
        m_vend[1] = p[1];
        m_vend[2] = p[2];
    
    
        outer3(m_raxis, m_vstart, m_vend);
        normalize3(m_raxis);
        double angle = vangle3(m_vstart, m_vend) * 3;
    
        if (abs(angle) < 1e-6)
          return;
    
        GetRotateMatrix(m_tmp_matrix, angle, m_raxis);
    
      }
    
      void mrotate::dragend() {
    
        //マウスが押されている間しか処理しない
        if (m_fmousepush == false)
          return;
    
        //マウスドラッグが終わったことを示す
        m_fmousepush = false;
    
    
        MultMatrix(m_cam_matrix, m_tmp_matrix);
        loadIdentity16(m_tmp_matrix);
      }
    }
    

    クラスの実装です。