#pragma comment(lib,"opengl32.lib") #pragma comment(lib,"glu32.lib") #pragma comment(lib,"glut32.lib") #pragma comment(lib,"glew32.lib") #pragma comment(lib,"glui32.lib" ) #pragma warning(disable : 4786) #include #include #include #include #include //============================================================================== class GLUTInteractor //============================================================================== { public: GLUTInteractor() {} virtual ~GLUTInteractor() {} // These functions return true if they handle the event virtual bool mouse( int button, int state, int x, int y ) { return false; } virtual bool motion( int x, int y ) { return false; } }; //============================================================================== class ViewInteractor //============================================================================== { public: float origin[2]; // The current view in world coordinates float size[2]; int screenWidth, screenHeight; bool panDragging; bool zoomDragging; int lastX, lastY; // tracks last mouse position //------------------------------------------------------------------------------ ViewInteractor( float ox=0, float oy=0, float w=512, float h=512 ) { origin[0] = ox; origin[1] = oy; size[0] = w; size[1] = h; screenWidth = w; screenHeight = h; panDragging = false; zoomDragging = false; } //------------------------------------------------------------------------------ bool mouse( int button, int state, int x, int y ) { bool handled = false; // save mouse position lastX = x; lastY = y; if( button == GLUT_LEFT_BUTTON && // handle panning state == GLUT_DOWN && (glutGetModifiers() & GLUT_ACTIVE_SHIFT) ) { panDragging = true; handled = true; } else if( button == GLUT_LEFT_BUTTON && // handle zooming state == GLUT_DOWN && (glutGetModifiers() & GLUT_ACTIVE_CTRL) ) { zoomDragging = true; handled = true; } else if( button == GLUT_LEFT_BUTTON && state == GLUT_UP ) { zoomDragging = false; panDragging = false; } return handled; } //------------------------------------------------------------------------------ bool motion( int x, int y ) { bool handled = false; int dx = x - lastX; int dy = lastY - y; if( panDragging ) { float scale = size[0]/screenWidth; // assume same scale in y origin[0] -= dx * scale; origin[1] -= dy * scale; handled = true; } else if( zoomDragging ) { float cx = origin[0] + size[0]/2 ; // save center of screen float cy = origin[1] + size[1]/2 ; // compute a scale factor float scale = 1 + fabs( float(dy)/screenHeight ); if( dy < 0 ) scale = 1 / scale; // scale size and reset origin size[0] *= scale; size[1] *= scale; origin[0] = cx - size[0]/2; origin[1] = cy - size[1]/2; handled = true; } lastX = x; lastY = y; return handled; } //------------------------------------------------------------------------------ void setupView() { glViewport(0, 0, screenWidth, screenHeight); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluOrtho2D( origin[0], origin[0]+size[0], origin[1], origin[1]+size[1] ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); } }; //============================================================================== class Image //============================================================================== { public: int w, h; unsigned char* rgbData; unsigned texID; // transformation stuff float translation[2]; float rotation; float scale[2]; float skew; //------------------------------------------------------------------------------ Image() : w(0), h(0), rgbData(0), texID(0) { translation[0] = translation[1] = 0; rotation = 0; scale[0] = scale[1] = 1; skew = 0; } //------------------------------------------------------------------------------ ~Image() { delete rgbData; } //------------------------------------------------------------------------------ // returns false on failure bool loadPPM( const char* filename ) { // This code lifted from GLVU FILE* fp = fopen(filename, "rb"); if (fp==NULL) { printf("Unable to open %s!\n",filename); rgbData=NULL; w=0; h=0; return false; } int c,s; do{ do { s=fgetc(fp); } while (s!='\n'); } while ((c=fgetc(fp))=='#'); ungetc(c,fp); fscanf(fp, "%d %d\n255\n", &w, &h); int numComponents = w*h*3; if (rgbData==NULL) rgbData = new unsigned char[numComponents]; fread(rgbData,numComponents,1,fp); fclose(fp); return true; } //------------------------------------------------------------------------------ void initTexture() { // generate a unique texture id glGenTextures( 1, &texID ); // bind the texture so we can manipulate it glBindTexture(GL_TEXTURE_2D, texID); // set interpolation parameters glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // upload the data to the texture glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData ); } //------------------------------------------------------------------------------ void setupTransform() { float skewMatrix[] = { 1, skew, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; glTranslatef( translation[0], translation[1], 0); glScalef( scale[0], scale[1], 1); glRotatef( rotation, 0, 0, 1); glMultTransposeMatrixf( skewMatrix ); // OpenGL takes column major matrices. // Ours is row major } //------------------------------------------------------------------------------ void draw() { glBindTexture(GL_TEXTURE_2D, texID); glBegin(GL_POLYGON); glTexCoord2d(0,1); glVertex2d(0,0); glTexCoord2d(1,1); glVertex2d(w, 0); glTexCoord2d(1,0); glVertex2d(w, h); glTexCoord2d(0,0); glVertex2d(0, h); glEnd(); } //------------------------------------------------------------------------------ void drawBorder() { glBegin( GL_LINE_LOOP ); glVertex2d(0,0); glVertex2d(w, 0); glVertex2d(w, h); glVertex2d(0, h); glEnd(); } }; //============================================================================== class ImageInteractor //============================================================================== { public: bool imageDragging; int lastX, lastY; ImageInteractor(); bool mouse( int button, int state, int x, int y ); bool motion( int x, int y ); int pick( int x, int y ); }; // glut callbacks void display(); void idle(); void keyboard(unsigned char k, int x, int y); void mouse( int button, int state, int x, int y ); void motion( int x, int y ); void reshape( int w, int y ); void init(); // GUI stuff void createGUI(); void GUICallback( int id ); void syncGUI(); void checkForGLError( char *msg ); // global variables ViewInteractor view( 0, 0, 800, 600 ); ImageInteractor imageInteractor; int imageCount; Image* images; int curImage = 0; //------------------------------------------------------------------------------ void main( int argc, char* argv[] ) { if( argc == 1 ) { printf("usage: ImagePlay ...\n" ); return; } // Create a window and an OpenGL context glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA ); glutInitWindowSize( view.screenWidth, view.screenHeight ); glutCreateWindow("Image Play"); glewInit(); // load and stack images imageCount = argc - 1; images = new Image[imageCount]; for( int i=0; i < argc - 1; i++ ) { images[i].loadPPM( argv[i+1] ); images[i].initTexture(); images[i].translation[0] += i*20; images[i].translation[1] += i*20; } curImage = 0; createGUI(); glutDisplayFunc( display ); glutMouseFunc( mouse ); glutMotionFunc( motion ); glutReshapeFunc( reshape ); glutKeyboardFunc( keyboard ); glutMainLoop(); } //----------------------------------------------------------------------------- void keyboard(unsigned char k, int x, int y) { printf("key:%c\n", k ); // use numbers to select current image if( '1' <= k && k <= '9' ) { int i = k - '1'; printf("i:%d\n", i ); if( i < imageCount ) curImage = i; syncGUI(); } switch( k ) { case 'R': view.origin[0] = 0; view.origin[1] = 0; break; case 'r': images[curImage].translation[0] = 0; images[curImage].translation[1] = 0; images[curImage].scale[0] = 1; images[curImage].scale[1] = 1; images[curImage].rotation = 0; images[curImage].skew = 0; syncGUI(); break; } glutPostRedisplay(); } //----------------------------------------------------------------------------- void mouse( int button, int state, int x, int y ) { bool handled = view.mouse( button, state, x, y ); if( !handled ) handled = imageInteractor.mouse( button, state, x, y ); glutPostRedisplay(); // redraw } //------------------------------------------------------------------------------ void motion( int x, int y ) { bool handled = view.motion( x, y ); if( !handled ) handled = imageInteractor.motion( x, y ); glutPostRedisplay(); // redraw } //------------------------------------------------------------------------------ void reshape( int w, int h ) { float scale = view.size[0] / view.screenWidth; float oldH = view.size[1]; view.size[0] = w * scale; // resize view.size[1] = h * scale; view.origin[1] -= view.size[1] - oldH; view.screenWidth = w; view.screenHeight = h; // change view to have the same aspect ratio as the screen //float aspectRatio = float(w)/h; //view.size[0] = view.size[1] * aspectRatio; glutPostRedisplay(); // redraw } //----------------------------------------------------------------------------- void display() { checkForGLError( "display() - begin" ); glClearColor(0,0,0,1); glClear( GL_COLOR_BUFFER_BIT ); view.setupView(); // Set texturing mode to take colors directly from texture glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable( GL_TEXTURE_2D ); // draw images for( int i=0; i < imageCount; i++ ) { glPushMatrix(); images[i].setupTransform(); images[i].draw(); if( i == curImage ) { // draw border glDisable( GL_TEXTURE_2D ); glColor3f(1,0,0); images[curImage].drawBorder(); glEnable( GL_TEXTURE_2D ); } glPopMatrix(); } glutSwapBuffers(); checkForGLError( "display() - end" ); } //------------------------------------------------------------------------------ void checkForGLError( char *msg ) { GLenum errCode; const GLubyte *errStr; if ((errCode = glGetError()) != GL_NO_ERROR) { errStr = gluErrorString(errCode); fprintf(stderr,"OpenGL ERROR: %s: %s\n", errStr, msg); } } //////////////////////////////////////////////////////////////////////////////// // // ImageInteractor // //////////////////////////////////////////////////////////////////////////////// ImageInteractor::ImageInteractor() { imageDragging = false; } //------------------------------------------------------------------------------ bool ImageInteractor::mouse( int button, int state, int x, int y ) { bool handled = false; // save mouse position lastX = x; lastY = y; if( button == GLUT_LEFT_BUTTON && // handle panning state == GLUT_DOWN ) { int i = pick( x, y ); if( i >= 0 ) { printf("Selected %d\n", i ); curImage = i; syncGUI(); imageDragging = true; handled = true; } } else if( button == GLUT_LEFT_BUTTON && state == GLUT_UP ) { imageDragging = false; } return handled; } //------------------------------------------------------------------------------ bool ImageInteractor::motion( int x, int y ) { bool handled = false; int dx = x - lastX; int dy = lastY - y; if( imageDragging ) { float scale = view.size[0]/view.screenWidth; // assume same scale in y images[curImage].translation[0] += dx * scale; images[curImage].translation[1] += dy * scale; syncGUI(); handled = true; } lastX = x; lastY = y; return handled; } //------------------------------------------------------------------------------ int ImageInteractor::pick( int x, int y ) { const int BUFSIZE = 256; int unsigned buffer[BUFSIZE]; float M[16]; int viewport[4]; view.setupView(); // get the projection matrix and viewport glGetFloatv( GL_PROJECTION_MATRIX, M ); glGetIntegerv( GL_VIEWPORT, viewport ); // set the modified projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPickMatrix( x, viewport[3] - y, 3, 3, viewport ); glMultMatrixf( M ); glMatrixMode( GL_MODELVIEW ); // render in selection mode glSelectBuffer( BUFSIZE, buffer ); glRenderMode( GL_SELECT ); glInitNames(); glPushName(0); for( int i=imageCount-1; i >= 0; i-- ) { glLoadName(i); glPushMatrix(); images[i].setupTransform(); images[i].draw(); glPopMatrix(); } int hits = glRenderMode( GL_RENDER ); printf("hits:%d\n", hits ); if( hits ) return buffer[3]; // name of first primitive in the hit else return -1; } //////////////////////////////////////////////////////////////////////////////// // // G U I S T U F F // //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ float tx=0, ty=0; float sx=0, sy=0; float skew=0; float rot=0; GLUI* glui = NULL; int mainWindow; void createGUI() { mainWindow = glutGetWindow(); GLUI_Spinner* spinner; glui = GLUI_Master.create_glui( "Transform parameters" ); spinner = glui->add_spinner( "Translate X:", GLUI_SPINNER_FLOAT, &tx, 0, GUICallback ); spinner->set_float_limits( -2000, 2000 ); spinner = glui->add_spinner( "Translate Y:", GLUI_SPINNER_FLOAT, &ty, 0, GUICallback ); spinner->set_float_limits( -2000, 2000 ); spinner = glui->add_spinner( "Rotation:", GLUI_SPINNER_FLOAT, &rot, 0, GUICallback ); spinner->set_float_limits( -360, 360 ); spinner = glui->add_spinner( "Scale X:", GLUI_SPINNER_FLOAT, &sx, 0, GUICallback ); spinner->set_float_limits( .1, 10 ); spinner = glui->add_spinner( "Scale Y:", GLUI_SPINNER_FLOAT, &sy, 0, GUICallback ); spinner->set_float_limits( .1, 10 ); spinner = glui->add_spinner( "Skew:", GLUI_SPINNER_FLOAT, &skew, 0, GUICallback ); spinner->set_float_limits( -5, 5 ); syncGUI(); } //------------------------------------------------------------------------------ void syncGUI() { tx = images[curImage].translation[0]; ty = images[curImage].translation[1]; sx = images[curImage].scale[0]; sy = images[curImage].scale[1]; rot = images[curImage].rotation; skew = images[curImage].skew; if( glui ) glui->sync_live(); } //------------------------------------------------------------------------------ void GUICallback( int id ) { images[curImage].translation[0] = tx; images[curImage].translation[1] = ty; images[curImage].scale[0] = sx; images[curImage].scale[1] = sy; images[curImage].rotation = rot; images[curImage].skew = skew; // post redisplay to main window glutSetWindow( mainWindow ); glutPostRedisplay(); }