/*The standard libraries for C and extra math (we'll be needing some sines and cosines possibly) here*/ #include #include /* These are the openGL and GLUI calls. Notice that we only call glut, not gl and glu. Glut has these calls inside it!*/ #include #include #define M_PI 3.14159265 /* title of these windows: */ const char *WINDOWTITLE = { "Bouncing cube animation, Bartholemew_prj3" }; const char *GLUITITLE = { "User Interface Window" };/* what the glui package defines as true and false: */ const int GLUITRUE = { true }; const int GLUIFALSE = { false }; /* the escape key: */ #define ESCAPE 0x1b /* gravity */ #define GRAVITY 9.81 // meters/second^2 /* initial window size: */ const int INIT_WINDOW_SIZE = { 800 }; /* multiplication factors for input interaction: */ /* (these are known from previous experience) */ const float ANGFACT = { 1. }; const float SCLFACT = { 0.005f }; /* minimum allowable scale factor: */ const float MINSCALE = { 0.05f }; /* active mouse buttons (or them together): */ const int LEFT = { 4 }; const int MIDDLE = { 2 }; const int RIGHT = { 1 }; /* which projection: */ const int ORTHO = { GLUIFALSE }; const int PERSP = { GLUITRUE };/* which button: */ #define RESET 0 #define QUIT 1 /* window background color (rgba): */ const float BACKCOLOR[ ] = { 0., 0., 0., 0. }; /* color and line width for the axes: */ const float AXES_COLOR[ ] = { 1., .5, 0. }; const float AXES_WIDTH = 3.; /* handy to have around: */ const int OFF = { 0 }; const int ON = { 1 }; #define FALSE 0 #define TRUE 1 /* convenient way to square and cube a number: */ inline int SQR( int x ) { return x * x; } inline float SQR( float x ) { return x * x; } inline int CUBE( int x ) { return x * x * x; } inline float CUBE( float x ) { return x * x * x; } /** ** non-constant global variables: **/ int ActiveButton; /* current button that is down */ int AxesList; /* list to hold the axes */ int AxesOn; /* ON or OFF */ int Debug; /* ON means print debug info */ GLUI * Glui; /* instance of glui window */ int GluiWindow; /* the glut id for the glui window */ int GrWindow; /* window id for graphics window */ int Objectlist; /* object display lists */ float RotMatrix[4][4]; /* set by glui rotation widget */ float Scale,Scale2; /* scaling factors */ int WhichProjection; /* ORTHO or PERSP */ int Xmouse, Ymouse; /* mouse values */ float Xrot, Yrot; /* rotation angles in degrees */ float TransXYZ[3]; /* set by glui translation widgets */ int computed_collision;/* collision just computed, prevents repeated collision calcs */ int anon=1; int keyon=1; float t=0.0f; float X,Y,Z; float Thetax, Thetay, Thetaz; float mass = 10.0f; float one_over_mass; /*Compute this once, and then we'll save some processor time*/ float Ia ; float CMToCorner_PerpA[3]; float collision_normal[3]; float linear_Velocity[3]; float angular_Velocity[3]; int STOPPED = 0; /*stops the animation if you are too close to zero in height and too slow in vel*/ int MOTION ; int tempsomething; float g_radius=0.15f; /* The coefficient of Restitution */ float COEFFICIENT_OF_RESTITUTION =0.9f; /*===================* ** function prototypes: *===================*/ void Animate( void ); /* All of our animation code will go here, including the calculations of impulsive collisions*/ void Buttons( int ); /* a glut callback we will fill up with a switch statement as to what happens with a button press*/ void Display( void ); /* good old friend the display function callback*/ void InitGraphics( void ); /* initializes graphics*/ void InitLists( void ); /* initializes display lists*/ void InitGlui( void ); /* calculates the triple integral of the differential-complex-fourier-laplace-cosine...initializes glui*/ void Keyboard( unsigned char, int, int ); /* our keyboard callback function, just like project 2, a switch statement for key presses*/ void MouseButton( int, int, int, int ); /* handles mouse clicks with the left, middle or right button, glut callback function*/ void MouseMotion( int, int ); /* handles mouse tracking and positioning, a glut callback function again*/ void Reset( void ); /* this will serve to reset the animation and various numbers*/ void Resize( int, int ); /* handles window resizing, works the same as with project 2*/ void cross( float [3], float [3], float [3] ); /* calculates the cross product of two vectors, and puts the output in the third*/ float dot( float [3], float [3] ); /* returns the scalar dot product of two vectors*/ float unit( float [3], float [3] ); /* normalizes the input vector to one, fills the second vector as the normalized input*/ void scalarproduct(float [3], float ,float [3]); /* takes the scalar product of a vector and a scalar and fills the third variable (vector)*/ int CheckCollision(float,float,float); /* checks if a collision occurs at a particular x,y,z*/ float norm(float [3]); /* computes the norm of the input vector*/ float distance(float [3],float [3]); /* computes the distance between two vectors*/ void myTimer(int); /* a timer function glut callback, will be triggered every so often*/ int main( int argc, char *argv[] ) { /* Initalize OpenGL Toolkit */ glutInit( &argc, argv ); /* setup all the graphics stuff, including registering event callbacks with glut*/ InitGraphics(); /* create the display structures that will not change: */ InitLists(); /* init all the global variables used by Display(): */ /* this will also post a redisplay */ /* it is important to call this before InitGlui(): */ /* so that the variables that glui will control are correct */ /* when each glui widget is created*/ Reset(); /* setup all the user interface stuff: */ InitGlui(); /*------------------------------------------------------------------ Call OpenGL main loop. This function does not return until the graphics window is closed or destroyed. ------------------------------------------------------------------*/ glutMainLoop(); /* this is here to make the compiler happy: */ return 0; } /** ** initialize the glut and OpenGL libraries: ** also setup display lists and callback functions **/ void InitGraphics( void ) { if( Debug ) fprintf( stderr, "InitGraphics\n" ); /* setup the display mode: */ /* ( *must* be done before call to glutCreateWindow() ) */ /* ask for color, double-buffering, and z-buffering: */ glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ); /* set the initial window configuration: */ glutInitWindowSize( INIT_WINDOW_SIZE, INIT_WINDOW_SIZE ); /* open the window and set its title: */ GrWindow = glutCreateWindow( WINDOWTITLE ); glutSetWindowTitle( WINDOWTITLE ); glutPositionWindow(400, 40); /* setup the clear values: */ glClearColor( BACKCOLOR[0], BACKCOLOR[1], BACKCOLOR[2], BACKCOLOR[3] ); /* setup the callback routines: */ /* DisplayFunc -- redrawthe window */ /* ReshapeFunc -- handle the user resizing the window */ /* KeyboardFunc -- handle a keyboard input */ /* MouseFunc -- handle the mouse button going down or up */ /* MotionFunc -- handle the mouse moving with a button down */ /* PassiveMotionFunc -- handle the mouse moving with a button up*/ /* EntryFunc -- handle the cursor entering or leaving the window */ /* SpecialFunc -- handle special keys on the keyboard */ /* SpaceballMotionFunc -- handle spaceball translation */ /* SpaceballRotateFunc -- handle spaceball rotation */ /* SpaceballButtonFunc -- handle spaceball button hits */ /* ButtonBoxFunc -- handle button box hits */ /* DialsFunc -- handle dial rotations */ /* TabletMotionFunc -- handle digitizing tablet motion */ /* TabletButtonFunc -- handle digitizing tablet button hits */ /* MenuStateFunc -- declare when a pop-up menu is in use */ /* IdleFunc -- what to do when nothing else is going on */ /* TimerFunc -- trigger something to happen every so often */ glutSetWindow( GrWindow ); glutDisplayFunc( Display ); glutReshapeFunc( Resize ); glutKeyboardFunc( Keyboard ); glutMouseFunc( MouseButton ); glutMotionFunc( MouseMotion ); glutPassiveMotionFunc( NULL ); glutEntryFunc( NULL ); glutSpecialFunc( NULL ); glutSpaceballMotionFunc( NULL ); glutSpaceballRotateFunc( NULL ); glutSpaceballButtonFunc( NULL ); glutButtonBoxFunc( NULL ); glutDialsFunc( NULL ); glutTabletMotionFunc( NULL ); glutTabletButtonFunc( NULL ); glutMenuStateFunc( NULL ); // DO NOT SET THE GLUT IDLE FUNCTION HERE !! // glutIdleFunc( NULL ); // let glui take care of it in InitGlui() /*But we will set our timer function here:*/ glutTimerFunc( 33, myTimer, 1 ); } void InitLists( void ) { if( Debug ) fprintf( stderr, "InitLists\n" ); } /**============================================== / ============================================== ** initialize the glui window: / ============================================== **============================================*/ void InitGlui( void ) { if( Debug ) fprintf( stderr, "InitGlui\n" ); /*================================================== These are the Glui pointer variables identifying the panels and translation group ===================================================*/ GLUI_Panel *panel, *bigpanel; GLUI_Translation *trans, *scale; /*============================================= GLUI_Master.create_glui creates a new glui window with the title as an argument. We defined the title above as a #define ===============================================*/ Glui = GLUI_Master.create_glui( (char *) GLUITITLE); /*================================================ Add static text in the order called by making the following one line call: ================================================= */ Glui->add_statictext( "This will add static text" ); /*================================================== Add a separator by making the following line of code: ====================================================*/ Glui->add_separator(); /*================================================== Add a checkbox by making the following line of code: ====================================================*/ Glui->add_checkbox( "Perspective", &WhichProjection ); /*================================================== Add a 'panel' of controls with the name as the argument, by making the following line of code: ====================================================*/ bigpanel = Glui->add_panel( "Controls" ); /*================================================== You can add controls to the panel group which has been created with slightly different calls to create the control. Checkbox example is below arguments: (which panel to add to),(checkbox title),(variable) ====================================================*/ Glui->add_checkbox_to_panel(bigpanel,"Animation on",&anon); Glui->add_separator(); /*===================================================== add a new column of controls so they aren't all in a vertical line here =====================================================*/ Glui->add_column_to_panel( bigpanel, TRUE ); /*==================================================== The transformation widgets panel is given to you below ======================================================*/ panel = Glui->add_panel( "Object Transformation" ); Glui->add_rotation_to_panel( panel, "Rotation", (float *) RotMatrix ); Glui->add_column_to_panel( panel, FALSE ); scale = Glui->add_translation_to_panel( panel, "Scale", GLUI_TRANSLATION_Y , &Scale2 ); scale->set_speed( 0.01f ); Glui->add_column_to_panel( panel, FALSE ); trans = Glui->add_translation_to_panel( panel, "Trans XY", GLUI_TRANSLATION_XY, &TransXYZ[0] ); trans->set_speed( 0.1f ); Glui->add_column_to_panel( panel, FALSE ); trans = Glui->add_translation_to_panel( panel, "Trans Z", GLUI_TRANSLATION_Z , &TransXYZ[2] ); trans->set_speed( 0.1f ); /*=====================================================*/ /*a checkbox for debugging*/ Glui->add_checkbox( "Debug", &Debug ); /*===================================================== to add a panel with no border, type either GLUI_PANEL_NONE or FALSE =====================================================*/ panel = Glui->add_panel( "" ,GLUI_PANEL_NONE); /*===================================================== Add a button to the panel, with a label for the button, a variable, and the callback function to handle the button click =====================================================*/ Glui->add_button_to_panel( panel, "Reset", RESET, (GLUI_Update_CB) Buttons ); Glui->add_column_to_panel( panel, FALSE ); Glui->add_button_to_panel( panel, "Quit", QUIT, (GLUI_Update_CB) Buttons ); /* tell glui what graphics window it needs to post a redisplay to: */ Glui->set_main_gfx_window( GrWindow ); /* set the graphics window's idle function: */ GLUI_Master.set_glutIdleFunc( Animate ); } /** ** the keyboard callback: **/ void Keyboard( unsigned char c, int x, int y ) { if( Debug ) fprintf( stderr, "DoKeyboard: '%c' (0x%0x)\n", c, c ); switch( c ) { case 'q': case 'Q': case ESCAPE: Buttons( QUIT ); /* will not return here - you quit the program by executing this call*/ break; default: fprintf( stderr, "Don't know what to do with keyboard hit:: '%c' (0x%0x)\n", c, c ); } /*======================================================= synchronize the GLUI display with the variables: use this to keep what the GLUI interface displays and what is current for the variables they are associated with the same ========================================================*/ Glui->sync_live(); /* force a call to Display(): */ /* ====================================================== Makes the window 'GrWindow' the current window, and post a redisplay (ie call the display function). This serves to update the image display. Otherwise you would click and see the button change, but nothing would change in the animation =======================================================*/ glutSetWindow( GrWindow ); glutPostRedisplay(); } /** ** called when the mouse button transitions down or up: **/ void MouseButton ( int button, /* GLUT_*_BUTTON */ int state, /* GLUT_UP or GLUT_DOWN */ int x, int y /* where mouse was when button hit */ ) { int b; /* LEFT, MIDDLE, or RIGHT */ if( Debug ) fprintf( stderr, "MouseButton: %d, %d, %d, %d\n", button, state, x, y ); /* get the proper button bit mask: */ switch( button ) { case GLUT_LEFT_BUTTON: b = LEFT; break; case GLUT_MIDDLE_BUTTON: b = MIDDLE; break; case GLUT_RIGHT_BUTTON: b = RIGHT; break; default: b = 0; fprintf( stderr, "Unknown mouse button: %d\n", button ); } /* button down sets the bit, up clears the bit: */ if( state == GLUT_DOWN ) { Xmouse = x; Ymouse = y; ActiveButton |= b; /* set the proper bit */ } else ActiveButton &= ~b; /* clear the proper bit */ } /** ** called when the mouse moves while a button is down: **/ void MouseMotion ( int x, int y /* mouse coordinates, passed in by GLUT*/ ) { int dx, dy; /* change in mouse coordinates */ if( Debug ) fprintf( stderr, "MouseMotion: %d, %d\n", x, y ); dx = x - Xmouse; /* change in mouse coords */ dy = y - Ymouse; /*==================================== If the button which is down is the left button, rotate the scene in realtime =====================================*/ if( ActiveButton & LEFT ) { Xrot += ( ANGFACT*dy ); Yrot += ( ANGFACT*dx ); } /*==================================== If the button which is down is the middle button, scale the scene in realtime =====================================*/ if( ActiveButton & MIDDLE ) { Scale += SCLFACT * (float) ( dx - dy ); /* keep object from turning inside-out or disappearing: */ if( Scale < MINSCALE ) Scale = MINSCALE; } Xmouse = x; /* new current position */ Ymouse = y; glutSetWindow( GrWindow ); glutPostRedisplay(); } /**===================================== ** reset the transformations, and other variables: ** ** this only sets the global variables (initializes them)-- ** the glut main loop is responsible for redrawing the scene *=======================================*/ void Reset( void ) { ActiveButton = 0; Debug = GLUIFALSE; Scale = 0.3f; Scale2 = 0.0f; /* because add 1. to it in Display() */ WhichProjection = PERSP; Xrot = -80.; Yrot = 30.; TransXYZ[0] = TransXYZ[1] = TransXYZ[2] = 0.; RotMatrix[0][1] = RotMatrix[0][2] = RotMatrix[0][3] = 0.; RotMatrix[1][0] = RotMatrix[1][2] = RotMatrix[1][3] = 0.; RotMatrix[2][0] = RotMatrix[2][1] = RotMatrix[2][3] = 0.; RotMatrix[3][0] = RotMatrix[3][1] = RotMatrix[3][3] = 0.; RotMatrix[0][0] = RotMatrix[1][1] = RotMatrix[2][2] = RotMatrix[3][3] = 1.; X = 0.; Y = 0.; Z = 6.; Thetax = 0.; Thetay = 0.; Thetaz = 0.; linear_Velocity[0] = 2.0f; linear_Velocity[1] = 0.0f; linear_Velocity[2] = 6.0f; angular_Velocity[0] = 0.2f; angular_Velocity[1] = 0.1f; angular_Velocity[2] = 1.0f; t=0; /* for a sphere, the collision normal will always be perpendicular to the collision surface (flat ground plane here)*/ collision_normal[0] = 0.; collision_normal[1] = 0.; collision_normal[2] = 1.; STOPPED = FALSE; MOTION = 1; /* find the inertia for the ball*/ Ia=.225*mass; one_over_mass = 1./mass; /*Faster to compute this once*/ g_radius = 0.5f; glutSetWindow( GrWindow ); glutPostRedisplay(); } /*============================* ** called when user resizes the window: *=============================*/ void Resize( int width, int height ) { if( Debug ) fprintf( stderr, "ReSize: %d, %d\n", width, height ); /*============================================================ don't really need to do anything since window size is */ /* checked each time in Display(), however if you want to optimize your code you could do resizing here... plus we'll be using this in the next homework, so better to start the structure now. ==============================================================*/ glutSetWindow( GrWindow ); glutPostRedisplay(); } float dot( float v1[3], float v2[3] ) { return( v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2] ); } void scalarproduct(float v1[3], float c,float tmp[3]) { tmp[0] = v1[0]*c; tmp[1] = v1[1]*c; tmp[2] = v1[2]*c; } void cross( float v1[3], float v2[3], float vout[3] ) { float tmp[3]; tmp[0] = v1[1]*v2[2] - v2[1]*v1[2]; tmp[1] = v2[0]*v1[2] - v1[0]*v2[2]; tmp[2] = v1[0]*v2[1] - v2[0]*v1[1]; vout[0] = tmp[0]; vout[1] = tmp[1]; vout[2] = tmp[2]; } float unit( float vin[3], float vout[3] ) { float dist, f ; dist = vin[0]*vin[0] + vin[1]*vin[1] + vin[2]*vin[2]; if( dist > 0.0 ) { dist = sqrt( dist ); f = 1. / dist; vout[0] = f * vin[0]; vout[1] = f * vin[1]; vout[2] = f * vin[2]; } else { vout[0] = vin[0]; vout[1] = vin[1]; vout[2] = vin[2]; } return( dist ); } float norm(float n[3]) { //computes a two norm of the input vector return(sqrt(SQR(n[0]) + SQR(n[1]) + SQR(n[2]) )); } float distance(float v1[3],float v2[3]) { return(sqrt(SQR(v1[0]-v2[0]) + SQR(v1[1]-v2[1]) + SQR(v1[2]-v2[2]) )); } void myTimer(int timevalue) { if(Debug) fprintf( stderr, "mytimer\n" ); /*Do something here...perhaps, a suggestion would be to change a variable as an indicator of when to draw?*/ MOTION=1; /*glut does something funny here. You have to include in your function the following line of code to reset the timer each time the timer goes off - like an alarm clock which switches off after the alarm occurs, and must be turned on again*/ glutTimerFunc(33,myTimer,1); } //this checks if the sphere collides with any surface int CheckCollision(float x,float y, float z) { /*You may please add your collision checking code here!*/ return(0); } /**======================================== ** this is where one would put code that is to be called ** everytime the glut main loop has nothing to do ** ** this is typically where animation parameters are set ** ** do not call Display() from here -- let glutMainLoop() do it *=========================================*/ void Animate( void ) { /* put animation stuff in here -- change some global variables */ /* for Display() to find: */ if (MOTION) { MOTION = 0; int Collide = CheckCollision(X,Y,Z); if (!STOPPED) { if (Debug) { // fprintf(stderr,"collide is %d %d\n",Collide, computed_collision); } if (Collide && (computed_collision == FALSE)) { computed_collision=TRUE; double impulse_numerator = -(1+COEFFICIENT_OF_RESTITUTION)*dot(linear_Velocity,collision_normal); if( Debug ) { fprintf(stderr, "Collision!\n"); fprintf(stderr, "Impulse numerator is %f\n",impulse_numerator); } //normal_times_one_over_mass[0] = collision_normal[0]*one_over_mass; //normal_times_one_over_mass[1] = ..; //normal_times_one_over_mass[2] = ..; //impulse_denominator = ???; //impulse = ??? //TA DA, new linear velocity! That wasn't so bad, was it? /* notice the linear velocity only changes normal to the bounce (recall we have a frictionless surface, so only normal forces are transmitted - all other components of the collision normal are zero). We keep these other equations for possible changes in the future...hint-hint*/ // linear_Velocity[0] = linear_Velocity[0] + ???; // linear_Velocity[1] = linear_Velocity[1] + ???; //linear_Velocity[2] = linear_Velocity[2] + ????; //now to compute angular velocity?...(extra credit, see Extra credit section) computed_collision = TRUE; } //now we'll add gravity to the mix! t+=0.033f; float dt=0.033f; //linear_Velocity[2]=linear_Velocity[2]-GRAVITY*dt; //Now we'll do the equation updating...position and velocity X = X + dt * sin(t*3.)*3.; Y=Y+dt*cos(t*3.)*3.; Z=Y-sin(cos(t*3.)); if (Z the radius of the sphere?*/ /*Continue rotating at the same speed*/ Thetax = Thetax + dt * cos(3.*t); Thetay =Thetay + dt*sin(t*30.); Thetaz = Thetaz+dt*5.; } } /* force a call to Display() next time it is convenient: */ glutSetWindow( GrWindow ); glutPostRedisplay(); } /*===================* ** glui buttons callback: *====================*/ void Buttons( int id ) { switch( id ) { case RESET: // something must happen here ... perhaps a mysterious function call...if I could only remember the name...? break; case QUIT: /* gracefully close the glui window: */ /* gracefully close out the graphics: */ /* gracefully close the graphics window: */ /* gracefully exit the program: */ Glui->close(); glFinish(); glutDestroyWindow( GrWindow ); exit( 0 ); } Glui->sync_live(); } /*============================* ** draw the complete scene: *============================*/ void Display( void ) { int dx, dy, d; /* viewport dimensions */ int xl, yb; /* lower-left corner of viewport */ float scale2; /* real glui scale factor */ //if( Debug ) //{ //fprintf( stderr, "Display\n" ); //} Ê /* set which window we want to do the graphics into: */ glutSetWindow( GrWindow ); /* erase the background: */ glDrawBuffer( GL_BACK ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glEnable( GL_DEPTH_TEST ); /* specify shading to be smooth: */ glShadeModel( GL_SMOOTH ); /* set the viewport to a square centered in the window, basically doing the resize functions here: */ dx = glutGet( GLUT_WINDOW_WIDTH ); dy = glutGet( GLUT_WINDOW_HEIGHT ); d = dx < dy ? dx : dy; /* minimum dimension */ xl = ( dx - d ) / 2; yb = ( dy - d ) / 2; glViewport( xl, yb, d, d ); /* set the viewing volume: */ /* remember that the eye is at the origin looking in -Z */ /* remember that the Z values are actually */ /* given as DISTANCES IN FRONT OF THE EYE */ glMatrixMode( GL_PROJECTION ); glLoadIdentity(); if( WhichProjection == ORTHO ) glOrtho( -3., 3., -3., 3., 0.1, 1000. ); else gluPerspective( 90., 1., 0.1, 1000. ); /* place the object into the viewing volume: */ glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); gluLookAt( 0., 0., 3., 0., 0., 0., 0., 1., 0. ); /* rotate the scene: */ glRotatef( Yrot, 0., 1., 0. ); glRotatef( Xrot, 1., 0., 0. ); glMultMatrixf( (const GLfloat *) RotMatrix ); /* scale the scene: */ glScalef( Scale, Scale, Scale ); scale2 = 1. + Scale2; /* because glui translation starts at 0. */ if( scale2 < MINSCALE ) scale2 = MINSCALE; glScalef( scale2, scale2, scale2 ); /* possibly draw the axes: */ /* translate the object: */ /* note the minus sign on the z value */ /* this is to make the appearance of the glui z translate */ /* widget more intuitively match the translate behavior */ glTranslatef( TransXYZ[0], TransXYZ[1], -TransXYZ[2] ); //put your display code here! glPushMatrix(); glTranslatef(X,Y,Z); glRotatef(Thetax, 1.,0.,0.); glRotatef(Thetay, 0.,1.,0.); glRotatef(Thetaz, 0.,0.,1.); glTranslatef(X,Y,Z); glutWireTeapot(2.); glPopMatrix(); /* swap the double-buffered framebuffers: */ glutSwapBuffers(); /* be sure the graphics buffer has been sent: */ glFlush(); }