Вращение трекбольной мыши в OpenGL

Я пытаюсь реализовать базовое решение для трекбола с openGL.

Есть 2 поворота: - вокруг оси х (в правильном направлении), связанных с движением мыши вверх / вниз. - вокруг оси y (направление вверх), связанной с движением правой / левой мыши. Куб центрирован на 0, камера на оси z. Оси нарисованы: x синим, y зеленым, z красным.

2 разные группы вращений:

  1. 90° вокруг x, перемещая мышь вниз. Z вниз, x вправо, y впереди. Затем еще один поворот: 90° вокруг z (z как показано), движение мыши справа. z в правильном направлении, x вверх и y впереди.

  2. Я снова обрабатываю, позиция начинается с нуля. 90° вокруг y. y: вверх, z: вправо, x: впереди Еще один поворот на 90° вокруг z. x: вверх, z: вправо: y: впереди.

Я идентифицирую другое поведение между вторыми вращениями.

Для согласованности я ждал, что для первой операции второе вращение было вокруг z или для второй операции, второе вращение было вокруг оси x. Зачем?

Для меня, вторая операция имеет вращение goood, это поведение, которое я хотел бы иметь. Спасибо за помощь.

Ниже кода

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 ответ

Кажется, вы столкнулись с классической проблемой с углами Эйлера, порядок последовательных вращений имеет значение.

Кроме того, для интуитивного поведения трекбола для трехмерного вращения необходимо учитывать, что оси модели и глобального вращения не выровнены, и поэтому при применении дельт трекбола к глобальным осям вращение может происходить в неожиданном направлении. Чтобы исправить это, необходимо выполнить преобразование координат пользовательского ввода в глобальное пространство или вычислить матрицу вращения в пользовательском пространстве и преобразовать эту матрицу вращения из пространства модели в глобальное пространство, чтобы рендеринг выполнялся правильно.

Другие вопросы по тегам