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

时间:2014-03-10 11:51:34

标签: opengl fltk trackball

我正在开发一个FLTK项目(我第一次尝试使用GUI和opengl:请耐心等待我!)并有一个Fl_Gl_Window,根据其他一些小部件显示各种内容。其中一个选项是以3D显示屏幕内容,并且可以使用鼠标将其旋转为3D。原则上一切都很好(我在窗口处理程序中使用Fl :: event函数来实现鼠标/窗口位置并简单地更新以给定顺序应用的x,y和z旋转角度),但是我正在做的方式它是不直观的(因为非通勤轮换等)所以我正在实现一个轨迹球(类似于这里的轨迹球:http://www.csee.umbc.edu/~squire/download/trackball.c)。我理解这一切是如何工作的,并且可以通过第一次鼠标拖动使其沿着正确的轴旋转。但...

据我所知,问题在于,为了普遍工作(即使用多个鼠标拖动),必须维护模型视图矩阵,以便相对于当前显示的方向旋转对象,以便每次拖动鼠标时都会应用glRotatef。现在,我用FLTK学习一些基本的openGL的方法就是有一个draw()函数,只要有任何变化就会调用它,但是这个(据我在FLTK和我说的那样)必须每次都从头开始窗口具有根据用户选项而改变的内容(它们可能选择2D视图等等)并且还在稍后绘制不打算旋转的内容。因此,我无法看到如何对其进行编码,以便每次重绘都会对模型视图矩阵进行过度更新

a)在某些重绘上,它需要默认返回无旋转(例如2D选项)(但我仍然想'记住'3D对象的旋转)

b)场景的其余部分不能旋转,所以我必须将矩阵弹回到之前的glOrtho ...

我能看到的直接方式是

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

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 个答案:

答案 0 :(得分:1)

答案 1 :(得分:0)

第二种方式确实是通常在这里完成的,但是在你自己的代码中。在引擎盖下,glRotatef只做矩阵乘法:它设置一个旋转矩阵并将其乘以当前选择的矩阵(可以推测为模型视图),然后将其存储回该矩阵。

我认为你的第二种方式真正意味着使用glLoadMatrixf和glGet来加载和存储矩阵,同时在GL矩阵堆栈上修改它。但是,现代的方法是自己进行所有矩阵计算(所有矩阵修改都在最新的OpenGL版本中弃用)。像GLM这样的库可以提供很多帮助,您只需存储多个矩阵并在需要时将其加载到GL中。

关于第三种方式,我猜它吸引你的原因是因为你不需要读取和写入GL,但只有 写入它。如果是这种情况,我会推荐使用像GLM这样的矩阵操作。如果“令人愉悦”的组件是你只存储4个值而不是16个,我建议你研究四元数,它可以存储旋转并以一种非常类似于glRotatef使用的轴角的形式连接旋转 - 它很容易就可以了转换为轴角度和从轴角度转换。当然,旋转矩阵也可以转换为轴角,但转换要困难一些。