How to rotate entire scene based on mouse events?

Hello,

I need to perform rotation of the canvas or scene based on mouse click and drag. I am still not sure whether the method I’ve used for computing the rotation angle based on mouse events is correct or not. I have the following projection settings

 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 glRotatef(atan2f(currentPoint.y-previousPoint.y, currentPoint.x-previousPoint.x), 0.0, 0.0, -1.0);
 glOrtho(centerp.x-screenwidth/2, centerp.x+screenwidth/2, 
         centerp.y-screenheight/2, centerp.y+screenheight/2, 1.0, 10000.0); 

With the above projection, I am able to produce rotation along z-axis. i.e. I can rotate the canvas clockwise, or anti-clockwise direction in 2D. The same didn’t work for 3D. I tried using

glRotatef(atan2f(currentPoint.y-previousPoint.y, currentPoint.x-previousPoint.x), 1.0, 1.0, -1.0); 

With this setting, I get rotation very fast and only in one direction (i.e. diagonally). It doesn’t depend on the mouse positions. Click anywhere and drag, it rotates diagonally from top-right to bottom-left. What am I missing? How can I rotate entire scene (canvas) in 3D?

Any suggestions or comments?

Thanks & Regards
Rakesh Patil

Generally, what you’ve done is wrong. I have no idea how it can work. glRotate in your case does nothing since next command recreates projection matrix. glRotaet should affect model-view matrix.

I also don’t know. Saw it in one example and tried to attempt it. Well, if this is wrong, then can you point me to some tutorials and/or examples for mouse event based 3D rotation?

Thanks & Regards

Rakesh Patil

For the beginning, take a look at chapter 3 of the old Red Book. That would help you to understand how transformations work.
Then try to rotate your scene around two axes according to deltaX and deltaY mouse movements.

Since you want to rotate a whole scene (view transformation), intended rotations should be performed before other model transformations.
Try something like this:


// On mouse move
angleY = (currentPoint.x-previousPoint.x) * factX;
angleX = (currentPoint.f-previousPoint.y) * factY;

// Draw scene function
glLoadIdentity();
glTranslatef(0.0, 0.0, translateZ);
	glRotatef(m_angleX, 1.0, 0.0, 0.0);
	glRotatef(m_angleY, 0.0, 1.0, 0.0);

I see. What you explained is rotating the model isn’t it? I think the previous code does give the rotation, not by rotating the model, but by rotating the camera in opposite direction. That’s why I was getting rotation with my previous code. I remember reading somewhere, panning can be done either by moving the model along the mouse move direction, or by moving the camera in opposite direction. Is that correct?

Well, I just tried your solution, but with that I am not getting desired output. I googled on this topic and came across some articles in which they suggested using Quaternions for rotation. I couldn’t find any suitable tutorials for this. Any suggestions?

Thanks & Regards
Rakesh Patil

OpenGL doesn’t have a “camera”.

Rotating the scene or rotating the camera are the same thing. That’s why I have point you to read a chapter about transformations.
You have to get basic concepts before going on further.

Great! If you don’t explain what you want how you could expect anyone can help you.
I’m sorry for this, but your post looks like this to me: “I’m not familiar with the basic concepts. I have a solution which output is not suitable for my purpose. Your solution is also not acceptable, but I won’t tell you what’s wrong. I’m just not getting desired output. I heard that there is something called Quaternions that might help, but I didn’t find any suitable tutorial.” :confused:

Quaternions are better for the rotation definition since they eliminate gimbal lock. There are a lot of code samples on the net. I think even glm has quaternion implementation (I’m not sure because I’m not using it, but according to some questions on the forum it does support). Just search for them. But be aware that they are further abstraction of complex numbers and they are hard to be comprehend. Maybe you can just use something found on the net…

Hi, I had an implementation previously that also requires rotating a camera around the scene. Just a suggestion here, in my implementation I used the function gluLookAt(…) to setup my camera. This function has an eye position (position of ‘camera’), a look-at position describing where the camera is pointing, and the camera up vector.

Some things are assumed in my implementation: the camera always looks at the origin, and no panning or camera distance alterations is required. But these can be incorporated with mathematical transformations, if required.

So, rotating the camera simply means changing the eye position (eye_x, eye_y, eye_z) with a rotation around the center. To do this, we can multiply a 3x3 rotation matrix (with angle parameters defined by mouse movements) with the initial eye position to obtain the transformed (rotated) camera position.

Hope this helps :slight_smile:

I’m sorry. I might have not conveyed my requirements properly. How to compute the angle of rotation from the mouse events?

@wmchiew: Are you using Orthographics projection?

Thanks

