用FLTK在opengl中实现轨迹球旋转:如何';记住';连续旋转

用FLTK在opengl中实现轨迹球旋转:如何';记住';连续旋转,opengl,fltk,trackball,Opengl,Fltk,Trackball,我正在从事一个FLTK项目(我第一次尝试GUIs和opengl:请耐心听我说!),并且有一个Fl_Gl_窗口,它根据一些其他小部件显示各种内容。其中一个选项是以3D显示屏幕内容,并使用户能够使用鼠标在3D中旋转屏幕。原则上一切都很好(我使用窗口处理程序中的Fl::event函数来实现鼠标/窗口位置,并简单地更新了以给定顺序应用的x、y和z旋转角度),但我这样做的方式是不直观的(因为非交换旋转等),所以我实现了一个轨迹球(类似于这里的:)。我理解这一切是如何工作的,并且可以让它在第一次鼠标拖动时按

我正在从事一个FLTK项目(我第一次尝试GUIs和opengl:请耐心听我说!),并且有一个Fl_Gl_窗口,它根据一些其他小部件显示各种内容。其中一个选项是以3D显示屏幕内容,并使用户能够使用鼠标在3D中旋转屏幕。原则上一切都很好(我使用窗口处理程序中的Fl::event函数来实现鼠标/窗口位置,并简单地更新了以给定顺序应用的x、y和z旋转角度),但我这样做的方式是不直观的(因为非交换旋转等),所以我实现了一个轨迹球(类似于这里的:)。我理解这一切是如何工作的,并且可以让它在第一次鼠标拖动时按照预期沿着正确的轴旋转。但是

问题是,据我所知,为了正常工作(即使用多个鼠标拖动),必须维护modelview矩阵,以便相对于当前显示的方向旋转对象,以便在每次鼠标拖动时对其应用glRotatef。现在,我学习了一些基本的openGL和FLTK的方法,就是有一个draw()函数,在任何事情发生变化时都会调用它,但这(据我和FLTK所知)必须从零开始,因为窗口的内容根据用户选项而变化(他们可能会选择2D视图等)并在以后绘制不打算旋转的对象。因此,我不知道如何对其进行编码,以使modelview矩阵在每次重新绘制时都能成功地更新

a) 在某些重绘时,它需要默认返回到无旋转(例如2D选项)(但我仍然希望“记住”3D对象的旋转)

b) 场景的其余部分不会旋转,所以我必须将矩阵弹出到上一个glOrtho

我能看到的直接解决办法是

1) 构建一个包含所有鼠标拖动的数组并进行相应的记录,例如,在第10次鼠标拖动时,通过glRotatef应用10次旋转(我不喜欢将此作为解决方案,它很难看!)

2) 记录modelview矩阵的状态,并在适当时保存和加载它。(我读过一些文章,认为这不是openGL应该使用的方式?)

3) 找到一个数学变换,它能够减少

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

这个解决方案将是最。。。令人愉快

(Psuedo-)代码:

class MyGl:公共Fl\U Gl\U窗口{
无效抽取();
int句柄(int);
无效映射到轨迹球(双*);
无效计算旋转();
//数据包括:
double old_vec[3];//半球上的旧鼠标位置
double new_vec[3];//在半球上新建鼠标位置
双ang;//旋转量;
双旋转[3];//旋转轴信息
公众:
MyGl(整数X,整数Y,整数W,整数H,常量字符*L):Fl_Gl_窗口(X,Y,W,H,L){
//构造器。。。
ang=0;rot[0]=0;rot[1]=0;rot[2]=0;
}
}
void MyGl::draw(){
如果(3D){
glLoadIdentity();
glViewport(0,0,w(),h());
格洛托(minx,maxx,miny,maxy,minz,maxz);
glPushMatrix();
glRotatef(ang,rot[0],rot[1],rot[2]);
//可以保存以前的旋转并将其放在此处,即:
//glRotatef(ang_old,rot_old[0],rot_old[1],rot_old[2]);
//看起来很笨重,需要无限次的旋转和记忆
//用户保持跟踪
绘制对象();//包括glBegin()、glVertex3f()和glEnd()等。
glPopMatrix();
//绘制其他未旋转的对象
}
}
int MyGl::handle(int e){
开关(e){
案例:鼠标按下
map_to_trackball(old_vec);//将鼠标的起始位置投影到半球上
//如果在draw()中记录连续执行的所有旧旋转
//我会在这里救他们。

返回1;//第二种方法确实是这里通常使用的方法,但是使用您自己的代码。在引擎盖下,glRotatef只做矩阵乘法:它设置一个旋转矩阵,并将其乘以当前选择的矩阵(可能是modelview),然后将其存储回该矩阵

我想第二种方法的真正含义是使用glLoadMatrixf和glGet加载和存储矩阵,同时在GL矩阵堆栈上修改它。然而,现代的方法是自己进行所有矩阵计算(所有矩阵修改在最新的OpenGL版本中都被弃用).类库非常有用,您只需存储多个矩阵,并在需要时将其加载到GL即可


关于第三种方法,我想它吸引你的原因是因为你不需要读和写GL,只需要写它。如果是这样的话,我建议你自己用GLM之类的东西来做矩阵运算。如果组件是只存储4个值而不是16个值,我建议您研究四元数,它可以存储旋转,并以非常类似于glRotatef使用的轴角度的形式轻松连接旋转,并且可以轻松转换为轴角度和轴角度。当然,旋转矩阵也可以转换为轴角度,但是rsion有点困难。

这里有一个相当不错的实现:。

已经实现了轨迹球…我只是忘记了URL。我是十多年前发现的(
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
}