Solved

Implementing a fly-through function

Posted on 2011-02-16
17
1,155 Views
Last Modified: 2013-12-26
I am interested in implemeting a fly-through function for a 3D image within OpenGL for use within C#.  

From MATLAB:

"
A fly-through is an effect created by moving the camera through three-dimensional space, giving the impression that you are flying along with the camera as if in an aircraft. You can fly through regions of a scene that might be otherwise obscured by objects in the scene or you can fly by a scene by keeping the camera focused on a particular point.

To accomplish these effects, move a camera along a particular path, the x-axis for example, in a series of steps. To produce a fly-through, move both the camera position and the camera target at the same time.
"

Assuming that I have a representation of an object as a set of (x, y, z) coordinates, the ability to fully rotate this object along each axis (I am using a standard TrackBall algorithm for this), what extra computations do I need to achieve a fly-though?

Initially I thought a combination of zoom and rotation would function, however a similar function within Adobe changes the camera AND the target properties simultaneously. Zoom and rotation only alters the camera parameters (x, y, z). If I could work out the relationship between the camera and the target, and map onto some OpenGL functions, that would be a starting point I think.

References:

http://www.mathworks.com/help/techdoc/visualize/f4-45695.html

http://acrobatusers.com/auc/content/tutorials/id_2100/nov2009_3dmodel.pdf
0
Comment
Question by:alexhenryjames
  • 10
  • 7
17 Comments
 
LVL 20

Expert Comment

by:ikework
ID: 34907278
Hey alexhenryjames,

easiest would be moving the camera-position for starters.

Can you show what functions you use to setup your camera?


ike
0
 

Author Comment

by:alexhenryjames
ID: 34907443
Hi,.

The code I am experimenting with comes from this example (MFC / OpenGL):

http://www.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html. It contains the OpenGL routines I am using, in that example rotating a shape with a virtual trackball.

I think the functions you refer to are in the OnSize event within SierpinskiSolidsView.cpp:

   //
   // Determine the new aspect ratio
   //
   GLdouble gldAspect = (GLdouble) cx/ (GLdouble) cy;
   //
   // Reset the projection matrix with the new aspect ratio.
   //
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(40.0, gldAspect, 1.001, 60.0);
   glTranslatef( 0.0, 0.0, -25.0 );
   //
   // Set the viewport to take up the entire window.
   //
   glViewport(0, 0, cx, cy);
   windowSize.x = cx;
   windowSize.y = cy;
0
 
LVL 20

Expert Comment

by:ikework
ID: 34908173
Thats the code to setup the viewport/projection. Can you show your render-function?
0
 

Author Comment

by:alexhenryjames
ID: 34908526
Hello,

On (left) Mouse Move, we have:

<...>

curPoint = trackBallMapping( point );  // Map the mouse position to a logical sphere location.
                        direction = curPoint - lastPoint;
                        float velocity = direction.Length();
                        if( velocity > 0.0001 )
                        {
                              //
                              // Rotate about the axis that is perpendicular to the great circle connecting the mouse movements.
                              //
                              Vec3f rotAxis;
                              rotAxis.crossProd( lastPoint, curPoint );
                              rot_angle = velocity * m_ROTSCALE;
                              //
                              // We need to apply the rotation as the last transformation.
                              //   1. Get the current matrix and save it.
                              //   2. Set the matrix to the identity matrix (clear it).
                              //   3. Apply the trackball rotation.
                              //   4. Pre-multiply it by the saved matrix.
                              //
                              glGetFloatv( GL_MODELVIEW_MATRIX, (GLfloat *) objectXform );
                              glLoadIdentity();
                              glRotatef( rot_angle, rotAxis.x, rotAxis.y, rotAxis.z );
                              glMultMatrixf( (GLfloat *) objectXform );
                              //
                              //  If we want to see it, we need to force the system to redraw the scene.
                              //
                              Invalidate( TRUE );
                        }
                        break;

<...>

 SierpinskiSolidsView.cpp
0
 
LVL 20

Expert Comment

by:ikework
ID: 34908596
That is On (left) Mouse Move as you say, I mean the function that renders your ogjects each frame.
0
 

Author Comment

by:alexhenryjames
ID: 34908694
My apologies,

In the example, the rendering is a combination of cubes and tetrahedrons:

TETRAHEDRON:


