/* ** Demonstrates simple projective texture mapping. ** ** Button1 changes view, Button2 moves texture. ** ** (See: Segal, Korobkin, van Widenfelt, Foran, and Haeberli ** "Fast Shadows and Lighting Effects Using Texture Mapping", SIGGRAPH '92) ** ** 1994,1995 -- David G Yu ** ** cc -o projtex projtex.c texture.c -lglut -lGLU -lGL -lX11 -lm */ #include #include #include #include #include "texture.h" /* Some files do not define M_PI... */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif int winWidth, winHeight; GLboolean redrawContinuously; float angle, axis[3]; enum MoveModes { MoveNone, MoveView, MoveObject, MoveTexture }; enum MoveModes mode = MoveNone; GLfloat objectXform[4][4]; GLfloat textureXform[4][4]; void (*drawObject)(void); void (*loadTexture)(void); GLboolean textureEnabled = GL_TRUE; GLboolean showProjection = GL_TRUE; GLboolean linearFilter = GL_TRUE; char *texFilename = NULL; GLfloat zoomFactor = 1.0; /*****************************************************************/ /* matrix = identity */ void matrixIdentity(GLfloat matrix[16]) { matrix[ 0] = 1.0; matrix[ 1] = 0.0; matrix[ 2] = 0.0; matrix[ 3] = 0.0; matrix[ 4] = 0.0; matrix[ 5] = 1.0; matrix[ 6] = 0.0; matrix[ 7] = 0.0; matrix[ 8] = 0.0; matrix[ 9] = 0.0; matrix[10] = 1.0; matrix[11] = 0.0; matrix[12] = 0.0; matrix[13] = 0.0; matrix[14] = 0.0; matrix[15] = 1.0; } /* matrix2 = transpose(matrix1) */ void matrixTranspose(GLfloat matrix2[16], GLfloat matrix1[16]) { matrix2[ 0] = matrix1[ 0]; matrix2[ 1] = matrix1[ 4]; matrix2[ 2] = matrix1[ 8]; matrix2[ 3] = matrix1[12]; matrix2[ 4] = matrix1[ 1]; matrix2[ 5] = matrix1[ 5]; matrix2[ 6] = matrix1[ 9]; matrix2[ 7] = matrix1[13]; matrix2[ 8] = matrix1[ 2]; matrix2[ 9] = matrix1[ 6]; matrix2[10] = matrix1[10]; matrix2[11] = matrix1[14]; matrix2[12] = matrix1[ 3]; matrix2[13] = matrix1[ 7]; matrix2[14] = matrix1[14]; matrix2[15] = matrix1[15]; } /*****************************************************************/ /* load SGI .rgb image (pad with a border of the specified width and color) */ static void imgLoad(char *filenameIn, int borderIn, GLfloat borderColorIn[4], int *wOut, int *hOut, GLubyte **imgOut) { int border = borderIn; int width, height; int w, h; GLubyte *image, *img, *p; int i, j, components; image = (GLubyte *)read_texture(filenameIn, &width, &height, &components); w = width + 2*border; h = height + 2*border; img = (GLubyte *) calloc(w*h, 4*sizeof(unsigned char)); p = img; for (j=-border; j 1.0) { glDisable(GL_DEPTH_TEST); glCopyPixels(0, 0, winWidth/zoomFactor, winHeight/zoomFactor, GL_COLOR); glEnable(GL_DEPTH_TEST); } glFlush(); glutSwapBuffers(); checkErrors(); } /*****************************************************************/ /* simple trackball-like motion control */ GLboolean trackingMotion = GL_FALSE; float lastPos[3]; int lastTime; void ptov(int x, int y, int width, int height, float v[3]) { float d, a; /* project x,y onto a hemi-sphere centered within width, height */ v[0] = (2.0*x - width) / width; v[1] = (height - 2.0*y) / height; d = sqrt(v[0]*v[0] + v[1]*v[1]); v[2] = cos((M_PI/2.0) * ((d < 1.0) ? d : 1.0)); a = 1.0 / sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); v[0] *= a; v[1] *= a; v[2] *= a; } void startMotion(int x, int y, int but, int time) { if (but == GLUT_LEFT_BUTTON) { mode = MoveView; } else if (but == GLUT_RIGHT_BUTTON) { mode = MoveTexture; } else { return; } trackingMotion = GL_TRUE; redrawContinuously = GL_FALSE; lastTime = time; ptov(x, y, winWidth, winHeight, lastPos); } /*ARGSUSED*/ void stopMotion(int x, int y, int but, int time) { if ((but == GLUT_LEFT_BUTTON && mode == MoveView) || (but == GLUT_RIGHT_BUTTON && mode == MoveTexture)) { trackingMotion = GL_FALSE; } else { return; } if (time == lastTime) { redrawContinuously = GL_TRUE; glutIdleFunc(display); } else { angle = 0.0; redrawContinuously = GL_FALSE; glutIdleFunc(0); } if (!redrawContinuously) { mode = MoveNone; } } void trackMotion(int x, int y) { if (trackingMotion) { float curPos[3], dx, dy, dz; ptov(x, y, winWidth, winHeight, curPos); dx = curPos[0] - lastPos[0]; dy = curPos[1] - lastPos[1]; dz = curPos[2] - lastPos[2]; angle = 90.0 * sqrt(dx*dx + dy*dy + dz*dz); axis[0] = lastPos[1]*curPos[2] - lastPos[2]*curPos[1]; axis[1] = lastPos[2]*curPos[0] - lastPos[0]*curPos[2]; axis[2] = lastPos[0]*curPos[1] - lastPos[1]*curPos[0]; lastTime = glutGet(GLUT_ELAPSED_TIME); lastPos[0] = curPos[0]; lastPos[1] = curPos[1]; lastPos[2] = curPos[2]; glutPostRedisplay(); } } /*****************************************************************/ void object(void) { static int object; object++; object %= 3; switch (object) { case 0: drawObject = drawCube; break; case 1: drawObject = drawDodecahedron; break; case 2: drawObject = drawSphere; break; default: break; } } void texture(void) { static int texture; texture++; texture %= 3; switch (texture) { case 0: loadTexture = NULL; textureEnabled = GL_FALSE; break; case 1: loadTexture = loadImageTexture; (*loadTexture)(); textureEnabled = GL_TRUE; break; case 2: loadTexture = loadSpotlightTexture; (*loadTexture)(); textureEnabled = GL_TRUE; break; default: break; } } void help(void) { printf("'h' - help\n"); printf("'l' - toggle linear/nearest filter\n"); printf("'s' - toggle projection frustum\n"); printf("'t' - toggle projected texture\n"); printf("'o' - toggle object\n"); printf("'z' - increase zoom factor\n"); printf("'Z' - decrease zoom factor\n"); printf("left mouse - move view\n"); printf("right mouse - move projection\n"); } /*ARGSUSED1*/ void key(unsigned char key, int x, int y) { switch(key) { case '\033': exit(EXIT_SUCCESS); break; case 'l': linearFilter = !linearFilter; (*loadTexture)(); break; case 's': showProjection = !showProjection; break; case 't': texture(); break; case 'o': object(); break; case 'z': zoomFactor += 1.0; glPixelZoom(zoomFactor, zoomFactor); glViewport(0, 0, winWidth/zoomFactor, winHeight/zoomFactor); break; case 'Z': zoomFactor -= 1.0; if (zoomFactor < 1.0) zoomFactor = 1.0; glPixelZoom(zoomFactor, zoomFactor); glViewport(0, 0, winWidth/zoomFactor, winHeight/zoomFactor); break; case 'h': help(); break; } glutPostRedisplay(); } void mouse(int button, int state, int x, int y) { if(state == GLUT_DOWN) startMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME)); else if (state == GLUT_UP) stopMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME)); glutPostRedisplay(); } void reshape(int w, int h) { winWidth = w; winHeight = h; glViewport(0, 0, w/zoomFactor, h/zoomFactor); } void usage(char **argv) { fprintf(stderr, "usage: %s \n", argv[0]); fprintf(stderr, "\n"); } int main(int argc, char **argv) { glutInitWindowSize(512, 512); glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE); (void)glutCreateWindow("projtex"); if (argc > 1) { texFilename = argv[argc-1]; } else { usage(argv); exit(EXIT_FAILURE); } loadTexture = loadImageTexture; drawObject = drawCube; initialize(); glutDisplayFunc(display); glutKeyboardFunc(key); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMotionFunc(trackMotion); glutMainLoop(); return 0; }