A Strange Inconsistency with the Right-Hand Rule While Using glRotatef

Maybe someone can lend me a quick hand with this one. I’ve been working through the OpenGL SuperBible (3rd edition) just to get a feel for the API. In the example below from the fourth chapter three simple spheres are drawn using GLUT and they rotate around another sphere that rests in the middle. Very straightforward stuff and the program runs just fine…

Problem is that spheres that rotate around the x- and z-axes are–as best as I can reason–rotating the wrong way: they are rotating CW instead of the expected CCW. Maybe I’m just not visualizing this the correct way, but I’m pretty sure that I am.

The first sphere, which rotates around the y-axis, is issued a glRotatef() command to establish a vector which points in the positive y-direction, and it rotates CCW. The other two spheres also have positive vectors along the x- and z- axes, but they are inconsistently reversed in their rotations. I cannot figure out an explanation as to why.

The code for the program is below. The important parts are in the middle of the RenderScene() function.


#include <windows.h>

#include <GL\gl.h>
#include <GL\glu.h>

#include <GL\glut.h>

// Rotation amounts
static GLfloat xRot		= 0.0f;
static GLfloat yRot		= 0.0f;



/* Called to draw scene */

void RenderScene( void )	{

	// Angle of revolution around the nucleus
	static GLfloat fElect1 = 0.0f;

	// Clear the window with current clearing color
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	// Reset the modelview matrix
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	// Translate the whole scene out and into view	
	// This is the initial viewing transformation
	glTranslatef( 0.0f, 0.0f, -100.0f );	


	// Yellow electron
	glColor3ub( 255, 255, 0 );
	// First Electron Orbit
	// Save viewing transformation
	glPushMatrix();
	// Rotate by angle of revolution
	glRotatef( fElect1, 0.0f, 1.0f, 0.0f );
	// Translate out from origin to orbit distance
	glTranslatef( 90.0f, 0.0f, 0.0f );
	// Draw the electron
	glutSolidSphere( 6.0f, 15, 15 );
	// Restore the viewing transformation	
	glPopMatrix();

	// Red Nucleus
	glColor3ub( 255, 0, 0 );
	glutSolidSphere( 10.0f, 15, 15 );


	// White electron
	glColor3ub( 255, 255, 255 );
	// Second Electron Orbit
	glPushMatrix();
	glRotatef( fElect1, 0.0f, 0.0f, 1.0f );
	glTranslatef( 80.0f, 0.0f, 0.0f );
	glutSolidSphere( 6.0f, 15, 15 );
	glPopMatrix();

	// Purple electron
	glColor3ub( 124, 75, 190 );
	// Third Electron Orbit
	glPushMatrix();
	glRotatef( fElect1, 1.0f, 0.0f, 0.0f );
	glTranslatef( 0.0f, 0.0f, -60.0f );
	glutSolidSphere( 6.0f, 15, 15 );
	glPopMatrix();


	// Increment the angle of revolution
	fElect1 += 0.5f;
	if( fElect1 > 360.0f )	{ fElect1 = 0.0f; }

	// Show the image
	glutSwapBuffers();

}



/* This function does any needed initialization on the rendering context */

void SetupRC()	{

	glEnable( GL_DEPTH_TEST );	// Hidden surface removal
	//glFrontFace( GL_CCW );		// Counter clock-wise polygons face out
	//glEnable( GL_CULL_FACE );	// Do not calculate inside of jet

	// Black background
	glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );	

}



/* Respond to arrow keys */

void SpecialKeys( int key, int x, int y )	{
	
	if( key == GLUT_KEY_UP )	{ xRot-= 5.0f; }

	if( key == GLUT_KEY_DOWN )	{ xRot += 5.0f; }

	if( key == GLUT_KEY_LEFT )	{ yRot -= 5.0f; }

	if( key == GLUT_KEY_RIGHT )	{ yRot += 5.0f; }

	if( key > 356.0f )	{ xRot = 0.0f; }

	if( key < -1.0f )	{ xRot = 355.0f; }

	if( key > 356.0f )	{ yRot = 0.0f; }

	if( key < -1.0f )	{ yRot = 355.0f; }

	// Refresh the Window
	glutPostRedisplay();

}



/* Timer function to refresh the screen for the given period */

void TimerFunc( int value )	{
    
	glutPostRedisplay();
    glutTimerFunc( 25, TimerFunc, 1 );

}



/* Window has changed size, recalculate projection */

void ChangeSize( int w, int h )	{

    GLfloat nRange = 100.0f;

    // Prevent a divide by zero
	if(h == 0)	{ h = 1; }

    // Set Viewport to window dimensions
    glViewport( 0, 0, w, h );

    // Reset coordinate system
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();

    // Establish clipping volume (left, right, bottom, top, near, far)
    if (w <= h)	{
        glOrtho (-nRange, nRange, nRange*h/w, -nRange*h/w, -nRange*2.0f, nRange*2.0f);

	} else {
        glOrtho (-nRange*w/h, nRange*w/h, nRange, -nRange, -nRange*2.0f, nRange*2.0f);
	
	}

	// Reset Model View matrix stack
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

}



/* Main program entry point */

int main( int argc, char* argv[] )	{

	glutInit( &argc, argv );
	glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
	glutInitWindowSize( 800, 600 );
	glutCreateWindow( "Atom2" );
	glutReshapeFunc( ChangeSize );
	glutSpecialFunc( SpecialKeys );
	glutDisplayFunc( RenderScene );

	glutTimerFunc( 500, TimerFunc, 1 );
	SetupRC();
	glutMainLoop();

	return 0;

}

This is probably due to the Y axis inversion in the projection matrix set by the glOrtho() function.

Bingo! That was exactly it. Thank you.