/* picking.c */ /* Purpose : A simple picking example */ #ifdef __APPLE__ #include #else #include #endif #include #include #include #define KEY_ESC 27 /* New types */ typedef struct ViewInfo ViewInfo; struct ViewInfo { GLfloat radius; GLfloat angle; GLfloat elevation; }; typedef struct ViewVolInfo ViewVolInfo; struct ViewVolInfo { GLfloat nearClip; GLfloat farClip; GLfloat fovy; /* Field Of View in the y direction*/ GLfloat aspect; /* Aspect ratio */ }; /* Module level variables and constants */ static const int mMaxSize = 200; static ViewInfo mViewInfo = { 200.0, /* radius */ 50.0, /* angle */ 20.0, /* elevation */ }; static ViewVolInfo mViewVolInfo = { 20.0, /* near clip */ 400.0, /* far clip */ 70.0, /* field of view */ 1.0 /* aspect ratio */ }; /* Functions */ GLvoid checkError( const char* const label ) { GLenum error; error = glGetError(); while ( GL_NO_ERROR != error ) { fprintf( stderr,"%s: %s\n", label, (char*) gluErrorString(error) ); error = glGetError(); } } void reshape( GLint width, GLint height ) { /* Get the viewing volume set up to match the aspect ratio of the viewport */ mViewVolInfo.aspect = (GLdouble)width / (GLdouble)height; /* We still want the output to cover the whole window */ glViewport( 0, 0, width, height ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( mViewVolInfo.fovy, mViewVolInfo.aspect, mViewVolInfo.nearClip, mViewVolInfo.farClip ); glMatrixMode( GL_MODELVIEW ); } GLvoid keyboard( GLubyte key, GLint x, GLint y) { switch (key) { case KEY_ESC: /* exit when escape key is pressed */ exit(0); break; case 'a': case 'A': mViewInfo.elevation += 2.0; if( mViewInfo.elevation > 90.0){ mViewInfo.elevation = 90.0; } break; case 'z': case 'Z': mViewInfo.elevation -= 2.0; if( mViewInfo.elevation < 0.0){ mViewInfo.elevation = 0.0; } break; } glutPostRedisplay(); } void specialkeys( GLint key, GLint xpos, GLint ypos ) { switch( key ) { case GLUT_KEY_LEFT: /* move left */ mViewInfo.angle += 2.0; break; case GLUT_KEY_RIGHT: /* move left */ mViewInfo.angle -= 2.0; break; case GLUT_KEY_UP: /* move left */ mViewInfo.radius -= mMaxSize * 0.005; break; case GLUT_KEY_DOWN: /* move left */ mViewInfo.radius += mMaxSize * 0.005; break; } glutPostRedisplay(); } /* Draw a simple scene with three objects. */ void drawScene( void ) { glLoadIdentity(); /* Reset the ModelView transformation every time we draw */ /* move the centre of the world to the near clipping plane */ glTranslatef( 0, 0, -mViewVolInfo.nearClip ); /* set up viewing position */ glTranslatef( 0, 0, -mViewInfo.radius ); glRotatef( mViewInfo.elevation, 1, 0, 0 ); glRotatef( mViewInfo.angle, 0, 1, 0 ); /* no need to check if we are in selection mode as glLoadName, glPushName, glPopName are all ignored if we are not already selection mode */ /* Change to name on the top of the name stack to 1. This relies on there being an entry on the name stack to change. (ie must glPushName before using glLoadName) */ glLoadName(1); glutSolidSphere( 10.0, 20, 20 ); glLoadName(2); glPushName(20); glTranslatef( 0.0, 0.0, -50.0 ); glutSolidCube( 10.0 ); glPopName(); glLoadName(3); glPushName(30); glTranslatef( 30.0, 0.0, 0.0 ); glutSolidCube( 20.0 ); } GLvoid display( GLvoid ) { /* Do all your OpenGL rendering here */ glClear( GL_COLOR_BUFFER_BIT ); /* Draw some objects for picking */ drawScene(); /* Don't check errors every frame - expensive. Only do it while debugging. */ checkError( "display" ); glFlush(); } /* Print the name buffer to standard out. */ void processHits( GLint hits, GLuint nameBuffer[] ) { unsigned int i; unsigned int j; GLuint names; GLuint* pPtr; printf( "hits = %d\n", hits ); pPtr = nameBuffer; for( i = 0; i < hits; ++i ) { names = *pPtr; printf("number of names for hit = %u\n", names); ++pPtr; printf( "min depth = %f\t", *pPtr/(pow(2,32)-1.0) ); ++pPtr; printf( "max depth = %f\n", *pPtr/(pow(2,32)-1.0) ); ++pPtr; printf("Names:\t"); for( j = 0; j < names; ++j, ++pPtr ) { printf( "%u\t", *pPtr ); } printf("\n\n"); } printf("\n\n"); } /* When the left mouse button is clicked, enter opengl selection mode and try to figure out what object was clicked on */ GLvoid mouse( GLint button, GLint state, GLint x, GLint y ) { GLuint nameBuffer[50]; GLint hits; GLint viewport[4]; GLfloat projMatrix[16]; if( GLUT_LEFT_BUTTON == button && GLUT_DOWN == state ) { glSelectBuffer( 50, nameBuffer ); /* select a buffer for the hit results */ glRenderMode( GL_SELECT ); /* must change to select mode _before_ you touch the name stack */ /* initialise the name stack */ glInitNames(); glPushName( 70 ); /* put a dummy on the stack so we can call glLoadName without error */ /* set up the viewing volume for selection */ /* we need to premultiply the current projection matrix by the gluPickMatrix. OpenGL always postmultiplies though. Thus we need to get a copy of the current projection matrix, load up gluPickMatrix, and then postmultiply gluPickMatrix by the current projection matrix. */ /* get a copy of the current projection matrix */ glGetFloatv( GL_PROJECTION_MATRIX, projMatrix ); glMatrixMode( GL_PROJECTION ); glPushMatrix(); glLoadIdentity(); /* pick a 20x20 region around the cursor */ glGetIntegerv( GL_VIEWPORT, viewport ); gluPickMatrix( (GLdouble)x, (GLdouble)( viewport[3] - y ), 20, 20, viewport ); glMultMatrixf( projMatrix ); /* post multiply the "current" projection matrix */ glMatrixMode( GL_MODELVIEW ); drawScene(); glFlush(); /* restore the projection matrix */ glMatrixMode( GL_PROJECTION ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); /* Now process the information obtained */ hits = glRenderMode( GL_RENDER ); if( -1 != hits ) { processHits( hits, nameBuffer ); } else { fprintf( stderr, "Picking hits overflowed the name buffer\n" ); } } } void init( void ) { glClearColor( 0.2, 0.3, 0.5, 1.0 ); } int main( int argc, char* argv[] ) { glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGBA ); glutCreateWindow( argv[0] ); glutDisplayFunc( display ); glutKeyboardFunc( keyboard ); glutReshapeFunc( reshape ); glutSpecialFunc( specialkeys ); glutMouseFunc( mouse ); init(); glutMainLoop(); return 0; }