Obtaining axes rotations from view direction

How do can I generate angle rotations around axes given a view direction and up vector (not perpendicular)?

So basically I want to do glRotates myself.


Below is the code I’ve written but I can’t seem to find what’s wrong with it:

kVec3D cameraPos = kInitVec3D(0, 0, 100);
kVec3D cameraLkt = kInitVec3D( 0, 0, 0 );
kVec3D cameraDir = kSubVec3D( &cameraLkt, &cameraPos );
kVec3D cameraUp = kInitVec3D(0, 1, 0);
kNormalize( &cameraDir );

kVec3D xAxis = kInitVec3D(1, 0, 0);
kVec3D yAxis = kInitVec3D(0, 1, 0);
kVec3D zAxis = kInitVec3D(0, 0, 1);
kVec3D xPerp = kCrossVec3D( &xAxis, &cameraDir );
kVec3D yPerp = kCrossVec3D( &yAxis, &cameraDir );
kVec3D zPerp = kCrossVec3D( &zAxis, &cameraDir );

float fromX = acosf( kDotVec3D( &cameraDir, &xAxis ) );
float fromY = acosf( kDotVec3D( &cameraDir, &yAxis ) );
float fromZ = acosf( kDotVec3D( &cameraDir, &zAxis ) );

if( kLength( &xPerp ) < 0.0001f ) fromX = 0; else kNormalize( &xPerp );
if( kLength( &yPerp ) < 0.0001f ) fromY = 0; else kNormalize( &yPerp );
if( kLength( &zPerp ) < 0.0001f ) fromZ = 0; else kNormalize( &zPerp );'

glRotatef( kINDEG( fromX ), xPerp.x, xPerp.y, xPerp.z );
glRotatef( kINDEG( fromY ), yPerp.x, yPerp.y, yPerp.z );
glRotatef( kINDEG( fromZ ), zPerp.x, zPerp.y, zPerp.z );
glTranslatef( -cameraPos.x, -cameraPos.y, -cameraPos.z );

It may help give you some ideas if you look at the Open Source MESA library and how it implements gluLookAt. See source code for Mesa3D:MesaLib-7.5.* in the /Mesa-7.5/src/glu/sgi/libutil/project.c file and locate the the following code snippet

gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx,
	  GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy,
	  GLdouble upz)
    float forward[3], side[3], up[3];
    GLfloat m[4][4];

    forward[0] = centerx - eyex;
    forward[1] = centery - eyey;
    forward[2] = centerz - eyez;

    up[0] = upx;
    up[1] = upy;
    up[2] = upz;


    /* Side = forward x up */
    cross(forward, up, side);

    /* Recompute up as: up = side x forward */
    cross(side, forward, up);

    m[0][0] = side[0];
    m[1][0] = side[1];
    m[2][0] = side[2];

    m[0][1] = up[0];
    m[1][1] = up[1];
    m[2][1] = up[2];

    m[0][2] = -forward[0];
    m[1][2] = -forward[1];
    m[2][2] = -forward[2];

    glTranslated(-eyex, -eyey, -eyez);

Awesome, thanks that did it.
One issue though, these values don’t work:

eye = 0, 100, 0
lkt = 0, 0, 0
up = 0, 1, 0

it doesn’t draw the model

but same values with this eye value works fine:

eye = 0,100,0.0001

So do I have to make sure that view direction is never coincident with one of the axes?

Just the Y axis is special, when your Up vector is (0,1,0) .

Yes, you must make certain to not allow “up” to be coincident with “forward”.

If you think about it and plot your original vectors, how could you ever physically have a camera oriented up and simultaneously look along the same viewing direction?

In general, when you calculate the

    /* Side = forward x up */
    cross(forward, up, side);

you will get Side=0 if you incorrectly allow “up” to be coincident with “forward”=center-eye. This is the case that you observed with your particular numbers. So in general, you must avoid “forward” x “up” == 0. To keep the code fast most programs enforce that outside the gluLookAt by carefully selecting vectors rather than having to check with every call to gluLookAt. The up vector should be in the plane perpendicular to the forward vector.