/* * IS2780 Assignment 2 summer 2008 Due May 28 to awetzel@psc.edu * * This is the template code for a simple maze "game". The player's task * is to drag an arrow through the maze from the red starting dot to touch * the Blue middle square without touching the black maze walls on the way. * The game starts when you drag across the red starting dot. The arrow you * are dragging is red if you have not yet touched the starting dot or if * you've already won or lost. It turns blue to show when the game is * running and to indicate that your job is to get to the blue square. * If you touch the maze walls or release your button press (this is * to prevent cheating by skipping over walls) the arrow becomes red * to show that you have lost the game and need to start again. We'll * give you a demo and a working Windows exe so you will get the idea. * Its NOT as easy as it seems especially if you have a laptop trackpad!!! * * By the way, the maze came from this web page ... * http://commons.wikimedia.org/wiki/Image:Triple-Spiral-labyrinth.svg * but I added the blue square and red starting dot, and made the background * greeen. The resulting ppm image files that you need are * http://staff.psc.edu/awetzel/mymaze.ppm and/or an easier one called * http://staff.psc.edu/awetzel/tinymaze.ppm * * Your assignment of course is to fill out this template to reimplement * some of the things that are removed from our working version. These * are marked in the code at the comment possitions "XXX Your code here" * The particular tasks are * 1. test the RGB image value corresponding to the current mouse position * 2. compute the estimated direction of current motion * 3. "manually" rotate and position the arrow in the direction of motion * 4. print some additional text on the screen to count redraw cycles * 5. design a new arrow shape and make it work properly * 6. fix the game ending with something to better distinguish a win * 7. EXTRA CREDIT - other SIGNIFICANT improvements of your own design * * The specific aims of these tasks is to 1) reinforce your understanding * of what an image is and how its laid out in memory. 2+3) refresh your * understanding of elementary trig that will be very important later * 4) show you how to position and print bitmap text 5) understand 2D * shapes and their manipulation 6+7) exercise your creativity * Before you change anything the program should already successfully * show the maze image but not do anything except handle the ESCape key. * * Please email your completed solution code to me by class time next week. */ #include #include #include #include #include void init(int argc, char **argv); void init_glut(int argc, char **argv); void init_gl(int argc, char **argv); void reshape(int w, int h); void display(void); void keyboard(unsigned char key, int x, int y); void mouse_buttons(int button, int state, int mousex, int mousey); void mouse_motion(int mousex, int mousey); int arrowx = 0, arrowy = 0; float arrow_lastx = 0, arrow_lasty = 0; // floats to do a lagged average int gamerunning; int ppm_wid, ppm_ht, ppm_range; unsigned char *pixels; int main(int argc, char **argv) { init(argc, argv); init_glut(argc, argv); init_gl(argc, argv); glutReportErrors(); glutMainLoop(); return(0); } // initialize, in this case, means read the maze image file // WARNING: this does not handle PPM header comments and other variations!!! void init(int argc, char **argv) { FILE *fp; char tmptxt[100], *fname = "mymaze.ppm"; if(!(fp = fopen(fname, "rb"))) { // open binary file for read fprintf(stderr, "ERROR: can't open %s\n", fname); fname = "tinymaze.ppm"; if(!(fp = fopen(fname, "rb"))) { fprintf(stderr, "ERROR: can't open %s\n", fname); exit(1); } } fscanf (fp, "%[^\n] ", tmptxt); fprintf(stderr, "file magic string <%s>\n", tmptxt); if(strcmp(tmptxt, "P6")) { fprintf(stderr, "Sorry: this only does P6 == PPM\n"); exit(1); } fscanf (fp, "%d%d", &ppm_wid, &ppm_ht); fprintf(stderr, "ppm image size %d X by %d Y\n", ppm_wid, ppm_ht); fscanf(fp, "%d\n", &ppm_range); if(ppm_range != 255) { fprintf(stderr, "Sorry: range %d is not allowed\n"); exit(1); } pixels = (unsigned char *)malloc(ppm_ht*ppm_wid*3); if(!pixels) { fprintf(stderr, "malloc failed on image size %d %d\n", ppm_ht, ppm_wid); exit(1); } fread(pixels, 1, ppm_ht*ppm_wid*3, fp); fclose(fp); return; } void init_glut(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE); glutInitWindowPosition(300, 200); // a reasonable screen XY ??? glutInitWindowSize(ppm_wid, ppm_ht); // window size same as image //glutFullScreen(); // if you want to experiment glutCreateWindow("Assignment 02"); glutDisplayFunc(display); // this does the 1st drawing glutReshapeFunc(reshape); glutMouseFunc(mouse_buttons); // this catches mouse buttons glutMotionFunc(mouse_motion); // this works even if no button pressed glutKeyboardFunc(keyboard); return; } void init_gl(int argc, char **argv) { glClearColor(1, 1, 1, 1); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); return; } void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, ppm_wid, 0, ppm_ht, 1, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0); return; } void keyboard(unsigned char key, int x, int y) { switch(key) { case 27: // escape key exit(0); break; default: fprintf(stderr, "unknown key %d at %d %d\n", key, x, y); } glutPostRedisplay(); return; } void draw_string(float x, float y, char *s) { glColor3f(0.0, 0.0, 0.0); glRasterPos2f(x, y) ; // place left baseline text start position while(*s) glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *s++); } int mouse_saved_button, mouse_saved_state; // useful in mouse_motion() void mouse_buttons(int button, int state, int mousex, int mousey) { mouse_saved_button = button; mouse_saved_state = state; switch(button) { case GLUT_LEFT_BUTTON: if(state == GLUT_DOWN) { //fprintf(stderr, "just pressed %d %d\n", mousex, mousey); // help debug } if(state == GLUT_UP) { if(gamerunning == 1) { // may change this for debug fprintf(stderr, "No button lifting!!!\n"); gamerunning = -1; // Don't cheat!!! } glutPostRedisplay(); //fprintf(stderr, "just released %d %d\n", mousex, mousey); // help debug } break; } return; } void mouse_motion(int mousex, int mousey) { int R, G, B; unsigned char *p; switch(mouse_saved_button) { case GLUT_LEFT_BUTTON: //fprintf(stderr, "%f %f %d %d %d %d\n", //arrow_lastx, arrow_lasty, arrowx, arrowy, mousex, mousey); arrow_lastx = 0.7 * arrow_lastx + 0.3 * arrowx; arrow_lasty = 0.7 * arrow_lasty + 0.3 * arrowy; arrowx = mousex; arrowy = mousey; if(mouse_saved_state == GLUT_DOWN) { // 1) XXX Your code to retrieve the pixel RGB value. // First make the pointer p point to the R value // for the current pixel inside the pixels array. // Then successively retrive the R, G, B values. //fprintf(stderr, "GLUT_DOWN at %d %d RGB %d %d %d %d\n", // mousex, mousey, R, G, B, gamerunning); // 6) XXX Your code to make the ending more dramatic // make the screen flash or something else in the // following if else sections if(R == 255) { if(gamerunning == 0) fprintf(stderr, "Game started!\n"); gamerunning = 1; } else if(gamerunning == 1 && B > 10) { fprintf(stderr, "You Won :-)\n"); gamerunning = 2; } else if(gamerunning == 1 && G < 100) { fprintf(stderr, "Gameover, You Lost :-(\n"); gamerunning = -1; } } glutPostRedisplay(); // something moved so redisplay here break; } return; } // 5) XXX Your code to make a new and improved arrow shape #define NPTS 4 // there are 4 points in this arrowhead shape // the ashape array contains the original shape XY coordinate pairs float ashape[NPTS][2] = { 0.0, 0.0, 24.0, -10.0, 20.0, 0, 24.0, 10.0 }; void display(void) { char txt[100]; // for holding screen print text float rev_y, cosdir, sindir; float rshape[NPTS][2]; // transformed coordinates for the shape float dx, dy, hyp; // current motion and its right triangle hypotenuse int i; glPixelZoom(1, -1); // "Proper" orientation for pixel ops glRasterPos2f(0.0, ppm_ht); // XY position to draw the maze // now put the maze image into the frame buffer (try leaving this out) glDrawPixels(ppm_wid, ppm_ht, GL_RGB, GL_UNSIGNED_BYTE, pixels); // 4) XXX Your code to print a display cycle counter on the screen switch(gamerunning) { case -1: draw_string(10.0, ppm_ht - 20.0, "You Lost :-("); break; case 0: draw_string(10.0, ppm_ht - 20.0, "Start on Red Circle"); break; case 1: draw_string(10.0, ppm_ht - 20.0, "GAME Running"); sprintf(txt, "X = %3d Y = %3d", arrowx, arrowy); draw_string(10.0, ppm_ht - 40.0, txt); break; case 2: draw_string(10.0, ppm_ht - 20.0, "You Won :-)"); break; } // 2) XXX Your code to compute cosdir and sindir from the values // of the current and last arrow positions (arrowx, arrow_lastx, // arrowy, arrow_lasty rev_y = ppm_ht - arrowy - 1; // rev_y is just Y flipped for(i = 0; i < NPTS; i++) { // 3) XXX Your code to produce properly transformed 2D // shape coordinates in the rshape array by using the // current arrow position, the ashape values and the // sindir,cosdir values you computed for item 2) } // Make the color coded filled arrow shape if(gamerunning == 1) glColor3ub(0, 0, 255); else glColor3ub(255, 0, 0); glBegin(GL_POLYGON); for(i = 0; i < NPTS; i++) glVertex2fv(rshape[i]); glEnd(); // It looks nicer with a black outline glColor3ub(0, 0, 0); glBegin(GL_LINE_LOOP); for(i = 0; i < NPTS; i++) glVertex2fv(rshape[i]); glEnd(); glutSwapBuffers(); return; } // 7) XXX Your extra credit code could go anywhere