// -*- Mode: c++ -*- #include #include #include #include #include #include "uistate.h" static const int KEY_SPACE = ' '; static const int KEY_ESCAPE = '\033'; // code template inline T Min( const T a, const T b ) { return a <= b ? a : b; // use <= to maintain order } void Draw( void ) { static GLfloat polygonColor[] = { .25f, .8f, .8f }; static GLfloat polygonVertices[][2] = { {-.5f, .5f}, { .5f, .5f}, { .5f, -.5f}, {-.5f, -.5f} }; // clear out buffer glClearColor( 0.f, 0.f, 0.f, 0.f ); glClear( GL_COLOR_BUFFER_BIT ); // draw primitives glColor3fv( polygonColor ); glBegin( GL_POLYGON ); glVertex2fv( polygonVertices[0] ); glVertex2fv( polygonVertices[1] ); glVertex2fv( polygonVertices[2] ); glVertex2fv( polygonVertices[3] ); glEnd(); // show result glutSwapBuffers(); } void Reshape( int width, int height ) { // the user just changed the window size // Adjust projection parameters glViewport( 0, 0, width, height ); // correct projection to avoid distortion glMatrixMode( GL_PROJECTION ); glLoadIdentity(); // make it normalized if( width > height ){ GLdouble aspect = GLdouble( width ) / height; gluOrtho2D( -aspect, aspect, -1., 1. ); }else{ GLdouble aspect = GLdouble( height ) / width; gluOrtho2D( -1., 1., -aspect, aspect ); } // and back to ModelView glMatrixMode( GL_MODELVIEW ); } void Visible( int state ) { // if visibility status of window changes switch( state ){ case GLUT_VISIBLE: // install animation function if any glutIdleFunc( UIState::Animate() ); break; case GLUT_NOT_VISIBLE: glutIdleFunc( 0 ); break; default: assert( 0 ); } } void Keyboard( unsigned char c, int x, int y ) { switch( c ){ case KEY_SPACE: // nice convention: reset the model matrix UIState::ResetModelTransform(); glutPostRedisplay(); break; case KEY_ESCAPE: // this is really bad -- the program cannot // do proper cleanup etc. // the exception mechanism provides a much better // solution, but it still cannot be relied upon in // som compilers, so we do not use it exit( 0 ); break; default: // ignore any character not (yet) recognized ; } } void SpecialKeys( int key, int x, int y ) { // function keys and other non-alphanumeric keys } void IncrementalRotation( void ) { UIState::ApplyModelTransform(); UIState::Angle() += UIState::AngleIncr(); glutPostRedisplay(); } void Motion( int x, int y ) { // how much motion from the time the Motion callback was enabled? float deltax = x - UIState::MouseX(), deltay = UIState::MouseY() - y; UIState::MouseX() = x; UIState::MouseY() = y; // let's make it relative to the current viewport int vp[4]; glGetIntegerv( GL_VIEWPORT, vp ); // just in case someone makes the window zero width or height; // in a big program we should handle this somehow, // but in a small one we just exit assert( vp[2] - vp[0] ); assert( vp[1] - vp[3] ); float rescale = min( vp[2] - vp[0], vp[3] - vp[1] ) * .5f; deltax /= rescale; deltay /= rescale; UIState::TransX() += deltax; UIState::TransY() += deltay; UIState::ApplyModelTransform(); glutPostRedisplay(); } // this is dummy callback that can be registered when we want to // ignore mouse motion void IgnoreMotion( int, int ) {} // button click callback void Mouse( int button, int state, int x, int y ) { switch( button ){ case GLUT_LEFT_BUTTON: // animation control if( state == GLUT_DOWN ){ // enable animation UIState::Animate() = IncrementalRotation; }else if( state == GLUT_UP ){ // stop UIState::Animate() = 0; }else{ // huh? assert( 0 ); } // install glutIdleFunc( UIState::Animate() ); glutPostRedisplay(); break; case GLUT_MIDDLE_BUTTON: // translation control if( state == GLUT_DOWN ){ // keep track of motion UIState::MouseX() = x; UIState::MouseY() = y; glutMotionFunc( Motion ); }else if( state == GLUT_UP ){ // ignore motion again UIState::MouseX() = UIState::UNDEF; UIState::MouseY() = UIState::UNDEF; glutMotionFunc( IgnoreMotion ); }else{ // huh? assert( 0 ); } break; case GLUT_RIGHT_BUTTON: break; default: assert( 0 ); } } void DrawModeMenu( int entry ) { switch( entry ){ case GL_FILL: case GL_LINE: glPolygonMode( GL_FRONT_AND_BACK, entry ); break; default: assert( 0 ); } glutPostRedisplay(); } void MainMenu( int entry ) { switch( entry ){ case KEY_ESCAPE: exit( 0 ); break; default: assert( 0 ); } } void Init( void ) { // all the callbacks // it does not matter much, but // for performance reasons we set those not used explicitly to 0 glutDisplayFunc( Draw ); glutReshapeFunc( Reshape ); glutKeyboardFunc( Keyboard ); glutSpecialFunc( SpecialKeys ); glutMouseFunc( Mouse ); glutMotionFunc( IgnoreMotion ); glutPassiveMotionFunc( 0 ); glutVisibilityFunc( Visible ); glutMenuStatusFunc( 0 ); glutEntryFunc( 0 ); // create menus int submenu = glutCreateMenu( DrawModeMenu ); glutAddMenuEntry( " Filled ", GL_FILL ); glutAddMenuEntry( " Outline ", GL_LINE ); glutCreateMenu( MainMenu ); glutAddSubMenu(" Polygon mode ", submenu ); glutAddMenuEntry( " Quit ", KEY_ESCAPE ); glutAttachMenu( GLUT_RIGHT_BUTTON ); // setup orthographic projection glMatrixMode( GL_PROJECTION ); glLoadIdentity(); // make it normalized gluOrtho2D( -1., 1., -1., 1. ); // back to the model matrix glMatrixMode( GL_MODELVIEW ); } int main( int argc, char *argv[] ) { // let glut eat any command line args that it owns glutInit( &argc, argv ); // full color, double buffered glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE ); glutCreateWindow( "Simple example" ); // setup remaining state Init(); // start processing events glutMainLoop(); return 0; }