MAE 152 -- Fall 2003

Project 3a: Bouncing ball simulation: Window into the virtual world

Due date: Fri. Nov. 7, 2003, 5pm

100 Points with 20 points extra credit

Last edited Nov. 6, 2003


Objectives: (extra credit now posted)

This project introduces some of the more compelling aspects of openGL - 3D and animation!!!


Project Definition:

Discussion

Physical simulation coupled with computer graphics and the virtual world is becoming an increasingly common application for mechanical engineering. This is applied to everything from entertainment to pre-prototype testing of buildings, public transportation systems, and other advanced technologies. OpenGL has the advantage of allowing all this to happen in real-time.

We are going to create a simulation of an impulsive collision (ie occurring over a very short time) between an elastic ball and a frictionless plane.

There are many levels of complexity at which to simulate, as well as numerical strategies for integrating the underlying differential equations. In this case, the numerical integration will be at a simple level - euler integration, and the simulation will include the impulse of the collision combined with gravitational effects.

Consider a brief review of impulsive force and euler integration. With impulse it is sometimes confusing to imagine an instantaneous change in velocity. As you ponder impulse, you suddenly remember a childhood memory. It is an intense game of tag on the playground (Those of you who have never played tag really REALLY missed out). You are being chased by your friend, who is "it." Your game has suffered today because you are still trying to understand the concept of impulse. As you are thinking about this, you glance back to realize your friend is quickly overtaking you. It is at that unfortunate moment that you make the unfortunate decision to turn, at the same moment another individual in the game running from the other direction turns...BANG!!!

While lying on the ground stunned from your "collision," you realize suddenly what instantaneous change in velocity means, and impulsive force makes sense to you. You are so excited that the rest of the day is spent in the nurse's office, since you answered the question of what your name is as "Instantaneous change in velocity."

Consider the equation of y' = f(t,y), where y' = dy / dt. Euler integration is a simple numerical integration scheme by which our function f() is sliced up into a finite number of rectangular slices, all of the same width (explicit euler integration) dt = t[n+1]-t[n]. Then we can find

y[n+1] = y[n] +dt * f( t[n], y[n] )

In our case we are dealing with f(t,y) as being velocity, with vectors components in each direction, and position in X, Y, Z. In the case of our program, dt will be 1/30 of a second, or about 33msec. or 0.033sec. You will use this to step through your animation, 33 msec at a time.

 

The changes over time are displayed as a sequence of still frames to create the illusion of motion. This is called photoscopic motion Photoscopic motion is what you see when you watch television, see a movie, use a computer, or play video games (unless you are very "old school" and play text-only games!). Photoscopic motion returns us to the concept of a gestalt, as well as our perceptual limitations.

Figure 1: Example of photoscopic motion

Take a moment to consider all of the times during the day when you see or perceive motion. It appears that your perception is continuous, ie a car driving by, frisbee flying through the air, or a wave crashing in the ocean all are experienced in a smooth motion. The fact is that your perception is not continuous. As an example, each and every person has a hole in their vision (blind spot) on their retina where the ocular nerve attaches at the back of your eye. But you do not walk around seeing a black hole in your vision. Instead your brilliantly put together brain fills in the unknown information from various sources. The same happens with motion perception. After a frequency of around 24-30Hz (Thirty times per second), the perception of a discrete change from one point to another (or one image to another) is seen as a smooth transition. That is why you can watch television without seeing a sequence of flashing still frames (that would be extremely disturbing!). Television is broadcasted at 29.97Hz, which is referred to as 'drop-frame' rate. In fact, the number of frames displayed per second is referred to as the frame rate. Animation is merely the drawing of a sequence of frames, one after the other, in which an object or scene has some sort of progression through motion or other change (color, for example). This must be timed in order to convey some movement with a particular velocity. If your computer is able to display the frames faster than the animation is intended to be displayed, and no timing is implemented, the animation would race by in the blink of an eye.

The other two aspects to this project will be a graphical interface and multiple camera views. The GUI allows the user to give the ball different initial velocities and positions, as well as providing animation controls and viewing controls. The multiple camera views will illustrate how different points of view can elucidate entirely new interpretations of the same information. Below is a sample GUI created using the GLUI library:

Figure 2: Sample GUI: Top to bottom- 3 checkboxes, 1 spinner (editbox), one object group containing two radio items, one button

Anyone who has written programs for Microsoft Windows, Unix, MacOS, or any other operating system know that creating even a simple graphical user interface can be an extremely time consuming and involving process. This detracts from the time one wishes to spend on the important aspects of the program. The GLUI library (or download the library and required files here) (which uses GLUT to display windows and functional interface elements) is designed for the express purpose of creating interfaces with the greatest of ease- almost all interface elements (button, checkbox, etc.) require only one line of code to be added, and all the rest is taken care of for you! There are also other libraries which can be found that do something similar, but the simplicity of GLUI is difficult to match, and it fulfills all our interface purposes for this course.

Assignment:

