简体   繁体   English

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

[英]implementing trackball rotation in opengl with FLTK: how to 'remember' successive rotations

I'm working on a FLTK project (my first attempt at GUIs and opengl: please bear with me!) and have an Fl_Gl_Window which displays various things depending on some other widgets. 我正在从事FLTK项目(我第一次尝试使用GUI和opengl:请多多包涵!),并拥有一个Fl_Gl_Window,它根据其他小部件显示各种内容。 One of the options is to display the contents of the screen in 3D and to have the use be able to rotate it in 3D with the mouse. 选项之一是以3D显示屏幕内容,并可以使用鼠标以3D旋转屏幕。 In principle all fine (I use the Fl::event functions in the window handler to achieve mouse/window positions and simply updated an x,y,and z rotation angle which were applied in a given order), but the way I was doing it was unintuitive (because of non-commuting rotations etc etc) so I am implementing a trackball (similar to the one here: http://www.csee.umbc.edu/~squire/download/trackball.c ). 原则上,一切都很好(我在窗口处理程序中使用Fl :: event函数来实现鼠标/窗口的位置,并简单地更新了以给定顺序应用的x,y和z旋转角度),但是我的方式是这是不直观的(因为不进行换向等),所以我正在实现一个轨迹球(类似于此处的轨迹球: http : //www.csee.umbc.edu/~squire/download/trackball.c )。 I understand how this all works and can get it to rotate as expected along the correct axis with the first mouse drag. 我了解这一切的工作原理,并且可以通过第一次拖动鼠标使其沿着正确的轴按预期的方向旋转。 But... 但...

The problem is, as far as I can tell, is that in order to work generally (ie with multiple mouse drags) the modelview matrix has to be maintained in order to rotate the object relative to the currently displayed orientation so that a glRotatef is applied to it with every mouse drag. 据我所知,问题在于,为了正常工作(即使用多个鼠标拖动),必须保持模型视图矩阵,以便相对于当前显示的方向旋转对象,以便应用glRotatef每次拖动鼠标都可以对其进行操作。 Now, the way I have learned some basic openGL with FLTK is to have a draw() function which is called whenever anything changes, but this (as far as I can tell in FLTK and for me) has to start from scratch every time as the window has contents which changes depending on user options (they might select the 2D view etc. etc.) and also draws things later that are not intended to be rotated. 现在,我用FLTK学习一些基本的openGL的方法是有一个draw()函数,只要有任何变化,都可以调用该函数,但是(据我所知在FLTK中,就我而言)每次都必须从头开始。窗口中的内容根据用户选项(它们可能会选择2D视图等)而改变,并且以后还会绘制不打算旋转的内容。 As such I can't see how to code it such that the modelview matrix is sucessively updated with every redraw as 因此,我看不到如何编码,以便每次重新绘制时都会成功更新modelview矩阵

a) on some redraws it will need to default back to no rotations (eg 2D option) (but I would still like to 'remember' the rotation of the 3D object) a)在某些重绘上,它将需要默认恢复为不旋转(例如2D选项)(但我仍然想“记住” 3D对象的旋转)

b) the rest of the scene is not to be rotated so I have to pop the matrix back to the previous glOrtho... b)场景的其余部分不旋转,因此我必须将矩阵弹出回到先前的glOrtho ...

Immediate ways round it I can see are 我能看到的直接方法是

1) build an array of all mouse drags and record accordingly so that, for example, on the 10th mouse drag 10 rotations are applied through glRotatef (I don't like this as a solution, it is ugly!) 1)建立一个所有鼠标拖动的数组并进行相应记录,例如,在第10次鼠标拖动上,通过glRotatef进行10轮旋转(我不喜欢这样做,这很丑陋!)

2) Record the state of the modelview matrix and save & load it when appropriate. 2)记录模型视图矩阵的状态,并在适当时保存并加载。 (I have read things that suggest this is not how openGL is supposed to be used?) (我读过一些东西,暗示这不是应该使用openGL的方式吗?)

3) Find a mathematical transform that manages to reduce 3)找到可以减少的数学变换

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

into

glRotatef(ang_tot,ax1_tot,ax2_tot,ax3_tot);

This solution would be the most... pleasing. 这个解决方案将是最令人愉悦的。

(Psuedo-)code: (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
}

这是一个相当不错的实现: https : //github.com/sanko/fltk-2.0.x/blob/master/test/trackball.c

The second way is indeed what is usually done here, but in your own code. 第二种方法确实是通常在这里执行的操作,但是使用您自己的代码。 Under the hood, glRotatef does nothing more than matrix multiplication: It sets up a rotation matrix and multiplies it by the currently selected matrix (presumable the modelview), which is then stored back to that matrix. 在幕后,glRotatef只做矩阵乘法:它设置一个旋转矩阵,并将其乘以当前选择的矩阵(可能是模型视图),然后将其存储回该矩阵。

I suppose what you really meant with your second way was to use glLoadMatrixf and glGet to load&store the matrix, while modifying it on the GL matrix stack. 我想第二种方法的真正含义是使用glLoadMatrixf和glGet加载和存储矩阵,同时在GL矩阵堆栈上进行修改。 However, the modern way to do this is to do all the matrix calculations yourself (all the matrix modification is deprecated in the most recent OpenGL versions). 但是,执行此操作的现代方法是您自己进行所有矩阵计算(在最新的OpenGL版本中不建议使用所有矩阵修改)。 Libraries like GLM help a great deal and you can simply store multiple matrices and load it to the GL whenever needed. GLM这样的库有很多帮助,您可以简单地存储多个矩阵,并在需要时将其加载到GL。

Concerning the third way, I guess that the reason it appeals to you is because you don't need to read from and write to the GL, but only write to it. 关于第三种方式,我想它之所以吸引您,是因为您不需要读取和写入GL,而只需对其进行写入。 If that's the case, doing the matrix operations yourself using something like GLM is what I would recommend. 如果是这种情况,我建议您使用GLM之类的方法自己进行矩阵运算。 If the "pleasing" component is that you only store 4 values instead of 16, I suggest you look into quaternions, which can store rotations and easily concatenate rotations in a form very similar to axis-angle that glRotatef uses - and it can easily be converted to and from axis-angle. 如果“令人愉悦”的组件是您仅存储4个值而不是16个值,则建议您查看四元数,它可以存储旋转并以类似于glRotatef使用的轴角的形式轻松地连接旋转-并且可以很容易地转换为轴角或从轴角转换。 Of course, rotational matrices can also be converted to axis-angle, but the conversion is a little more difficult. 当然,旋转矩阵也可以转换为轴角,但是转换要困难一些。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM