轨迹球鼠标旋转OpenGL

时间:2016-02-15 14:35:58

标签: opengl trackball

我正在尝试使用openGL实现轨迹球的基本解决方案。

有2次轮换: - 围绕x轴(右方向),与上/下鼠标移动相关联。 - 围绕y轴(向上方向),与鼠标左右移动相关联。 立方体以0为中心,相机位于z轴上。绘制轴:x为蓝色,y为绿色,z为红色。

2个不同的轮换组:

  1. 通过向下移动鼠标在x周围90°。 Z朝下,x右,y在前面。 然后另一个旋转:z周围90°(显示z),鼠标右移。 z朝向正确的方向,x向上和向前y。

  2. 我再次处理,位置从头开始。 y°90°。 y:up,z:right,x:在前面 绕z旋转90°。 x:up,z:right:y:在前面。

  3. 我确定了第二次轮换之间的不同行为。

    为了连贯性,我等待第一次操作,第二次旋转在z附近 或者对于第二次操作,第二次旋转是围绕x轴。 为什么?

    对我来说,第二次操作有旋转,这是我想要的行为。 谢谢您的帮助。

    代码

    cube.cpp:

        #include <SDL/SDL.h>
        #include <GL/gl.h>
        #include <GL/glu.h>
        #include <cstdlib>
        #include <iostream>
    
        #include "sdlglutils.h"
        #include "trackballcamera.h"
    
        using namespace std;
    
        void Dessiner();
        double angleZ = 0;
        double angleX = 0;
        double angleY = 0;
    
        TrackBallCamera * camera;
    
        int main(int argc, char *argv[])
        {
        cout<<"in main cube"<<endl;
        freopen("CON", "w", stdout);
        freopen("CON", "r", stdin);
        freopen("CON", "w", stderr);
    
    
        SDL_Event event;
    
        SDL_Init(SDL_INIT_VIDEO);
        atexit(SDL_Quit);
        SDL_WM_SetCaption("SDL GL Application", NULL);
        SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);
    
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective(70,(double)640/480,1,1000);
    
        glEnable(GL_DEPTH_TEST);
    
        camera = new TrackBallCamera();
        camera->setScrollSensivity(10);
    
        Dessiner();
    
        Uint32 last_time = SDL_GetTicks();
        Uint32 current_time,ellapsed_time;
        Uint32 start_time;
    
        GLUquadric* params;
        params = gluNewQuadric();
    
        for (;;)
        {
            start_time = SDL_GetTicks();
            while (SDL_PollEvent(&event))
            {
    
                switch(event.type)
                {
                    case SDL_QUIT:
                    exit(0);
                    break;
    
                    case SDL_MOUSEBUTTONUP:
                    cout<<"SDL_MOUSEBUTTONUP"<<endl;
                    camera->OnMouseButton(event.button); 
                    break;
    
                    case SDL_MOUSEBUTTONDOWN:
                    cout<<"SDL_MOUSEBUTTONDOWN"<<endl;
                    camera->OnMouseButton(event.button); 
                    break;
    
                    case SDL_MOUSEMOTION:
                    camera->OnMouseMotion(event.motion);
                    break;
    
    
                }
            }
    
            current_time = SDL_GetTicks();
            ellapsed_time = current_time - last_time;
            last_time = current_time;
    
            //angleZ += 0.05 * ellapsed_time;
            //angleX += 0.05 * ellapsed_time;
    
            Dessiner();
    
            ellapsed_time = SDL_GetTicks() - start_time;
            if (ellapsed_time < 10)
            {
                SDL_Delay(10 - ellapsed_time);
            }
    
        }
    
        return 0;
    }
    
    void dessinerRepere(unsigned int echelle = 10)
    {
        //glPushMatrix();
        glScalef(echelle,echelle,echelle);
        glBegin(GL_LINES);
        glColor3ub(0,0,255);
        glVertex3i(0,0,0);
        glVertex3i(1,0,0);
        glColor3ub(0,255,0);
        glVertex3i(0,0,0);
        glVertex3i(0,1,0);
        glColor3ub(255,0,0);
        glVertex3i(0,0,0);
        glVertex3i(0,0,1);
        glEnd();
        //glPopMatrix();
    }
    
    
    void Dessiner()
    {
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity( );
    
        //gluLookAt(20,20,20,0,0,0,0,0,1);
    
        camera->look(0,0,camera->getDistance()
                     ,0,0,0
                     ,0,1,0);
    
        //glRotated(angleZ,0,0,1);
        //glRotated(angleX,1,0,0);
    
        glBegin(GL_QUADS);
    
        glColor3ub(255,0,0); //face rouge
        glVertex3d(1,1,1);
        glVertex3d(1,1,-1);
        glVertex3d(-1,1,-1);
        glVertex3d(-1,1,1);
    
        glColor3ub(0,255,0); //face verte
        glVertex3d(1,-1,1);
        glVertex3d(1,-1,-1);
        glVertex3d(1,1,-1);
        glVertex3d(1,1,1);
    
        glColor3ub(0,0,255); //face bleue
        glVertex3d(-1,-1,1);
        glVertex3d(-1,-1,-1);
        glVertex3d(1,-1,-1);
        glVertex3d(1,-1,1);
    
        glColor3ub(255,255,0); //face jaune
        glVertex3d(-1,1,1);
        glVertex3d(-1,1,-1);
        glVertex3d(-1,-1,-1);
        glVertex3d(-1,-1,1);
    
        glColor3ub(0,255,255); //face cyan
        glVertex3d(1,1,-1);
        glVertex3d(1,-1,-1);
        glVertex3d(-1,-1,-1);
        glVertex3d(-1,1,-1);
    
        glColor3ub(255,0,255); //face magenta
        glVertex3d(1,-1,1);
        glVertex3d(1,1,1);
        glVertex3d(-1,1,1);
        glVertex3d(-1,-1,1);
    
        glEnd();
        //glLoadIdentity( );
        dessinerRepere();
    
        glFlush();
        SDL_GL_SwapBuffers();
    }
    

    trackballcamera.cpp:

    #include "trackballcamera.h"
    
    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <cmath>
    #include "trackballcamera.h"
    #include "sdlglutils.h"
    
    void TrackBallCamera::setDistance(const double & newDistance)
    {
        _distance = newDistance;
    }
    
    const double & TrackBallCamera::getDistance() const
    {
        return _distance;
    }
    
    TrackBallCamera::TrackBallCamera()
    {
        const char *hand1[] =
            {
                /* width height num_colors chars_per_pixel */
                " 16 16 3 1 ",
                /* colors */
                "X c #000000",
                ". c #ffffff",
                "  c None",
                /* pixels */
                "       XX       ",
                "   XX X..XXX    ",
                "  X..XX..X..X   ",
                "  X..XX..X..X X ",
                "   X..X..X..XX.X",
                "   X..X..X..X..X",
                " XX X.......X..X",
                "X..XX..........X",
                "X...X.........X ",
                " X............X ",
                "  X...........X ",
                "  X..........X  ",
                "   X.........X  ",
                "    X.......X   ",
                "     X......X   ",
                "     X......X   ",
                "0,0"
            };
    
        const char *hand2[] =
            {
                /* width height num_colors chars_per_pixel */
                " 16 16 3 1 ",
                /* colors */
                "X c #000000",
                ". c #ffffff",
                "  c None",
                /* pixels */
                "                ",
                "                ",
                "                ",
                "                ",
                "    XX XX XX    ",
                "   X..X..X..XX  ",
                "   X........X.X ",
                "    X.........X ",
                "   XX.........X ",
                "  X...........X ",
                "  X...........X ",
                "  X..........X  ",
                "   X.........X  ",
                "    X.......X   ",
                "     X......X   ",
                "     X......X   ",
                "0,0"
            };
        _hand1 = cursorFromXPM(hand1);
        _hand2 = cursorFromXPM(hand2);
        SDL_SetCursor(_hand1);
        _holdRotation = false;
        _holdTranslation = false;
        _angleX = 0;
        _angleY = 0;
        _angleZ = 0;
        _transX = 0;
        _transY = 0;
        _transZ = 0;
        _distance = 50;
        _motionSensivity = 0.3;
        //_scrollSensivity = 1;
        _scrollSensivity = 10;
    }
    
    void TrackBallCamera::OnMouseMotion(const SDL_MouseMotionEvent & event)
    {
        printf("OnMouseMotion; _holdRotation:%d; _holdTranslation:%d\n", _holdRotation, _holdTranslation);
        if (_holdRotation)
        {
            _angleY += event.xrel*_motionSensivity;
            _angleX += event.yrel*_motionSensivity;
            printf("_angleX:%lf; _angleY:%lf\n",_angleX,_angleY);
    //        if (_angleY > 90)
    //            _angleY = 90;
    //        else if (_angleY < -90)
    //            _angleY = -90;
        }
    
        if (_holdTranslation)
        {
            _transX += event.xrel*_motionSensivity;
            _transY += event.yrel*_motionSensivity;
            printf("_transZ:%lf, _transY:%lf\n",_transZ, _transY);
        }
    
    }
    
    void TrackBallCamera::OnMouseButton(const SDL_MouseButtonEvent & event)
    {
        printf("OnMouseButton\n");
    
        if (event.button == SDL_BUTTON_LEFT)
        {
            if ((_holdRotation)&&(event.type == SDL_MOUSEBUTTONUP))
            {
                _holdRotation = false;
                SDL_SetCursor(_hand1);
            }
            else if ((!_holdRotation)&&(event.type == SDL_MOUSEBUTTONDOWN))
            {
                _holdRotation = true;
                SDL_SetCursor(_hand2);
            }
            printf("_holdRotation:%d\n",_holdRotation);
        }
    
        else if (event.button == SDL_BUTTON_RIGHT)
        {
            if ((_holdTranslation)&&(event.type == SDL_MOUSEBUTTONUP))
            {
                _holdTranslation = false;
                SDL_SetCursor(_hand1);
            }
            else if ((!_holdTranslation)&&(event.type == SDL_MOUSEBUTTONDOWN))
            {
                _holdTranslation = true;
                SDL_SetCursor(_hand2);
            }
        }
    
        else if ((event.button == SDL_BUTTON_WHEELUP)&&(event.type == SDL_MOUSEBUTTONDOWN))
        {
            printf("OK WHEELUP, _scrollSensivity:%lf; _distance:%lf\n", _scrollSensivity,_distance);
            _distance -= _scrollSensivity;
            //if (_distance < 0.1)
                //_distance = 0.1;
            printf("APRES WHEELUP, _distance:%lf\n", _distance);
        }
        else if ((event.button == SDL_BUTTON_WHEELDOWN)&&(event.type == SDL_MOUSEBUTTONDOWN))
        {
                printf("OK WHEELDOWN, _distance:%lf\n",_distance);
                _distance += _scrollSensivity;
        }
    }
    
    void TrackBallCamera::OnKeyboard(const SDL_KeyboardEvent & event)
    {
        if ((event.type == SDL_KEYDOWN)&&(event.keysym.sym == SDLK_HOME))
        {
            _angleY = 0;
            _angleX = 0;
        }
    }
    
    void TrackBallCamera::setMotionSensivity(double sensivity)
    {
        _motionSensivity = sensivity;
    }
    
    void TrackBallCamera::setScrollSensivity(double sensivity)
    {
        _scrollSensivity = sensivity;
    }
    
    TrackBallCamera::~TrackBallCamera()
    {
        SDL_FreeCursor(_hand1);
        SDL_FreeCursor(_hand2);
        SDL_SetCursor(NULL);
    }
    
    //camera->look(cloud.pointPosition[lastElement*3],0,75,cloud.pointPosition[lastElement*3],0,0,0,1,0);
    void TrackBallCamera::look(const GLfloat& xPointOfView
                                ,const GLfloat& yPointOfView
                                ,const GLfloat& zPointOfView
                                ,const GLfloat& xCenter
                                ,const GLfloat& yCenter
                                ,const GLfloat& zCenter
                                ,const GLfloat& xVerticalVector
                                ,const GLfloat& yVerticalVector
                                ,const GLfloat& zVerticalVector
                                )
    
    //void TrackBallCamera::look()
    {
        //gluLookAt(_distance,0,0,
                  //0,0,0,
                  //0,0,1);
        //printf("look, xPointOfView:%lf\n",xPointOfView);
        gluLookAt(xPointOfView,yPointOfView,zPointOfView,
                  xCenter,yCenter,zCenter,
                  xVerticalVector,yVerticalVector,zVerticalVector);
    
        glRotated(_angleX,1,0,0);
        glRotated(_angleY,0,1,0);
        //glRotated(_angleZ,0,0,1);
        //glTranslated(_transX,0,0);
        //glTranslated(0,_transY,0);
        //glTranslated(0,0,_transZ);
    }
    

    trackballcamera.h:

    #ifndef TRACKBALLCAMERA_H
    #define TRACKBALLCAMERA_H
    
    #include <SDL/SDL.h>
    #include <GL/gl.h>
    
    class TrackBallCamera
    {
    public:
        TrackBallCamera();
    
        virtual void OnMouseMotion(const SDL_MouseMotionEvent & event);
        virtual void OnMouseButton(const SDL_MouseButtonEvent & event);
        virtual void OnKeyboard(const SDL_KeyboardEvent & event);
    
        //virtual void look();
        virtual void look(const GLfloat& xPointOfView
                                ,const GLfloat& yPointOfView
                                ,const GLfloat& zPointOfView
                                ,const GLfloat& xCenter
                                ,const GLfloat& yCenter
                                ,const GLfloat& zCenter
                                ,const GLfloat& xVerticalVector
                                ,const GLfloat& yVerticalVector
                                ,const GLfloat& zVerticalVector
                                );
        virtual void setMotionSensivity(double sensivity);
        virtual void setScrollSensivity(double sensivity);
    
        void setDistance(const double & newDistance);
        const double & getDistance() const ;
    
        virtual ~TrackBallCamera();
    
    protected:
        double _motionSensivity;
        double _scrollSensivity;
        //bool _hold;
        bool _holdRotation;
        bool _holdTranslation;
        double _distance;
        double _angleX;
        double _angleY;
        double _angleZ;
        double _transX;
        double _transY;
        double _transZ;
        SDL_Cursor * _hand1;
        SDL_Cursor * _hand2;
    };
    
    #endif //TRACKBALLCAMERA_H
    

    sdlglutils.cpp:

    #include "sdlglutils.h"
    #include <SDL/SDL.h>
    //#include <SDL/SDL_image.h>
    #include <GL/glu.h>
    
    #include <cstring>
    #include <cstdlib>
    
    
    
    SDL_Cursor * cursorFromXPM(const char * xpm[])
    {
        int i, row, col;
        int width, height;
        Uint8 * data;
        Uint8 * mask;
        int hot_x, hot_y;
        SDL_Cursor * cursor = NULL;
    
        sscanf(xpm[0], "%d %d", &width, &height);
        data = (Uint8*)calloc(width/8*height,sizeof(Uint8));
        mask = (Uint8*)calloc(width/8*height,sizeof(Uint8));
    
        i = -1;
        for ( row=0; row<height; ++row )
        {
            for ( col=0; col<width; ++col )
            {
                if ( col % 8 )
                {
                    data[i] <<= 1;
                    mask[i] <<= 1;
                }
                else
                {
                    ++i;
                    data[i] = mask[i] = 0;
                }
                switch (xpm[4+row][col])
                {
                    case 'X':
                    data[i] |= 0x01;
                    mask[i] |= 0x01;
                    break;
                    case '.':
                    mask[i] |= 0x01;
                    break;
                    case ' ':
                    break;
                }
            }
        }
        sscanf(xpm[4+row], "%d,%d", &hot_x, &hot_y);
        //printf("data :%" PRIu8 "; mask:%" PRIu8 ";width:%d; height:%d; hot_x:%d; hot_y:%d\n", *data, *mask, width, height, hot_x, hot_y);
        cursor = SDL_CreateCursor(data, mask, width, height, hot_x, hot_y);
        free(data);
        free(mask);
        return cursor;
    }
    

    sdlglutils.h:

    #ifndef SDLGLUTILS_H
    #define SDLGLUTILS_H
    
    #include <GL/gl.h>
    #include <SDL/SDL.h>
    
    SDL_Cursor * cursorFromXPM(const char * xpm[]);
    
    #endif //SDLGLUTILS_H
    

1 个答案:

答案 0 :(得分:0)

您似乎遇到了Euler Angles的经典问题,增量轮换的顺序很重要。

此外,对于3D旋转的直观轨迹球行为,您需要考虑模型和全局旋转轴未对齐,因此当您将轨迹球增量应用于全局轴时,旋转可能处于意外方向。要解决此问题,您需要将用户输入坐标转换为全局空间,或者在用户空间中计算旋转矩阵,并将旋转矩阵从模型空间转换为全局空间,以便渲染做正确的事情。