реализация вращения трекбола в opengl с помощью FLTK: как «запомнить» последовательные вращения

Я работаю над проектом FLTK (моя первая попытка графического интерфейса и opengl: пожалуйста, потерпите меня!) И у меня есть Fl_Gl_Window, который отображает различные вещи в зависимости от некоторых других виджетов. Один из вариантов — отображать содержимое экрана в 3D и иметь возможность вращать его в 3D с помощью мыши. В принципе, все в порядке (я использую функции Fl::event в обработчике окна для достижения положения мыши/окна и просто обновляю угол поворота x, y и z, которые применялись в заданном порядке), но то, как я это делал это было неинтуитивно (из-за некоммутирующих вращений и т. д. и т. д.), поэтому я реализую трекбол (похожий на тот, что здесь: http://www.csee.umbc.edu/~squire/download/trackball.c). Я понимаю, как все это работает, и могу заставить его вращаться, как и ожидалось, вдоль правильной оси при первом перетаскивании мышью. Но...

Проблема, насколько я могу судить, заключается в том, что для работы в целом (т.е. с несколькими перетаскиваниями мыши) необходимо поддерживать матрицу просмотра модели, чтобы повернуть объект относительно текущей отображаемой ориентации, чтобы применить glRotatef к нему при каждом перетаскивании мышью. Итак, способ, которым я научился базовому openGL с FLTK, состоит в том, чтобы иметь функцию draw(), которая вызывается всякий раз, когда что-либо изменяется, но это (насколько я могу судить в FLTK и для меня) должно начинаться с нуля каждый раз, когда окно имеет содержимое, которое меняется в зависимости от параметров пользователя (они могут выбрать 2D-вид и т. д. и т. д.), а также позже рисует объекты, которые не предназначены для поворота. Таким образом, я не вижу, как его закодировать, чтобы матрица просмотра модели последовательно обновлялась при каждой перерисовке, как

а) при некоторых перерисовках потребуется по умолчанию вернуться к отсутствию вращения (например, вариант 2D) (но я все же хотел бы «запомнить» вращение 3D-объекта)

б) остальная часть сцены не должна вращаться, поэтому я должен вернуть матрицу к предыдущему glOrtho...

Непосредственные пути вокруг этого, которые я вижу,

1) построить массив всех перетаскиваний мыши и записать соответственно так, чтобы, например, на 10-й перетаскивание мыши применялось 10 вращений через glRotatef (мне это как решение не нравится, некрасиво!)

2) Запишите состояние матрицы представления модели и сохраните и загрузите ее, когда это необходимо. (Я читал вещи, которые предполагают, что это не то, как предполагается использовать openGL?)

3) Найдите математическое преобразование, которое позволяет уменьшить

glRotatef(ang1,ax1_1,ax2_1,ax3_1);
glRotatef(ang2,ax1_2,ax2_2,ax3_2);

в

glRotatef(ang_tot,ax1_tot,ax2_tot,ax3_tot);

Это решение было бы самым... приятным.

(Псевдо)код:

class MyGl : public Fl_Gl_Window {

   void draw();
   int handle(int);
   void map_to_trackball(double*);
   void calc_rotation();

   //data including:
   double old_vec[3];//old mouse position on hemisphere
   double new_vec[3];//new mouse position on hemisphere
   double ang; //rotation amount;
   double rot[3]; //axis of rotation information

   public:

   MyGl(int X, int Y, int W, int H, const char *L): Fl_Gl_Window(X, Y, W, H, L) {
    //constructor...
      ang=0;rot[0]=0;rot[1]=0;rot[2]=0;
   }
}

void MyGl::draw(){

    if (3D){

        glLoadIdentity();
        glViewport(0,0,w(),h());
        glOrtho(minx,maxx,miny,maxy,minz,maxz);
        glPushMatrix();
        glRotatef(ang,rot[0],rot[1],rot[2]);

        //could save previous rotations and put them here ie:
        // glRotatef(ang_old,rot_old[0],rot_old[1],rot_old[2]);
        // seems clunky and would require a limitless number of rotations and memory if
        // the user keeps tracking

        draw_object(); //including glBegin(), glVertex3f() and glEnd() etc.
        glPopMatrix();

        // draw other non rotated things
    }

}


int MyGl::handle(int e){

    switch(e){
        case: MOUSE_DOWN
            map_to_trackball(old_vec);//projects starting mouse position onto hemisphere
            //if recording all old rotations for successive implementation in draw() 
            // would save them here.             
            return 1; //<-- needed in FLTK to recognise drag event
        case: DRAG (//pseudocode
            map_to_trackball(new_vec);//projects current dragged mouse 
                                      //position onto hemisphere
            calc_rotation(); //calculates and sets ang and rot[3] 
                             //using old_vec and new_vec
            break;
    }
    return Fl_Gl_Window::handle(e);
}

void map_to_trackball(double* v){
  // basically trackball_ptov() from http://www.csee.umbc.edu/~squire/download/trackball.c
}

void calc_rotation(){
  // basically mouseMotion() from http://www.csee.umbc.edu/~squire/download/trackball.c
}

person user3353819    schedule 10.03.2014    source источник
comment
Трекбол уже реализован... Я просто забыл URL. Я нашел его более десяти лет назад. :(   -  person DejanLekic    schedule 10.03.2014


Ответы (2)


Вот довольно приличная реализация: https://github.com/sanko/fltk-2.0.x/blob/master/test/trackball.c .

person DejanLekic    schedule 10.03.2014

Второй способ — это то, что обычно делается здесь, но в вашем собственном коде. Под капотом glRotatef не делает ничего, кроме умножения матриц: он устанавливает матрицу вращения и умножает ее на текущую выбранную матрицу (предположительно модельное представление), которая затем сохраняется обратно в эту матрицу.

Я полагаю, что вы действительно имели в виду свой второй способ, это использовать glLoadMatrixf и glGet для загрузки и сохранения матрицы, изменяя ее в стеке матрицы GL. Однако современный способ сделать это — выполнить все вычисления матриц самостоятельно (все модификации матриц устарели в самых последних версиях OpenGL). Такие библиотеки, как GLM, очень помогают, и вы можете просто хранить несколько матриц и загружать их в GL при необходимости.

Что касается третьего способа, я думаю, причина, по которой он вам нравится, заключается в том, что вам не нужно читать и писать в GL, а только писать в него. Если это так, я бы рекомендовал выполнять операции с матрицами самостоятельно, используя что-то вроде GLM. Если «приятным» компонентом является то, что вы храните только 4 значения вместо 16, я предлагаю вам изучить кватернионы, которые могут хранить повороты и легко объединять повороты в форме, очень похожей на угол оси, который использует glRotatef, — и это может быть легко преобразуется в ось-угол и обратно. Конечно, матрицы вращения также можно преобразовать в ось-угол, но преобразование немного сложнее.

person ltjax    schedule 10.03.2014