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

StackOverflow https://stackoverflow.com/questions/22299318

  •  12-06-2023
  •  | 
  •  

Question

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. 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. 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). 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. 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. As such I can't see how to code it such that the modelview matrix is sucessively updated with every redraw as

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)

b) the rest of the scene is not to be rotated so I have to pop the matrix back to the previous 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!)

2) Record the state of the modelview matrix and save & load it when appropriate. (I have read things that suggest this is not how openGL is supposed to be used?)

3) Find a mathematical transform that manages to reduce

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:

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
}
Was it helpful?

Solution

Here is a fairly decent implementation: https://github.com/sanko/fltk-2.0.x/blob/master/test/trackball.c .

OTHER TIPS

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.

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. 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). Libraries like GLM help a great deal and you can simply store multiple matrices and load it to the GL whenever needed.

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. If that's the case, doing the matrix operations yourself using something like GLM is what I would recommend. 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. Of course, rotational matrices can also be converted to axis-angle, but the conversion is a little more difficult.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top