void Tetrahedron::DrawFaces(const float rotAngle )
{
      Vec3f centroid;
      centroid = Pts[0];
      centroid += Pts[1];
      centroid += Pts[2];
      centroid += Pts[3];
      centroid *= 0.25;
      glPushMatrix();
      glTranslatef( centroid.x, centroid.y, centroid.z );
      glRotatef( rotAngle, 0.5, 1.0, 1.0 );
      glTranslatef( -centroid.x, -centroid.y, -centroid.z );

      Vec3f crossVec;
      glBegin( GL_TRIANGLES );
            //glColor3f( 1.0, 1.0, 1.0 );
            crossVec.crossProd( Pts[0], Pts[2], Pts[1] );
            glNormal3f( crossVec.x, crossVec.y, crossVec.z );
            glVertex3f( Pts[0].x, Pts[0].y, Pts[0].z );
            glVertex3f( Pts[2].x, Pts[2].y, Pts[2].z );
            glVertex3f( Pts[1].x, Pts[1].y, Pts[1].z );

            //glColor3f( 0.0, 0.0, 1.0 );
            crossVec.crossProd( Pts[0], Pts[1], Pts[3] );
            glNormal3f( crossVec.x, crossVec.y, crossVec.z );
            glVertex3f( Pts[0].x, Pts[0].y, Pts[0].z );
            glVertex3f( Pts[1].x, Pts[1].y, Pts[1].z );
            glVertex3f( Pts[3].x, Pts[3].y, Pts[3].z );

            //glColor3f( 1.0, 1.0, 0.0 );
            crossVec.crossProd( Pts[1], Pts[2], Pts[3] );
            glNormal3f( crossVec.x, crossVec.y, crossVec.z );
            glVertex3f( Pts[1].x, Pts[1].y, Pts[1].z );
            glVertex3f( Pts[2].x, Pts[2].y, Pts[2].z );
            glVertex3f( Pts[3].x, Pts[3].y, Pts[3].z );

            //glColor3f( 0.0, 1.0, 1.0 );
            crossVec.crossProd( Pts[2], Pts[0], Pts[3] );
            glNormal3f( crossVec.x, crossVec.y, crossVec.z );
            glVertex3f( Pts[2].x, Pts[2].y, Pts[2].z );
            glVertex3f( Pts[0].x, Pts[0].y, Pts[0].z );
            glVertex3f( Pts[3].x, Pts[3].y, Pts[3].z );

        glEnd();

        glPopMatrix();

}
0
 
LVL 20

Expert Comment

by:ikework
ID: 34909219
Ok I see, can you post the function that calls this Draw function?
0
 
LVL 20

Expert Comment

by:ikework
ID: 34909422
I dont use MFC, but if I remember correctly, there was a "WM_PAINT" message, can you search for that message in your project and post the function which is called there?
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 

Author Comment

by:alexhenryjames
ID: 34911580

Hello, draw functions below

/////////////////////////////////////////////////////////////////////////////
// CSierpinskiSolidsView drawing

void CSierpinskiSolidsView::OnDraw(CDC* pDC)
{
      CSierpinskiSolidsDoc* pDoc = GetDocument();
      ASSERT_VALID(pDoc);

      // Make the HGLRC current.
      BOOL bResult = wglMakeCurrent (pDC->m_hDC, m_hrc);
      if (!bResult)
      {
        TRACE("wglMakeCurrent Failed %x\r\n", GetLastError() ) ;
      }

      //
      // Draw the scene
      //
      glMatrixMode( GL_MODELVIEW );
      glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

      MAX_Level = pDoc->Recursion_Level;

      if( Shadows )
      {
            //
            //  Initialize the random number generator to get consistent results
            //    for each frame or each pass thru with multi-pass techniques.
            //
            srand( 0 );
            //
            //  Draw the scene flattened onto the ground plane.
            //
            DrawShadows( pDoc );
      }

      //
      //  Initialize the random number generator to get consistent results
      //    for each frame or each pass thru with multi-pass techniques.
      //
      srand( 0 );

      if( pDoc->Draw_Tetra )
            SierpinskiGen( pDoc->BaseTetra, 0 );


      if( pDoc->Draw_Sponge )
            SierpinskiGenCube( pDoc->BaseCube, 0 );
      
      glFinish();

      // Swap buffers.
      SwapBuffers(pDC->m_hDC) ;

      //
      // If we are in a self-animated mode, update the animation parameters
      //    and force a redraw.
      //
      if( Spin )
      {
            rotAngle += 2.0;
            Invalidate( TRUE );
      }

}

