Реализация вращения трекбола в 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) (но я все же хотел бы "запомнить" поворот трехмерного объекта)

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

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

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);

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

(Psuedo-) код:

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
}

2 ответа

Решение

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

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

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

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

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