# Implementing a fly-through function

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
ikework

Hey alexhenryjames,

easiest would be moving the camera-position for starters.

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

ike
alexhenryjames

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);
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;
Thats the code to setup the viewport/projection. Can you show your render-function?

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 );
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
That is On (left) Mouse Move as you say, I mean the function that renders your ogjects each frame.

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();

}
Ok I see, can you post the function that calls this Draw function?
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?

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;

{
//
//  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.
//
}

//
//  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 );
}

}

{
//
// 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 );
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) );

}
}
ikework

membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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 :)

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?
SOLUTION

membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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/