/* * IupGLCanvas PROJECTION Sample * Description : Creates a IUP GL canvas and uses OpenGL to draw on it * IUP TUIO multi-touch sample (PROJECTION = Zoom, Pan and Rotate) * The texture is loaded using Drag/Drop operation and IUP IM function * Remark : IUP must be linked to the IM library * TODO: both TUIO+OpenGL examples need some work so they can be used in real applications. * Depending on the interaction device the movements, rotations and scales can be very noisy. */ #include #include #include #include #include #include #ifdef WIN32 #include #endif #include #include #define USE_TUIO #ifdef USE_TUIO #include #endif double window = 50.0; double left, right, bottom, top; double moveX = 0, moveY = 0; double rotationAngle = 0, rotateVal = 0; double zoomFactor = 1, scaleVal = 1; double translateX = 0, translateY = 0; GLuint textureID; Ihandle* image; unsigned char* data; void LoadTexture() { int width = IupGetInt(image, "WIDTH"); int height = IupGetInt(image, "HEIGHT"); data = (unsigned char*)IupGetAttribute(image, "WID"); // allocate a texture name glGenTextures(1, &textureID ); // select our current texture glBindTexture(GL_TEXTURE_2D, textureID); // select modulate to mix texture with color for shading glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // select filter for the small areas glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // select filter for the large areas glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, width, height, GL_RGB, GL_UNSIGNED_BYTE, data); glEnable(GL_TEXTURE_2D); } void FreeTexture() { glDisable(GL_TEXTURE_2D); glDeleteTextures(1, &textureID); } void reshape(int w, int h) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); left = bottom = -window; right = top = window; if (w > h) { left = -window*w/h; right = window*w/h; } else { bottom = -window*h/w; top = window*h/w; } gluOrtho2D(left, right, bottom, top); glTranslated(moveX+translateX, moveY+translateY, 0.0); glRotated(rotateVal+rotationAngle, 0.0, 0.0, 1.0); glScaled(scaleVal*zoomFactor, scaleVal*zoomFactor, 1.0); glMatrixMode(GL_MODELVIEW); } int drop_image(Ihandle *self, const char* filename, int num, int x, int y) { image = IupLoadImage(filename); if (!image) { char* err_msg = IupGetGlobal("IUPIM_LASTERROR"); if (err_msg) IupMessage("Error", err_msg); IupRedraw(self, 0); return IUP_CLOSE; } IupRedraw(self, 0); (void)x; (void)y; (void)num; return IUP_DEFAULT; } int k_any(Ihandle *self, int key) { if(key == K_ESC) return IUP_CLOSE; switch(key) { case K_UP: // moving the window to up moveY += 1.0; break; case K_DOWN: // moving the window to down moveY -= 1.0; break; case K_LEFT: // moving the window to the left moveX -= 1.0; break; case K_RIGHT: // moving the window to the right moveX += 1.0; break; case K_PGUP: zoomFactor += 0.1; // zoom in break; case K_PGDN: zoomFactor -= 0.1; // zoom out if(zoomFactor < 0) zoomFactor = 0; break; case K_HOME: rotationAngle -= 1.0; // rotate clockwise break; case K_END: rotationAngle += 1.0; // rotate counter-clockwise break; } IupRedraw(self, 0); return IUP_DEFAULT; } int redraw(Ihandle *self, float x, float y) { int w, h; char *size = IupGetAttribute(self, "RASTERSIZE"); sscanf(size, "%dx%d", &w, &h); IupGLMakeCurrent(self); glViewport(0, 0, w, h); reshape(w, h); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); LoadTexture(); glPushMatrix(); glBegin( GL_QUADS ); glTexCoord2d(0.0, 0.0); glVertex2d(-10.0, 10.0); glTexCoord2d(1.0, 0.0); glVertex2d( 10.0, 10.0); glTexCoord2d(1.0, 1.0); glVertex2d( 10.0, -10.0); glTexCoord2d(0.0, 1.0); glVertex2d(-10.0, -10.0); glEnd(); glPopMatrix(); FreeTexture(); IupGLSwapBuffers(self); (void)x; (void)y; return IUP_DEFAULT; } /////////////////////////////////////////////////////////////////////////////// #ifdef USE_TUIO #define GRAD_PI 57.2957787 /* 180.0f / 3.1415927f */ #define MAX_ANGLE_VARIANCE 10.0 #define INVERT_Y_AXIS(_y, _h) (_h - _y - 1) int oldX = 0, oldY = 0; double initDist = 0, initAngle = 0; // Calculates the angle of this vector in grad in the trigonometric sense. // Source: touchlib (http://www.nuigroup.com/touchlib/) double getAngleTrig(double X, double Y) { if(X == 0.0) return Y < 0.0 ? 270.0 : 90.0; else if(Y == 0) return X < 0.0 ? 180.0 : 0.0; if(Y > 0.0) if(X > 0.0) return atan(Y/X) * GRAD_PI; else return 180.0-atan(Y/-X) * GRAD_PI; else if(X > 0.0) return 360.0-atan(-Y/X) * GRAD_PI; else return 180.0+atan(-Y/-X) * GRAD_PI; } /* Zoom, Move and Rotate gestures were mapped using this link as reference: http://wiki.nuigroup.com/Gesture_recognition */ int multitouch_cb(Ihandle *self, int count, int* id, int* px, int* py, int *pstate) { int w, h, fullw, fullh; char *size = IupGetAttribute(self, "RASTERSIZE"); char *full = IupGetGlobal("FULLSIZE"); sscanf(size, "%dx%d", &w, &h); sscanf(full, "%dx%d", &fullw, &fullh); /* if self is the canvas, then must convert to screen coordinates */ px[0] += IupGetInt(self, "X"); py[0] += IupGetInt(self, "Y"); /* Now, convert to the GL window coordinates */ px[0] = (int)(px[0]*(right-left)/fullw - right); py[0] = (int)(py[0]*(top-bottom)/fullh - top); // DOWN - first blob on surface if(pstate[0] == 'D') { oldX = px[0]; oldY = INVERT_Y_AXIS(py[0], h); } // DOWN - second blob on surface if(count == 2) { /* if self is the canvas, then must convert to screen coordinates */ px[1] += IupGetInt(self, "X"); py[1] += IupGetInt(self, "Y"); /* Now, convert to the GL window coordinates */ px[1] = (int)(px[1]*(right-left)/fullw - right); py[1] = (int)(py[1]*(top-bottom)/fullh - top); if(pstate[1] == 'D') { int initDiffX; int initDiffY; initDiffX = px[1] - oldX; initDiffY = INVERT_Y_AXIS(py[1], h) - oldY; initAngle = getAngleTrig(initDiffX, initDiffY); initDist = sqrt(pow((double)initDiffX, 2) + pow((double)initDiffY, 2)); } } // MOVE - there is one blob on surface, and it is moving if(count == 1 && pstate[0] == 'M') { translateX += (double)(px[0] - oldX); // PAN GESTURE translateY += (double)(INVERT_Y_AXIS(py[0], h) - oldY); // (translate XY) oldX = px[0]; oldY = INVERT_Y_AXIS(py[0], h); } // MOVE - there are two blobs on surface, and they are moving else if(count == 2) { if(pstate[0] == 'M' && pstate[1] == 'M') { int curDiffX = px[1]-px[0]; int curDiffY = INVERT_Y_AXIS(py[1], h) - INVERT_Y_AXIS(py[0], h); double curAngle = getAngleTrig((double)curDiffX, (double)curDiffY); double curDist = sqrt(pow((double)curDiffX, 2) + pow((double)curDiffY, 2)); if(abs(curAngle-initAngle) > MAX_ANGLE_VARIANCE) { // ROTATE GESTURE (rotate angle) rotationAngle = curAngle-initAngle; } else { // ZOOM GESTURE (scale factor) if(initDist > 0) // Prevent a divide by zero zoomFactor = curDist/initDist; } } } // UP - the first blob is out of surface... Now, the second blob becomes the first! // So, we must reset the initial values if(pstate[0] == 'U') { if(count == 2) { oldX = px[1]; oldY = INVERT_Y_AXIS(py[1], h); } moveX += translateX; moveY += translateY; translateX = 0; translateY = 0; initDist = 0; initAngle = 0; } // UP - the second blob is out of surface... // So, we must reset the initial values if(count == 2) { if(pstate[1] == 'U') { initDist = 0; initAngle = 0; rotateVal += rotationAngle; if(abs(rotateVal) > 360) rotateVal = (int)rotateVal % 360; rotationAngle = 0; scaleVal *= zoomFactor; if(scaleVal < 0) scaleVal = 1; zoomFactor = 1; } } // Updating the display IupRedraw(self, 0); (void)id; return IUP_DEFAULT; } #endif /////////////////////////////////////////////////////////////////////////////// int main(int argc, char **argv) { Ihandle *canvas, *finale, *dlg; IupOpen(&argc, &argv); IupGLCanvasOpen(); canvas = IupGLCanvas(NULL); IupSetCallback(canvas, "ACTION", (Icallback) redraw); IupSetCallback(canvas, "K_ANY", (Icallback) k_any); IupSetCallback(canvas, "DROPFILES_CB", (Icallback) drop_image); IupSetAttribute(canvas, IUP_BUFFER, IUP_DOUBLE); IupSetAttribute(canvas, "RASTERSIZE", "450x450"); #ifdef USE_TUIO { Ihandle *tuio; IupTuioOpen(); tuio = IupTuioClient(0); IupSetAttribute(tuio, "CONNECT", "YES"); IupSetAttributeHandle(tuio, "TARGETCANVAS", canvas); IupSetCallback(canvas, "MULTITOUCH_CB",(Icallback)multitouch_cb); } #endif finale = IupHbox(IupFill(), canvas, IupFill(), NULL); dlg = IupDialog(finale); IupSetAttribute(dlg, "TITLE", "IupGLCanvas Multi-touch PROJECTION"); IupMap(dlg); IupGLMakeCurrent(canvas); IupShowXY(dlg, IUP_CENTER, IUP_CENTER); IupMainLoop(); IupClose(); return EXIT_SUCCESS; }