Helicopter

    A.First Edition

    This is the comp471 assignment 2 which requires me to construct the model of helicopter?

    B.The problem

    Problem Statement:
    OpenGL and glut libraries support 3D scene modeling by providing high-level primitives, such
    as cube, cylinder, sphere, cone, torus and even a teapot! Combined with a suitable modeling
    transformation, these can be extended to model more shapes such as an ellipsoid, elliptical
    cylinder, blocks of different dimensions, etc. Write an OpenGL program to model and view a
    helicopter. If you like you may take the skeleton from the sample program on the Tutorials web
    page (off the course web page), cut out unwanted parts, and then add your own parts to it. The
    helicopter must be modeled using a modeling hierarchy.
    For example ¨C a simple 4-legged dining table could be modeled as follows:
    The top is modeled as a cube with non-uniform scaling along x, y and z to take the shape
    of a flat block. Similarly, a leg is modeled as a cube or cylinder, once again with nonuniform
    scaling. By applying suitable translation transformations, the leg can be copied
    three more times, to position them at the corners of a rectangle. Again using translations
    the table-top and the four legs can be assembled to form a model of the table. Another
    transformation to this hierarchy will enable you to position this anywhere in the room.
    Your program must comprise of the following:
    R1) Features of the helicopter:
    ¡¤ A suitable helicopter frame with landing gear (e.g., wheels), main rotors (with at least
    three, but no more than six, blades; see picture on next page), cockpit area, tail, and tail
    rotors. For the cockpit area, include structures representing the windows that the pilot
    can see out through ¨C for this assignment, the fronts of these windows may be solid.
    R2) Camera Motion
    ¡¤ Use the up/down arrow keys to move/rotate the center of projection up/down about
    a point (say, an approximate center of gravity) centrally located on the helicopter.
    ¡¤ Use the left/right arrow keys to move/rotate the center of projection left/right about
    the point (as mentioned above) centrally located on the helicopter.
    ¡¤ Use the f/b keys to move forward and backwards respectively on the line joining the
    point (as mentioned above) centrally located on the helicopter and the center of
    projection.
    R3) The following keyboard commands to change the view:
    ¡¤ Wire frame/Shaded Model. Toggle between the wire-frame and solid-shaded model
    using w/W. Use different colors in the shaded model to distinguish between the
    aforementioned features of the helicopter. (Simple flat shading would do. Smooth
    shading and use of lights not required).
    ¡¤ Toggle between views. Orthographic (parallel) (Using o/O) or Perspective (Using p/P)
    ¡¤ Zoom in ¨C Zoom out. z/Z to zoom into and out of the world respectively. Remember
    zooming should make the image appear larger and it is different from moving the
    camera forward / backward.
    ¡¤ View window. Resize the objects while maintaining aspect ratio, after a window
    resize.
    ¡¤ Reset view to default. Use c/C to clear viewing parameters and return to the
    initial view.
    ¡¤ Graceful Exit. Exit from the program, by pressing Esc key.

C.The idea of program
¡¡

At the beginning, I want to wrap all object in my "glutobject" class which defines the universal behaviour for all high-

level object model so that programmer can do the model construction by parameter. One more step forward, we can construct

a project by script file by developing some simple tools.

However, I underestimated the difficulty because it is the very first time for me to play OpenGL and there are many

features which I am not familiar. This is why I gave up after three-day-struggle. However, it may not a total defeat.

Therefore, this version is almost completely based on tutor's framework except that I change all model functions from

original motorcycle to this helicopter.

    D.The major functions

    There is a couple of things which I want to mention. First, I defined a little fancy function which takes a drawing function

    as parameter then divides the object either horizontally or vertically into several parts by repeatedly using clipping. It

    is called "divider_x" and "divider_y". The other one is that the sequence of rotating model and gluLookAt function is highly

    important. I realized this only after almost 45 minutes later when I always found helicopter is not rotating around origin

    but rather w.r.t eye position! Therefore the following program segment explains everything:

    void displayCallbackProc (void)
    						// This is the callback procedure for capturing OpenGL Display events.
    						// All the 'happening' things happen here :)
    {
    	GLfloat eye_x, eye_y, eye_z;

    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glColor3f(1.0, 1.0, 1.0);

    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();

    /************************************************************
    The view transformation
    *************************************************************/
    	eye_x=radius*sin(theta)*sin(phi);
    	eye_y=radius*cos(theta);
    	eye_z=radius*sin(theta)*cos(phi);

    	gluLookAt(eye_x, eye_y, eye_z, 0, 0, 0, 0, 1, 0);

    	//the following sequence is HIGHLY important because
    	//you want the object to rotate w.r.t. to original world
    	//origin and you need to transform it between world coordinate and
    	//view-coordinate
    	glTranslatef(-eye_x, -eye_y, -eye_z);
    	showReferenceAxis();
    	glScalef(2, 2, 2);

    	glRotatef(x_Angle, 1.0, 0.0, 0.0);	// Rotate the object by x_Angle about x-axis
    	glRotatef(y_Angle, 0.0, 1.0, 0.0);	// Rotate the object by y_Angle about y-axis
    	glRotatef(z_Angle, 0.0, 0.0, 1.0);	// Rotate the object by z_Angle about z-axis

    	drawHelicopter();

    	glTranslatef(eye_x, eye_y, eye_z);

    	glutSwapBuffers();	// Use of double buffering to avoid flicker.
    }

    You need to place your drawHelicopter function between two "glTranslatef" and gluLookAt function must be called first.

    ¡¡

    E.Further improvement

    Honestly speaking, my helicopter is not nice at all. One big reason is that I am not the kind of "art" guy who can draw

    picture very well. And I am not that kind of "engineering" guy who can draw the assembly plan very well. And I am now more

    interested in "framework" which is half-finished project previously. If you have any difficulty in compiling the file, pls

    refer to here.

    F.File listing

    1. helicopter.h

    2. helicopter.cpp

    file name: helicopter.h

    #include <GL/glut.h>

    //the view changing parameter
    const GLfloat ViewAngleOffset=0.01;
    //const GLfloat ZoomAngleOffset=0.5;
    //const GLfloat ZoomRatioOffset=0.5;
    const GLfloat ZoomRadiusOffset=0.05;

    //the view init parameter
    const GLfloat ViewStartRadius=5;
    const GLfloat ViewStartTheta=60;
    const GLfloat ViewStartPhi=20;

    //const GLfloat ViewStartRatio=1;

    //model start state parameter
    const GLfloat StartAxisAnglex=0;
    const GLfloat StartAxisAngley=0;
    const GLfloat StartAxisAnglez=0;

    //the view volume init parameter
    const GLfloat SIZE= 1000;			// To deal with the view volume
    const GLfloat NEAR_Z= 0.001;
    const GLfloat FAR_Z= 100.0;

    //color advance offset
    const GLint MaximumColorNumber=10;
    const GLint ColorAdvanceStep=10;
    const GLint ColorStartIndex=3;
    const GLfloat ColorAdvanceOffset=0.09;
    const GLfloat DefaultAxisLength=10;

    //constant PI
    const GLfloat PI= 3.14159265;		// An excessively abused used constant !!

    //cockpit relative size
    const GLfloat CockpitWidth=1.5;
    const GLfloat CockpitHeight=1.0;
    const GLfloat Front_Rear_Ratio=0.15;

    //skid relative size
    const GLfloat SkidSupportHeight=0.5;
    const GLfloat SkidSupportDistance=2;
    const GLfloat SkidWidth=10;
    const GLfloat SkidRadius=0.1;
    const GLfloat SkidJointOffset=0.8;
    const GLfloat BoomLength=10;
    const GLfloat TailRotorSupportRadius=0.3;
    const GLfloat TailRotorSupportOffset=TailRotorSupportRadius*0.5;

    //utility function for drawing models
    void drawOval(GLfloat x, GLfloat y, GLfloat z);
    void drawSphere(float fRadius, GLint slices, GLint rings);
    void drawCylinder(float fTopR, float fBottomR, float fHeight);
    void drawDisk(GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint rings,
    	GLdouble startAngle, GLdouble sweepAngle);
    void drawShearCylinder(GLfloat topRad, GLfloat bottomRad, GLfloat height, GLfloat angle);
    void drawPatialTorus(GLfloat rCrossSection, GLfloat rAxial, GLfloat sweepAngle);

    //small helper functions
    GLfloat deg2rad(GLfloat degree);
    void setColor();// a little fancy random color generator
    void initColorArray();

    //model helper functions for each module

    void drawSkidSupport(const GLfloat SkidSupportDistance, const GLfloat SkidSupportHeight);

    void divider_x(void(*pfunc)(), GLfloat start, GLfloat end, GLint num);
    void divider_y(void(*pfunc)(), GLfloat down, GLfloat up, GLint num);
    void drawRotor();
    void drawHelicopter();

    //setup functions
    void init (void);
    void showMenu(void);
    void showReferenceAxis(void);

    //callback functions
    void keyboardCallbackProc(unsigned char key, int x, int y);
    void specialCallbackProc (int key, int x, int y);

    file name: helicopter.cpp

    ¡¡

    /****************************************************************************************/
    /*
    /* Assignment2 : Building a helicopter model.
    /* Points to notice:
    /*		1. Use of glPushMatrix and glPopMatrix for constructing the each module hierarchially
    /*		2. Use of orthographic and perspective projection.
    /*		3. Use of Clip Planes
    /*		4. Use of glu routines to draw Quadric objects (spheres, cylinders, disks, etc).

    /* DATED 14th, oct, 2005		Qingzhe Huang	nickhuang99@hotmail.com
    /*
    /****************************************************************************************/

    #include <GL/glut.h>			// This is assuming that you have glut.h set in your include path.
    #include <stdio.h>			// Other necessary program related includes.
    #include <stdlib.h>
    #include <math.h>
    #include "helicopter.h"

    /*
    GLdouble xy[4] = {0.0, 0.0, 1.0, 0.0};
    GLdouble yz[4] = {1.0, 0.0, 0.0, 0.0};
    GLdouble zx[4] = {0.0, 1.0, 0.0, 0.0};
    */
    int curColorIndex=ColorStartIndex;

    float x_Angle = StartAxisAnglex;		// The rotation angles about x, y and z axis.
    float y_Angle = StartAxisAngley;
    float z_Angle = StartAxisAnglez;
    GLfloat viewPortw, viewPorth;
    //GLfloat ratio=ViewStartRatio;

    bool bAxis;			// To show or not to show the reference axis.

    bool bWire;			// To wireframe/not.

    GLfloat fovy = 90.0;		// For Perspective projections.
    int H= SIZE, W= SIZE;
    GLfloat fViewVol;			// For Orthographic projections.
    enum {PERSPECTIVE, ORTHOGRAPHIC} ePrjn = PERSPECTIVE;

    //the polar coordinate parameter
    GLfloat radius=ViewStartRadius, theta=deg2rad(ViewStartTheta), phi=deg2rad(ViewStartPhi);

    GLfloat colorArray[MaximumColorNumber][3];

    GLfloat deg2rad(GLfloat degree)
    {
    	return degree/360.0*2*PI;
    }

    void setColor()
    {
    	curColorIndex++;
    	curColorIndex%=MaximumColorNumber;
    	glColor3f(colorArray[curColorIndex][0], colorArray[curColorIndex][1],
    		colorArray[curColorIndex][2]);
    }

    /*
    void setColor()
    {
    	GLfloat r,g,b;
    	curColorIndex++;
    	if (curColorIndex>ColorAdvanceStep)
    	{
    		r=1;
    	}
    	else
    	{
    		r=(curColorIndex%ColorAdvanceStep)*ColorAdvanceOffset;
    	}
    	if (curColorIndex>2*ColorAdvanceStep)
    	{
    		g=1;
    	}
    	else
    	{
    		g=curColorIndex%(2*ColorAdvanceStep)*ColorAdvanceOffset;
    	}
    	if (curColorIndex>3*ColorAdvanceStep)
    	{
    		b=1;
    	}
    	else
    	{
    		b=curColorIndex%(ColorAdvanceStep*3)*ColorAdvanceOffset;
    	}

    	glColor3f(r,g,b);
    }
    */

    void initColorArray()
    {
    	GLfloat base[3]={0.8,0.5,0.2};

    	for (int i=0; i<MaximumColorNumber; i++)
    	{
    		for (int j=0; j<3; j++)
    		{
    			base[j]+=0.1;
    			if (base[j]>1.0)
    			{
    				base[j]-=1.0;
    			}
    			colorArray[i][j]=base[j];
    		}
    	}
    }

    /****************************************************************************************/
    void showMenu(void)
    				// Self Explanatory.
    {
    	printf("\n\n\n\n COMP471 ASSIGNMENT2 HELICOPTER. ");
    	printf("\n---------------------------------------------------------------");
    	printf("\n Operations: ");
    	printf("\n Use w/W to toggle between wireframe and shaded model.");
    	printf("\n Use o/O and p/P to toggle between ortho and perspective views.");
    	printf("\n Use a/A to show/hide the reference axis <x = RED, y = GREEN, z = BLUE>.");
    	printf("\n Use the x/X,y/Y and z/Z keys for rotations about x,y and z axis.");
    	printf("\n Use arrow up/down keys for camera rotations up/down about origin.");
    	printf("\n Use arrow left/right keys for camera rotations left/right about origin.");
    	printf("\n Use m/M to see the menu again.\n\n");

    }

    /****************************************************************************************/
    void init (void)
    						// Initializes the gl Graphics env and the program variables.
    {

    	glMatrixMode(GL_PROJECTION);
    							// Set the current openGL matrix to GL_PROJECTION ie projection matrix.
    	glLoadIdentity();
    							// Load identitiy values in the above.

    	if(ePrjn == PERSPECTIVE)
    	{
    		gluPerspective(fovy, (GLfloat)W/(GLfloat)H, NEAR_Z, FAR_Z);
    							// This sets the view volume to be a Perspective one.
    	}
    	else
    	{
    		fViewVol = 50.0;
    		glOrtho(-fViewVol, fViewVol, -fViewVol, fViewVol, NEAR_Z, FAR_Z);
    							// This sets the view volume to be a Orthographic one.
    	}

    	glMatrixMode(GL_MODELVIEW);
    							// Change the current matrix mode to Model-View matrix.
    	glLoadIdentity();

    	glClearColor (0.5, 0.5, 0.5, 0.0);
    						// set the background color to black.

    	glShadeModel(GL_SMOOTH);
    	glEnable(GL_DEPTH_TEST);
    	bAxis = true;

    	bWire = false;
    	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    	showMenu();
    	initColorArray();
    }

    /****************************************************************************************/
    void keyboardCallbackProc(unsigned char key, int x, int y)
    						// This is the callback procedure for capturing OpenGL Keyboard events.
    {
    	switch(key)
    	{
    	case 'a':				// Show axes.
    	case 'A':
    		bAxis=!bAxis;
    		break;

    	case 'P':				//Change to perspective projections
    	case 'p':
    		ePrjn = PERSPECTIVE;
    		init();
    		break;

    	case 'O':				//Change to orthographic projections
    	case 'o':
    		ePrjn = ORTHOGRAPHIC;
    		init();
    		break;

    	case 'x':
    		x_Angle += 0.5;
    		break;
    	case 'X':
    		x_Angle -= 0.5;
    		break;
    	case 'y':
    		y_Angle += 0.5;
    		break;
    	case 'Y':
    		y_Angle -= 0.5;
    		break;
    	case 'z':
    		z_Angle += 0.5;
    		break;
    	case 'Z':
    		z_Angle -= 0.5;
    		break;

    	case 'C':
    	case 'c' :				// Clear viewing parameters and return to initial view.
    		init();
    		break;

    	case 'f':
    	case 'F':
    		if (ePrjn==	PERSPECTIVE)
    		{
    			if (radius>ZoomRadiusOffset)
    			{
    				radius-=ZoomRadiusOffset;
    			}
    		}
    		break;
    	case 'b':
    	case 'B':
    		if (ePrjn==	PERSPECTIVE)
    		{
    			radius+=ZoomRadiusOffset;
    		}
    		break;

    	case 'M' :
    	case 'm' :
    		showMenu();
    		break;
    	case 'w':
    	case 'W':
    		if (bWire == false)
    		{
    			bWire = true;	// Wireframe model.
    			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    		}
    		else
    		{
    			bWire = false;	// Solid model.
    			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    		}
    		break;

    	case 27 :				//ESCAPE Code for exiting program.
    		exit(1);
    		break;

    	}
    	glutPostRedisplay();	// Direct the glut system to redisplay the screen.
    }

    /****************************************************************************************/
    // This is the callback procedure for capturing special charater events.
    void specialCallbackProc (int key, int x, int y)
    {
    	switch (key)
    	{
    		case GLUT_KEY_LEFT:		// Rotate the model about the y-axis.
    			phi -= ViewAngleOffset;
    			break;

    		case GLUT_KEY_RIGHT:
    			phi += ViewAngleOffset;
    			break;

    		case GLUT_KEY_UP :		// Rotate the model about the x-axis.
    			theta -= ViewAngleOffset;
    			break;

    		case GLUT_KEY_DOWN :
    			theta += ViewAngleOffset;
    			break;
    	}
    	glutPostRedisplay();
    }

    /****************************************************************************************/
    void reShapeCallbackProc(int w, int h)
    						// This is the callback procedure for capturing reShape event for window resizing.
    {
    	glViewport(0, 0, w, h);
    						// Set the viewport to current window size.
    	W = w;
    	H = h;

    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	if(ePrjn == PERSPECTIVE)
    	{
    		gluPerspective(fovy, (GLfloat)W/(GLfloat)H, NEAR_Z, FAR_Z);
    	}
    	else
    	{
    		if (w > h)
    		{
    			W = (fViewVol * w) / h;
    			H = fViewVol;
    		}
    		else
    		{
    			W = fViewVol;
    			H = (fViewVol * h) / w;
    		}
    		glOrtho(-W, W, -H, H, NEAR_Z, FAR_Z);
    	}					// Change the view volume to maintain the aspect ratio wrt to viewport.

    	glMatrixMode(GL_MODELVIEW);

    	glutPostRedisplay();
    }

    /****************************************************************************************/
    void drawSphere(float fRadius, GLint slices, GLint rings)
    							// Used to generate a Sphere shape.
    {
    	GLUquadricObj* pObj;
    	pObj =  gluNewQuadric();
    							// Creates a new quadrics object and returns a pointer to it.

    	gluQuadricDrawStyle(pObj, GLU_FILL);

    	gluSphere(pObj, fRadius,slices, rings);
    							// Draw the sphere with a radius : fRadius.
    	gluDeleteQuadric(pObj);
    							// Free the Quadric

    }

    /****************************************************************************************/
    // Used to generate a cylinder shape.
    void drawCylinder(float fTopR, float fBottomR, float fHeight)
    {
    	GLUquadricObj* pObj;
    							// To keep the original texture intact we need to set the current color to WHITE.

    	pObj = gluNewQuadric();
    							// Creates a new quadrics object and returns a pointer to it.
    	gluQuadricDrawStyle(pObj, GLU_FILL);

    	gluCylinder(pObj, fTopR, fBottomR, fHeight, 20, 20);
    							// Draw the cylinder with a radius : fRadius.
    	gluDeleteQuadric(pObj);
    							// Free the Quadric

    }

    /****************************************************************************************/

    // Used to generate a cylinder shape.
    void drawDisk(GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint rings,
    GLdouble startAngle, GLdouble sweepAngle)
    {
    	GLUquadricObj* pObj;

    	pObj = gluNewQuadric();
    							// Creates a new quadrics object and returns a pointer to it.
    	gluQuadricDrawStyle(pObj, GLU_FILL);

    	gluPartialDisk(pObj, innerRadius, outerRadius, slices, rings, startAngle, sweepAngle);
    						// Draw the cylinder with a radius : fRadius.
    	gluDeleteQuadric(pObj);
    							// Free the Quadric
    }

    /****************************************************************************************/
    void showReferenceAxis(void)
    						// Draw the reference axis
    {
    	if(bAxis)
    	{
    	 glPushAttrib(GL_ALL_ATTRIB_BITS);
    						// Pushes attributes like current color to attribute stack.
    	 glBegin(GL_LINES);
    						// X axis red
          glColor3f(1, 0, 0);
          glVertex3f(0, 0, 0);
          glVertex3f(DefaultAxisLength, 0, 0);
    						// Y axis green
          glColor3f(0, 1, 0);
          glVertex3f(0, 0, 0);
          glVertex3f(0, DefaultAxisLength, 0);
    						// Z axis blue
          glColor3f(0, 0, 1);
          glVertex3f(0, 0, 0);
          glVertex3f(0, 0, DefaultAxisLength);
    	  glEnd();
    	  glPopAttrib();
    						// // Pops attributes like current color from the attribute stack and sets as current.
    	}
    }

    void drawHalfOval(GLfloat x, GLfloat y, GLfloat z)
    {
    	GLdouble xz_clip[4]={0, 1, 0, 0};
    	glPushMatrix();
    		glClipPlane (GL_CLIP_PLANE0, xz_clip);
    		glEnable (GL_CLIP_PLANE0);
    		glScalef(x, y, z);
    		glutSolidSphere(1.0, 20, 20);
    		glDisable(GL_CLIP_PLANE0);
    	glPopMatrix();
    }

    void divider_x(void(*pfunc)(), GLfloat start, GLfloat end, GLint num)
    {
    	GLdouble left_clip[4], right_clip[4];
    	GLfloat step=(end-start)/num;

    	glPushMatrix();
    		left_clip[0]=1;
    		left_clip[1]=left_clip[2]=right_clip[1]=right_clip[2]=0;
    		left_clip[3]=-start;
    		right_clip[0]=-1;
    		right_clip[3]=start+step;

    		glEnable(GL_CLIP_PLANE4);
    		glEnable(GL_CLIP_PLANE5);
    		do
    		{
    			setColor();
    			glClipPlane(GL_CLIP_PLANE4, left_clip);
    			glClipPlane(GL_CLIP_PLANE5, right_clip);
    			pfunc();
    			left_clip[3]+=step;
    			right_clip[3]+=step;
    		}
    		while(right_clip[3]<end);
    		glDisable(GL_CLIP_PLANE4);
    		glDisable(GL_CLIP_PLANE5);
    	glPopMatrix();
    }

    void divider_y(void(*pfunc)(), GLfloat down, GLfloat up, GLint num)
    {
    	GLdouble up_clip[4], down_clip[4];
    	GLfloat step=(up-down)/num;

    	glPushMatrix();
    		down_clip[1]=1;
    		down_clip[0]=down_clip[2]=up_clip[0]=up_clip[2]=0;
    		down_clip[3]=-down;
    		up_clip[1]=-1;
    		up_clip[3]=down+step;

    		glEnable(GL_CLIP_PLANE2);
    		glEnable(GL_CLIP_PLANE3);
    		do
    		{
    			setColor();
    			glClipPlane(GL_CLIP_PLANE2, up_clip);
    			glClipPlane(GL_CLIP_PLANE3, down_clip);
    			pfunc();
    			up_clip[3]+=step;
    			down_clip[3]+=step;
    		}
    		while(up_clip[3]<up);
    		glDisable(GL_CLIP_PLANE2);
    		glDisable(GL_CLIP_PLANE3);
    	glPopMatrix();
    }

    void drawWindow()
    {
    	GLdouble front_clip[4]={-1, 0, 0, CockpitWidth*Front_Rear_Ratio};

    	glPushMatrix();

    			setColor();
    			glClipPlane (GL_CLIP_PLANE1, front_clip);
    			glEnable (GL_CLIP_PLANE1);
    			drawHalfOval(CockpitWidth, CockpitHeight, 1.0);
    			glDisable(GL_CLIP_PLANE1);

    	glPopMatrix();
    }

    //draw the cockpit of helicopter
    void drawCockpit()
    {
    	void drawWindow();

    	GLdouble rear_clip[4]={1, 0, 0, -CockpitWidth*Front_Rear_Ratio};

    	glPushMatrix();
    		glPushMatrix();
    			//draw window part

    			glClipPlane (GL_CLIP_PLANE1, rear_clip);
    			glEnable (GL_CLIP_PLANE1);
    			//draw upper-rear part of cockpit
    			setColor();

    			//front_clip[3]=CockpitWidth*0.8;
    			drawHalfOval(CockpitWidth, CockpitHeight, 1.0);
    			glDisable(GL_CLIP_PLANE1);
    			divider_x(drawWindow, -CockpitWidth, CockpitWidth*Front_Rear_Ratio, 3);
    			//drawWindow();

    		glPopMatrix();

    		//draw lower part of cockpit
    		glRotatef(180, 0, 0, 1);
    		setColor();
    		drawHalfOval(CockpitWidth, 0.5*CockpitHeight, 1.0);
    	glPopMatrix();
    }

    void drawOval(GLfloat x, GLfloat y, GLfloat z)
    {
    	glPushMatrix();
    	//setColor();
    	glScalef(x, y, z);
    	glutSolidSphere(1.0, 20, 20);
    	glPopMatrix();
    }

    void drawBlade()
    {
    	const GLfloat BladeWidth=0.1;
    	const GLfloat BladeHeight=0.01;
    	const GLfloat BladeLength=1.0;//always 1.0
    	glPushMatrix();
    		//setColor();
    		glTranslatef(0, 0, BladeLength);
    		drawOval(BladeWidth, BladeHeight, BladeLength);
    	glPopMatrix();
    }

    void drawMainRotor()
    {
    	const GLfloat MainRotorAxisHeight=0.2;
    	const GLfloat MainRotorAxisRadius=0.05;

    	glPushMatrix();
    		setColor();
    		glPushMatrix();
    			//draw rotor axis
    			glRotatef(-90, 1,0,0);
    			drawCylinder(MainRotorAxisRadius, MainRotorAxisRadius, MainRotorAxisHeight);
    		glPopMatrix();
    			setColor();
    			glTranslatef(0, MainRotorAxisHeight, 0);
    			drawRotor();

    	glPopMatrix();
    }

    void drawShearCylinder(GLfloat topRad, GLfloat bottomRad, GLfloat height, GLfloat angle)
    {
    	//by default the angle is 45
    	GLfloat matrix[16]=
    	{
    		1,0,0,0,
    		1,1,0,0,
    		0,0,1,0,
    		0,0,0,1
    	};
    	//matrix[4]=cos(angle);
    	//setColor();
    	glPushMatrix();
    		glMultMatrixf(matrix);
    		glRotatef(-90, 1,0,0);
    		glScalef(bottomRad, bottomRad, height);
    		drawCylinder(topRad, bottomRad, height);

    	glPopMatrix();
    }

    void drawSkidSupport(const GLfloat SkidSupportDistance, const GLfloat SkidSupportHeight)
    {

    	const GLfloat SkidSupportRadius=0.3;

    	glPushMatrix();

    		glPushMatrix();
    			glTranslatef(-SkidSupportDistance/2.0, 0, SkidSupportDistance/2.0);
    			//glRotatef(-90, 1,0,0);
    			drawShearCylinder(SkidSupportRadius, SkidSupportRadius, SkidSupportHeight, 45);
    		glPopMatrix();
    		glPushMatrix();
    			glTranslatef(SkidSupportDistance/2.0, 0, SkidSupportDistance/2.0);
    			drawShearCylinder(SkidSupportRadius, SkidSupportRadius, SkidSupportHeight, 45);
    		glPopMatrix();
    		glPushMatrix();
    			glTranslatef(SkidSupportDistance/2.0, 0, -SkidSupportDistance/2.0);
    			drawShearCylinder(SkidSupportRadius, SkidSupportRadius, SkidSupportHeight, 45);
    		glPopMatrix();
    			glTranslatef(-SkidSupportDistance/2.0, 0, -SkidSupportDistance/2.0);
    			drawShearCylinder(SkidSupportRadius, SkidSupportRadius, SkidSupportHeight, 45);
    	glPopMatrix();
    }

    void drawSkidBottom(const GLfloat SkidRadius, const GLfloat SkidJointOffset,
    					const GLfloat SkidSupportDistance)
    {
    	const GLfloat ratio=1;
    	GLfloat SkidLength=SkidSupportDistance+SkidJointOffset*2;

    	glPushMatrix();
    		setColor();
    		glRotatef(90, 0, 1,0);
    		glScalef(ratio*1.7, ratio, 1);
    		drawCylinder(SkidRadius/ratio, SkidRadius/ratio, SkidLength);
    	glPopMatrix();
    }

    void drawBoom()
    {
    	const GLfloat BoomJointRadius=BoomLength/3;
    	const GLfloat BoomTopRadius=BoomLength/10;
    	const GLfloat BoomBaseHeight=1;
    	const GLfloat BoomBaseRadius=BoomLength/2;
    	glPushMatrix();
    		setColor();

    		glPushMatrix();
    			glRotatef(90, 0,1,0);
    			drawCylinder(BoomJointRadius, BoomTopRadius, BoomLength);
    		glPopMatrix();
    			setColor();
    			glTranslatef(-BoomBaseHeight, 0, 0);
    			glRotatef(90, 0, 1, 0);
    			drawCylinder(BoomBaseRadius, BoomJointRadius, BoomBaseHeight);
    	glPopMatrix();
    }

    void drawTailRotor()
    {
    	const GLfloat ratio=0.4;
    	glPushMatrix();
    		//setColor();
    		glScalef(ratio,ratio,ratio);
    		drawMainRotor();

    	glPopMatrix();
    }
    void drawTailBalancer()
    {
    	const GLfloat ratio=0.2;
    	glPushMatrix();
    		glTranslatef(BoomLength*ratio+TailRotorSupportOffset,
    			TailRotorSupportRadius-0.1, 0);
    		glScalef(4, 4, 0.1);
    		drawShearCylinder(TailRotorSupportRadius, TailRotorSupportRadius*0.5,
    			TailRotorSupportRadius, 45);
    	glPopMatrix();
    }

    void drawTailBoom()
    {
    	//these helper functions will only be used by this function
    	void drawBoom();
    	void drawTailRotor();
    	void drawTailBalancer();

    	const GLfloat ratio=0.2;
    	glPushMatrix();
    		glPushMatrix();
    			glScalef(ratio, ratio,ratio);
    			divider_x(drawBoom, 0, BoomLength, 36);
    			//drawBoom();
    		glPopMatrix();
    		glPushMatrix();
    			//draw the tail
    			glTranslatef(BoomLength*ratio+TailRotorSupportOffset, 0, 0);
    			setColor();
    			glutSolidSphere(TailRotorSupportRadius, 20, 20);
    		glPopMatrix();

    		glPushMatrix();

    			glTranslatef(BoomLength*ratio+TailRotorSupportOffset,
    				0, TailRotorSupportRadius);
    			glScalef(1.3, 0.8, 0.6);
    			glRotatef(90, 1, 0, 0);
    			drawTailRotor();
    		glPopMatrix();
    		drawTailBalancer();

    	glPopMatrix();
    }

    void drawPatialTorus(GLfloat rCrossSection, GLfloat rAxial, GLfloat sweepAngle)
    {
    	GLdouble clipPlane0[4]={1,0,0,0};//y=0 plane
    	GLdouble clipPlane1[4]={tan(sweepAngle), 1, 0, 0}; //y=x*tan(angle) plane

    	glPushMatrix();
    		//setColor();
    		glRotatef(180, 0, 0,1);
    		glTranslatef(0, -rAxial, 0);
    		glClipPlane(GL_CLIP_PLANE0, clipPlane0);
    		glClipPlane(GL_CLIP_PLANE1, clipPlane1);
    		glEnable(GL_CLIP_PLANE0);
    		glEnable(GL_CLIP_PLANE1);
    		glutSolidTorus(rCrossSection, rAxial, 40, 40);
    		glDisable(GL_CLIP_PLANE0);
    		glDisable(GL_CLIP_PLANE1);
    	glPopMatrix();
    }

    void drawSkid()
    {
    	//helper function only be called inside this
    	void drawSkidBottom(const GLfloat SkidRadius, const GLfloat SkidJointOffset,
    					const GLfloat SkidSupportDistance);

    	glPushMatrix();

    		drawSkidSupport(SkidSupportDistance, SkidSupportHeight);
    		glPushMatrix();
    			glTranslatef(-SkidSupportDistance/2.0-SkidJointOffset,
    					-SkidRadius, SkidSupportDistance/2.0);
    			drawSkidBottom(SkidRadius, SkidJointOffset, SkidSupportDistance);
    		glPopMatrix();
    		glPushMatrix();
    			glTranslatef(-SkidSupportDistance/2.0-SkidJointOffset,
    					-SkidRadius, -SkidSupportDistance/2.0);
    			drawSkidBottom(SkidRadius, SkidJointOffset, SkidSupportDistance);
    		glPopMatrix();
    		glPushMatrix();
    			glTranslatef(-SkidSupportDistance+0.2, -SkidRadius, -SkidSupportDistance/2.0);
    			drawPatialTorus(SkidRadius+0.02, 2.3, 40);
    		glPopMatrix();
    			glTranslatef(-SkidSupportDistance+0.2, -SkidRadius, SkidSupportDistance/2.0);
    			drawPatialTorus(SkidRadius+0.02, 2.3, 40);
    	glPopMatrix();

    }

    void drawRotor()
    {
    	void drawBlade();

    	glPushMatrix();
    		drawBlade();
    		glRotatef(120, 0, 1, 0);
    		drawBlade();
    		glRotatef(120, 0, 1, 0);
    		drawBlade();
    	glPopMatrix();
    }

    //I don't want other application accidentally have a name conflict
    //with my helper function, so I declare those function inside its calling
    //function
    void drawHelicopter()
    {
    	//the major module of helicopter
    	void drawCockpit();
    	void drawTailBoom();
    	void drawMainRotor();
    	void drawSkid();

    	curColorIndex=ColorStartIndex;
    	glPushMatrix();
    		glPushMatrix();
    			//glRotatef(180, 0, 1, 0);
    			drawCockpit();
    		glPopMatrix();

    		glPushMatrix();
    			glTranslatef(CockpitWidth-0.08, CockpitHeight/5, 0);
    			//glRotatef(180, 0, 1, 0);
    			glScalef(0.8, 0.4, 0.4);
    			drawTailBoom();
    		glPopMatrix();

    		glPushMatrix();
    			glTranslatef(0, CockpitHeight, 0 );
    			drawMainRotor();
    		glPopMatrix();
    		glPushMatrix();
    			glTranslatef(0, -SkidSupportHeight, 0);
    			glScalef(0.6, 1, 0.5);
    			drawSkid();
    		glPopMatrix();

    	glPopMatrix();

    }

    /****************************************************************************************/
    void displayCallbackProc (void)
    						// This is the callback procedure for capturing OpenGL Display events.
    						// All the 'happening' things happen here :)
    {
    	GLfloat eye_x, eye_y, eye_z;

    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glColor3f(1.0, 1.0, 1.0);

    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();

    	//glTranslatef(0, 0, -20.0);	// Translate the object by 20 units in -ve z-direction.
    	//glScalef(4, 4, 4);

    /************************************************************
    The view transformation
    *************************************************************/
    	//theta_rad=deg2rad(theta);
    	//phi_rad=deg2rad(phi);
    	eye_x=radius*sin(theta)*sin(phi);
    	eye_y=radius*cos(theta);
    	eye_z=radius*sin(theta)*cos(phi);

    	//glPushMatrix();

    	gluLookAt(eye_x, eye_y, eye_z, 0, 0, 0, 0, 1, 0);

    	//the following sequence is HIGHLY important because
    	//you want the object to rotate w.r.t. to original world
    	//origin and you need to transform it between world coordinate and
    	//view-coordinate
    	glTranslatef(-eye_x, -eye_y, -eye_z);
    	showReferenceAxis();
    	glScalef(2, 2, 2);

    	glRotatef(x_Angle, 1.0, 0.0, 0.0);	// Rotate the object by x_Angle about x-axis
    	glRotatef(y_Angle, 0.0, 1.0, 0.0);	// Rotate the object by y_Angle about y-axis
    	glRotatef(z_Angle, 0.0, 0.0, 1.0);	// Rotate the object by z_Angle about z-axis

    	drawHelicopter();

    	glTranslatef(eye_x, eye_y, eye_z);

    	glutSwapBuffers();	// Use of double buffering to avoid flicker.
    }

    /****************************************************************************************/
    int main (int argc, char *argv[])
    						// The main program.
    {

    	/* All customary glut env initializations. */
    	glutInit(&argc, argv);
    	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    	glutInitWindowSize(SIZE, SIZE);
    	glutInitWindowPosition(100, 100);
    	glutCreateWindow("A primitive model of a bike ! ~RG");

    	/* glut env initializations done. */

    	init();				// Initialize the openGL env. variables and the application global variables.

    	/* Callback registrations with the OpenGL env are done here */

    	glutDisplayFunc(displayCallbackProc);
    	glutKeyboardFunc(keyboardCallbackProc);
    	glutSpecialFunc(specialCallbackProc);
    	glutReshapeFunc(reShapeCallbackProc);

    	/* Callback registrations done.*/

    	glutMainLoop();
    						// Inside glutMainLoop(); all the mouse/KB events pertaining to the
    						// application window are dispatched. This loop is never exited so the
    						// statements after glutMainLoop(); are never executed !
    	return 1;
    }

    /*************************** END OF PROGRAM ************************************************/