Implementing a Virtual Trackball

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