KIT, MANGALORE
BUMP MAPPING
CHAPTER 1 1.1 INTRODUCTION Computer graphics provides one of the most natural means of communicating with a computer .It includes the creation,storage,and manipulation of models and images of objects. Computer graphics mainly concerns with the pictorial synthesis of real and imaginary objects from their computer based models .It is more useful in the production of dynamically varying pictures pictures which can show changes changes over time. Dynamically Dynamically iterative iterative graphics offers a wide range of user interface modes.
In OpenGL Interface Interface most of our application application will be designed designed to access OpenGL directly through functions in three libraries. Functions in the main GL library have names that begin with with the the lett letter er gl and and are stor stored ed in libr library ary usua usuall lly y refer referred red to GL . Open OpenGL GL Util Utilit ity y Tool Toolki kitt GLUT,which provides the minimum functionality that should be expected in any modern windowing system.
Dept Of CS&E
Page 1
KIT, MANGALORE
BUMP MAPPING
1.2 PROJECT AIM Program to demonstrate how to add lighting and material properties to a bump mapping
:
BUMP
In geometry, a bump (pl. tori) is a surface of revolution generated by revolving a circle in three three dime dimens nsio iona nall spac spacee abou aboutt an axis axis copl coplan anar ar with with the the circl circle. e. Real Real worl world d exam exampl ples es of (approximately) toroidal objects include doughnuts, inner tubes, many lifebuoys, O-rings and Vortex rings.
Diagrammatical explanation of bump :
(a.) Bump Mapping
(c.)
(b.) A bump is the product of two circles.
(d.)
(e.)
(f.) This construction shows the bump divided into the maximum of seven regions,every one of which touches every other.
Dept Of CS&E
Page 2
KIT, MANGALORE
BUMP MAPPING
CHAPTER 2 2.1 LIGHT IN OPENGL glEnable(int cap):- glEnable is used to enable lighting on the whole when you pass
GL_LIGHTING parameter to it. gEnable is also responsible for enabling a particular light source in your 3D scene. This is done by calling glEnable(GL_LIGHTn); where n is the index number of the color you are enabling, ranging from 0 to 7 because OpenGL lets you specify a maximum of eight light sources. glDisable(int cap):- This function disables whatever properties were previously set with
glEnable. glShadeModel(int mode); Selects the polygon shading model. Mode is the flag representing the shading mode. This flag can be set to either GL_FLAT or GL_SMOOTH. GL_SMOOTH shading is the default shading model, causes the computed colors of vertices to be interpolated as the primitive is rasterized, assigning different colors to each resulting pixel fragment. GL_FLAT shading selects the computed color of just one vertex and assigns it to all the pixel fragments generated by rasterizing a single primitive. In either case, the computed color of a vertex is the result of lighting, if lighting is enabled, or it is the current color at the time the vertex was specified, if lighting is disabled. glEnable(GL_LIGHT0);
All of the provided 8 OpenGL light sources are enabled by default. And just for convenience, if you want to turn off an enabled light source, you would use the glDisable function: glDisable(GL_LIGHT0);
2.2. LIGHTNING PROPERTIES
THE TWO TYPES OF DEFINING LIGHT PROPERTIES IN OPENGL (LIGHT SOURCE and SURFACE MATERIAL)
The first type of light properties is the one that describes a light source and the second type of light properties is the one that describes the light reflected by the material of an object’s surface.
The color of each light source is characterized by the color, in RGBA format, that it emits and is defined by using the function: glLight. The properties of the material the object is made of are defined by calling the function: glMaterial, and are characterized by the amount
Dept Of CS&E
Page 3
KIT, MANGALORE
BUMP MAPPING
of light that the material reflects. The surface material properties are also characterized by the RGBA color format.
2.3 SMOOTH AND FLAT SHADING
glShadeModel(GL_SMOOTH); After this call, all of the polygons will be smoothly shaded by using the Gourad-shading technique and according to the nearby light sources and polygon's material properties.
In the traditional Gourad-shading technique the illumination is computed exactly at the vertices and the values are interpolated across the polygon.
2.4 DEFINING A LIGHT SOURCE
OpenGL allows a maximum of 8 light sources in a scene at once. Each of the light sources can be either enabled or disabled. All of the 8 light sources are initially disabled, and are enabled with a call to glEnable.
For each of the 8 light sources OpenGL has a name in the following format: GL_LIGHTn
Where n is the index number of the light source you are specifying. n can be a value ranging from 0 to 7. It should be obvious that to specify a light source number 1 you would use GL_LIGHT0. By order, the light source number 8 is specified as GL_LIGHT7.
To define a light source, OpenGL presents the programmer with a basic set of functions, specification of which divides itself into these already familiar to us light groups: Ambient Light, Diffuse Light, Specular Light and Emissive Light. For each of these types of light OpenGL #defines four models: GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR and GL_EMISSIVE respectively.
For each one of the light sources you need to call the glLightfv function with parameters which specify the what, and more importantly the how. To add a component of specular light to a light source, you would make the following function call:
GLfloat specular[] = {1.0f, 1.0f, 1.0f , 1.0f}; glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
specular[] is a 4-parameter array.
Dept Of CS&E
Page 4
KIT, MANGALORE
BUMP MAPPING
specular[] = { floatRed, floatGreen, floatBlue, floatAlpha};
The first three parameters are the RGB values which can range from anywhere between 0.0f and 1.0f. 0.0f being no color, and 1.0f being full color. 0.5f would identify an average between the very bright color and the very dim color.
The same glLightfv mechanism is used to specify any of the other three shading models. For example, to set and enable the Ambient Light component of a light source so that it emits Ambient Light of moderately pale white color (R=0.5f, G=0.5f, B=0.5f) you could use the following call and parameters:
GLfloat ambient[] = { 1.0f, 1.0f, 1.0f }; GlLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
But assigning ambient, diffuse and specular types of light to a light source is not usually enough. You also have to specify the position of the light source. And this is, similarly to defining light components, done with the glLight function in the
following way: Glfloat position[] = { -1.5f, 1.0f, -4.0f, 1.0f }; GlLightfv(GL_LIGHT0, GL_POSITION, position);
Dept Of CS&E
Page 5
KIT, MANGALORE
BUMP MAPPING
CHAPTER 3
3.1 MATERIAL PROPERTIES DEFINING SURFACE MATERIAL PROPERTIES :
In OpenGL, by assigning a material property to an object (defined by RGB color format), you are theoretically assigning the color which is reflected by that object.
When you enable lighting with glEnable(GL_LIGHTING) you are generally expected to be assigning material properties with the glMaterial command as shown in the following code sample: float mcolor[] = { 1.0f, 0.0f, 0.0f, 1.0f }; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mcolor);
glMaterial
postfixed
by
fv
takes
vector-based
color
coordinates.
The
GL_AMBIENT_AND_DIFFUSE parameter specifies that mcolor will be applied to both ambient and diffuse components of the material. This is done for convenience because in most cases Ambient and Diffuse properties of a material should be set to the same color.
3.2 The SPECULAR REFLECTION component
The SPECULAR REFLETION component of the material defines the effect the material has on the reflected light. This functionality can be obtained by defining the GL_SPECULAR component of the object‘s material. This property adds a glossy surface to the object you are modeling.
3.3 USAGES •
There are a number of keyboard commands that control the animation. They must be
typed into the graphics window, and are listed below:
3.4 CONTROLLING RESOLUTION OF THE BUMP MESH •
Press “W” to increase the number wraps.
•
Press “w” to decrease the number wraps.
•
Press “N” to increase the number of segments per wrap.
Dept Of CS&E
Page 6
KIT, MANGALORE
BUMP MAPPING
•
Press “n” to decrease the number of segments per wrap.
•
Press “q” to toggle between quadrangles and triangles.
3.5 CONTROLLING THE ANIMATION •
Press the “a” key to toggle the animation off and on.
•
Press the “s” key to perform a single step of the animation.
The left and right arrow keys: •
Controls the rate of rotation around the y-axis. The up and down arrow keys: •
Increase and decrease the rate of rotation around the x-axis.
In order to reverse
rotational direction you must zero or reset the bump (“0” or “r”). Press the “r” key to reset the bump back to initial •
•
Position, with no rotation. Press “0” (zero) to zero the rotation rates.
3.6 CONTROLLING LIGHTS •
Press ‘1’ or ‘2’ to toggle the first or second light off and on.
•
Press ‘f’ to toggle between flat and smooth shading.
•
Press ‘l’ to toggle local modes on and off (local viewer and positional light, or non-local viewer and directional light).
3.7 COMMANDS SHOWING OPENGL FEATURES •
Pressing “p” toggles between wireframe and polygon mode.
•
Pressing “f” key toggles between flat and smooth shading.
Dept Of CS&E
Page 7
KIT, MANGALORE
BUMP MAPPING
CHAPTER 4 4.1. SOURCE CODE #include #include #include #include
// OpenGL Graphics Utility Library
void myKeyboardFunc( unsigned char key, int x, int y ); void mySpecialKeyFunc( int key, int x, int y ); void updateScene( void ); void initRendering(); void resizeWindow(int w, int h); void KeyUp(); void KeyDown(); void KeyLeft(); void KeyRight(); void ResetAnimation(); void ZeroRotation(); void ShadeModelToggle(); void FillModeToggle(); void QuadTriangleToggle(); void LocalToggle(); void Light0Toggle(); void Light1Toggle(); void WrapMore(); void WrapLess(); void NumPerWrapMore(); void NumPerWrapLess();
// Bump specific routines. void putVert(int i, int j);
const float PI2 = 2.0f*3.1415926535;
GLenum runMode = GL_TRUE;
GLenum shadeModel = GL_FLAT;
Dept Of CS&E
// Toggles between GL_FLAT and GL_SMOOTH
Page 8
KIT, MANGALORE
BUMP MAPPING
GLenum polygonMode = GL_LINE;
// Toggles between GL_LINE and GL_FILL
// Variables controlling the animation
float RotX = 0.0f;
// Rotational position around x-axis
float RotY = 0.0f;
// Rotational position around y-axis
float RotIncrementX = 0.0;
// Rotational increment, x-axis
float RotIncrementY = 0.0;
// Rotational increment, y-axis
const float RotIncFactor = 1.5;
// Factor change in rot rate per key stroke
// Variables controlling the fineness of the polygonal mesh
int NumWraps = 10; int NumPerWrap = 8;
// Variables controlling the size of the bump
float MajorRadius = 3.0; float MinorRadius = 1.0;
// Mode flags
int QuadMode = 1; GLenum LocalMode = GL_TRUE;
// Quad/Triangle toggling // Local viewer/non-local viewer mode
int Light0Flag = 1;
// Is light #0 on?
int Light1Flag = 1;
// Is light #1 on?
// Lighting values
float ambientLight[4] = {0.6, 0.6, 0.6, 1.0}; float Lt0amb[4] = {0.8, 0.8, 0.16, 1.0}; float Lt0diff[4] = {1.0, 1.0, 0.2, 1.0}; float Lt0spec[4] = {1.0, 1.0, 0.2, 1.0}; float Lt0pos[4] = {1.7*4.0, 0.0, 0.0, 1.0};
// 4 = MajorRadius + MinorRadius
float Lt1amb[4] = {0.0, 0.0, 0.5, 1.0}; float Lt1diff[4] = {0.0, 0.0, 0.5, 1.0}; float Lt1spec[4] = {0.0, 0.0, 1.0, 1.0}; float Lt1pos[4] = {0.0, 1.2*4.0, 0.0, 1.0};
// Material values
float Noemit[4] = {0.0, 0.0, 0.0, 1.0}; float Matspec[4] = {1.0, 1.0, 1.0, 1.0}; float Matnonspec[4] = {0.4, 0.05, 0.4, 1.0}; float Matshiny = 16.0;
Dept Of CS&E
Page 9
KIT, MANGALORE
BUMP MAPPING
// glutKeyboardFunc is called below to set this function to handle //
all "normal" key presses.
void myKeyboardFunc( unsigned char key, int x, int y )
{ switch ( key ) { case 'a':
runMode = !runMode; break; case 's':
runMode = GL_TRUE; updateScene(); runMode = GL_FALSE; break; case 27: // Escape key
exit(1); case 'r': // Reset the animation (resets everything)
ResetAnimation(); break; case '0': // Zero the rotation rates
ZeroRotation(); break; case 'f': // Shade mode toggles from flat to smooth
ShadeModelToggle(); break; case 'p': // Polygon mode toggles between fill and line
FillModeToggle(); break; case 'w':
// Decrement number of wraps around bump
WrapLess(); break; case 'W':
// Increment number of wraps around bump
WrapMore(); break; case 'n': // Decrement number of polys per wrap
NumPerWrapLess(); break;
case 'N':
// Increment number of polys per wrap
NumPerWrapMore();
Dept Of CS&E
Page 10
KIT, MANGALORE
BUMP MAPPING
break; case 'q': // Toggle between triangles and Quadrilaterals
QuadTriangleToggle(); break; case 'l': // Toggle between local and non-local viewer ('l' is for 'local')
LocalToggle(); break; case '1': // Toggle light #0 on and off
Light0Toggle(); break; case '2': // Toggle light #1 on and off
Light1Toggle(); break; } }
// glutSpecialFunc is called below to set this function to handle //
all "special" key presses. See glut.h for the names of
//
special keys.
void mySpecialKeyFunc( int key, int x, int y )
{ switch ( key ) { case GLUT_KEY_UP: // Either increase upward rotation, or slow downward rotation
KeyUp(); break; case GLUT_KEY_DOWN: // Either increase downwardward rotation, or slow upward rotation
KeyDown(); break; case GLUT_KEY_LEFT: // Either increase left rotation, or slow down rightward rotation.
KeyLeft(); break; case GLUT_KEY_RIGHT: // Either increase right rotation, or slow down leftward rotation.
KeyRight(); break; } }
Dept Of CS&E
Page 11
KIT, MANGALORE
BUMP MAPPING
// The routines below are coded so that the only way to change from //
one direction of rotation to the opposite direction is to first
// reset the animation,
void KeyUp() {
if ( RotIncrementX == 0.0 ) { RotIncrementX = -0.1;
// Initially, one-tenth degree rotation per update
} else if ( RotIncrementX < 0.0f) { RotIncrementX *= RotIncFactor; } else { RotIncrementX /= RotIncFactor; } }
void KeyDown() {
if ( RotIncrementX == 0.0 ) { RotIncrementX = 0.1;
// Initially, one-tenth degree rotation per update
} else if ( RotIncrementX > 0.0f) { RotIncrementX *= RotIncFactor; } else { RotIncrementX /= RotIncFactor; } }
void KeyLeft() {
if ( RotIncrementY == 0.0 ) { RotIncrementY = -0.1;
// Initially, one-tenth degree rotation per update
} else if ( RotIncrementY < 0.0) { RotIncrementY *= RotIncFactor; } else {
Dept Of CS&E
Page 12
KIT, MANGALORE
BUMP MAPPING
RotIncrementY /= RotIncFactor; } }
void KeyRight()
{ if ( RotIncrementY == 0.0 ) { RotIncrementY = 0.1;
// Initially, one-tenth degree rotation per update
} else if ( RotIncrementY > 0.0) { RotIncrementY *= RotIncFactor; } else { RotIncrementY /= RotIncFactor; } }
// Resets position and sets rotation rate back to zero. void ResetAnimation()
{ RotX = RotY = RotIncrementX = RotIncrementY = 0.0; }
// Sets rotation rates back to zero. void ZeroRotation()
{ RotIncrementX = RotIncrementY = 0.0; }
// Toggle between smooth and flat shading void ShadeModelToggle()
{ if ( shadeModel == GL_FLAT ) { shadeModel = GL_SMOOTH; } else {
Dept Of CS&E
Page 13
KIT, MANGALORE
BUMP MAPPING
shadeModel = GL_FLAT; } }
// Toggle between line mode and fill mode for polygons. void FillModeToggle()
{ if ( polygonMode == GL_LINE ) { polygonMode = GL_FILL; } else { polygonMode = GL_LINE; } }
// Toggle quadrilaterial and triangle mode void QuadTriangleToggle()
{ QuadMode = 1-QuadMode; }
// Toggle from local to global mode void LocalToggle()
{ LocalMode = !LocalMode; if ( LocalMode ) { Lt0pos[3] = Lt1pos[3] = 1.0;
// Put lights back at finite location.
Lt0pos[3] = Lt1pos[3] = 0.0;
// Put lights at infinity too.
} else {
} }
// The next two routines toggle the lights on and off void Light0Toggle()
{
Dept Of CS&E
Page 14
KIT, MANGALORE
BUMP MAPPING
Light0Flag = 1-Light0Flag; }
void Light1Toggle() { Light1Flag = 1-Light1Flag; }
// Increment number of wraps void WrapMore()
{ NumWraps++; }
// Decrement number of wraps void WrapLess()
{ if (NumWraps>4) { NumWraps--; } }
// Increment number of segments per wrap void NumPerWrapMore()
{ NumPerWrap++; }
// Decrement number segments per wrap void NumPerWrapLess()
{ if (NumPerWrap>4) { NumPerWrap--; } }
/*
Dept Of CS&E
Page 15
KIT, MANGALORE
BUMP MAPPING
* issue vertex command for segment number j of wrap number i.
*/ void putVert(int i, int j)
{ float wrapFrac = (j%NumPerWrap)/(float)NumPerWrap; float phi = PI2*wrapFrac; float theta = PI2*(i%NumWraps+wrapFrac)/(float)NumWraps; float sinphi = sin(phi); float cosphi = cos(phi); float sintheta = sin(theta); float costheta = cos(theta); float y = MinorRadius*sinphi; float r = MajorRadius + MinorRadius*cosphi; float x = sintheta*r; float z = costheta*r; glNormal3f(sintheta*cosphi, sinphi, costheta*cosphi); glVertex3f(x,y,z); }
void updateScene( void )
{ int i,j;
// Clear the redering window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel( shadeModel );
// Set the shading to flat or smooth.
glPolygonMode(GL_FRONT_AND_BACK, polygonMode); // Set to be "wire" or "solid" glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, LocalMode);
// Set up lights
if ( Light0Flag==1 || Light1Flag==1 ) { // Emissive spheres have no other color. glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Noemit); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Noemit); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0); } if ( Light0Flag==1 )
Dept Of CS&E
Page 16
KIT, MANGALORE
BUMP MAPPING
{ glPushMatrix(); glTranslatef(Lt0pos[0], Lt0pos[1], Lt0pos[2]); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Lt0spec); glutSolidSphere(0.2,5,5); glPopMatrix(); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_POSITION, Lt0pos); } else { glDisable(GL_LIGHT0); } if ( Light1Flag==1 ) { glPushMatrix(); glTranslatef(Lt1pos[0], Lt1pos[1], Lt1pos[2]); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Lt1spec); glutSolidSphere(0.2,5,5); glPopMatrix(); glEnable(GL_LIGHT1); glLightfv(GL_LIGHT1, GL_POSITION, Lt1pos); } else { glDisable(GL_LIGHT1); }
// Bump Materials
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Matnonspec); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Matspec); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, Matshiny); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Noemit);
glPushMatrix();
// Save to use again next time.
// Update the orientation of the bump, if the animation is running.
if ( runMode ) { RotY += RotIncrementY;
Dept Of CS&E
Page 17
KIT, MANGALORE
BUMP MAPPING
if ( fabs(RotY)>360.0 ) { RotY -= 360.0*((int)(RotY/360.0)); } RotX += RotIncrementX; if ( fabs(RotX)>360.0 ) { RotX -= 360.0*((int)(RotX/360.0)); } } // Set the orientation.
glRotatef( RotX, 1.0, 0.0, 0.0); glRotatef( RotY, 0.0, 1.0, 0.0);
// Draw the bump
glColor3f( 1.0, 0.5, 1.0 );
glBegin( QuadMode==1 ? GL_QUAD_STRIP : GL_TRIANGLE_STRIP );
for (i=0; i
// Draw the reference pyramid
glTranslatef( -MajorRadius-MinorRadius-0.3, 0.0, 0.0); glScalef( 0.2f, 0.2f, 0.2f ); glColor3f( 1.0f, 1.0f, 0.0f ); glBegin(GL_TRIANGLE_STRIP); glVertex3f( -0.5, 0.0, sqrt(3.0)*0.5 ); glVertex3f( -0.5, 0.0, -sqrt(3.0)*0.5 ); glVertex3f( 1.0, 0.0, 0.0); glVertex3f( 0.0, sqrt(2.0), 0.0); glVertex3f( -0.5, 0.0, sqrt(3.0)*0.5 ); glVertex3f( -0.5, 0.0, -sqrt(3.0)*0.5 ); glEnd();
Dept Of CS&E
Page 18
KIT, MANGALORE glPopMatrix();
BUMP MAPPING // Restore to original matrix as set in resizeWindow()
// Flush the pipeline, swap the buffers
glFlush(); glutSwapBuffers(); }
// Initialize OpenGL void initRendering()
{ glEnable( GL_DEPTH_TEST ); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0);
// Enable lighting calculations // Turn on lights (unnecessary here, since also in Animate()
glEnable(GL_LIGHT1); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
// Ambient light
// Light 0 (Position is set in updateScene)
glLightfv(GL_LIGHT0, GL_AMBIENT, Lt0amb); glLightfv(GL_LIGHT0, GL_DIFFUSE, Lt0diff); glLightfv(GL_LIGHT0, GL_SPECULAR, Lt0spec);
// Light 1 (Position is set in updateScene)
glLightfv(GL_LIGHT1, GL_AMBIENT, Lt1amb); glLightfv(GL_LIGHT1, GL_DIFFUSE, Lt1diff); glLightfv(GL_LIGHT1, GL_SPECULAR, Lt1spec); }
// Called when the window is resized // Sets up the projection view matrix (somewhat poorly, however) void resizeWindow(int w, int h)
{ float aspectRatio; glViewport( 0, 0, w, h );
// View port uses whole window
h = (w == 0) ? 1 : h; aspectRatio = (float)w/(float)h;
// Set up the proection view matrix
glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 60.0, aspectRatio, 1.0, 30.0 );
Dept Of CS&E
Page 19
KIT, MANGALORE
BUMP MAPPING
glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // Move system 10 units away to be able to view from the origin.
glTranslatef(0.0, 0.0, -10.0);
// Tilt system 15 degrees downward in order to view from above // the xy-plane.
glRotatef(15.0, 1.0,0.0,0.0); }
// Main routine // Set up OpenGL, hook up callbacks, and start the main loop
int main( int argc, char** argv ) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); // Window position (from top corner), and size (width and hieght)
glutInitWindowPosition( 10, 60 ); glutInitWindowSize( 620, 160 ); glutCreateWindow( "BUMP MAPPING demo" ); // Initialize OpenGL rendering modes
initRendering(); resizeWindow(620,160); // Set up callback functions for key presses
glutKeyboardFunc( myKeyboardFunc ); glutSpecialFunc( mySpecialKeyFunc ); // Set up the callback function for resizing windows
glutReshapeFunc( resizeWindow ); // Call this for background processing
glutIdleFunc( updateScene ); // Call this whenever window needs redrawing
glutDisplayFunc( updateScene ); // Start the main loop. glutMainLoop never returns.
glutMainLoop( ); return(0);
// This line is never reached.
}
Dept Of CS&E
Page 20