Assignment 1: A Bouncing Ball

Due: February

Simulate a bouncing ball in two dimensions, incorporating both gravity and elastic (no velocity is lost) collisions with a surface. Model the ball as a closed polygon (filled) that has a sufficient number of sides to look smooth. The ball should be within a four-sided "box". It should start at a random position within the box with a random velocity (including in a random direction). Thus, each execution of the program should result in a different animation.

The box should be at least 500 pixels high and 500 pixels wide.

The ball should be between 30 and 100 pixels in diameter.

In choosing the random starting state, the velocity should not be so high that it is impossible to follow the movement of the ball.

User input

Clicking the left mouse button should pause/continue the animation. Clicking the right button should generate a new animation from scratch (i.e., restart the ball with a new random position and velocity).

Animation

To create animation you'll need to update the state of the model and redraw the image every 30th of a second, or so. Here "state of the model" represents the position and velocity of the "ball". So we need 4 values: the (x,y) coordinates of the ball, and the horizontal and vertical components of a vector representing the ball's current velocity, (Dx, Dy). Assuming there are no collisions, the new position of the ball (xn, yn) after a frame of animation should be (x+Dx, y+Dy). Thus our animation loop should look something like:

  while (we want app to run)
    x = x + Dx;  y = y + Dy;  // Update state
    postGlutRedisplay();
    sleep(1/30th second);  

This is almost right, but there are some potential problems with getting Glut and the sleep() to work well together. Also, it is not clear where the above code should go. Is it in our display call back? If so, then we would be forcing a frame of animation whenever a display is invoked, and that's not quite right either, since display() is sometimes called independently of animations.

Instead, we should make use of Glut's built-in timer capability. We can define a timer callback function, and an amount of time to elapse before that function is invoked. Here's how that can be accomplished:

static void timerCallback (int value)
{
   /* Do timer processing? */
    x = x + Dx;  y = y + Dy;  // Update state   
    postGlutRedisplay(); /* this invokes glutPostRedisplay(), if necessary */

   /* call back again after elapsedMSecs milliseconds have passed */
   glutTimerFunc (elapsedMSecs, timerCallback, value);
}
    

In a more complex example the first line of the function "// Update state" should probably be a call to a function named something like, "update()". update() would be responsible for changing the state of the world, but not rendering it. In this case it would just change the x and y variables, watching for collisions with the wall, etc.

The last line of the function registers the timer callback to be invoked again in "elapsedMSecs" milliseconds (the third argument is a value to be passed to the callback). The first line of the function ("Do timer processing?") is where you might put code if you were worrying about dealing with inaccuracies in the timer (for example, what if this function were invoked before or after it should have been because of variations in the system clock, CPU load, etc.?) but you shouldn't need to worry about that for this assignment.

You'll have to make sure the timer callback is registered in your init code so that the callback will be invoked the first time. For example, via

glutTimerFunc (1.0/60000, timerCallback, value); // = 1/60 of a second

After that, the timer callback re-registers itself with each invocation, as explained above.

Code Portability

If you are not including Angel.h, to provide compatibility with other platforms please use the following code when you want to include glut.h in your code:

  #if defined(__APPLE__) || defined(MACOSX)
  #include <GLUT/glut.h>
  #else
  #include <GL/glut.h>
  #endif  

If you use a sleep function in your code to provide animation, rather than glutTimerFunc(), then to provide compatibility with other platforms, please place the following code where you include headers:

  #if defined(__APPLE__) || defined(MACOSX)
  #include <unistd.h>
  #else
  #include <windows.h>
  #endif  

Then when you want to sleep your program, use code that looks like this:

  #if defined(__APPLE__) || defined(MACOSX)
  usleep(200000);  // Sleep for 200,000 microseconds
  #else
  sleep(200); // Window's sleep function argument is in milliseconds.
  #endif  

Graduate Credit

Those students taking this course for graduate credit must provide additional functionality. Undergraduates will be given extra credit. Here are the criteria:

  1. The program should query the user (console/terminal input is fine) for the slope of top edge of the quadrilateral. This should be an integer value between 0 and 45, representing degrees from horizontal. The animation can then begin, with the ball bouncing in the defined quadrilateral. One possible shape is shown below, with a being the angle input by the user.
  2. The simulation should include friction--each collision should reduce the velocity by 1%
  3. The simulation should include gravity--the vertical component of velocity should decrease linearly with each frame of animation.

Bouncing container

Hints:

You can render an approximation of a circle (filled) via a bunch of triangles, each of them having one of its vertices as the center of the circle, and the other two as points along the circumference. Use a GL_TRIANGLE_FAN.

Let (x0, y0) be the coordinates of the center of the circle. The points along the circumference can be generated:

for ANGLE = 0 to 360 step 5
       x = sind(ANGLE)*RADIUS, y = cosd(ANGLE)*RADIUS

You may need to refresh your knowledge of the Newtonian equations that specify the effect of gravity on a moving object, as well as the trigonometry necessary to calculate the angle at which the ball will rebound from a surface. To simplify your calculations, you probably want the box to be a rectangle. In that case, you can get by with simply reversing the signs of the delta x and delta y terms (velocities), as appropriate.

Use double-buffering to make your animation smooth.

Consider the use of GL_PERSPECTIVE_CORRECTION_HINT via glHint in your program.

If you want to get fancy, you can get your animation to run at a frame rate independent of the CPU speed by using glutGet(GLUT_ELAPSED_TIME).

You will have to experiment with the relationship between frame rates and velocity to get an attractive animation. You can use calls to sleep() to control the frame rate, but this is not absolutely necessary.

Submission:

Submit your source code to the dropbox within emuonline.