The project is as follows. Create a simulation of a sphere being dropped from some initial height, and some initial velocity (including rotation), including the sphere bouncing and losing energy. Let the sphere be displayed as a wireframe with some shape (such as a cube) inside it of a solid color. Provide a graphical user interface which will allow the user to perform the following graphically:

Allow the user to click inside the drawing area, and rotate or scale the scene.

There are many ways to approximate the collision of the ball with the ground. Since the ball-ground collision is over a very short period of time, we will use an impulse force calculation to perform our simulation. For a complete review of collision from your earlier coursework, refer to your statics/dynamics book(s) (ie Vector Mechanics for Engineers: Statics and Dynamics, Beer and Johnston, sixth edition or later). You will find a chapter covering impact and collision (at least one). A brief overview of the theory is provided for you. We will assume a frictionless surface (see extra credit for inclusion of rotation and friction)

Hints (steps to a successful project):

Warning!!!! Don't let this be you!!!

Please read and follow instructions!!!

 

1) If you have not done so already, read the brief discussion above. Do not skim it, questions may be derived from this discussion for your test! Besides, it is important information.

2) Read through the glui manual pdf file from beginning to end. You aren't expected to understand everything, but it is worthwhile reading. It is only 28 pages long and will make your assignment go faster with many less "hicups."

3)For practice, we will start again by creating a new project. Just in case any of you are still having some confusion doing this with Visual C++, we're going to explain this mainly graphically, in honor of our discussion of GUI's.. Just follow along in Visual C++ (if that is your development environment of choice here):

a) Open Visual C++ from the start menu. You should see something like the following:

b) Let's create a new project:

c) Create the project

d) Make sure to select 'empty project'

e) You should see the following information window

f) The project should now be open in Visual C++. We still need to add the file that will contain our source

g) We can at this point add a pre-existing file or create a new one

h) Make sure to click the file tab, then create a new C++ file, save it to the same folder as your project, and be sure to add it to the project.

i) Now we need to change some settings to let us use openGL calls in our code

j) Make sure 'all configurations' is selected, then go to the 'link' tab, and add to the object/library modules the three libraries 'opengl32.lib glu32.lib glut32.lib glui32.lib' separated by spaces only, NOT commas.

The rest is coding.

(for all future projects, you may find this tutorial to get to an empty project created in Visual C++ ready for openGL in the handouts area)

4) We will use some of the same functions used in the previous homework. These functions will be provided for you as we go in this assignment. Any code will be italicized and inside a box, so to be clear what is code and what is not. Let's start with our empty file. Be sure it is open. If not, open your project (if it is not open in Visual C++) and double click your cpp file from the file list, as in figure 'f' above.

In order to get the most out of this next group of hints, please read each code box and description through before copying and pasting it into your project. The code is given to you in this fashion so you can try to understand each function as much as possible as you go.

Please also do the following after pasting each section in place in your code :select all the code by typing ctrl-A, then go to edit-advanced-format selection to properly format the code's appearance.

We need to first include some header files:

/*The standard libraries for C and extra math (we'll be needing some sines and cosines possibly) here*/

#include <stdio.h>
#include <math.h>

/* These are the openGL and GLUI calls. Notice that we only call glut, not gl and glu. Glut has these calls inside it!*/
#include <GL/glut.h>
#include <GL/glui.h>

This, combined with adding the libraries to our project in the project settings, enables openGL calls in our application. If something is wrong here, you will probably get some link errors when you try to call opengl functions.

5)We'll need various constants in this program, so go ahead and add them now :

#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;
}

6)Now let's create a couple of sections: non-constant global variables, and our function prototypes. Realize this is just a starter list, and you will add to them as necessary.

/**
** 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*/

7) Now it is time to create the main program function. We will do all the initialization and then registration of callbacks, then go into the glutmainloop as before.

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;
}

8) Good, now we need to fill in all the functions. First, we'll start with InitGraphics(), which initializes all our callbacks, creates our main window, and sets up some parameters for our animation. Look through this function. For more information about each function call, go to the glut manual

/**
** 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 );


}

9) Now we'll create the initlists function to initialize our object lists. Object lists can hold opengl commands, as well as create precompiled shapes like 'legoman' or 'car' that you can draw just by calling the list name. For now leave this function empty, but for extra credit you can fill it according to extra credit requirements.

void InitLists( void )
{


if( Debug )
fprintf( stderr, "InitLists\n" );

}

10) Here is one of the significant new learnings of this homework: How to use GLUI interface elements. You read the glui manual already, but here's most of the interface you will need for your project, and the code to do it. Glui is using GLUT to create for you most of the interface elements you are used to. Unfortunately it is written with the C++ convention for some calls and variables, but if you follow the examples below, you should be able to create any GUI you need with your current knowledge. We can explain the different format to you if you have questions, however.

/**==============================================
/ ==============================================

** 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 );


}

11) Our good old friend the keyboard callback. This works the same way that the keyboard callback worked for project 2, basically a big switch statement executes some function if a key which is pressed is defined. Otherwise is does not do anything.


/**
** 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();

}

 

12)

/**
** 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 */


}

 

13) If the mouse moves while a button is down, ie you click and slide the mouse right without letting the mouse button up, we will rotate the scene or scale it, depending on the mouse button currently pressed.

