Upload
kinny1974
View
219
Download
0
Embed Size (px)
Citation preview
7/29/2019 Implementing a Virtual Trackball
1/4
/07/12 Implementing a Virtual Trackball
ww.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html
Implementing a Virtual Trackball or Examiner Viewer
Roger Crawfis
The Ohio State University
A common interaction style for three-dimensional graphics mimics the paradigm of holding an object in your hand and
inspecting, or examining it. You can easily rotate about any axis, as well as bring it closer towards you. Without a data glove or
other type of 3D-input device, we need to provide this interface with the customary 2D mouse. This tutorial will assume a two
(or more) button mouse. The major source for the mathematics behind this comes from Ed Angel's OpenGL: A Primer.
The algorithm for accomplishing this, needs to perform the following steps (not neseccarily in order).
1. Detect the left-button of the mouse being depressed.
2. Keep track of the last known mouse position.
3. Treat the mouse position as the projection of a point on the hemi-sphere down to the image plane (along the z-axis), and determine that
point on the hemi-sphere.
4. Detect the mouse movement
5. Determine the g reat circle connecting the old mouse-hemi-sphere point to the cu rrent mouse-hemi-sphere point.
6. Calculate the normal to th is plane. This will be the axis about which to rotate.
7. Set the OpenGL state to modify the MODELVIEW matrix.
8. Read off the current matrix, since we want this operation to be the last transformation, not the first, and OpenGL does things LIFO.
9. Reset the model-view matrix to the identity
10. Rotate about the axis
11. Multiply the resulting matrix by the saved matrix.
12. Force a redraw of the scene.
Let's walk through these steps in more detail, and with code.
1. Detect the left-button of the mouse being depressed.2. Keep track of the last known mouse position.
This will be operating system dependent. Under Microsoft Visual Studio, you need to catch the WM_LBUTTONDOWN
event. In CLassWizard, select the View class. Scroll down the messages to find WM_LBUTTONDOWN, select it and click
on Add Function.
1. Detect the left-button
of the mouse being
//
// The OnLButtonDown method of the View class is called whever the
// left mouse button is depressed. We simply save the point and
// set the current interaction state to the trackball. Future
// calls to OnMouseMove will check the current state.
//
void CSierpinskiSolidsView::OnLButtonDown(UINT nFlags, CPoint point
{
//
7/29/2019 Implementing a Virtual Trackball
2/4
/07/12 Implementing a Virtual Trackball
ww.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html
depressed.
2. Keep track of the last
known mouse position
(mapped to the hemi-
sphere).
3. Set the OpenGL state
to modify theMODELVIEW matrix.
// Turn on user interactive rotations.
// As the user moves the mouse, the scene will rotate.
//
Movement = ROTATE;
//
// Map the mouse position to a logical sphere location.
// Keep it in the class variable lastPoint.
//
lastPoint = trackBallMapping( point );
//
// Make sure we are modifying the MODELVIEW matrix.
//
glMatrixMode( GL_MODELVIEW );
CView::OnLButtonDown(nFlags, point);
}
3. Treat the mouse position as the projection of a po int on the hemi-sphere down to the image plane (along the z-axis), anddetermine that point on the hemi-sphere.
We want grab a sphere with the mouse and drag it. What we want is the intersection of the ray going through the pixel with a
sphere centered on the image plane. This sphere will have a finite extent. If the ray does not intersect it, we will find the closest
point on the sphere that does. This will lie on the 2D circle in the image plane (z=0). The mouse point lies within this circle, then
it needs to be pulled up to the surface. If we consider the sphere as having a unit radius, then we simple add a z component to
our point to make it a unit length vector from the center. This is the point on the sphere. See the class notes for diagrams. The
current window size in terms of pixels is saved in windowSize for reference. Also, we make use of a simple vector class Vec3f.
Treat the mouse
position as the
projection of a
point on the
hemi-spheredown to the
image plane
(along the z-
axis), and
determine that
point on the
hemi-sphere.
//
// Util ity routine to calcu late the 3D position of a
// pro jected unit vector onto the xy-plane. Given any
// po int on the xy-plane, we can think of it as the projection
// from a sphere down onto the plane. The inverse is what we
// are a fter.
//
Vec3fCSierpinskiSolidsView::trackBallMapping(CPoint point)
{
Vec3f v;
float d;
v.x = (2.0*point.x - windowSize.x) / windowSize.x;
v.y = (windowSize.y - 2.0*point.y) / windowSize.y;
v.z = 0.0;
d = v.Length();
d = (d
7/29/2019 Implementing a Virtual Trackball
3/4
/07/12 Implementing a Virtual Trackball
ww.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html
Detect the mouse movements and update the display.
Detect the mouse
movement
voidCSierpinskiSolidsView::OnMouseMove(UINTnFlags, CPointpoint)
{
//
// Handle any necessary mouse movements
//
Vec3fdirection;floatpixel_diff;
floatrot_angle, zoom_factor;
Vec3fcurPoint;
switch (Movement)
{
case ROTATE :// Left-mouse button is being held down
{
curPoint = trackBallMapping( point );// Map the mouse posi tion to a logical
// sphere location.
direction = curPoint - lastPoint;
float velocity = direct ion.Length() ;
if( velocity > 0.0001 )// If little movement - do nothing.
{
Determine the great
circle connecting the
old mouse-hemi-
sphere point to the
current mouse-hemi-
sphere point.
Calculate the normal
to this plane. This will
be the axis about
which to rotate.
//
// Rotate about the axis that is perpendicular to the great circle connecting the
mouse movements.
//
Vec3frotAxis;
rotAxis.crossProd( lastPoint, curPoint );
rot_angle = veloci ty * m_ROTSCALE;
Read off the current
matrix, since we want
this operation to be
the last transformation,
not the first, and
OpenGL does things
LIFO.
Reset the model-view
matrix to the identityRotate about the axis
Multiply the resulting
matrix by the saved
matrix.
//
// We need to apply the rotation as the last t ransformat ion.
// 1. Get the curren t matrix and save it.
// 2. Set the matrix to the identi ty matrix (clear it).
// 3. Apply the trackbal l rota tion.
// 4. Pre-multiply i t by the saved matrix.
//
glGetFloatv( GL_MODELVIEW_MATRIX, (GLfloat *) objectXform
);glLoadIdentity() ;
glRotatef( rot_ang le, rotAxis.x, rotAxis.y, rotAxis.z );
glMultMatrixf( (GLfloat *) objectXform );
Force a redraw of the
scene.
//
// If we want to see it , we need to force the system to redraw the scene.
//
Invalidate( TRUE );
}
break;
7/29/2019 Implementing a Virtual Trackball
4/4
/07/12 Implementing a Virtual Trackball
ww.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html
}
Bonus: Code for
zooming into thepicture. Note, that this
is done
in GL_PROJECTION
mode. We simply
make an eye-space
zoom the very first
thing to occur.
case ZOOM :// Right-mouse but ton is being held down
//
// Zoom into or away from the scene based upon how far the
// mouse moved in the x-direct ion.
// This implementation does th is by scaling the eye-space.
// This should be the first opera tion performed by the GL_PROJECTION matrix.
// 1. Calculate the signed d istance
// a. movement to the left is negative ( zoom out).
// b. movement to the right is posit ive (zoom in).// 2. Calculate a scale factor for the scene s = 1 + a*dx
// 3. Call glScalef to have the scale be the first transformation.
//
pixel_diff = point .x - lastPoin t.x;
zoom_factor = 1.0 + pixel_di ff * m_ZOOMSCALE;
glScalef( zoom_factor, zoom_factor, zoom_factor ) ;
//
// Set the current point, so the lastPoint wi ll be saved properly below .
//
curPoint.x = (float) point.x; curPoint.y = (float) point.y; (float) curPoint.z = 0;
//
// If we want to see it , we need to force the system to redraw the scene.
//
Invalidate( TRUE );
break;
}
Save the current
mouse point as the
starting point for the
next movement.
//
// Save the location of the current point for the next movement.
//
lastPoint = curPoint;
CView::OnMouseMove(nFlags, point);
}
That's almost it. The only thing left to do is to catch the WM_LBUTTONUP event and turn off rotations for when the mousemoves. FYI. The method OnMouseMove is being called all of the time, even when a mouse button is not depressed. In this
case, it just passes right through without doing anything.
Turn off the
rotations
void CSierpinskiSolidsView::OnLButtonUp(UINT nFlags, CPoint point)
{
//
// Turn-off the rotations.
//
Movement = NONE;
CView::OnLButtonUp(nFlags, point);
}
That's it. For the Zoom above, we simply add routines to set the Movement=Zoom when the right mouse button is depressed,
and to unset it when it is released.
Source with a Sierpinski Gasket generator
Last updated Tuesday, January 21, 2003