void CSierpinskiSolidsView::DrawShadows( CSierpinskiSolidsDoc* pDoc )
{
      //
      // Set-up the transformation matrix to flatten the scene onto the ground plane.
      ////////////  Note, this does not work yet. ////////////////
      //
      
      glFrontFace( GL_CW );
      glPushMatrix();
      glScalef( 1, 1, -0.99 );
      glTranslatef( 0, 0, 2.4 );

      if( pDoc->Draw_Tetra )
            SierpinskiGen( pDoc->BaseTetra, 0 );


      if( pDoc->Draw_Sponge )
            SierpinskiGenCube( pDoc->BaseCube, 0 );

      glPopMatrix();
      glFrontFace( GL_CCW );
      //
      // Draw the ground plane
      //
      glDisable( GL_LIGHTING );
      glDisable( GL_DEPTH_TEST );
      glEnable( GL_BLEND );
      glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
      glColor4f( 0.7, 0.7, 0.7, 0.8 );
      glBegin( GL_QUADS );
            glVertex3f( -4.0, -4.0, -1.2 );
            glVertex3f(  4.0, -4.0, -1.2 );
            glVertex3f(  4.0,  4.0, -1.2 );
            glVertex3f( -4.0,  4.0, -1.2 );
      glEnd();
      glEnable( GL_LIGHTING );
      glEnable( GL_DEPTH_TEST );
      glDisable( GL_BLEND );


}

void CSierpinskiSolidsView::SierpinskiGen(Tetrahedron &tetra, int level)
{
      //
      // If we reached the maximum recursion level,
      //    stop and draw the tetrahedron.
      // If animation is turned on, the rotAngle will force the
      //   tetrahedron to spin.
      //
      if( level > MAX_Level )
            tetra.DrawFaces( rotAngle*level );
      //
      // Otherwise, split the tetrahedron into 5 smaller tetrahedra,
      //    Drawing the 4 that are on the corners.
      // Logically, we split each edge into two.
      // Build the tetra connecting each original vertex with the
      // midpoints of the edges it shares.
      //
      else
      {
            Tetrahedron subtetra;
            //
            // Tetrahedron at the first vertex.
            //
            subtetra.Pts[0] = tetra.Pts[0];
            subtetra.Pts[1] = tetra.mid[0];
            subtetra.Pts[2] = tetra.mid[4];
            subtetra.Pts[3] = tetra.mid[3];
            //
            // The Tetrahedron class contains information on the
            //    edge midpoints. Bad programming here, in that this
            //    class implicitly knows the ordering of the edges.
            //   ------- Fix later --------
            //         Add a method to Tetrahedron that subdivides itself
            //           and passes back the resulting five tetrahedra.
            //
            subtetra.CalcMidpts();
            //
            // Recursively subdivide each tetra.
            // Increment the level by one to note we are
            //   one level further in the recursion that our
            //   parent.
            // Some permutation (acceleration) on the level is
            //   added to force some nodes to terminate earlier
            //   than others.
            //
            SierpinskiGen( subtetra, (level+1) );

            glColor3f( 1.0, 1.0, 0.0 );
            //
            // Tetrahedron at the second vertex.
            //
            subtetra.Pts[0] = tetra.Pts[1];
            subtetra.Pts[1] = tetra.mid[1];
            subtetra.Pts[2] = tetra.mid[0];
            subtetra.Pts[3] = tetra.mid[5];
            subtetra.CalcMidpts();
            SierpinskiGen( subtetra, (level+2) );

            glColor3f( 0.0, 1.0, 1.0 );
            //
            // Tetrahedron at the third vertex.
            //
            subtetra.Pts[0] = tetra.Pts[2];
            subtetra.Pts[1] = tetra.mid[4];
            subtetra.Pts[2] = tetra.mid[1];
            subtetra.Pts[3] = tetra.mid[2];
            subtetra.CalcMidpts();
            SierpinskiGen( subtetra, (level+3) );

            glColor3f( 1.0, 0.0, 1.0 );
            //
            // Tetrahedron at the fourth vertex.
            //
            subtetra.Pts[3] = tetra.Pts[3];
            subtetra.Pts[0] = tetra.mid[3];
            subtetra.Pts[1] = tetra.mid[5];
            subtetra.Pts[2] = tetra.mid[2];
            subtetra.CalcMidpts();
            SierpinskiGen( subtetra, (level+1) );

      }
}
0
 