What are you trying to rotate, specifically? In opengl, the simplest scene transformations are described by 3 matrices: model, view and projection. In general, model matrix deals with your objects, view matrix deals with the camera, and projection matrix defines your projection type.

So, the projection type (orthographic glOrtho(…) or perspective gluPerspective(…)) is independent of the camera setup (view matrix gluLookAt(…)).

To answer your first question: How to compute angle of rotation from mouse events?
It is just as Aleksandar has given in post #4. Your rotation angle will be dependent on the offset of your mouse pointer position (currentPoint - previousPoint). In the code the x-offset rotates around the x-axis and y-offset around the y-axis. You just need to define how much your rotation angle is with respect to the offset. In the given code this is by adjusting the value of factX and factY.

Mouse gestures can be interpreted according to needs of your application.
If you know the orientation of lookAt vector at the time gesture starts (V1), and you know its orientation after gesture (V2), then you should rotate around vector V = V1 x V2, and the angle is ang = arccos((V1.V2)/|V1.V2|), where x is vector product, . - scalar (dot) product, || - norma (length).

With quaternions, if qc ia a quaternion that represents orientation of the camera, and qr is a quaternion that represents desired rotation, then
qc = qc * qr

The problem is to define quaternion that represents rotation. If you know the axis around which you want to rotate and the angle, then:


void Quaternion::fromAxis(const vec &axis, double angle)
{
  double sinAngle;
  angle *= 0.5;
  vect vn(v);
  vn.normalize();
 
  sinAngle = sin(angle);
 
  x = (vn.x * sinAngle);
  y = (vn.y * sinAngle);
  z = (vn.z * sinAngle); 
  w = cos(angle);
}

Hi,

Sorry for the late reply. I was stuck up with some other work and so this project was kept on hold. Coming to the point, this is what I did:

OnMouseDown event:

firstVector = ScreenToWorld(event.GetX(), event.GetY());

OnMouseDown & OnMouseDrag event:

secondVector = ScreenToWorld(event.GetX(), event.GetY());
rotAxis = EVector3::CrossProduct(firstVector, secondVector);
double ag = EVector3::DotProduct(firstVector, secondVector);
rotAngle += ag;

OnMouseUp event:

secondVector = ScreenToWorld(event.GetX(), event.GetY());
rotAxis = EVector3::CrossProduct(firstVector, secondVector);
double ag = EVector3::DotProduct(firstVector, secondVector);
rotAngle += ag;

OnPaintEvent

glViewPort(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(centerp.x-screenwidth/2, centerp.x+screenwidth/2, 
         centerp.y-screenheight/2, centerp.y+screenheight/2, 1.0, 10000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(rotAngle * 180/PI, rotAxis.x, rotAxis.y, rotAxis.z);

With this setup, I do get rotation and model get rotated in single direction, irrespective of the direction of the mouse movement. I mean if the mouse is moved from left to right, then the scene should get rotated around y-axis isn’t it? But here I am getting rotation in single direction. I am sure I am missing something or I might be doing something wrong. First of all, is the logic correct what I am doing? Please guide me what is my mistake?

Thanks

Where does ScreenToWorld() get its Z coordinate from?

If the world Z coordinate is a constant, firstVector and secondVector will both be parallel to the screen, and rotAxis will always be the Z axis.

If you want trackball like behaviour, the rotation axis should be the cross product of the Z axis and the relative mouse motion (i.e. the current position minus the mouse-down position).

[QUOTE=GClements;1255637]Where does ScreenToWorld() get its Z coordinate from?

If the world Z coordinate is a constant, firstVector and secondVector will both be parallel to the screen, and rotAxis will always be the Z axis.

If you want trackball like behaviour, the rotation axis should be the cross product of the Z axis and the relative mouse motion (i.e. the current position minus the mouse-down position).[/QUOTE]

I’m using gluUnproject to convert screen coordinate to world coordinate… z value is coming from unproject function…

gluUnProject() takes a Z coordinate as its parameter. Where is it getting it from? If it’s constant, then everything in my previous post still applies.

Below is my code for screentoworld function

EVector3 ScreenToWorld(int mouseX, int mouseY)
{
   glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
   glGetDoublev(GL_PROJECTION_MATRIX, projection);
   glGetIntegerv(GL_VIEWPORT, viewport);

   posx = (double)mouseX;
   posy = (double)mouseY;
   glReadPixels(mousex, mousey, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &posz);
   
   gluUnproject(posx, posy, posz, modelview, projection, viewport, &worldx, &worldy, &worldz);
   return EVector3(worldx, worldy, worldz);
}

So the z value is taken from the depth component, which is read using glReadPixel function. Actually, I need to make to more general form so that rotation works for both: constant z, and varying z values.

Thanks