/**
** 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();

}

 

14)The reset function is responsible for initializing various variables. We call this in main as an initializing function, and whenever the reset button is pressed. You may need to add variables or change some of these values as you complete your program.


/**=====================================
** 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();


}

 

15)We are not using this function for this homework, since most of the resizing is taken care of in display, and there are some issues when redrawing the scene, however keep this handy (ie put it in place) for the part B of this project.

/*============================*
** 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();

}

 

16)You are given the functions for performing dot products, cross products, normalizing, finding the distance between two points, and finding a scalar multiplication result. Aren't we nice?


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]) ));
}

 

17)A simple timer glut callback function. This will send a flag (ie change a variable) to tell our program when to animate, thus keeping a steady 30fps maximum. There are also ways of dropping frames if the computer a program is run on is too slow to keep up with 30fps, but that is left for discussion and the interested programmer.

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?*/

 

/*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);
}

 

18)Collision code will be for you to fill in. In this case we are only checking for collision with one surface - the ground plane, so you can just compare to see if the edge of the sphere has come close to the ground ( z=0 ). Additionally, since we lose energy each time there is a collision, one has to check to see if the ball is bouncing such a small amount that we consider it to be at rest. Otherwise you get a very unpleasant looking 'jiggle,' and possibly the ball sinking through the ground when the velocity variable is filled with an odd value.

Another key addition here is to add a check to see if the ball is for some reason passing through the ground, and if so, reset it to being above ground where physics says it should be (though there is a very very very slight chance that statistically all repulsions simultaneously do not occur and the ball slips through the ground, but that would be VERY star-trek-esque).

All-in-all this should be a relatively short routine, so if you start having 50 lines of code, step away for a moment and consider that there is a MUCH easier way to accomplish this task.

//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);
}

 

19)Probably the most important function of all in this project - the animate routine! Don't worry, lots of help here will be given. This is where you will include the calculations of impulse if a collision occurred. This is also where you will include the movement update steps for when no collision occurred. Ultimately this is a function to update the values for position and velocity after each timestep. The flow of the function should be as follows:

 

Impulse equation:

Post collision linear velocity:

Relative velocities (ground is Vb = 0):

Relative normal velocity:

Impulse magnitude:

 

Steps to calculating new velocity change by impulsive collision (no rotation):

 

1)calculate the impulse numerator (remember that V and n are vectors)

2)calculate the impulse denominator (n is a vector, M is a scalar)

3) impulse = impulse_numerator / impulse_denominator

4) linear_Velocity = linear_Velocity + impulse/mass * collision_normal . You do not need to store the linear velocities in big arrays, just calculate for each direction component of the vector (x,y,z).

 


/**========================================
** 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;


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 * ( linear_Velocity[0] );

//Y=..;

//Z=..;

if (Z<g_radius)
/*Then what? How do we assure Z> the radius of the sphere?*/

/*Continue rotating at the same speed*/
Thetax = Thetax + dt * ( angular_Velocity[0] );
//Thetay =..;
//Thetaz = ..;

 

}


}

/* force a call to Display() next time it is convenient: */

glutSetWindow( GrWindow );
glutPostRedisplay();


}

 

20) Easy parcheezy, the buttons routine works almost the same way as the keyboard routine. Now you have the variable ID of the button which was pressed passed into the function. Then you have a switch statement which controls what the button does when pressed. You need one line in the reset button case, and quit is taken care of for you. There may be other buttons you created, so just add cases for their ID's. As you create buttons, be sure to define new ID's for each one, like RESET. Don't leave out the glui->synclive(); call at the bottom to update the GUI elements.

/*===================*
** 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();

}

 

21) Another key function. The display callback. This will be called quite frequently. Here you will draw your objects at the scene position and orientation, and the object positions and orientations. Much of the display setup is here already, you just need to add your object code down below. Recall from lecture about the order of transformation (the spinning triangles example - download the example from the web page to understand order of operations on matrix transformations).


/*============================*
** 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!


/* swap the double-buffered framebuffers: */
glutSwapBuffers();

/* be sure the graphics buffer has been sent: */
glFlush();

}

End of homework. Good luck, see you in the lab...

I hope this is an educational project which you leave having gained something meaningful.

 

Alex

Extra Credit (20 pts) - POSTPONED UNTIL NEXT WEEK'S PROGRAM IS DUE! YOU WILL BE ABLE TO GET 40PTS EXTRA CREDIT ON NEXT PROGRAM!!!

1) Modify your code so the user can click a check box to include friction, and thus rotation as part of the physics in the simulation. There are more equations which need to be considered with this. The impulse will no longer be only applied to the linear velocity, but also to the angular velocity.

 


 

Grading

A compilable program which creates a window with your user name at the title 30pts
A glui window with all controls drawing properly 20 pts
Animation works and is timed correctly 20 pts
Collision works and is timed correctly 10 pts
Controls all function according to requirements 10 pts
view is zoomable, rotatable, translatable, etc. 10 pts
Extra Credit 20 pts
POTENTIAL TOTAL 120pts