LVL 20

Accepted Solution

by:
ikework earned 300 total points
ID: 34914894
Ok, I downloaded the same project from http://www.cse.ohio-state.edu/~crawfis/Graphics/VirtualTrackball.html

All you have to do is add

1) add a glTranslatef with a modulated z-values to move into the scene, look for "// 1 new code" in the code snippet

2) make sure the application redraws the scene all the time by adding an Invalidate to the end of OnDraw, look for "// 2 new code" in the code snippet


thats it :)
void CSierpinskiSolidsView::OnDraw(CDC* pDC)
{
    CSierpinskiSolidsDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    // Make the HGLRC current.
    BOOL bResult = wglMakeCurrent (pDC->m_hDC, m_hrc);
    if (!bResult)
    {
      TRACE("wglMakeCurrent Failed %x\r\n", GetLastError() ) ;
    }

    //
    // Draw the scene
    //
    glMatrixMode( GL_MODELVIEW );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    // 1 new code begins here
    glLoadIdentity();
    static float z = 10.0f;
    glTranslatef(0.0f, 0.0f, z);
    z += 0.1;
    if(z > 25.0f) z = 10.0f;
    // 1 new code ends here
    
    MAX_Level = pDoc->Recursion_Level;

    
    ....
    
    
    if( Spin )
    {
        rotAngle += 2.0;
        Invalidate( TRUE );
    }
    // 2 new code begin
    else
    {
        Invalidate( TRUE );
    }
    // 2 new code end
    
}

Open in new window

0
 
LVL 20

Expert Comment

by:ikework
ID: 34914972
Check out this tutorial, it explains much better how to move through a 3D world and the concept of moving the world/camera:

  http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=10

Have fun :)
0
 

Author Comment

by:alexhenryjames
ID: 34915509
Thanks very much; one last question, how would I integrate my mouse movement function in which I compute the desired angle of rotation and trackball projected coordinates, and then render the model, with the above, which appears to override it?
0
 
LVL 20

Assisted Solution

by:ikework
ikework earned 300 total points
ID: 34915620
The mousefunctions shouldnt change the GL-matrices directly, they should only store the values needed to make those matrix manipulations (i.e. rot_angle and rot_axis)  in the OnDraw function.
0
 
LVL 20

Expert Comment

by:ikework
ID: 34915673
Other than that I would throw this sample program away and start over with a new clean little app, since this demo does some weird things, which make understanding an OpenGL app even harder.

If you want to learn OpenGL its better to start basic, glut and the red book are perfect for that.

The Red Book is online here:

http://www.opengl.org/documentation/red_book/
0
 

Author Comment

by:alexhenryjames
ID: 34915676
So to finish off, I should be able to rotate, zoom and perform glTranslatef on moving all of the code to the OnDraw, and maintan (x, y, z) values in between OnDraws to enable the 'camera fly' function.
0
 
LVL 20

Expert Comment

by:ikework
ID: 34915712
Yes you can, its not gonna be easy to change this demo, its not made with that in mind, so as I said, given that you are still learning OpenGL, I would not change this project, it is too hard for an unexperienced OpenGL programmer.

But certainly it can be changed in that manner, yes. Its just not what I would recommend ;)
0
 

Author Comment

by:alexhenryjames
ID: 34915735
Thanks again, I am a GUI developer (no OpenGL experience but will probably pick up a book now (F.S. Hill)) and we have a 3D developer to implement; I am just using this example to aid specification. Thanks also for the tutorial link.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Artificial Intelligence comes in many forms, and for game developers, Path-Finding is an important ability for making an NPC (Non-Playable Character) maneuver through terrain.  A* is a particularly easy way to approach it.  I’ll start with the algor…
As game developers, we quickly learn that Artificial Intelligence (AI) doesn’t need to be so tough.  To reference Space Ghost: “Moltar, I have a giant brain that is able to reduce any complex machine into a simple yes or no answer. (http://www.youtu…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

759 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now