/* * triangle.c -- A simple example of OpenGL and GLUT. * * I have hacked this from the original triangle.c program to check that a few * more of the capabilities that we will be needing are easy to * implement--Kobus. * * IMPORTANT--this is a proof of concept hack. It is not scalable. Student * programs are expected to better structured! * */ #include #include /* // Sometimes glu.h includes glut.h--sometimes not. */ #ifdef MAC_OSX # include # include #else # include # include #endif /* // Some debugging macros. Normally I make macros upper case; these are // exceptions. */ #define dbc(X) \ if (debug_level >= 1) \ { \ fprintf(stderr, \ "dbc: "#X" is ->%c<- (0x%x, %d) on source line %d of %s\n", \ (int)(X), (int)(X), (int)(X), __LINE__, __FILE__); \ } #define dbp(X) \ if (debug_level >= 1) \ { \ fprintf(stderr, "%s\n", (X)); \ } #define dbi(X) \ if (debug_level >= 1) \ { \ fprintf(stderr, \ "dbi: "#X" is %ld on source line %d of %s\n", (long)(X), \ __LINE__, __FILE__); \ } #define dbw() \ if (debug_level >= 1) \ { \ fprintf(stderr, \ "At line %d of %s\n", __LINE__, __FILE__); \ } /* -------------------------------------------------------------------------- */ #define NOT_SET (-99) /* Something negative. */ /* -------------------------------------------------------------------------- */ /* // Watch name clashes. I'm sticking my initials in front of these so that when // I include someone elses code, I won't have to worry if, say, "SQUARE" is in // their namespace. */ typedef enum KJB_object { KJB_TRIANGLE, KJB_SQUARE } KJB_object; typedef enum KJB_color { KJB_RED, KJB_GREEN, KJB_BLUE, KJB_WHITE } KJB_color; /* -------------------------------------------------------------------------- */ /* // Some prefer to write their programs backwards, so we don't need to declare // functions up front--I prefer to write top down, so I have added function // declarations to the original program. */ static void display_CB(void); /* Called whenever redisplay needed */ static void key_CB(unsigned char key, int x, int y); /* Called on key press */ static void mouse_CB(int button, int state, int x, int y); /* Called on mouse click */ static void mouse_move_CB(int x, int y); /* Called on mouse click */ static void select_triangle_color(int value); /* For menu */ static void add_object_CB(int value); /* For submenu */ /* -------------------------------------------------------------------------- */ static int debug_level = 1; /* Used by the macros above. */ /* // For a real program you will need to store position, color, etc. of each item // relavent to your world as part of a carefully thought out data structure. // But here we have only one object and this is a "test" program, so I have // used global variables for my sole object. */ static int displacement_x; /* Displacement from the original position. */ static int displacement_y; /* Initialized at 0 because static. */ static int drag_start_x = NOT_SET; static int drag_start_y = NOT_SET; static float triangle_red = 1.0; static float triangle_green = 1.0; static float triangle_blue = 1.0; /* -------------------------------------------------------------------------- */ int main(int argc, char *argv[]) { int win; int submenu; glutInit(&argc, argv); /* initialize GLUT system */ glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInitWindowSize(400, 500); win = glutCreateWindow("Triangle"); /* create window */ /* set background to black */ glClearColor((GLclampf)0.0,(GLclampf)0.0,(GLclampf)0.0,(GLclampf)0.0); gluOrtho2D(0.0, 400.0, 0.0, 500.0); /* how object is mapped to window */ glutDisplayFunc(display_CB); /* set window's display callback */ glutKeyboardFunc(key_CB); /* set window's key callback */ glutMouseFunc(mouse_CB); /* set window's mouse callback */ glutMotionFunc(mouse_move_CB); /* set window's mouse move with button pressed callback */ /* Create a menu which is accessed by the right button. */ submenu = glutCreateMenu(select_triangle_color); glutAddMenuEntry("Red", KJB_RED); glutAddMenuEntry("Green", KJB_GREEN); glutAddMenuEntry("Blue", KJB_BLUE); glutAddMenuEntry("White", KJB_WHITE); glutCreateMenu(add_object_CB); glutAddMenuEntry("Triangle", KJB_TRIANGLE); glutAddMenuEntry("Square", KJB_SQUARE); glutAddSubMenu("Color", submenu); glutAttachMenu(GLUT_RIGHT_BUTTON); glutMainLoop(); /* start processing events... */ /* Execution never reaches this point, so return value is failure */ return EXIT_FAILURE; } /* /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ */ /* // Function called whenever redisplay needed (try hiding the window and then // exposing it. The debug macro should print something.) */ static void display_CB(void) { dbw(); /* Print debugging stuff on stderr. */ glClear(GL_COLOR_BUFFER_BIT); /* clear the display */ /* set current color */ glColor3d(triangle_red, triangle_green, triangle_blue); /* draw filled triangle */ glBegin(GL_POLYGON); /* Specify each vertex of triangle */ glVertex2i(200 + displacement_x, 125 - displacement_y); glVertex2i(100 + displacement_x, 375 - displacement_y); glVertex2i(300 + displacement_x, 375 - displacement_y); #ifdef WANT_EXACT /* * It seems that OpenGL may handle floating point arithmetic badly, or, at * least in a hard to describe way, and thus, if you really want a point to * be displayed exactly where you say, it is best to use a floating point * and add 0.5 to gaurd against roundoff problems. Note that the integer * version (glVertex2i()), just maps the integers to floating point, and * then subsequent calculations can lose precision, putting the points a bit * of from what you expect. * * (At least, this is the latest theory. Thanks to David Forester for * pointing out that the original explanation, based on a suspected off by * one problem in gluOrtho2D(), is likely to be incorrect, and that loss of * precision was likely the problem). */ glVertex2d(0.5 + (double)(200 + displacement_x), 0.5 + (double)(125 - displacement_y)); glVertex2d(0.5 + (double)(100 + displacement_x), 0.5 + (double)(375 - displacement_y)); glVertex2d(0.5 + (double)(300 + displacement_x), 0.5 + (double)(375 - displacement_y)); #endif glEnd(); /* OpenGL draws the filled triangle */ glFlush(); /* Complete any pending operations */ glutSwapBuffers(); /* Make the drawing buffer the frame buffer and vice versa */ } /* /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ */ /* Function called on key press */ static void key_CB(unsigned char key, int x, int y) { dbc(key); /* Print debugging stuff on stderr. */ dbi(x); /* Print debugging stuff on stderr. */ dbi(y); /* Print debugging stuff on stderr. */ if( key == 'q' ) exit(0); } /* /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ */ /* Function called on mouse click */ static void mouse_CB(int button, int state, int x, int y) { dbi(button); /* Print debugging stuff on stderr. */ dbi(state); /* Print debugging stuff on stderr. */ dbi(x); /* Print debugging stuff on stderr. */ dbi(y); /* Print debugging stuff on stderr. */ if (button == 0) { if (state == 0) { dbp("Press"); drag_start_x = x; drag_start_y = y; } else if (state == 1) { dbp("Release"); drag_start_x = NOT_SET; drag_start_y = NOT_SET; } } } /* /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ */ /* Function called on mouse move while depressed. */ static void mouse_move_CB(int x, int y) { dbi(x); /* Print debugging stuff on stderr. */ dbi(y); /* Print debugging stuff on stderr. */ dbi(drag_start_x); /* Print debugging stuff on stderr. */ dbi(drag_start_y); /* Print debugging stuff on stderr. */ if ((drag_start_x >= 0) && (drag_start_y >= 0)) { displacement_x += (x - drag_start_x); displacement_y += (y - drag_start_y); display_CB(); drag_start_x = x; drag_start_y = y; } } /* /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ */ static void add_object_CB(int value) { dbi(value); /* Print debugging stuff on stderr. */ if (value == KJB_TRIANGLE) { fprintf(stderr, "We only do one triangle, and you already have it.\n"); } else if (value == KJB_SQUARE) { fprintf(stderr, "We only do triangles; you can't have a square.\n"); } else { dbp("\n!!! Can't happen bug !!!\n"); abort(); } } /* /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ */ static void select_triangle_color(int value) { dbi(value); /* Print debugging stuff on stderr. */ if (value == KJB_RED) { triangle_red = 1.0; triangle_green = 0.0; triangle_blue = 0.0; } else if (value == KJB_GREEN) { triangle_red = 0.0; triangle_green = 1.0; triangle_blue = 0.0; } else if (value == KJB_BLUE) { triangle_red = 0.0; triangle_green = 0.0; triangle_blue = 1.0; } else if (value == KJB_WHITE) { triangle_red = 1.0; triangle_green = 1.0; triangle_blue = 1.0; } else { dbp("\n!!! Can't happen bug !!!\n"); abort(); } display_CB(); }